From 0947f613b1f5480258a1bf197ccc7a85a54ab7e1 Mon Sep 17 00:00:00 2001
From: David Marcec <dmarcecguzman@gmail.com>
Date: Sat, 1 Aug 2020 16:25:08 +1000
Subject: [PATCH] mix buffer depopping

---
 src/audio_core/command_generator.cpp | 123 ++++++++++++++++++++-------
 src/audio_core/command_generator.h   |   8 +-
 2 files changed, 101 insertions(+), 30 deletions(-)

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<s32>((static_cast<s64>(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<s32>(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<s64, 2>& 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<s32> mix_buffer{};
     std::vector<s32> sample_buffer{};
+    std::vector<s32> depop_buffer{};
     bool dumping_frame{false};
 };
 } // namespace AudioCore