summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2016-03-30 20:17:52 -0400
committerGravatar bunnei2016-03-30 20:17:52 -0400
commit644fbbeb13db8147ce74cfbde80f62ee492c201a (patch)
tree0c9e5ee0a8e9bb98cc2df13d5584f756c7cfeea2 /src
parentMerge pull request #1611 from ObsidianX/cfg-common-fix (diff)
parentDSP: Implement audio filters (simple, biquad) (diff)
downloadyuzu-644fbbeb13db8147ce74cfbde80f62ee492c201a.tar.gz
yuzu-644fbbeb13db8147ce74cfbde80f62ee492c201a.tar.xz
yuzu-644fbbeb13db8147ce74cfbde80f62ee492c201a.zip
Merge pull request #1572 from MerryMage/audio-filter
DSP: Implement audio filters (simple, biquad)
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/hle/common.h35
-rw-r--r--src/audio_core/hle/dsp.h17
-rw-r--r--src/audio_core/hle/filter.cpp115
-rw-r--r--src/audio_core/hle/filter.h112
5 files changed, 275 insertions, 7 deletions
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
2 audio_core.cpp 2 audio_core.cpp
3 codec.cpp 3 codec.cpp
4 hle/dsp.cpp 4 hle/dsp.cpp
5 hle/filter.cpp
5 hle/pipe.cpp 6 hle/pipe.cpp
6 ) 7 )
7 8
8set(HEADERS 9set(HEADERS
9 audio_core.h 10 audio_core.h
10 codec.h 11 codec.h
12 hle/common.h
11 hle/dsp.h 13 hle/dsp.h
14 hle/filter.h
12 hle/pipe.h 15 hle/pipe.h
13 sink.h 16 sink.h
14 ) 17 )
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9
10#include "audio_core/audio_core.h"
11
12#include "common/common_types.h"
13
14namespace DSP {
15namespace HLE {
16
17/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
18using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>;
19
20/// The DSP is quadraphonic internally.
21using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>;
22
23/**
24 * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
25 * FilterT::ProcessSample is called sequentially on the samples.
26 */
27template<typename FrameT, typename FilterT>
28void FilterFrame(FrameT& frame, FilterT& filter) {
29 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) {
30 return filter.ProcessSample(sample);
31 });
32}
33
34} // namespace HLE
35} // 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 {
126 union { 126 union {
127 u32_le dirty_raw; 127 u32_le dirty_raw;
128 128
129 BitField<0, 1, u32_le> format_dirty;
130 BitField<1, 1, u32_le> mono_or_stereo_dirty;
129 BitField<2, 1, u32_le> adpcm_coefficients_dirty; 131 BitField<2, 1, u32_le> adpcm_coefficients_dirty;
130 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. 132 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
133 BitField<4, 1, u32_le> partial_reset_flag;
131 134
132 BitField<16, 1, u32_le> enable_dirty; 135 BitField<16, 1, u32_le> enable_dirty;
133 BitField<17, 1, u32_le> interpolation_dirty; 136 BitField<17, 1, u32_le> interpolation_dirty;
@@ -143,8 +146,7 @@ struct SourceConfiguration {
143 BitField<27, 1, u32_le> gain_2_dirty; 146 BitField<27, 1, u32_le> gain_2_dirty;
144 BitField<28, 1, u32_le> sync_dirty; 147 BitField<28, 1, u32_le> sync_dirty;
145 BitField<29, 1, u32_le> reset_flag; 148 BitField<29, 1, u32_le> reset_flag;
146 149 BitField<30, 1, u32_le> embedded_buffer_dirty;
147 BitField<31, 1, u32_le> embedded_buffer_dirty;
148 }; 150 };
149 151
150 // Gain control 152 // Gain control
@@ -175,7 +177,8 @@ struct SourceConfiguration {
175 /** 177 /**
176 * This is the simplest normalized first-order digital recursive filter. 178 * This is the simplest normalized first-order digital recursive filter.
177 * The transfer function of this filter is: 179 * The transfer function of this filter is:
178 * H(z) = b0 / (1 + a1 z^-1) 180 * H(z) = b0 / (1 - a1 z^-1)
181 * Note the feedbackward coefficient is negated.
179 * Values are signed fixed point with 15 fractional bits. 182 * Values are signed fixed point with 15 fractional bits.
180 */ 183 */
181 struct SimpleFilter { 184 struct SimpleFilter {
@@ -192,11 +195,11 @@ struct SourceConfiguration {
192 * Values are signed fixed point with 14 fractional bits. 195 * Values are signed fixed point with 14 fractional bits.
193 */ 196 */
194 struct BiquadFilter { 197 struct BiquadFilter {
195 s16_le b0;
196 s16_le b1;
197 s16_le b2;
198 s16_le a1;
199 s16_le a2; 198 s16_le a2;
199 s16_le a1;
200 s16_le b2;
201 s16_le b1;
202 s16_le b0;
200 }; 203 };
201 204
202 union { 205 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstddef>
7
8#include "audio_core/hle/common.h"
9#include "audio_core/hle/dsp.h"
10#include "audio_core/hle/filter.h"
11
12#include "common/common_types.h"
13#include "common/math_util.h"
14
15namespace DSP {
16namespace HLE {
17
18void SourceFilters::Reset() {
19 Enable(false, false);
20}
21
22void SourceFilters::Enable(bool simple, bool biquad) {
23 simple_filter_enabled = simple;
24 biquad_filter_enabled = biquad;
25
26 if (!simple)
27 simple_filter.Reset();
28 if (!biquad)
29 biquad_filter.Reset();
30}
31
32void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
33 simple_filter.Configure(config);
34}
35
36void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
37 biquad_filter.Configure(config);
38}
39
40void SourceFilters::ProcessFrame(StereoFrame16& frame) {
41 if (!simple_filter_enabled && !biquad_filter_enabled)
42 return;
43
44 if (simple_filter_enabled) {
45 FilterFrame(frame, simple_filter);
46 }
47
48 if (biquad_filter_enabled) {
49 FilterFrame(frame, biquad_filter);
50 }
51}
52
53// SimpleFilter
54
55void SourceFilters::SimpleFilter::Reset() {
56 y1.fill(0);
57 // Configure as passthrough.
58 a1 = 0;
59 b0 = 1 << 15;
60}
61
62void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
63 a1 = config.a1;
64 b0 = config.b0;
65}
66
67std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) {
68 std::array<s16, 2> y0;
69 for (size_t i = 0; i < 2; i++) {
70 const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15;
71 y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
72 }
73
74 y1 = y0;
75
76 return y0;
77}
78
79// BiquadFilter
80
81void SourceFilters::BiquadFilter::Reset() {
82 x1.fill(0);
83 x2.fill(0);
84 y1.fill(0);
85 y2.fill(0);
86 // Configure as passthrough.
87 a1 = a2 = b1 = b2 = 0;
88 b0 = 1 << 14;
89}
90
91void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
92 a1 = config.a1;
93 a2 = config.a2;
94 b0 = config.b0;
95 b1 = config.b1;
96 b2 = config.b2;
97}
98
99std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) {
100 std::array<s16, 2> y0;
101 for (size_t i = 0; i < 2; i++) {
102 const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14;
103 y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
104 }
105
106 x2 = x1;
107 x1 = x0;
108 y2 = y1;
109 y1 = y0;
110
111 return y0;
112}
113
114} // namespace HLE
115} // 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "audio_core/hle/common.h"
10#include "audio_core/hle/dsp.h"
11
12#include "common/common_types.h"
13
14namespace DSP {
15namespace HLE {
16
17/// Preprocessing filters. There is an independent set of filters for each Source.
18class SourceFilters final {
19 SourceFilters() { Reset(); }
20
21 /// Reset internal state.
22 void Reset();
23
24 /**
25 * Enable/Disable filters
26 * See also: SourceConfiguration::Configuration::simple_filter_enabled,
27 * SourceConfiguration::Configuration::biquad_filter_enabled.
28 * @param simple If true, enables the simple filter. If false, disables it.
29 * @param simple If true, enables the biquad filter. If false, disables it.
30 */
31 void Enable(bool simple, bool biquad);
32
33 /**
34 * Configure simple filter.
35 * @param config Configuration from DSP shared memory.
36 */
37 void Configure(SourceConfiguration::Configuration::SimpleFilter config);
38
39 /**
40 * Configure biquad filter.
41 * @param config Configuration from DSP shared memory.
42 */
43 void Configure(SourceConfiguration::Configuration::BiquadFilter config);
44
45 /**
46 * Processes a frame in-place.
47 * @param frame Audio samples to process. Modified in-place.
48 */
49 void ProcessFrame(StereoFrame16& frame);
50
51private:
52 bool simple_filter_enabled;
53 bool biquad_filter_enabled;
54
55 struct SimpleFilter {
56 SimpleFilter() { Reset(); }
57
58 /// Resets internal state.
59 void Reset();
60
61 /**
62 * Configures this filter with application settings.
63 * @param config Configuration from DSP shared memory.
64 */
65 void Configure(SourceConfiguration::Configuration::SimpleFilter config);
66
67 /**
68 * Processes a single stereo PCM16 sample.
69 * @param x0 Input sample
70 * @return Output sample
71 */
72 std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
73
74 private:
75 // Configuration
76 s32 a1, b0;
77 // Internal state
78 std::array<s16, 2> y1;
79 } simple_filter;
80
81 struct BiquadFilter {
82 BiquadFilter() { Reset(); }
83
84 /// Resets internal state.
85 void Reset();
86
87 /**
88 * Configures this filter with application settings.
89 * @param config Configuration from DSP shared memory.
90 */
91 void Configure(SourceConfiguration::Configuration::BiquadFilter config);
92
93 /**
94 * Processes a single stereo PCM16 sample.
95 * @param x0 Input sample
96 * @return Output sample
97 */
98 std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
99
100 private:
101 // Configuration
102 s32 a1, a2, b0, b1, b2;
103 // Internal state
104 std::array<s16, 2> x1;
105 std::array<s16, 2> x2;
106 std::array<s16, 2> y1;
107 std::array<s16, 2> y2;
108 } biquad_filter;
109};
110
111} // namespace HLE
112} // namespace DSP