From 2a99464ef1a35cb97cb94636d99779caa34f3cb7 Mon Sep 17 00:00:00 2001
From: MerryMage <MerryMage@users.noreply.github.com>
Date: Sat, 26 Mar 2016 02:20:34 +0000
Subject: [PATCH] DSP: Implement audio filters (simple, biquad)

---
 src/audio_core/CMakeLists.txt |   3 +
 src/audio_core/hle/common.h   |  35 +++++++++++
 src/audio_core/hle/dsp.h      |  17 ++---
 src/audio_core/hle/filter.cpp | 115 ++++++++++++++++++++++++++++++++++
 src/audio_core/hle/filter.h   | 112 +++++++++++++++++++++++++++++++++
 5 files changed, 275 insertions(+), 7 deletions(-)
 create mode 100644 src/audio_core/hle/common.h
 create mode 100644 src/audio_core/hle/filter.cpp
 create mode 100644 src/audio_core/hle/filter.h

diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index c4bad8cb0..869da5e83 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -2,13 +2,16 @@ set(SRCS
             audio_core.cpp
             codec.cpp
             hle/dsp.cpp
+            hle/filter.cpp
             hle/pipe.cpp
             )
 
 set(HEADERS
             audio_core.h
             codec.h
+            hle/common.h
             hle/dsp.h
+            hle/filter.h
             hle/pipe.h
             sink.h
             )
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h
new file mode 100644
index 000000000..37d441eb2
--- /dev/null
+++ b/src/audio_core/hle/common.h
@@ -0,0 +1,35 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+
+#include "audio_core/audio_core.h"
+
+#include "common/common_types.h"
+
+namespace DSP {
+namespace HLE {
+
+/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
+using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>;
+
+/// The DSP is quadraphonic internally.
+using QuadFrame32   = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>;
+
+/**
+ * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
+ * FilterT::ProcessSample is called sequentially on the samples.
+ */
+template<typename FrameT, typename FilterT>
+void FilterFrame(FrameT& frame, FilterT& filter) {
+    std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) {
+        return filter.ProcessSample(sample);
+    });
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index 376436c29..c15ef0b7a 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -126,8 +126,11 @@ struct SourceConfiguration {
         union {
             u32_le dirty_raw;
 
+            BitField<0, 1, u32_le> format_dirty;
+            BitField<1, 1, u32_le> mono_or_stereo_dirty;
             BitField<2, 1, u32_le> adpcm_coefficients_dirty;
             BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
+            BitField<4, 1, u32_le> partial_reset_flag;
 
             BitField<16, 1, u32_le> enable_dirty;
             BitField<17, 1, u32_le> interpolation_dirty;
@@ -143,8 +146,7 @@ struct SourceConfiguration {
             BitField<27, 1, u32_le> gain_2_dirty;
             BitField<28, 1, u32_le> sync_dirty;
             BitField<29, 1, u32_le> reset_flag;
-
-            BitField<31, 1, u32_le> embedded_buffer_dirty;
+            BitField<30, 1, u32_le> embedded_buffer_dirty;
         };
 
         // Gain control
@@ -175,7 +177,8 @@ struct SourceConfiguration {
         /**
          * This is the simplest normalized first-order digital recursive filter.
          * The transfer function of this filter is:
-         *     H(z) = b0 / (1 + a1 z^-1)
+         *     H(z) = b0 / (1 - a1 z^-1)
+         * Note the feedbackward coefficient is negated.
          * Values are signed fixed point with 15 fractional bits.
          */
         struct SimpleFilter {
@@ -192,11 +195,11 @@ struct SourceConfiguration {
          * Values are signed fixed point with 14 fractional bits.
          */
         struct BiquadFilter {
-            s16_le b0;
-            s16_le b1;
-            s16_le b2;
-            s16_le a1;
             s16_le a2;
+            s16_le a1;
+            s16_le b2;
+            s16_le b1;
+            s16_le b0;
         };
 
         union {
diff --git a/src/audio_core/hle/filter.cpp b/src/audio_core/hle/filter.cpp
new file mode 100644
index 000000000..2c65ef026
--- /dev/null
+++ b/src/audio_core/hle/filter.cpp
@@ -0,0 +1,115 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstddef>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/filter.h"
+
+#include "common/common_types.h"
+#include "common/math_util.h"
+
+namespace DSP {
+namespace HLE {
+
+void SourceFilters::Reset() {
+    Enable(false, false);
+}
+
+void SourceFilters::Enable(bool simple, bool biquad) {
+    simple_filter_enabled = simple;
+    biquad_filter_enabled = biquad;
+
+    if (!simple)
+        simple_filter.Reset();
+    if (!biquad)
+        biquad_filter.Reset();
+}
+
+void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
+    simple_filter.Configure(config);
+}
+
+void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
+    biquad_filter.Configure(config);
+}
+
+void SourceFilters::ProcessFrame(StereoFrame16& frame) {
+    if (!simple_filter_enabled && !biquad_filter_enabled)
+        return;
+
+    if (simple_filter_enabled) {
+        FilterFrame(frame, simple_filter);
+    }
+
+    if (biquad_filter_enabled) {
+        FilterFrame(frame, biquad_filter);
+    }
+}
+
+// SimpleFilter
+
+void SourceFilters::SimpleFilter::Reset() {
+    y1.fill(0);
+    // Configure as passthrough.
+    a1 = 0;
+    b0 = 1 << 15;
+}
+
+void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
+    a1 = config.a1;
+    b0 = config.b0;
+}
+
+std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) {
+    std::array<s16, 2> y0;
+    for (size_t i = 0; i < 2; i++) {
+        const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15;
+        y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
+    }
+
+    y1 = y0;
+
+    return y0;
+}
+
+// BiquadFilter
+
+void SourceFilters::BiquadFilter::Reset() {
+    x1.fill(0);
+    x2.fill(0);
+    y1.fill(0);
+    y2.fill(0);
+    // Configure as passthrough.
+    a1 = a2 = b1 = b2 = 0;
+    b0 = 1 << 14;
+}
+
+void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
+    a1 = config.a1;
+    a2 = config.a2;
+    b0 = config.b0;
+    b1 = config.b1;
+    b2 = config.b2;
+}
+
+std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) {
+    std::array<s16, 2> y0;
+    for (size_t i = 0; i < 2; i++) {
+        const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14;
+        y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
+    }
+
+    x2 = x1;
+    x1 = x0;
+    y2 = y1;
+    y1 = y0;
+
+    return y0;
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h
new file mode 100644
index 000000000..75738f600
--- /dev/null
+++ b/src/audio_core/hle/filter.h
@@ -0,0 +1,112 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+
+#include "common/common_types.h"
+
+namespace DSP {
+namespace HLE {
+
+/// Preprocessing filters. There is an independent set of filters for each Source.
+class SourceFilters final {
+    SourceFilters() { Reset(); }
+
+    /// Reset internal state.
+    void Reset();
+
+    /**
+     * Enable/Disable filters
+     * See also: SourceConfiguration::Configuration::simple_filter_enabled,
+     *           SourceConfiguration::Configuration::biquad_filter_enabled.
+     * @param simple If true, enables the simple filter. If false, disables it.
+     * @param simple If true, enables the biquad filter. If false, disables it.
+     */
+    void Enable(bool simple, bool biquad);
+
+    /**
+     * Configure simple filter.
+     * @param config Configuration from DSP shared memory.
+     */
+    void Configure(SourceConfiguration::Configuration::SimpleFilter config);
+
+    /**
+     * Configure biquad filter.
+     * @param config Configuration from DSP shared memory.
+     */
+    void Configure(SourceConfiguration::Configuration::BiquadFilter config);
+
+    /**
+     * Processes a frame in-place.
+     * @param frame Audio samples to process. Modified in-place.
+     */
+    void ProcessFrame(StereoFrame16& frame);
+
+private:
+    bool simple_filter_enabled;
+    bool biquad_filter_enabled;
+
+    struct SimpleFilter {
+        SimpleFilter() { Reset(); }
+
+        /// Resets internal state.
+        void Reset();
+
+        /**
+         * Configures this filter with application settings.
+         * @param config Configuration from DSP shared memory.
+         */
+        void Configure(SourceConfiguration::Configuration::SimpleFilter config);
+
+        /**
+         * Processes a single stereo PCM16 sample.
+         * @param x0 Input sample
+         * @return Output sample
+         */
+        std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
+
+    private:
+        // Configuration
+        s32 a1, b0;
+        // Internal state
+        std::array<s16, 2> y1;
+    } simple_filter;
+
+    struct BiquadFilter {
+        BiquadFilter() { Reset(); }
+
+        /// Resets internal state.
+        void Reset();
+
+        /**
+         * Configures this filter with application settings.
+         * @param config Configuration from DSP shared memory.
+         */
+        void Configure(SourceConfiguration::Configuration::BiquadFilter config);
+
+        /**
+         * Processes a single stereo PCM16 sample.
+         * @param x0 Input sample
+         * @return Output sample
+         */
+        std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
+
+    private:
+        // Configuration
+        s32 a1, a2, b0, b1, b2;
+        // Internal state
+        std::array<s16, 2> x1;
+        std::array<s16, 2> x2;
+        std::array<s16, 2> y1;
+        std::array<s16, 2> y2;
+    } biquad_filter;
+};
+
+} // namespace HLE
+} // namespace DSP