summaryrefslogtreecommitdiff
path: root/src/audio_core/hle/dsp.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/hle/dsp.h')
-rw-r--r--src/audio_core/hle/dsp.h502
1 files changed, 502 insertions, 0 deletions
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
new file mode 100644
index 000000000..14c4000c6
--- /dev/null
+++ b/src/audio_core/hle/dsp.h
@@ -0,0 +1,502 @@
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 <cstddef>
8#include <type_traits>
9
10#include "audio_core/audio_core.h"
11
12#include "common/bit_field.h"
13#include "common/common_funcs.h"
14#include "common/common_types.h"
15#include "common/swap.h"
16
17namespace DSP {
18namespace HLE {
19
20// The application-accessible region of DSP memory consists of two parts.
21// Both are marked as IO and have Read/Write permissions.
22//
23// First Region: 0x1FF50000 (Size: 0x8000)
24// Second Region: 0x1FF70000 (Size: 0x8000)
25//
26// The DSP reads from each region alternately based on the frame counter for each region much like a
27// double-buffer. The frame counter is located as the very last u16 of each region and is incremented
28// each audio tick.
29
30struct SharedMemory;
31
32constexpr VAddr region0_base = 0x1FF50000;
33extern SharedMemory g_region0;
34
35constexpr VAddr region1_base = 0x1FF70000;
36extern SharedMemory g_region1;
37
38/**
39 * The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
40 * its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
41 * layout of the ARM11. Hence from the ARM11's point of view the memory space appears to be
42 * middle-endian.
43 *
44 * Unusually this does not appear to be an issue for floating point numbers. The DSP makes the more
45 * sensible choice of keeping that little-endian. There are also some exceptions such as the
46 * IntermediateMixSamples structure, which is little-endian.
47 *
48 * This struct implements the conversion to and from this middle-endianness.
49 */
50struct u32_dsp {
51 u32_dsp() = default;
52 operator u32() const {
53 return Convert(storage);
54 }
55 void operator=(u32 new_value) {
56 storage = Convert(new_value);
57 }
58private:
59 static constexpr u32 Convert(u32 value) {
60 return (value << 16) | (value >> 16);
61 }
62 u32_le storage;
63};
64#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
65static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
66#endif
67
68// There are 15 structures in each memory region. A table of them in the order they appear in memory
69// is presented below
70//
71// Pipe 2 # First Region DSP Address Purpose Control
72// 5 0x8400 DSP Status DSP
73// 9 0x8410 DSP Debug Info DSP
74// 6 0x8540 Final Mix Samples DSP
75// 2 0x8680 Source Status [24] DSP
76// 8 0x8710 Compressor Table Application
77// 4 0x9430 DSP Configuration Application
78// 7 0x9492 Intermediate Mix Samples DSP + App
79// 1 0x9E92 Source Configuration [24] Application
80// 3 0xA792 Source ADPCM Coefficients [24] Application
81// 10 0xA912 Surround Sound Related
82// 11 0xAA12 Surround Sound Related
83// 12 0xAAD2 Surround Sound Related
84// 13 0xAC52 Surround Sound Related
85// 14 0xAC5C Surround Sound Related
86// 0 0xBFFF Frame Counter Application
87//
88// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are
89// not fixed in stone. The addresses above are only an examplar; they're what this implementation
90// does and provides to applications.
91//
92// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the
93// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
94// second region via:
95// second_region_dsp_addr = first_region_dsp_addr | 0x10000
96//
97// Applications maintain most of its own audio state, the memory region is used mainly for
98// communication and not storage of state.
99//
100// In the documentation below, filter and effect transfer functions are specified in the z domain.
101// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
102// frequency domain, just like how the s domain is the analog frequency domain.)
103
104#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
105
106// GCC versions < 5.0 do not implement std::is_trivially_copyable.
107// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
108#if (__GNUC__ >= 5) || defined(__clang__)
109 #define ASSERT_DSP_STRUCT(name, size) \
110 static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
111 static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \
112 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
113#else
114 #define ASSERT_DSP_STRUCT(name, size) \
115 static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
116 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
117#endif
118
119struct SourceConfiguration {
120 struct Configuration {
121 /// These dirty flags are set by the application when it updates the fields in this struct.
122 /// The DSP clears these each audio frame.
123 union {
124 u32_le dirty_raw;
125
126 BitField<2, 1, u32_le> adpcm_coefficients_dirty;
127 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
128
129 BitField<16, 1, u32_le> enable_dirty;
130 BitField<17, 1, u32_le> interpolation_dirty;
131 BitField<18, 1, u32_le> rate_multiplier_dirty;
132 BitField<19, 1, u32_le> buffer_queue_dirty;
133 BitField<20, 1, u32_le> loop_related_dirty;
134 BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated.
135 BitField<22, 1, u32_le> filters_enabled_dirty;
136 BitField<23, 1, u32_le> simple_filter_dirty;
137 BitField<24, 1, u32_le> biquad_filter_dirty;
138 BitField<25, 1, u32_le> gain_0_dirty;
139 BitField<26, 1, u32_le> gain_1_dirty;
140 BitField<27, 1, u32_le> gain_2_dirty;
141 BitField<28, 1, u32_le> sync_dirty;
142 BitField<29, 1, u32_le> reset_flag;
143
144 BitField<31, 1, u32_le> embedded_buffer_dirty;
145 };
146
147 // Gain control
148
149 /**
150 * Gain is between 0.0-1.0. This determines how much will this source appear on
151 * each of the 12 channels that feed into the intermediate mixers.
152 * Each of the three intermediate mixers is fed two left and two right channels.
153 */
154 float_le gain[3][4];
155
156 // Interpolation
157
158 /// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
159 float_le rate_multiplier;
160
161 enum class InterpolationMode : u8 {
162 None = 0,
163 Linear = 1,
164 Polyphase = 2
165 };
166
167 InterpolationMode interpolation_mode;
168 INSERT_PADDING_BYTES(1); ///< Interpolation related
169
170 // Filters
171
172 /**
173 * This is the simplest normalized first-order digital recursive filter.
174 * The transfer function of this filter is:
175 * H(z) = b0 / (1 + a1 z^-1)
176 * Values are signed fixed point with 15 fractional bits.
177 */
178 struct SimpleFilter {
179 s16_le b0;
180 s16_le a1;
181 };
182
183 /**
184 * This is a normalised biquad filter (second-order).
185 * The transfer function of this filter is:
186 * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
187 * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation
188 * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
189 * Values are signed fixed point with 14 fractional bits.
190 */
191 struct BiquadFilter {
192 s16_le b0;
193 s16_le b1;
194 s16_le b2;
195 s16_le a1;
196 s16_le a2;
197 };
198
199 union {
200 u16_le filters_enabled;
201 BitField<0, 1, u16_le> simple_filter_enabled;
202 BitField<1, 1, u16_le> biquad_filter_enabled;
203 };
204
205 SimpleFilter simple_filter;
206 BiquadFilter biquad_filter;
207
208 // Buffer Queue
209
210 /// A buffer of audio data from the application, along with metadata about it.
211 struct Buffer {
212 /// Physical memory address of the start of the buffer
213 u32_dsp physical_address;
214
215 /// This is length in terms of samples.
216 /// Note that in different buffer formats a sample takes up different number of bytes.
217 u32_dsp length;
218
219 /// ADPCM Predictor (4 bits) and Scale (4 bits)
220 union {
221 u16_le adpcm_ps;
222 BitField<0, 4, u16_le> adpcm_scale;
223 BitField<4, 4, u16_le> adpcm_predictor;
224 };
225
226 /// ADPCM Historical Samples (y[n-1] and y[n-2])
227 u16_le adpcm_yn[2];
228
229 /// This is non-zero when the ADPCM values above are to be updated.
230 u8 adpcm_dirty;
231
232 /// Is a looping buffer.
233 u8 is_looping;
234
235 /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished.
236 /// This allows the emulated application to tell what buffer is currently playing
237 u16_le buffer_id;
238
239 INSERT_PADDING_DSPWORDS(1);
240 };
241
242 u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
243 Buffer buffers[4]; ///< Queued Buffers
244
245 // Playback controls
246
247 u32_dsp loop_related;
248 u8 enable;
249 INSERT_PADDING_BYTES(1);
250 u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
251 u32_dsp play_position; ///< Position. (Units: number of samples)
252 INSERT_PADDING_DSPWORDS(2);
253
254 // Embedded Buffer
255 // This buffer is often the first buffer to be used when initiating audio playback,
256 // after which the buffer queue is used.
257
258 u32_dsp physical_address;
259
260 /// This is length in terms of samples.
261 /// Note a sample takes up different number of bytes in different buffer formats.
262 u32_dsp length;
263
264 enum class MonoOrStereo : u16_le {
265 Mono = 1,
266 Stereo = 2
267 };
268
269 enum class Format : u16_le {
270 PCM8 = 0,
271 PCM16 = 1,
272 ADPCM = 2
273 };
274
275 union {
276 u16_le flags1_raw;
277 BitField<0, 2, MonoOrStereo> mono_or_stereo;
278 BitField<2, 2, Format> format;
279 BitField<5, 1, u16_le> fade_in;
280 };
281
282 /// ADPCM Predictor (4 bit) and Scale (4 bit)
283 union {
284 u16_le adpcm_ps;
285 BitField<0, 4, u16_le> adpcm_scale;
286 BitField<4, 4, u16_le> adpcm_predictor;
287 };
288
289 /// ADPCM Historical Samples (y[n-1] and y[n-2])
290 u16_le adpcm_yn[2];
291
292 union {
293 u16_le flags2_raw;
294 BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
295 BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
296 };
297
298 /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer).
299 u16_le buffer_id;
300 };
301
302 Configuration config[AudioCore::num_sources];
303};
304ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
305ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
306
307struct SourceStatus {
308 struct Status {
309 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
310 u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes
311 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
312 u32_dsp buffer_position; ///< Number of samples into the current buffer
313 u16_le previous_buffer_id; ///< Updated when a buffer finishes playing
314 INSERT_PADDING_DSPWORDS(1);
315 };
316
317 Status status[AudioCore::num_sources];
318};
319ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
320
321struct DspConfiguration {
322 /// These dirty flags are set by the application when it updates the fields in this struct.
323 /// The DSP clears these each audio frame.
324 union {
325 u32_le dirty_raw;
326
327 BitField<8, 1, u32_le> mixer1_enabled_dirty;
328 BitField<9, 1, u32_le> mixer2_enabled_dirty;
329 BitField<10, 1, u32_le> delay_effect_0_dirty;
330 BitField<11, 1, u32_le> delay_effect_1_dirty;
331 BitField<12, 1, u32_le> reverb_effect_0_dirty;
332 BitField<13, 1, u32_le> reverb_effect_1_dirty;
333
334 BitField<16, 1, u32_le> volume_0_dirty;
335
336 BitField<24, 1, u32_le> volume_1_dirty;
337 BitField<25, 1, u32_le> volume_2_dirty;
338 BitField<26, 1, u32_le> output_format_dirty;
339 BitField<27, 1, u32_le> limiter_enabled_dirty;
340 BitField<28, 1, u32_le> headphones_connected_dirty;
341 };
342
343 /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer
344 float_le volume[3];
345
346 INSERT_PADDING_DSPWORDS(3);
347
348 enum class OutputFormat : u16_le {
349 Mono = 0,
350 Stereo = 1,
351 Surround = 2
352 };
353
354 OutputFormat output_format;
355
356 u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
357 u16_le headphones_connected; ///< Application updates the DSP on headphone status.
358 INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
359 INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
360 u16_le mixer1_enabled;
361 u16_le mixer2_enabled;
362
363 /**
364 * This is delay with feedback.
365 * Transfer function:
366 * H(z) = a z^-N / (1 - b z^-1 + a g z^-N)
367 * where
368 * N = frame_count * samples_per_frame
369 * g, a and b are fixed point with 7 fractional bits
370 */
371 struct DelayEffect {
372 /// These dirty flags are set by the application when it updates the fields in this struct.
373 /// The DSP clears these each audio frame.
374 union {
375 u16_le dirty_raw;
376 BitField<0, 1, u16_le> enable_dirty;
377 BitField<1, 1, u16_le> work_buffer_address_dirty;
378 BitField<2, 1, u16_le> other_dirty; ///< Set when anything else has been changed
379 };
380
381 u16_le enable;
382 INSERT_PADDING_DSPWORDS(1);
383 u16_le outputs;
384 u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer.
385 u16_le frame_count; ///< Frames to delay by
386
387 // Coefficients
388 s16_le g; ///< Fixed point with 7 fractional bits
389 s16_le a; ///< Fixed point with 7 fractional bits
390 s16_le b; ///< Fixed point with 7 fractional bits
391 };
392
393 DelayEffect delay_effect[2];
394
395 struct ReverbEffect {
396 INSERT_PADDING_DSPWORDS(26); ///< TODO
397 };
398
399 ReverbEffect reverb_effect[2];
400
401 INSERT_PADDING_DSPWORDS(4);
402};
403ASSERT_DSP_STRUCT(DspConfiguration, 196);
404ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
405ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
406
407struct AdpcmCoefficients {
408 /// Coefficients are signed fixed point with 11 fractional bits.
409 /// Each source has 16 coefficients associated with it.
410 s16_le coeff[AudioCore::num_sources][16];
411};
412ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
413
414struct DspStatus {
415 u16_le unknown;
416 u16_le dropped_frames;
417 INSERT_PADDING_DSPWORDS(0xE);
418};
419ASSERT_DSP_STRUCT(DspStatus, 32);
420
421/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
422/// When the application writes to this region it has no effect.
423struct FinalMixSamples {
424 s16_le pcm16[2 * AudioCore::samples_per_frame];
425};
426ASSERT_DSP_STRUCT(FinalMixSamples, 640);
427
428/// DSP writes output of intermediate mixers 1 and 2 here.
429/// Writes to this region by the application edits the output of the intermediate mixers.
430/// This seems to be intended to allow the application to do custom effects on the ARM11.
431/// Values that exceed s16 range will be clipped by the DSP after further processing.
432struct IntermediateMixSamples {
433 struct Samples {
434 s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
435 };
436
437 Samples mix1;
438 Samples mix2;
439};
440ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120);
441
442/// Compressor table
443struct Compressor {
444 INSERT_PADDING_DSPWORDS(0xD20); ///< TODO
445};
446
447/// There is no easy way to implement this in a HLE implementation.
448struct DspDebug {
449 INSERT_PADDING_DSPWORDS(0x130);
450};
451ASSERT_DSP_STRUCT(DspDebug, 0x260);
452
453struct SharedMemory {
454 /// Padding
455 INSERT_PADDING_DSPWORDS(0x400);
456
457 DspStatus dsp_status;
458
459 DspDebug dsp_debug;
460
461 FinalMixSamples final_samples;
462
463 SourceStatus source_statuses;
464
465 Compressor compressor;
466
467 DspConfiguration dsp_configuration;
468
469 IntermediateMixSamples intermediate_mix_samples;
470
471 SourceConfiguration source_configurations;
472
473 AdpcmCoefficients adpcm_coefficients;
474
475 /// Unknown 10-14 (Surround sound related)
476 INSERT_PADDING_DSPWORDS(0x16ED);
477
478 u16_le frame_counter;
479};
480ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
481
482#undef INSERT_PADDING_DSPWORDS
483#undef ASSERT_DSP_STRUCT
484
485/// Initialize DSP hardware
486void Init();
487
488/// Shutdown DSP hardware
489void Shutdown();
490
491/**
492 * Perform processing and updates state of current shared memory buffer.
493 * This function is called every audio tick before triggering the audio interrupt.
494 * @return Whether an audio interrupt should be triggered this frame.
495 */
496bool Tick();
497
498/// Returns a mutable reference to the current region. Current region is selected based on the frame counter.
499SharedMemory& CurrentRegion();
500
501} // namespace HLE
502} // namespace DSP