mirror of
https://github.com/yuzu-emu/yuzu-android.git
synced 2025-06-20 04:17:54 -05:00
Project Andio
This commit is contained in:
24
src/audio_core/renderer/command/mix/clear_mix.cpp
Normal file
24
src/audio_core/renderer/command/mix/clear_mix.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/clear_mix.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
void ClearMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("ClearMixBufferCommand\n");
|
||||
}
|
||||
|
||||
void ClearMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes());
|
||||
}
|
||||
|
||||
bool ClearMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
45
src/audio_core/renderer/command/mix/clear_mix.h
Normal file
45
src/audio_core/renderer/command/mix/clear_mix.h
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for a clearing the mix buffers.
|
||||
* Used at the start of each command list.
|
||||
*/
|
||||
struct ClearMixBufferCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
27
src/audio_core/renderer/command/mix/copy_mix.cpp
Normal file
27
src/audio_core/renderer/command/mix/copy_mix.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/copy_mix.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
void CopyMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index,
|
||||
output_index);
|
||||
}
|
||||
|
||||
void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32));
|
||||
}
|
||||
|
||||
bool CopyMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
49
src/audio_core/renderer/command/mix/copy_mix.h
Normal file
49
src/audio_core/renderer/command/mix/copy_mix.h
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for a copying a mix buffer from input to output.
|
||||
*/
|
||||
struct CopyMixBufferCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Input mix buffer index
|
||||
s16 input_index;
|
||||
/// Output mix buffer index
|
||||
s16 output_index;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/common/common.h"
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/depop_for_mix_buffers.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
/**
|
||||
* Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time
|
||||
* according to decay.
|
||||
*
|
||||
* @param output - Output buffer to be depopped.
|
||||
* @param depop_sample - Depopped sample to apply to output samples.
|
||||
* @param decay_ - Amount to decay the depopped sample for every output sample.
|
||||
* @param sample_count - Samples to process.
|
||||
* @return Final decayed depop sample.
|
||||
*/
|
||||
static s32 ApplyDepopMix(std::span<s32> output, const s32 depop_sample,
|
||||
Common::FixedPoint<49, 15>& decay_, const u32 sample_count) {
|
||||
auto sample{std::abs(depop_sample)};
|
||||
auto decay{decay_.to_raw()};
|
||||
|
||||
if (depop_sample <= 0) {
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
sample = static_cast<s32>((static_cast<s64>(sample) * decay) >> 15);
|
||||
output[i] -= sample;
|
||||
}
|
||||
return -sample;
|
||||
} else {
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
sample = static_cast<s32>((static_cast<s64>(sample) * decay) >> 15);
|
||||
output[i] += sample;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
||||
void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input,
|
||||
count, decay.to_float());
|
||||
}
|
||||
|
||||
void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto end_index{std::min(processor.buffer_count, input + count)};
|
||||
std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index};
|
||||
|
||||
for (u32 index = input; index < end_index; index++) {
|
||||
const auto depop_sample{depop_buff[index]};
|
||||
if (depop_sample != 0) {
|
||||
auto input_buffer{processor.mix_buffers.subspan(index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
depop_buff[index] =
|
||||
ApplyDepopMix(input_buffer, depop_sample, decay, processor.sample_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DepopForMixBuffersCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
55
src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
Normal file
55
src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
Normal file
@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/fixed_point.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for depopping a mix buffer.
|
||||
* Adds a cumulation of previous samples to the current mix buffer with a decay.
|
||||
*/
|
||||
struct DepopForMixBuffersCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Starting input mix buffer index
|
||||
u32 input;
|
||||
/// Number of mix buffers to depop
|
||||
u32 count;
|
||||
/// Amount to decay the depop sample for each new sample
|
||||
Common::FixedPoint<49, 15> decay;
|
||||
/// Address of the depop buffer, holding the last sample for every mix buffer
|
||||
CpuAddr depop_buffer;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
36
src/audio_core/renderer/command/mix/depop_prepare.cpp
Normal file
36
src/audio_core/renderer/command/mix/depop_prepare.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/depop_prepare.h"
|
||||
#include "audio_core/renderer/voice/voice_state.h"
|
||||
#include "common/fixed_point.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("DepopPrepareCommand\n\tinputs: ");
|
||||
for (u32 i = 0; i < buffer_count; i++) {
|
||||
string += fmt::format("{:02X}, ", inputs[i]);
|
||||
}
|
||||
string += "\n";
|
||||
}
|
||||
|
||||
void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto samples{reinterpret_cast<s32*>(previous_samples)};
|
||||
auto buffer{std::span(reinterpret_cast<s32*>(depop_buffer), buffer_count)};
|
||||
|
||||
for (u32 i = 0; i < buffer_count; i++) {
|
||||
if (samples[i]) {
|
||||
buffer[inputs[i]] += samples[i];
|
||||
samples[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DepopPrepareCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
54
src/audio_core/renderer/command/mix/depop_prepare.h
Normal file
54
src/audio_core/renderer/command/mix/depop_prepare.h
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for preparing depop.
|
||||
* Adds the previusly output last samples to the depop buffer.
|
||||
*/
|
||||
struct DepopPrepareCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Depop buffer offset for each mix buffer
|
||||
std::array<s16, MaxMixBuffers> inputs;
|
||||
/// Pointer to the previous mix buffer samples
|
||||
CpuAddr previous_samples;
|
||||
/// Number of mix buffers to use
|
||||
u32 buffer_count;
|
||||
/// Pointer to the current depop values
|
||||
CpuAddr depop_buffer;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
70
src/audio_core/renderer/command/mix/mix.cpp
Normal file
70
src/audio_core/renderer/command/mix/mix.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/mix.h"
|
||||
#include "common/fixed_point.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
/**
|
||||
* Mix input mix buffer into output mix buffer, with volume applied to the input.
|
||||
*
|
||||
* @tparam Q - Number of bits for fixed point operations.
|
||||
* @param output - Output mix buffer.
|
||||
* @param input - Input mix buffer.
|
||||
* @param volume - Volume applied to the input.
|
||||
* @param sample_count - Number of samples to process.
|
||||
*/
|
||||
template <size_t Q>
|
||||
static void ApplyMix(std::span<s32> output, std::span<const s32> input, const f32 volume_,
|
||||
const u32 sample_count) {
|
||||
const Common::FixedPoint<64 - Q, Q> volume{volume_};
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
output[i] = (output[i] + input[i] * volume).to_int();
|
||||
}
|
||||
}
|
||||
|
||||
void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("MixCommand");
|
||||
string += fmt::format("\n\tinput {:02X}", input_index);
|
||||
string += fmt::format("\n\toutput {:02X}", output_index);
|
||||
string += fmt::format("\n\tvolume {:.8f}", volume);
|
||||
string += "\n";
|
||||
}
|
||||
|
||||
void MixCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
|
||||
// If volume is 0, nothing will be added to the output, so just skip.
|
||||
if (volume == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (precision) {
|
||||
case 15:
|
||||
ApplyMix<15>(output, input, volume, processor.sample_count);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
ApplyMix<23>(output, input, volume, processor.sample_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MixCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
54
src/audio_core/renderer/command/mix/mix.h
Normal file
54
src/audio_core/renderer/command/mix/mix.h
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume
|
||||
* applied to the input.
|
||||
*/
|
||||
struct MixCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Fixed point precision
|
||||
u8 precision;
|
||||
/// Input mix buffer index
|
||||
s16 input_index;
|
||||
/// Output mix buffer index
|
||||
s16 output_index;
|
||||
/// Mix volume applied to the input
|
||||
f32 volume;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
94
src/audio_core/renderer/command/mix/mix_ramp.cpp
Normal file
94
src/audio_core/renderer/command/mix/mix_ramp.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/mix_ramp.h"
|
||||
#include "common/fixed_point.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
/**
|
||||
* Mix input mix buffer into output mix buffer, with volume applied to the input.
|
||||
*
|
||||
* @tparam Q - Number of bits for fixed point operations.
|
||||
* @param output - Output mix buffer.
|
||||
* @param input - Input mix buffer.
|
||||
* @param volume - Volume applied to the input.
|
||||
* @param ramp - Ramp applied to volume every sample.
|
||||
* @param sample_count - Number of samples to process.
|
||||
* @return The final gained input sample, used for depopping.
|
||||
*/
|
||||
template <size_t Q>
|
||||
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
|
||||
const f32 ramp_, const u32 sample_count) {
|
||||
Common::FixedPoint<64 - Q, Q> volume{volume_};
|
||||
Common::FixedPoint<64 - Q, Q> sample{0};
|
||||
|
||||
if (ramp_ == 0.0f) {
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
sample = input[i] * volume;
|
||||
output[i] = (output[i] + sample).to_int();
|
||||
}
|
||||
} else {
|
||||
Common::FixedPoint<64 - Q, Q> ramp{ramp_};
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
sample = input[i] * volume;
|
||||
output[i] = (output[i] + sample).to_int();
|
||||
volume += ramp;
|
||||
}
|
||||
}
|
||||
return sample.to_int();
|
||||
}
|
||||
|
||||
template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, const f32, const f32,
|
||||
const u32);
|
||||
template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, const f32, const f32,
|
||||
const u32);
|
||||
|
||||
void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
|
||||
const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
|
||||
string += fmt::format("MixRampCommand");
|
||||
string += fmt::format("\n\tinput {:02X}", input_index);
|
||||
string += fmt::format("\n\toutput {:02X}", output_index);
|
||||
string += fmt::format("\n\tvolume {:.8f}", volume);
|
||||
string += fmt::format("\n\tprev_volume {:.8f}", prev_volume);
|
||||
string += fmt::format("\n\tramp {:.8f}", ramp);
|
||||
string += "\n";
|
||||
}
|
||||
|
||||
void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
|
||||
auto prev_sample_ptr{reinterpret_cast<s32*>(previous_sample)};
|
||||
|
||||
// If previous volume and ramp are both 0, nothing will be added to the output, so just skip.
|
||||
if (prev_volume == 0.0f && ramp == 0.0f) {
|
||||
*prev_sample_ptr = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (precision) {
|
||||
case 15:
|
||||
*prev_sample_ptr =
|
||||
ApplyMixRamp<15>(output, input, prev_volume, ramp, processor.sample_count);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
*prev_sample_ptr =
|
||||
ApplyMixRamp<23>(output, input, prev_volume, ramp, processor.sample_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MixRampCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
73
src/audio_core/renderer/command/mix/mix_ramp.h
Normal file
73
src/audio_core/renderer/command/mix/mix_ramp.h
Normal file
@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume
|
||||
* applied to the input, and volume ramping to smooth out the transition.
|
||||
*/
|
||||
struct MixRampCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Fixed point precision
|
||||
u8 precision;
|
||||
/// Input mix buffer index
|
||||
s16 input_index;
|
||||
/// Output mix buffer index
|
||||
s16 output_index;
|
||||
/// Previous mix volume
|
||||
f32 prev_volume;
|
||||
/// Current mix volume
|
||||
f32 volume;
|
||||
/// Pointer to the previous sample buffer, used for depopping
|
||||
CpuAddr previous_sample;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mix input mix buffer into output mix buffer, with volume applied to the input.
|
||||
* @tparam Q - Number of bits for fixed point operations.
|
||||
* @param output - Output mix buffer.
|
||||
* @param input - Input mix buffer.
|
||||
* @param volume - Volume applied to the input.
|
||||
* @param ramp - Ramp applied to volume every sample.
|
||||
* @param sample_count - Number of samples to process.
|
||||
* @return The final gained input sample, used for depopping.
|
||||
*/
|
||||
template <size_t Q>
|
||||
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
|
||||
const f32 ramp_, const u32 sample_count);
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
65
src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
Normal file
65
src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/mix_ramp.h"
|
||||
#include "audio_core/renderer/command/mix/mix_ramp_grouped.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
|
||||
string += "MixRampGroupedCommand";
|
||||
for (u32 i = 0; i < buffer_count; i++) {
|
||||
string += fmt::format("\n\t{}", i);
|
||||
const auto ramp{(volumes[i] - prev_volumes[i]) / static_cast<f32>(processor.sample_count)};
|
||||
string += fmt::format("\n\t\tinput {:02X}", inputs[i]);
|
||||
string += fmt::format("\n\t\toutput {:02X}", outputs[i]);
|
||||
string += fmt::format("\n\t\tvolume {:.8f}", volumes[i]);
|
||||
string += fmt::format("\n\t\tprev_volume {:.8f}", prev_volumes[i]);
|
||||
string += fmt::format("\n\t\tramp {:.8f}", ramp);
|
||||
string += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers};
|
||||
|
||||
for (u32 i = 0; i < buffer_count; i++) {
|
||||
auto last_sample{0};
|
||||
if (prev_volumes[i] != 0.0f || volumes[i] != 0.0f) {
|
||||
const auto output{processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
const auto input{processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
const auto ramp{(volumes[i] - prev_volumes[i]) /
|
||||
static_cast<f32>(processor.sample_count)};
|
||||
|
||||
if (prev_volumes[i] == 0.0f && ramp == 0.0f) {
|
||||
prev_samples[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (precision) {
|
||||
case 15:
|
||||
last_sample =
|
||||
ApplyMixRamp<15>(output, input, prev_volumes[i], ramp, processor.sample_count);
|
||||
break;
|
||||
case 23:
|
||||
last_sample =
|
||||
ApplyMixRamp<23>(output, input, prev_volumes[i], ramp, processor.sample_count);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
prev_samples[i] = last_sample;
|
||||
}
|
||||
}
|
||||
|
||||
bool MixRampGroupedCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
61
src/audio_core/renderer/command/mix/mix_ramp_grouped.h
Normal file
61
src/audio_core/renderer/command/mix/mix_ramp_grouped.h
Normal file
@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with
|
||||
* a volume applied to the input, and volume ramping to smooth out the transition.
|
||||
*/
|
||||
struct MixRampGroupedCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Fixed point precision
|
||||
u8 precision;
|
||||
/// Number of mix buffers to mix
|
||||
u32 buffer_count;
|
||||
/// Input mix buffer indexes for each mix buffer
|
||||
std::array<s16, MaxMixBuffers> inputs;
|
||||
/// Output mix buffer indexes for each mix buffer
|
||||
std::array<s16, MaxMixBuffers> outputs;
|
||||
/// Previous mix vloumes for each mix buffer
|
||||
std::array<f32, MaxMixBuffers> prev_volumes;
|
||||
/// Current mix vloumes for each mix buffer
|
||||
std::array<f32, MaxMixBuffers> volumes;
|
||||
/// Pointer to the previous sample buffer, used for depop
|
||||
CpuAddr previous_samples;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
72
src/audio_core/renderer/command/mix/volume.cpp
Normal file
72
src/audio_core/renderer/command/mix/volume.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/volume.h"
|
||||
#include "common/fixed_point.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
/**
|
||||
* Apply volume to the input mix buffer, saving to the output buffer.
|
||||
*
|
||||
* @tparam Q - Number of bits for fixed point operations.
|
||||
* @param output - Output mix buffer.
|
||||
* @param input - Input mix buffer.
|
||||
* @param volume - Volume applied to the input.
|
||||
* @param sample_count - Number of samples to process.
|
||||
*/
|
||||
template <size_t Q>
|
||||
static void ApplyUniformGain(std::span<s32> output, std::span<const s32> input, const f32 volume,
|
||||
const u32 sample_count) {
|
||||
if (volume == 1.0f) {
|
||||
std::memcpy(output.data(), input.data(), input.size_bytes());
|
||||
} else {
|
||||
const Common::FixedPoint<64 - Q, Q> gain{volume};
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
output[i] = (input[i] * gain).to_int();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("VolumeCommand");
|
||||
string += fmt::format("\n\tinput {:02X}", input_index);
|
||||
string += fmt::format("\n\toutput {:02X}", output_index);
|
||||
string += fmt::format("\n\tvolume {:.8f}", volume);
|
||||
string += "\n";
|
||||
}
|
||||
|
||||
void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
// If input and output buffers are the same, and the volume is 1.0f, this won't do
|
||||
// anything, so just skip.
|
||||
if (input_index == output_index && volume == 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
|
||||
switch (precision) {
|
||||
case 15:
|
||||
ApplyUniformGain<15>(output, input, volume, processor.sample_count);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
ApplyUniformGain<23>(output, input, volume, processor.sample_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool VolumeCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
53
src/audio_core/renderer/command/mix/volume.h
Normal file
53
src/audio_core/renderer/command/mix/volume.h
Normal file
@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for applying volume to a mix buffer.
|
||||
*/
|
||||
struct VolumeCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Fixed point precision
|
||||
u8 precision;
|
||||
/// Input mix buffer index
|
||||
s16 input_index;
|
||||
/// Output mix buffer index
|
||||
s16 output_index;
|
||||
/// Mix volume applied to the input
|
||||
f32 volume;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
84
src/audio_core/renderer/command/mix/volume_ramp.cpp
Normal file
84
src/audio_core/renderer/command/mix/volume_ramp.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/mix/volume_ramp.h"
|
||||
#include "common/fixed_point.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
/**
|
||||
* Apply volume with ramping to the input mix buffer, saving to the output buffer.
|
||||
*
|
||||
* @tparam Q - Number of bits for fixed point operations.
|
||||
* @param output - Output mix buffers.
|
||||
* @param input - Input mix buffers.
|
||||
* @param volume - Volume applied to the input.
|
||||
* @param ramp - Ramp applied to volume every sample.
|
||||
* @param sample_count - Number of samples to process.
|
||||
*/
|
||||
template <size_t Q>
|
||||
static void ApplyLinearEnvelopeGain(std::span<s32> output, std::span<const s32> input,
|
||||
const f32 volume, const f32 ramp_, const u32 sample_count) {
|
||||
if (volume == 0.0f && ramp_ == 0.0f) {
|
||||
std::memset(output.data(), 0, output.size_bytes());
|
||||
} else if (volume == 1.0f && ramp_ == 0.0f) {
|
||||
std::memcpy(output.data(), input.data(), output.size_bytes());
|
||||
} else if (ramp_ == 0.0f) {
|
||||
const Common::FixedPoint<64 - Q, Q> gain{volume};
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
output[i] = (input[i] * gain).to_int();
|
||||
}
|
||||
} else {
|
||||
Common::FixedPoint<64 - Q, Q> gain{volume};
|
||||
const Common::FixedPoint<64 - Q, Q> ramp{ramp_};
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
output[i] = (input[i] * gain).to_int();
|
||||
gain += ramp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
|
||||
const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
|
||||
string += fmt::format("VolumeRampCommand");
|
||||
string += fmt::format("\n\tinput {:02X}", input_index);
|
||||
string += fmt::format("\n\toutput {:02X}", output_index);
|
||||
string += fmt::format("\n\tvolume {:.8f}", volume);
|
||||
string += fmt::format("\n\tprev_volume {:.8f}", prev_volume);
|
||||
string += fmt::format("\n\tramp {:.8f}", ramp);
|
||||
string += "\n";
|
||||
}
|
||||
|
||||
void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
|
||||
processor.sample_count)};
|
||||
const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
|
||||
|
||||
// If input and output buffers are the same, and the volume is 1.0f, and there's no ramping,
|
||||
// this won't do anything, so just skip.
|
||||
if (input_index == output_index && prev_volume == 1.0f && ramp == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (precision) {
|
||||
case 15:
|
||||
ApplyLinearEnvelopeGain<15>(output, input, prev_volume, ramp, processor.sample_count);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
ApplyLinearEnvelopeGain<23>(output, input, prev_volume, ramp, processor.sample_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool VolumeRampCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
56
src/audio_core/renderer/command/mix/volume_ramp.h
Normal file
56
src/audio_core/renderer/command/mix/volume_ramp.h
Normal file
@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth
|
||||
* out the transition.
|
||||
*/
|
||||
struct VolumeRampCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Fixed point precision
|
||||
u8 precision;
|
||||
/// Input mix buffer index
|
||||
s16 input_index;
|
||||
/// Output mix buffer index
|
||||
s16 output_index;
|
||||
/// Previous mix volume applied to the input
|
||||
f32 prev_volume;
|
||||
/// Current mix volume applied to the input
|
||||
f32 volume;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
Reference in New Issue
Block a user