From 80ac1331b545d993aa7c205dc24f8b20a4d6d44e Mon Sep 17 00:00:00 2001 From: David Marcec Date: Mon, 17 Aug 2020 01:23:55 +1000 Subject: Preliminary effects --- src/audio_core/command_generator.cpp | 187 ++++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 4 deletions(-) (limited to 'src/audio_core/command_generator.cpp') diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 73608c9ed..84782cde6 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp @@ -4,6 +4,7 @@ #include "audio_core/algorithm/interpolate.h" #include "audio_core/command_generator.h" +#include "audio_core/effect_context.h" #include "audio_core/mix_context.h" #include "audio_core/voice_context.h" #include "core/memory.h" @@ -68,9 +69,10 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) { CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params, VoiceContext& voice_context, MixContext& mix_context, - SplitterContext& splitter_context, Core::Memory::Memory& memory) + SplitterContext& splitter_context, EffectContext& effect_context, + Core::Memory::Memory& memory) : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context), - splitter_context(splitter_context), memory(memory), + splitter_context(splitter_context), effect_context(effect_context), memory(memory), mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) * worker_params.sample_count), sample_buffer(MIX_BUFFER_SIZE), @@ -338,6 +340,120 @@ void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_ } } +void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) { + const std::size_t effect_count = effect_context.GetCount(); + const auto buffer_offset = mix_info.GetInParams().buffer_offset; + for (std::size_t i = 0; i < effect_count; i++) { + const auto index = mix_info.GetEffectOrder(i); + if (index == AudioCommon::NO_EFFECT_ORDER) { + break; + } + auto* info = effect_context.GetInfo(index); + const auto type = info->GetType(); + + // TODO(ogniK): Finish remaining effects + switch (type) { + case EffectType::Aux: + GenerateAuxCommand(buffer_offset, info, info->IsEnabled()); + break; + case EffectType::I3dl2Reverb: + GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled()); + break; + case EffectType::BiquadFilter: + GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled()); + break; + default: + break; + } + + info->UpdateForCommandGeneration(); + } +} + +void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, + bool enabled) { + if (!enabled) { + return; + } + const auto& params = dynamic_cast(info)->GetParams(); + const auto channel_count = params.channel_count; + for (s32 i = 0; i < channel_count; i++) { + // TODO(ogniK): Actually implement reverb + if (params.input[i] != params.output[i]) { + const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); + auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); + ApplyMix<1>(output, input, 32768, worker_params.sample_count); + } + } +} + +void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, + bool enabled) { + if (!enabled) { + return; + } + const auto& params = dynamic_cast(info)->GetParams(); + const auto channel_count = params.channel_count; + for (s32 i = 0; i < channel_count; i++) { + // TODO(ogniK): Actually implement biquad filter + if (params.input[i] != params.output[i]) { + const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); + auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); + ApplyMix<1>(output, input, 32768, worker_params.sample_count); + } + } +} + +void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) { + auto aux = dynamic_cast(info); + const auto& params = aux->GetParams(); + if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) { + const auto max_channels = params.count; + u32 offset{}; + for (u32 channel = 0; channel < max_channels; channel++) { + u32 write_count = 0; + if (channel == (max_channels - 1)) { + write_count = offset + worker_params.sample_count; + } + + const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset; + const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset; + + if (enabled) { + AuxInfoDSP send_info{}; + AuxInfoDSP recv_info{}; + memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP)); + memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP)); + + WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count, + GetMixBuffer(input_index), worker_params.sample_count, offset, + write_count); + memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP)); + + const auto samples_read = ReadAuxBuffer( + recv_info, aux->GetRecvBuffer(), params.sample_count, + GetMixBuffer(output_index), worker_params.sample_count, offset, write_count); + memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP)); + + if (samples_read != worker_params.sample_count && + samples_read <= params.sample_count) { + std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read); + } + } else { + AuxInfoDSP empty{}; + memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP)); + memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP)); + if (output_index != input_index) { + std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index), + worker_params.sample_count * sizeof(s32)); + } + } + + offset += worker_params.sample_count; + } + } +} + ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) { if (splitter_id == AudioCommon::NO_SPLITTER) { return nullptr; @@ -345,6 +461,66 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter return splitter_context.GetDestinationData(splitter_id, index); } +s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, + const s32* data, u32 sample_count, u32 write_offset, + u32 write_count) { + if (max_samples == 0) { + return 0; + } + u32 offset = dsp_info.write_offset + write_offset; + if (send_buffer == 0 || offset > max_samples) { + return 0; + } + + std::size_t data_offset{}; + u32 remaining = sample_count; + while (remaining > 0) { + // Get position in buffer + const auto base = send_buffer + (offset * sizeof(u32)); + const auto samples_to_grab = std::min(max_samples - offset, remaining); + // Write to output + memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32)); + offset = (offset + samples_to_grab) % max_samples; + remaining -= samples_to_grab; + data_offset += samples_to_grab; + } + + if (write_count != 0) { + dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples; + } + return sample_count; +} + +s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, + s32* out_data, u32 sample_count, u32 read_offset, + u32 read_count) { + if (max_samples == 0) { + return 0; + } + + u32 offset = recv_info.read_offset + read_offset; + if (recv_buffer == 0 || offset > max_samples) { + return 0; + } + + u32 remaining = sample_count; + while (remaining > 0) { + const auto base = recv_buffer + (offset * sizeof(u32)); + const auto samples_to_grab = std::min(max_samples - offset, remaining); + std::vector buffer(samples_to_grab); + memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32)); + std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32)); + out_data += samples_to_grab; + offset = (offset + samples_to_grab) % max_samples; + remaining -= samples_to_grab; + } + + if (read_count != 0) { + recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples; + } + return sample_count; +} + void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume, s32 channel, s32 node_id) { const auto last = static_cast(last_volume * 32768.0f); @@ -398,7 +574,9 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) { auto& in_params = mix_info.GetInParams(); GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, in_params.sample_rate); - // TODO(ogniK): Effects + + GenerateEffectCommand(mix_info); + GenerateMixCommands(mix_info); } @@ -476,7 +654,8 @@ void CommandGenerator::GenerateFinalMixCommand() { GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, in_params.sample_rate); - // TODO(ogniK): Effects + + GenerateEffectCommand(mix_info); for (s32 i = 0; i < in_params.buffer_count; i++) { const s32 gain = static_cast(in_params.volume * 32768.0f); -- cgit v1.2.3