summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/hle/common.h2
-rw-r--r--src/audio_core/hle/dsp.cpp24
-rw-r--r--src/audio_core/hle/dsp.h8
-rw-r--r--src/audio_core/hle/filter.h1
-rw-r--r--src/audio_core/hle/source.cpp320
-rw-r--r--src/audio_core/hle/source.h144
7 files changed, 496 insertions, 5 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 5a2747e78..4cd7aba67 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -4,6 +4,7 @@ set(SRCS
4 hle/dsp.cpp 4 hle/dsp.cpp
5 hle/filter.cpp 5 hle/filter.cpp
6 hle/pipe.cpp 6 hle/pipe.cpp
7 hle/source.cpp
7 interpolate.cpp 8 interpolate.cpp
8 sink_details.cpp 9 sink_details.cpp
9 ) 10 )
@@ -15,6 +16,7 @@ set(HEADERS
15 hle/dsp.h 16 hle/dsp.h
16 hle/filter.h 17 hle/filter.h
17 hle/pipe.h 18 hle/pipe.h
19 hle/source.h
18 interpolate.h 20 interpolate.h
19 null_sink.h 21 null_sink.h
20 sink.h 22 sink.h
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h
index 7910f42ae..596b67eaf 100644
--- a/src/audio_core/hle/common.h
+++ b/src/audio_core/hle/common.h
@@ -27,7 +27,7 @@ using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
27 */ 27 */
28template<typename FrameT, typename FilterT> 28template<typename FrameT, typename FilterT>
29void FilterFrame(FrameT& frame, FilterT& filter) { 29void FilterFrame(FrameT& frame, FilterT& filter) {
30 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) { 30 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) {
31 return filter.ProcessSample(sample); 31 return filter.ProcessSample(sample);
32 }); 32 });
33} 33}
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
index 4d44bd2d9..0cdbdb06a 100644
--- a/src/audio_core/hle/dsp.cpp
+++ b/src/audio_core/hle/dsp.cpp
@@ -2,10 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
5#include <memory> 6#include <memory>
6 7
7#include "audio_core/hle/dsp.h" 8#include "audio_core/hle/dsp.h"
8#include "audio_core/hle/pipe.h" 9#include "audio_core/hle/pipe.h"
10#include "audio_core/hle/source.h"
9#include "audio_core/sink.h" 11#include "audio_core/sink.h"
10 12
11namespace DSP { 13namespace DSP {
@@ -38,16 +40,38 @@ static SharedMemory& WriteRegion() {
38 return g_regions[1 - CurrentRegionIndex()]; 40 return g_regions[1 - CurrentRegionIndex()];
39} 41}
40 42
43static std::array<Source, num_sources> sources = {
44 Source(0), Source(1), Source(2), Source(3), Source(4), Source(5),
45 Source(6), Source(7), Source(8), Source(9), Source(10), Source(11),
46 Source(12), Source(13), Source(14), Source(15), Source(16), Source(17),
47 Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
48};
49
41static std::unique_ptr<AudioCore::Sink> sink; 50static std::unique_ptr<AudioCore::Sink> sink;
42 51
43void Init() { 52void Init() {
44 DSP::HLE::ResetPipes(); 53 DSP::HLE::ResetPipes();
54 for (auto& source : sources) {
55 source.Reset();
56 }
45} 57}
46 58
47void Shutdown() { 59void Shutdown() {
48} 60}
49 61
50bool Tick() { 62bool Tick() {
63 SharedMemory& read = ReadRegion();
64 SharedMemory& write = WriteRegion();
65
66 std::array<QuadFrame32, 3> intermediate_mixes = {};
67
68 for (size_t i = 0; i < num_sources; i++) {
69 write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
70 for (size_t mix = 0; mix < 3; mix++) {
71 sources[i].MixInto(intermediate_mixes[mix], mix);
72 }
73 }
74
51 return true; 75 return true;
52} 76}
53 77
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index 4f2410c27..4459a5668 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -169,9 +169,9 @@ struct SourceConfiguration {
169 float_le rate_multiplier; 169 float_le rate_multiplier;
170 170
171 enum class InterpolationMode : u8 { 171 enum class InterpolationMode : u8 {
172 None = 0, 172 Polyphase = 0,
173 Linear = 1, 173 Linear = 1,
174 Polyphase = 2 174 None = 2
175 }; 175 };
176 176
177 InterpolationMode interpolation_mode; 177 InterpolationMode interpolation_mode;
@@ -318,10 +318,10 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
318struct SourceStatus { 318struct SourceStatus {
319 struct Status { 319 struct Status {
320 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) 320 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
321 u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes 321 u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
322 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync 322 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
323 u32_dsp buffer_position; ///< Number of samples into the current buffer 323 u32_dsp buffer_position; ///< Number of samples into the current buffer
324 u16_le previous_buffer_id; ///< Updated when a buffer finishes playing 324 u16_le current_buffer_id; ///< Updated when a buffer finishes playing
325 INSERT_PADDING_DSPWORDS(1); 325 INSERT_PADDING_DSPWORDS(1);
326 }; 326 };
327 327
diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h
index 75738f600..43d2035cd 100644
--- a/src/audio_core/hle/filter.h
+++ b/src/audio_core/hle/filter.h
@@ -16,6 +16,7 @@ namespace HLE {
16 16
17/// Preprocessing filters. There is an independent set of filters for each Source. 17/// Preprocessing filters. There is an independent set of filters for each Source.
18class SourceFilters final { 18class SourceFilters final {
19public:
19 SourceFilters() { Reset(); } 20 SourceFilters() { Reset(); }
20 21
21 /// Reset internal state. 22 /// Reset internal state.
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp
new file mode 100644
index 000000000..daaf6e3f3
--- /dev/null
+++ b/src/audio_core/hle/source.cpp
@@ -0,0 +1,320 @@
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 <algorithm>
6#include <array>
7
8#include "audio_core/codec.h"
9#include "audio_core/hle/common.h"
10#include "audio_core/hle/source.h"
11#include "audio_core/interpolate.h"
12
13#include "common/assert.h"
14#include "common/logging/log.h"
15
16#include "core/memory.h"
17
18namespace DSP {
19namespace HLE {
20
21SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) {
22 ParseConfig(config, adpcm_coeffs);
23
24 if (state.enabled) {
25 GenerateFrame();
26 }
27
28 return GetCurrentStatus();
29}
30
31void Source::MixInto(QuadFrame32& dest, size_t intermediate_mix_id) const {
32 if (!state.enabled)
33 return;
34
35 const std::array<float, 4>& gains = state.gain.at(intermediate_mix_id);
36 for (size_t samplei = 0; samplei < samples_per_frame; samplei++) {
37 // Conversion from stereo (current_frame) to quadraphonic (dest) occurs here.
38 dest[samplei][0] += static_cast<s32>(gains[0] * current_frame[samplei][0]);
39 dest[samplei][1] += static_cast<s32>(gains[1] * current_frame[samplei][1]);
40 dest[samplei][2] += static_cast<s32>(gains[2] * current_frame[samplei][0]);
41 dest[samplei][3] += static_cast<s32>(gains[3] * current_frame[samplei][1]);
42 }
43}
44
45void Source::Reset() {
46 current_frame.fill({});
47 state = {};
48}
49
50void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) {
51 if (!config.dirty_raw) {
52 return;
53 }
54
55 if (config.reset_flag) {
56 config.reset_flag.Assign(0);
57 Reset();
58 LOG_TRACE(Audio_DSP, "source_id=%zu reset", source_id);
59 }
60
61 if (config.partial_reset_flag) {
62 config.partial_reset_flag.Assign(0);
63 state.input_queue = std::priority_queue<Buffer, std::vector<Buffer>, BufferOrder>{};
64 LOG_TRACE(Audio_DSP, "source_id=%zu partial_reset", source_id);
65 }
66
67 if (config.enable_dirty) {
68 config.enable_dirty.Assign(0);
69 state.enabled = config.enable != 0;
70 LOG_TRACE(Audio_DSP, "source_id=%zu enable=%d", source_id, state.enabled);
71 }
72
73 if (config.sync_dirty) {
74 config.sync_dirty.Assign(0);
75 state.sync = config.sync;
76 LOG_TRACE(Audio_DSP, "source_id=%zu sync=%u", source_id, state.sync);
77 }
78
79 if (config.rate_multiplier_dirty) {
80 config.rate_multiplier_dirty.Assign(0);
81 state.rate_multiplier = config.rate_multiplier;
82 LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier);
83
84 if (state.rate_multiplier <= 0) {
85 LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier);
86 state.rate_multiplier = 1.0f;
87 // Note: Actual firmware starts producing garbage if this occurs.
88 }
89 }
90
91 if (config.adpcm_coefficients_dirty) {
92 config.adpcm_coefficients_dirty.Assign(0);
93 std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(),
94 [](const auto& coeff) { return static_cast<s16>(coeff); });
95 LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
96 }
97
98 if (config.gain_0_dirty) {
99 config.gain_0_dirty.Assign(0);
100 std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(),
101 [](const auto& coeff) { return static_cast<float>(coeff); });
102 LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id);
103 }
104
105 if (config.gain_1_dirty) {
106 config.gain_1_dirty.Assign(0);
107 std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(),
108 [](const auto& coeff) { return static_cast<float>(coeff); });
109 LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id);
110 }
111
112 if (config.gain_2_dirty) {
113 config.gain_2_dirty.Assign(0);
114 std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(),
115 [](const auto& coeff) { return static_cast<float>(coeff); });
116 LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id);
117 }
118
119 if (config.filters_enabled_dirty) {
120 config.filters_enabled_dirty.Assign(0);
121 state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool());
122 LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu",
123 source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value());
124 }
125
126 if (config.simple_filter_dirty) {
127 config.simple_filter_dirty.Assign(0);
128 state.filters.Configure(config.simple_filter);
129 LOG_TRACE(Audio_DSP, "source_id=%zu simple filter update");
130 }
131
132 if (config.biquad_filter_dirty) {
133 config.biquad_filter_dirty.Assign(0);
134 state.filters.Configure(config.biquad_filter);
135 LOG_TRACE(Audio_DSP, "source_id=%zu biquad filter update");
136 }
137
138 if (config.interpolation_dirty) {
139 config.interpolation_dirty.Assign(0);
140 state.interpolation_mode = config.interpolation_mode;
141 LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode));
142 }
143
144 if (config.format_dirty || config.embedded_buffer_dirty) {
145 config.format_dirty.Assign(0);
146 state.format = config.format;
147 LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format));
148 }
149
150 if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
151 config.mono_or_stereo_dirty.Assign(0);
152 state.mono_or_stereo = config.mono_or_stereo;
153 LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo));
154 }
155
156 if (config.embedded_buffer_dirty) {
157 config.embedded_buffer_dirty.Assign(0);
158 state.input_queue.emplace(Buffer{
159 config.physical_address,
160 config.length,
161 static_cast<u8>(config.adpcm_ps),
162 { config.adpcm_yn[0], config.adpcm_yn[1] },
163 config.adpcm_dirty.ToBool(),
164 config.is_looping.ToBool(),
165 config.buffer_id,
166 state.mono_or_stereo,
167 state.format,
168 false
169 });
170 LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id);
171 }
172
173 if (config.buffer_queue_dirty) {
174 config.buffer_queue_dirty.Assign(0);
175 for (size_t i = 0; i < 4; i++) {
176 if (config.buffers_dirty & (1 << i)) {
177 const auto& b = config.buffers[i];
178 state.input_queue.emplace(Buffer{
179 b.physical_address,
180 b.length,
181 static_cast<u8>(b.adpcm_ps),
182 { b.adpcm_yn[0], b.adpcm_yn[1] },
183 b.adpcm_dirty != 0,
184 b.is_looping != 0,
185 b.buffer_id,
186 state.mono_or_stereo,
187 state.format,
188 true
189 });
190 LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id);
191 }
192 }
193 config.buffers_dirty = 0;
194 }
195
196 if (config.dirty_raw) {
197 LOG_DEBUG(Audio_DSP, "source_id=%zu remaining_dirty=%x", source_id, config.dirty_raw);
198 }
199
200 config.dirty_raw = 0;
201}
202
203void Source::GenerateFrame() {
204 current_frame.fill({});
205
206 if (state.current_buffer.empty() && !DequeueBuffer()) {
207 state.enabled = false;
208 state.buffer_update = true;
209 state.current_buffer_id = 0;
210 return;
211 }
212
213 size_t frame_position = 0;
214
215 state.current_sample_number = state.next_sample_number;
216 while (frame_position < current_frame.size()) {
217 if (state.current_buffer.empty() && !DequeueBuffer()) {
218 break;
219 }
220
221 const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position);
222
223 std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position);
224 state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy);
225
226 frame_position += size_to_copy;
227 state.next_sample_number += static_cast<u32>(size_to_copy);
228 }
229
230 state.filters.ProcessFrame(current_frame);
231}
232
233
234bool Source::DequeueBuffer() {
235 ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer");
236
237 if (state.input_queue.empty())
238 return false;
239
240 const Buffer buf = state.input_queue.top();
241 state.input_queue.pop();
242
243 if (buf.adpcm_dirty) {
244 state.adpcm_state.yn1 = buf.adpcm_yn[0];
245 state.adpcm_state.yn2 = buf.adpcm_yn[1];
246 }
247
248 if (buf.is_looping) {
249 LOG_ERROR(Audio_DSP, "Looped buffers are unimplemented at the moment");
250 }
251
252 const u8* const memory = Memory::GetPhysicalPointer(buf.physical_address);
253 if (memory) {
254 const unsigned num_channels = buf.mono_or_stereo == MonoOrStereo::Stereo ? 2 : 1;
255 switch (buf.format) {
256 case Format::PCM8:
257 state.current_buffer = Codec::DecodePCM8(num_channels, memory, buf.length);
258 break;
259 case Format::PCM16:
260 state.current_buffer = Codec::DecodePCM16(num_channels, memory, buf.length);
261 break;
262 case Format::ADPCM:
263 DEBUG_ASSERT(num_channels == 1);
264 state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state);
265 break;
266 default:
267 UNIMPLEMENTED();
268 break;
269 }
270 } else {
271 LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X",
272 source_id, buf.buffer_id, buf.length, buf.physical_address);
273 state.current_buffer.clear();
274 return true;
275 }
276
277 switch (state.interpolation_mode) {
278 case InterpolationMode::None:
279 state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier);
280 break;
281 case InterpolationMode::Linear:
282 state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
283 break;
284 case InterpolationMode::Polyphase:
285 // TODO(merry): Implement polyphase interpolation
286 state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
287 break;
288 default:
289 UNIMPLEMENTED();
290 break;
291 }
292
293 state.current_sample_number = 0;
294 state.next_sample_number = 0;
295 state.current_buffer_id = buf.buffer_id;
296 state.buffer_update = buf.from_queue;
297
298 LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
299 source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size());
300 return true;
301}
302
303SourceStatus::Status Source::GetCurrentStatus() {
304 SourceStatus::Status ret;
305
306 // Applications depend on the correct emulation of
307 // current_buffer_id_dirty and current_buffer_id to synchronise
308 // audio with video.
309 ret.is_enabled = state.enabled;
310 ret.current_buffer_id_dirty = state.buffer_update ? 1 : 0;
311 state.buffer_update = false;
312 ret.current_buffer_id = state.current_buffer_id;
313 ret.buffer_position = state.current_sample_number;
314 ret.sync = state.sync;
315
316 return ret;
317}
318
319} // namespace HLE
320} // namespace DSP
diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h
new file mode 100644
index 000000000..7ee08d424
--- /dev/null
+++ b/src/audio_core/hle/source.h
@@ -0,0 +1,144 @@
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#include <queue>
9#include <vector>
10
11#include "audio_core/codec.h"
12#include "audio_core/hle/common.h"
13#include "audio_core/hle/dsp.h"
14#include "audio_core/hle/filter.h"
15#include "audio_core/interpolate.h"
16
17#include "common/common_types.h"
18
19namespace DSP {
20namespace HLE {
21
22/**
23 * This module performs:
24 * - Buffer management
25 * - Decoding of buffers
26 * - Buffer resampling and interpolation
27 * - Per-source filtering (SimpleFilter, BiquadFilter)
28 * - Per-source gain
29 * - Other per-source processing
30 */
31class Source final {
32public:
33 explicit Source(size_t source_id_) : source_id(source_id_) {
34 Reset();
35 }
36
37 /// Resets internal state.
38 void Reset();
39
40 /**
41 * This is called once every audio frame. This performs per-source processing every frame.
42 * @param config The new configuration we've got for this Source from the application.
43 * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise).
44 * @return The current status of this Source. This is given back to the emulated application via SharedMemory.
45 */
46 SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
47
48 /**
49 * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer.
50 * @param dest The QuadFrame32 to mix into.
51 * @param intermediate_mix_id The id of the intermediate mix whose gains we are using.
52 */
53 void MixInto(QuadFrame32& dest, size_t intermediate_mix_id) const;
54
55private:
56 const size_t source_id;
57 StereoFrame16 current_frame;
58
59 using Format = SourceConfiguration::Configuration::Format;
60 using InterpolationMode = SourceConfiguration::Configuration::InterpolationMode;
61 using MonoOrStereo = SourceConfiguration::Configuration::MonoOrStereo;
62
63 /// Internal representation of a buffer for our buffer queue
64 struct Buffer {
65 PAddr physical_address;
66 u32 length;
67 u8 adpcm_ps;
68 std::array<u16, 2> adpcm_yn;
69 bool adpcm_dirty;
70 bool is_looping;
71 u16 buffer_id;
72
73 MonoOrStereo mono_or_stereo;
74 Format format;
75
76 bool from_queue;
77 };
78
79 struct BufferOrder {
80 bool operator() (const Buffer& a, const Buffer& b) const {
81 // Lower buffer_id comes first.
82 return a.buffer_id > b.buffer_id;
83 }
84 };
85
86 struct {
87
88 // State variables
89
90 bool enabled = false;
91 u16 sync = 0;
92
93 // Mixing
94
95 std::array<std::array<float, 4>, 3> gain = {};
96
97 // Buffer queue
98
99 std::priority_queue<Buffer, std::vector<Buffer>, BufferOrder> input_queue;
100 MonoOrStereo mono_or_stereo = MonoOrStereo::Mono;
101 Format format = Format::ADPCM;
102
103 // Current buffer
104
105 u32 current_sample_number = 0;
106 u32 next_sample_number = 0;
107 std::vector<std::array<s16, 2>> current_buffer;
108
109 // buffer_id state
110
111 bool buffer_update = false;
112 u32 current_buffer_id = 0;
113
114 // Decoding state
115
116 std::array<s16, 16> adpcm_coeffs = {};
117 Codec::ADPCMState adpcm_state = {};
118
119 // Resampling state
120
121 float rate_multiplier = 1.0;
122 InterpolationMode interpolation_mode = InterpolationMode::Polyphase;
123 AudioInterp::State interp_state = {};
124
125 // Filter state
126
127 SourceFilters filters;
128
129 } state;
130
131 // Internal functions
132
133 /// INTERNAL: Update our internal state based on the current config.
134 void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
135 /// INTERNAL: Generate the current audio output for this frame based on our internal state.
136 void GenerateFrame();
137 /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer.
138 bool DequeueBuffer();
139 /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
140 SourceStatus::Status GetCurrentStatus();
141};
142
143} // namespace HLE
144} // namespace DSP