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