diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 0c3b278ea..8f89292be 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp @@ -46,6 +46,24 @@ void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_c } } +s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) { + const bool positive = first_sample > 0; + auto final_sample = std::abs(first_sample); + for (s32 i = 0; i < sample_count; i++) { + final_sample = static_cast((static_cast(final_sample) * delta) >> 15); + if (positive) { + output[i] += final_sample; + } else { + output[i] -= final_sample; + } + } + if (positive) { + return final_sample; + } else { + return -final_sample; + } +} + } // namespace CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params, @@ -55,12 +73,15 @@ CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_p splitter_context(splitter_context), memory(memory), mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) * worker_params.sample_count), - sample_buffer(MIX_BUFFER_SIZE) {} + sample_buffer(MIX_BUFFER_SIZE), + depop_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) * + worker_params.sample_count) {} CommandGenerator::~CommandGenerator() = default; void CommandGenerator::ClearMixBuffers() { std::fill(mix_buffer.begin(), mix_buffer.end(), 0); std::fill(sample_buffer.begin(), sample_buffer.end(), 0); + // std::fill(depop_buffer.begin(), depop_buffer.end(), 0); } void CommandGenerator::GenerateVoiceCommands() { @@ -195,32 +216,39 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo auto& in_params = voice_info.GetInParams(); const auto depop = in_params.should_depop; - if (in_params.mix_id != AudioCommon::NO_MIX) { - [[maybe_unused]] auto& mix_info = mix_context.GetInfo(in_params.mix_id); - // mix_info. - // TODO(ogniK): Depop to destination mix - } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) { - // TODO(ogniK): Depop to splitter - } - if (depop) { - return; - } - - switch (in_params.sample_format) { - case SampleFormat::Pcm16: - DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel, - worker_params.sample_rate, worker_params.sample_count, - in_params.node_id); - break; - case SampleFormat::Adpcm: - ASSERT(channel == 0 && in_params.channel_count == 1); - DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(0), dsp_state, 0, - worker_params.sample_rate, worker_params.sample_count, - in_params.node_id); - break; - default: - UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); + if (in_params.mix_id != AudioCommon::NO_MIX) { + auto& mix_info = mix_context.GetInfo(in_params.mix_id); + const auto& mix_in = mix_info.GetInParams(); + GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset); + } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) { + s32 index{}; + while (const auto* destination = + GetDestinationData(in_params.splitter_info_id, index++)) { + if (!destination->IsConfigured()) { + continue; + } + auto& mix_info = mix_context.GetInfo(destination->GetMixId()); + const auto& mix_in = mix_info.GetInParams(); + GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset); + } + } + } else { + switch (in_params.sample_format) { + case SampleFormat::Pcm16: + DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel, + worker_params.sample_rate, worker_params.sample_count, + in_params.node_id); + break; + case SampleFormat::Adpcm: + ASSERT(channel == 0 && in_params.channel_count == 1); + DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(0), dsp_state, 0, + worker_params.sample_rate, worker_params.sample_count, + in_params.node_id); + break; + default: + UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); + } } } @@ -281,6 +309,34 @@ void AudioCore::CommandGenerator::GenerateBiquadFilterCommand( state = {s0, s1}; } +void CommandGenerator::GenerateDepopPrepareCommand(VoiceState& dsp_state, + std::size_t mix_buffer_count, + std::size_t mix_buffer_offset) { + for (std::size_t i = 0; i < mix_buffer_count; i++) { + auto& sample = dsp_state.previous_samples[i]; + if (sample != 0) { + depop_buffer[mix_buffer_offset + i] += sample; + sample = 0; + } + } +} + +void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count, + std::size_t mix_buffer_offset, + s32 sample_rate) { + const std::size_t end_offset = + std::min(mix_buffer_offset + mix_buffer_count, GetTotalMixBufferCount()); + const s32 delta = sample_rate == 48000 ? 0x7B29 : 0x78CB; + for (std::size_t i = mix_buffer_offset; i < end_offset; i++) { + if (depop_buffer[i] == 0) { + continue; + } + + depop_buffer[i] = + ApplyMixDepop(GetMixBuffer(i), depop_buffer[i], delta, worker_params.sample_count); + } +} + ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) { if (splitter_id == AudioCommon::NO_SPLITTER) { return nullptr; @@ -338,7 +394,9 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) { if (dumping_frame) { LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand"); } - // TODO(ogniK): Depop + auto& in_params = mix_info.GetInParams(); + GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, + in_params.sample_rate); // TODO(ogniK): Effects GenerateMixCommands(mix_info); } @@ -412,10 +470,13 @@ void CommandGenerator::GenerateFinalMixCommand() { if (dumping_frame) { LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand"); } - // TODO(ogniK): Depop - // TODO(ogniK): Effects auto& mix_info = mix_context.GetFinalMixInfo(); const auto in_params = mix_info.GetInParams(); + + GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, + in_params.sample_rate); + // TODO(ogniK): Effects + for (s32 i = 0; i < in_params.buffer_count; i++) { const s32 gain = static_cast(in_params.volume * 32768.0f); if (dumping_frame) { @@ -541,6 +602,10 @@ std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const { return worker_params.mix_buffer_count + channel; } +std::size_t CommandGenerator::GetTotalMixBufferCount() const { + return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT; +} + s32* CommandGenerator::GetChannelMixBuffer(s32 channel) { return GetMixBuffer(worker_params.mix_buffer_count + channel); } diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h index 3f49c1303..656ad8143 100644 --- a/src/audio_core/command_generator.h +++ b/src/audio_core/command_generator.h @@ -43,6 +43,8 @@ public: const s32* GetMixBuffer(std::size_t index) const; std::size_t GetMixChannelBufferOffset(s32 channel) const; + std::size_t GetTotalMixBufferCount() const; + private: void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel); void GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info, VoiceState& dsp_state, @@ -61,7 +63,10 @@ private: void GenerateBiquadFilterCommand(s32 mix_buffer, const BiquadFilterParameter& params, std::array& state, std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id); - void GenerateDepopPrepareCommand(VoiceState& dsp_state); + void GenerateDepopPrepareCommand(VoiceState& dsp_state, std::size_t mix_buffer_count, + std::size_t mix_buffer_offset); + void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count, + std::size_t mix_buffer_offset, s32 sample_rate); ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); // DSP Code @@ -79,6 +84,7 @@ private: Core::Memory::Memory& memory; std::vector mix_buffer{}; std::vector sample_buffer{}; + std::vector depop_buffer{}; bool dumping_frame{false}; }; } // namespace AudioCore