diff options
Diffstat (limited to 'src/audio_core/hle/dsp.h')
| -rw-r--r-- | src/audio_core/hle/dsp.h | 595 |
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 | |||
| 17 | namespace AudioCore { | ||
| 18 | class Sink; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace DSP { | ||
| 22 | namespace 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 | |||
| 34 | constexpr u32 region0_offset = 0x50000; | ||
| 35 | constexpr 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 | */ | ||
| 49 | struct 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 | |||
| 58 | private: | ||
| 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) | ||
| 65 | static_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 | |||
| 125 | struct 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 | }; | ||
| 317 | ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192); | ||
| 318 | ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20); | ||
| 319 | |||
| 320 | struct 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 | }; | ||
| 332 | ASSERT_DSP_STRUCT(SourceStatus::Status, 12); | ||
| 333 | |||
| 334 | struct 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 | }; | ||
| 419 | ASSERT_DSP_STRUCT(DspConfiguration, 196); | ||
| 420 | ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20); | ||
| 421 | ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52); | ||
| 422 | |||
| 423 | struct 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 | }; | ||
| 428 | ASSERT_DSP_STRUCT(AdpcmCoefficients, 768); | ||
| 429 | |||
| 430 | struct DspStatus { | ||
| 431 | u16_le unknown; | ||
| 432 | u16_le dropped_frames; | ||
| 433 | INSERT_PADDING_DSPWORDS(0xE); | ||
| 434 | }; | ||
| 435 | ASSERT_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. | ||
| 439 | struct FinalMixSamples { | ||
| 440 | s16_le pcm16[samples_per_frame][2]; | ||
| 441 | }; | ||
| 442 | ASSERT_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. | ||
| 448 | struct 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 | }; | ||
| 456 | ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120); | ||
| 457 | |||
| 458 | /// Compressor table | ||
| 459 | struct Compressor { | ||
| 460 | INSERT_PADDING_DSPWORDS(0xD20); ///< TODO | ||
| 461 | }; | ||
| 462 | |||
| 463 | /// There is no easy way to implement this in a HLE implementation. | ||
| 464 | struct DspDebug { | ||
| 465 | INSERT_PADDING_DSPWORDS(0x130); | ||
| 466 | }; | ||
| 467 | ASSERT_DSP_STRUCT(DspDebug, 0x260); | ||
| 468 | |||
| 469 | struct 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 | }; | ||
| 513 | ASSERT_DSP_STRUCT(SharedMemory, 0x8000); | ||
| 514 | |||
| 515 | union 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 | }; | ||
| 525 | static_assert(offsetof(DspMemory, region_0) == region0_offset, | ||
| 526 | "DSP region 0 is at the wrong offset"); | ||
| 527 | static_assert(offsetof(DspMemory, region_1) == region1_offset, | ||
| 528 | "DSP region 1 is at the wrong offset"); | ||
| 529 | |||
| 530 | extern DspMemory g_dsp_memory; | ||
| 531 | |||
| 532 | // Structures must have an offset that is a multiple of two. | ||
| 533 | static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, | ||
| 534 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 535 | static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, | ||
| 536 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 537 | static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, | ||
| 538 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 539 | static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, | ||
| 540 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 541 | static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, | ||
| 542 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 543 | static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, | ||
| 544 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 545 | static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, | ||
| 546 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 547 | static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, | ||
| 548 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 549 | static_assert(offsetof(SharedMemory, compressor) % 2 == 0, | ||
| 550 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 551 | static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, | ||
| 552 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 553 | static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, | ||
| 554 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 555 | static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, | ||
| 556 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 557 | static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, | ||
| 558 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 559 | static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, | ||
| 560 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 561 | static_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 | ||
| 568 | void Init(); | ||
| 569 | |||
| 570 | /// Shutdown DSP hardware | ||
| 571 | void 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 | */ | ||
| 578 | bool 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 | */ | ||
| 584 | void 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 | */ | ||
| 592 | void EnableStretching(bool enable); | ||
| 593 | |||
| 594 | } // namespace HLE | ||
| 595 | } // namespace DSP | ||