diff options
Diffstat (limited to 'src/audio_core/hle/dsp.h')
| -rw-r--r-- | src/audio_core/hle/dsp.h | 148 |
1 files changed, 86 insertions, 62 deletions
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index 565f20b6f..0a0f60ac1 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -8,9 +8,7 @@ #include <cstddef> #include <memory> #include <type_traits> - #include "audio_core/hle/common.h" - #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -23,15 +21,15 @@ class Sink; namespace DSP { namespace HLE { -// The application-accessible region of DSP memory consists of two parts. -// Both are marked as IO and have Read/Write permissions. +// The application-accessible region of DSP memory consists of two parts. Both are marked as IO and +// have Read/Write permissions. // // First Region: 0x1FF50000 (Size: 0x8000) // Second Region: 0x1FF70000 (Size: 0x8000) // // The DSP reads from each region alternately based on the frame counter for each region much like a -// double-buffer. The frame counter is located as the very last u16 of each region and is incremented -// each audio tick. +// double-buffer. The frame counter is located as the very last u16 of each region and is +// incremented each audio tick. constexpr VAddr region0_base = 0x1FF50000; constexpr VAddr region1_base = 0x1FF70000; @@ -56,6 +54,7 @@ struct u32_dsp { void operator=(u32 new_value) { storage = Convert(new_value); } + private: static constexpr u32 Convert(u32 value) { return (value << 16) | (value >> 16); @@ -89,13 +88,13 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe. // See also: DSP::HLE::PipeRead. // -// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are -// not fixed in stone. The addresses above are only an examplar; they're what this implementation -// does and provides to applications. +// Note that the above addresses do vary slightly between audio firmwares observed; the addresses +// are not fixed in stone. The addresses above are only an examplar; they're what this +// implementation does and provides to applications. // -// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the -// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the -// second region via: +// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using +// the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for +// the second region via: // second_region_dsp_addr = first_region_dsp_addr | 0x10000 // // Applications maintain most of its own audio state, the memory region is used mainly for @@ -103,21 +102,24 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial // // In the documentation below, filter and effect transfer functions are specified in the z domain. // (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital -// frequency domain, just like how the s domain is the analog frequency domain.) +// frequency domain, just like how the s domain is the analog frequency domain.) #define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words)) // GCC versions < 5.0 do not implement std::is_trivially_copyable. // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. #if (__GNUC__ >= 5) || defined(__clang__) - #define ASSERT_DSP_STRUCT(name, size) \ - static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ - static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \ - static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) +#define ASSERT_DSP_STRUCT(name, size) \ + static_assert(std::is_standard_layout<name>::value, \ + "DSP structure " #name " doesn't use standard layout"); \ + static_assert(std::is_trivially_copyable<name>::value, \ + "DSP structure " #name " isn't trivially copyable"); \ + static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) #else - #define ASSERT_DSP_STRUCT(name, size) \ - static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ - static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) +#define ASSERT_DSP_STRUCT(name, size) \ + static_assert(std::is_standard_layout<name>::value, \ + "DSP structure " #name " doesn't use standard layout"); \ + static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) #endif struct SourceConfiguration { @@ -130,7 +132,8 @@ struct SourceConfiguration { BitField<0, 1, u32_le> format_dirty; BitField<1, 1, u32_le> mono_or_stereo_dirty; BitField<2, 1, u32_le> adpcm_coefficients_dirty; - BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. + /// Tends to be set when a looped buffer is queued. + BitField<3, 1, u32_le> partial_embedded_buffer_dirty; BitField<4, 1, u32_le> partial_reset_flag; BitField<16, 1, u32_le> enable_dirty; @@ -138,7 +141,8 @@ struct SourceConfiguration { BitField<18, 1, u32_le> rate_multiplier_dirty; BitField<19, 1, u32_le> buffer_queue_dirty; BitField<20, 1, u32_le> loop_related_dirty; - BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated. + /// Tends to also be set when embedded buffer is updated. + BitField<21, 1, u32_le> play_position_dirty; BitField<22, 1, u32_le> filters_enabled_dirty; BitField<23, 1, u32_le> simple_filter_dirty; BitField<24, 1, u32_le> biquad_filter_dirty; @@ -153,9 +157,9 @@ struct SourceConfiguration { // Gain control /** - * Gain is between 0.0-1.0. This determines how much will this source appear on - * each of the 12 channels that feed into the intermediate mixers. - * Each of the three intermediate mixers is fed two left and two right channels. + * Gain is between 0.0-1.0. This determines how much will this source appear on each of the + * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers + * is fed two left and two right channels. */ float_le gain[3][4]; @@ -167,7 +171,7 @@ struct SourceConfiguration { enum class InterpolationMode : u8 { Polyphase = 0, Linear = 1, - None = 2 + None = 2, }; InterpolationMode interpolation_mode; @@ -191,8 +195,8 @@ struct SourceConfiguration { * This is a normalised biquad filter (second-order). * The transfer function of this filter is: * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) - * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation - * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html + * Nintendo chose to negate the feedbackward coefficients. This differs from standard + * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html * Values are signed fixed point with 14 fractional bits. */ struct BiquadFilter { @@ -239,23 +243,24 @@ struct SourceConfiguration { /// Is a looping buffer. u8 is_looping; - /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished. - /// This allows the emulated application to tell what buffer is currently playing + /// This value is shown in SourceStatus::previous_buffer_id when this buffer has + /// finished. This allows the emulated application to tell what buffer is currently + /// playing. u16_le buffer_id; INSERT_PADDING_DSPWORDS(1); }; - u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i]) - Buffer buffers[4]; ///< Queued Buffers + u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i]) + Buffer buffers[4]; ///< Queued Buffers // Playback controls u32_dsp loop_related; u8 enable; INSERT_PADDING_BYTES(1); - u16_le sync; ///< Application-side sync (See also: SourceStatus::sync) - u32_dsp play_position; ///< Position. (Units: number of samples) + u16_le sync; ///< Application-side sync (See also: SourceStatus::sync) + u32_dsp play_position; ///< Position. (Units: number of samples) INSERT_PADDING_DSPWORDS(2); // Embedded Buffer @@ -270,13 +275,13 @@ struct SourceConfiguration { enum class MonoOrStereo : u16_le { Mono = 1, - Stereo = 2 + Stereo = 2, }; enum class Format : u16_le { PCM8 = 0, PCM16 = 1, - ADPCM = 2 + ADPCM = 2, }; union { @@ -299,10 +304,11 @@ struct SourceConfiguration { union { u16_le flags2_raw; BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed? - BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? + BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? }; - /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer). + /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this + /// buffer). u16_le buffer_id; }; @@ -313,11 +319,11 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20); struct SourceStatus { struct Status { - u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) - u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes - u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync - u32_dsp buffer_position; ///< Number of samples into the current buffer - u16_le current_buffer_id; ///< Updated when a buffer finishes playing + u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) + u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes + u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync + u32_dsp buffer_position; ///< Number of samples into the current buffer + u16_le current_buffer_id; ///< Updated when a buffer finishes playing INSERT_PADDING_DSPWORDS(1); }; @@ -347,7 +353,8 @@ struct DspConfiguration { BitField<28, 1, u32_le> headphones_connected_dirty; }; - /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer + /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for + /// each at the final mixer. float_le volume[3]; INSERT_PADDING_DSPWORDS(3); @@ -355,7 +362,7 @@ struct DspConfiguration { enum class OutputFormat : u16_le { Mono = 0, Stereo = 1, - Surround = 2 + Surround = 2, }; OutputFormat output_format; @@ -388,8 +395,10 @@ struct DspConfiguration { u16_le enable; INSERT_PADDING_DSPWORDS(1); u16_le outputs; - u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer. - u16_le frame_count; ///< Frames to delay by + /// The application allocates a block of memory for the DSP to use as a work buffer. + u32_dsp work_buffer_address; + /// Frames to delay by + u16_le frame_count; // Coefficients s16_le g; ///< Fixed point with 7 fractional bits @@ -506,21 +515,36 @@ ASSERT_DSP_STRUCT(SharedMemory, 0x8000); extern std::array<SharedMemory, 2> g_regions; // Structures must have an offset that is a multiple of two. -static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); -static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, compressor) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, + "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); #undef INSERT_PADDING_DSPWORDS #undef ASSERT_DSP_STRUCT |
