diff options
Diffstat (limited to 'src/audio_core')
-rw-r--r-- | src/audio_core/audio_core.cpp | 20 | ||||
-rw-r--r-- | src/audio_core/audio_core.h | 2 | ||||
-rw-r--r-- | src/audio_core/codec.cpp | 27 | ||||
-rw-r--r-- | src/audio_core/codec.h | 11 | ||||
-rw-r--r-- | src/audio_core/hle/common.h | 12 | ||||
-rw-r--r-- | src/audio_core/hle/dsp.cpp | 17 | ||||
-rw-r--r-- | src/audio_core/hle/dsp.h | 148 | ||||
-rw-r--r-- | src/audio_core/hle/filter.cpp | 10 | ||||
-rw-r--r-- | src/audio_core/hle/filter.h | 14 | ||||
-rw-r--r-- | src/audio_core/hle/mixers.cpp | 67 | ||||
-rw-r--r-- | src/audio_core/hle/mixers.h | 10 | ||||
-rw-r--r-- | src/audio_core/hle/pipe.cpp | 23 | ||||
-rw-r--r-- | src/audio_core/hle/pipe.h | 12 | ||||
-rw-r--r-- | src/audio_core/hle/source.cpp | 86 | ||||
-rw-r--r-- | src/audio_core/hle/source.h | 19 | ||||
-rw-r--r-- | src/audio_core/interpolate.cpp | 31 | ||||
-rw-r--r-- | src/audio_core/interpolate.h | 7 | ||||
-rw-r--r-- | src/audio_core/null_sink.h | 1 | ||||
-rw-r--r-- | src/audio_core/sdl2_sink.cpp | 25 | ||||
-rw-r--r-- | src/audio_core/sdl2_sink.h | 1 | ||||
-rw-r--r-- | src/audio_core/sink.h | 9 | ||||
-rw-r--r-- | src/audio_core/sink_details.cpp | 6 | ||||
-rw-r--r-- | src/audio_core/time_stretch.cpp | 13 | ||||
-rw-r--r-- | src/audio_core/time_stretch.h | 7 |
24 files changed, 324 insertions, 254 deletions
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 8e19ec0c4..49260de7c 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -4,14 +4,12 @@ #include <memory> #include <string> - #include "audio_core/audio_core.h" #include "audio_core/hle/dsp.h" #include "audio_core/hle/pipe.h" #include "audio_core/null_sink.h" #include "audio_core/sink.h" #include "audio_core/sink_details.h" - #include "core/core_timing.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/dsp_dsp.h" @@ -42,10 +40,18 @@ void Init() { } void AddAddressSpace(Kernel::VMManager& address_space) { - auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); + auto r0_vma = address_space + .MapBackingMemory(DSP::HLE::region0_base, + reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), + sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO) + .MoveFrom(); address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); - auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); + auto r1_vma = address_space + .MapBackingMemory(DSP::HLE::region1_base, + reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), + sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO) + .MoveFrom(); address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); } @@ -58,9 +64,9 @@ void SelectSink(std::string sink_id) { return; } - auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { - return sink_detail.id == sink_id; - }); + auto iter = + std::find_if(g_sink_details.begin(), g_sink_details.end(), + [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); if (iter == g_sink_details.end()) { LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index 7e678aba5..0edf6dd15 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -12,7 +12,7 @@ class VMManager; namespace AudioCore { -constexpr int native_sample_rate = 32728; ///< 32kHz +constexpr int native_sample_rate = 32728; ///< 32kHz /// Initialise Audio Core void Init(); diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp index 3e23323f1..7a3bd7eb3 100644 --- a/src/audio_core/codec.cpp +++ b/src/audio_core/codec.cpp @@ -6,31 +6,32 @@ #include <cstddef> #include <cstring> #include <vector> - #include "audio_core/codec.h" - #include "common/assert.h" #include "common/common_types.h" #include "common/math_util.h" namespace Codec { -StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { +StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, + const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { // GC-ADPCM with scale factor and variable coefficients. // Frames are 8 bytes long containing 14 samples each. // Samples are 4 bits (one nibble) long. constexpr size_t FRAME_LEN = 8; constexpr size_t SAMPLES_PER_FRAME = 14; - constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }}; + constexpr std::array<int, 16> SIGNED_NIBBLES = { + {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; - const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. + const size_t ret_size = + sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. StereoBuffer16 ret(ret_size); - int yn1 = state.yn1, - yn2 = state.yn2; + int yn1 = state.yn1, yn2 = state.yn2; - const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. + const size_t NUM_FRAMES = + (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. for (size_t framei = 0; framei < NUM_FRAMES; framei++) { const int frame_header = data[framei * FRAME_LEN]; const int scale = 1 << (frame_header & 0xF); @@ -43,7 +44,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons // Decodes an audio sample. One nibble produces one sample. const auto decode_sample = [&](const int nibble) -> s16 { const int xn = nibble * scale; - // We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back. + // We first transform everything into 11 bit fixed point, perform the second order + // digital filter, then transform back. // 0x400 == 0.5 in 11 bit fixed point. // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; @@ -82,7 +84,8 @@ static s16 SignExtendS8(u8 x) { return static_cast<s16>(static_cast<s8>(x)); } -StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { +StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, + const size_t sample_count) { ASSERT(num_channels == 1 || num_channels == 2); StereoBuffer16 ret(sample_count); @@ -101,7 +104,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con return ret; } -StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { +StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, + const size_t sample_count) { ASSERT(num_channels == 1 || num_channels == 2); StereoBuffer16 ret(sample_count); @@ -118,5 +122,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co return ret; } - }; diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h index e695f2edc..2b0c395e6 100644 --- a/src/audio_core/codec.h +++ b/src/audio_core/codec.h @@ -6,7 +6,6 @@ #include <array> #include <vector> - #include "common/common_types.h" namespace Codec { @@ -29,7 +28,8 @@ struct ADPCMState { * @param state ADPCM state, this is updated with new state * @return Decoded stereo signed PCM16 data, sample_count in length */ -StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); +StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, + const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); /** * @param num_channels Number of channels @@ -37,7 +37,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons * @param sample_count Length of buffer in terms of number of samples * @return Decoded stereo signed PCM16 data, sample_count in length */ -StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); +StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, + const size_t sample_count); /** * @param num_channels Number of channels @@ -45,6 +46,6 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con * @param sample_count Length of buffer in terms of number of samples * @return Decoded stereo signed PCM16 data, sample_count in length */ -StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); - +StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, + const size_t sample_count); }; diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h index 596b67eaf..7fbc3ad9a 100644 --- a/src/audio_core/hle/common.h +++ b/src/audio_core/hle/common.h @@ -6,30 +6,28 @@ #include <algorithm> #include <array> - #include "common/common_types.h" namespace DSP { namespace HLE { constexpr int num_sources = 24; -constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate +constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; /// The DSP is quadraphonic internally. -using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; +using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; /** * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. * FilterT::ProcessSample is called sequentially on the samples. */ -template<typename FrameT, typename FilterT> +template <typename FrameT, typename FilterT> void FilterFrame(FrameT& frame, FilterT& filter) { - std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) { - return filter.ProcessSample(sample); - }); + std::transform(frame.begin(), frame.end(), frame.begin(), + [&filter](const auto& sample) { return filter.ProcessSample(sample); }); } } // namespace HLE diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp index 1420bf2dd..58690970a 100644 --- a/src/audio_core/hle/dsp.cpp +++ b/src/audio_core/hle/dsp.cpp @@ -4,7 +4,6 @@ #include <array> #include <memory> - #include "audio_core/hle/dsp.h" #include "audio_core/hle/mixers.h" #include "audio_core/hle/pipe.h" @@ -47,10 +46,9 @@ static SharedMemory& WriteRegion() { // Audio processing and mixing static std::array<Source, num_sources> sources = { - Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), - Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), - Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), - Source(18), Source(19), Source(20), Source(21), Source(22), Source(23) + Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(6), Source(7), + Source(8), Source(9), Source(10), Source(11), Source(12), Source(13), Source(14), Source(15), + Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23), }; static Mixers mixers; @@ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() { // Generate intermediate mixes for (size_t i = 0; i < num_sources; i++) { - write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); + write.source_statuses.status[i] = + sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); for (size_t mix = 0; mix < 3; mix++) { sources[i].MixInto(intermediate_mixes[mix], mix); } } // Generate final mix - write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); + write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, + write.intermediate_mix_samples, intermediate_mixes); StereoFrame16 output_frame = mixers.GetOutput(); @@ -152,7 +152,8 @@ void Shutdown() { bool Tick() { StereoFrame16 current_frame = {}; - // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region) + // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to + // shared memory region) current_frame = GenerateCurrentFrame(); OutputCurrentFrame(current_frame); 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 diff --git a/src/audio_core/hle/filter.cpp b/src/audio_core/hle/filter.cpp index 2c65ef026..b24a79b89 100644 --- a/src/audio_core/hle/filter.cpp +++ b/src/audio_core/hle/filter.cpp @@ -4,11 +4,9 @@ #include <array> #include <cstddef> - #include "audio_core/hle/common.h" #include "audio_core/hle/dsp.h" #include "audio_core/hle/filter.h" - #include "common/common_types.h" #include "common/math_util.h" @@ -59,7 +57,9 @@ void SourceFilters::SimpleFilter::Reset() { b0 = 1 << 15; } -void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { +void SourceFilters::SimpleFilter::Configure( + SourceConfiguration::Configuration::SimpleFilter config) { + a1 = config.a1; b0 = config.b0; } @@ -88,7 +88,9 @@ void SourceFilters::BiquadFilter::Reset() { b0 = 1 << 14; } -void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { +void SourceFilters::BiquadFilter::Configure( + SourceConfiguration::Configuration::BiquadFilter config) { + a1 = config.a1; a2 = config.a2; b0 = config.b0; diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h index 43d2035cd..4281a5898 100644 --- a/src/audio_core/hle/filter.h +++ b/src/audio_core/hle/filter.h @@ -5,10 +5,8 @@ #pragma once #include <array> - #include "audio_core/hle/common.h" #include "audio_core/hle/dsp.h" - #include "common/common_types.h" namespace DSP { @@ -17,7 +15,9 @@ namespace HLE { /// Preprocessing filters. There is an independent set of filters for each Source. class SourceFilters final { public: - SourceFilters() { Reset(); } + SourceFilters() { + Reset(); + } /// Reset internal state. void Reset(); @@ -54,7 +54,9 @@ private: bool biquad_filter_enabled; struct SimpleFilter { - SimpleFilter() { Reset(); } + SimpleFilter() { + Reset(); + } /// Resets internal state. void Reset(); @@ -80,7 +82,9 @@ private: } simple_filter; struct BiquadFilter { - BiquadFilter() { Reset(); } + BiquadFilter() { + Reset(); + } /// Resets internal state. void Reset(); diff --git a/src/audio_core/hle/mixers.cpp b/src/audio_core/hle/mixers.cpp index 18335f7f0..6cc81dfca 100644 --- a/src/audio_core/hle/mixers.cpp +++ b/src/audio_core/hle/mixers.cpp @@ -7,7 +7,6 @@ #include "audio_core/hle/common.h" #include "audio_core/hle/dsp.h" #include "audio_core/hle/mixers.h" - #include "common/assert.h" #include "common/logging/log.h" #include "common/math_util.h" @@ -20,11 +19,9 @@ void Mixers::Reset() { state = {}; } -DspStatus Mixers::Tick(DspConfiguration& config, - const IntermediateMixSamples& read_samples, - IntermediateMixSamples& write_samples, - const std::array<QuadFrame32, 3>& input) -{ +DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples, + IntermediateMixSamples& write_samples, + const std::array<QuadFrame32, 3>& input) { ParseConfig(config); AuxReturn(read_samples); @@ -73,13 +70,14 @@ void Mixers::ParseConfig(DspConfiguration& config) { if (config.output_format_dirty) { config.output_format_dirty.Assign(0); state.output_format = config.output_format; - LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); + LOG_TRACE(Audio_DSP, "mixers output_format = %zu", + static_cast<size_t>(config.output_format)); } if (config.headphones_connected_dirty) { config.headphones_connected_dirty.Assign(0); - // Do nothing. - // (Note: Whether headphones are connected does affect coefficients used for surround sound.) + // Do nothing. (Note: Whether headphones are connected does affect coefficients used for + // surround sound.) LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); } @@ -94,11 +92,10 @@ static s16 ClampToS16(s32 value) { return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); } -static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { - return { - ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), - ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) - }; +static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, + const std::array<s16, 2>& b) { + return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), + ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))}; } void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { @@ -106,27 +103,33 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample switch (state.output_format) { case OutputFormat::Mono: - std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), - [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { + std::transform( + current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), + [gain](const std::array<s16, 2>& accumulator, + const std::array<s32, 4>& sample) -> std::array<s16, 2> { // Downmix to mono - s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); + s16 mono = ClampToS16(static_cast<s32>( + (gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / + 2)); // Mix into current frame - return AddAndClampToS16(accumulator, { mono, mono }); + return AddAndClampToS16(accumulator, {mono, mono}); }); return; case OutputFormat::Surround: - // TODO(merry): Implement surround sound. - // fallthrough + // TODO(merry): Implement surround sound. + // fallthrough case OutputFormat::Stereo: - std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), - [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { + std::transform( + current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), + [gain](const std::array<s16, 2>& accumulator, + const std::array<s32, 4>& sample) -> std::array<s16, 2> { // Downmix to stereo s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); // Mix into current frame - return AddAndClampToS16(accumulator, { left, right }); + return AddAndClampToS16(accumulator, {left, right}); }); return; } @@ -135,12 +138,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample } void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { - // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. + // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to + // QuadFrame32. if (state.mixer1_enabled) { for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t channel = 0; channel < 4; channel++) { - state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; + state.intermediate_mix_buffer[1][sample][channel] = + read_samples.mix1.pcm32[channel][sample]; } } } @@ -148,14 +153,17 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { if (state.mixer2_enabled) { for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t channel = 0; channel < 4; channel++) { - state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; + state.intermediate_mix_buffer[2][sample][channel] = + read_samples.mix2.pcm32[channel][sample]; } } } } -void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { - // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. +void Mixers::AuxSend(IntermediateMixSamples& write_samples, + const std::array<QuadFrame32, 3>& input) { + // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to + // QuadFrame32. state.intermediate_mix_buffer[0] = input[0]; @@ -184,7 +192,8 @@ void Mixers::MixCurrentFrame() { current_frame.fill({}); for (size_t mix = 0; mix < 3; mix++) { - DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); + DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], + state.intermediate_mix_buffer[mix]); } // TODO(merry): Compressor. (We currently assume a disabled compressor.) diff --git a/src/audio_core/hle/mixers.h b/src/audio_core/hle/mixers.h index b52952eb5..bf4e865ae 100644 --- a/src/audio_core/hle/mixers.h +++ b/src/audio_core/hle/mixers.h @@ -5,7 +5,6 @@ #pragma once #include <array> - #include "audio_core/hle/common.h" #include "audio_core/hle/dsp.h" @@ -20,10 +19,8 @@ public: void Reset(); - DspStatus Tick(DspConfiguration& config, - const IntermediateMixSamples& read_samples, - IntermediateMixSamples& write_samples, - const std::array<QuadFrame32, 3>& input); + DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples, + IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); StereoFrame16 GetOutput() const { return current_frame; @@ -53,7 +50,8 @@ private: void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); /// INTERNAL: Mix current_frame. void MixCurrentFrame(); - /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame. + /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate + /// into current_frame. void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); /// INTERNAL: Generate DspStatus based on internal state. DspStatus GetCurrentStatus() const; diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp index 44dff1345..b472c81d8 100644 --- a/src/audio_core/hle/pipe.cpp +++ b/src/audio_core/hle/pipe.cpp @@ -4,14 +4,11 @@ #include <array> #include <vector> - #include "audio_core/hle/dsp.h" #include "audio_core/hle/pipe.h" - #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" - #include "core/hle/service/dsp_dsp.h" namespace DSP { @@ -44,8 +41,10 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { std::vector<u8>& data = pipe_data[pipe_index]; if (length > data.size()) { - LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", - pipe_index, length, data.size()); + LOG_WARNING( + Audio_DSP, + "pipe_number = %zu is out of data, application requested read of %u but %zu remain", + pipe_index, length, data.size()); length = static_cast<u32>(data.size()); } @@ -95,7 +94,7 @@ static void AudioPipeWriteStructAddresses() { 0x8000 + offsetof(SharedMemory, unknown11) / 2, 0x8000 + offsetof(SharedMemory, unknown12) / 2, 0x8000 + offsetof(SharedMemory, unknown13) / 2, - 0x8000 + offsetof(SharedMemory, unknown14) / 2 + 0x8000 + offsetof(SharedMemory, unknown14) / 2, }; // Begin with a u16 denoting the number of structs. @@ -112,7 +111,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { switch (pipe_number) { case DspPipe::Audio: { if (buffer.size() != 4) { - LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); + LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", + buffer.size()); return; } @@ -120,7 +120,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { Initalize = 0, Shutdown = 1, Wakeup = 2, - Sleep = 3 + Sleep = 3, }; // The difference between Initialize and Wakeup is that Input state is maintained @@ -152,7 +152,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { dsp_state = DspState::Sleeping; break; default: - LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); + LOG_ERROR(Audio_DSP, + "Application has requested unknown state transition of DSP hardware %hhu", + buffer[0]); dsp_state = DspState::Off; break; } @@ -160,7 +162,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { return; } default: - LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); + LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", + static_cast<size_t>(pipe_number)); UNIMPLEMENTED(); return; } diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h index b714c0496..ac053c029 100644 --- a/src/audio_core/hle/pipe.h +++ b/src/audio_core/hle/pipe.h @@ -6,7 +6,6 @@ #include <cstddef> #include <vector> - #include "common/common_types.h" namespace DSP { @@ -19,16 +18,18 @@ enum class DspPipe { Debug = 0, Dma = 1, Audio = 2, - Binary = 3 + Binary = 3, }; constexpr size_t NUM_DSP_PIPE = 8; /** * Reads `length` bytes from the DSP pipe identified with `pipe_number`. * @note Can read up to the maximum value of a u16 in bytes (65,535). - * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned. + * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty + * vector will be returned. * @note IF `length` is set to 0, an empty vector will be returned. - * @note IF `length` is greater than the amount of data available, this function will only read the available amount. + * @note IF `length` is greater than the amount of data available, this function will only read the + * available amount. * @param pipe_number a `DspPipe` * @param length the number of bytes to read. The max is 65,535 (max of u16). * @returns a vector of bytes from the specified pipe. On error, will be empty. @@ -52,8 +53,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer); enum class DspState { Off, On, - Sleeping + Sleeping, }; + /// Get the state of the DSP DspState GetDspState(); diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp index 30552fe26..2bbf7146e 100644 --- a/src/audio_core/hle/source.cpp +++ b/src/audio_core/hle/source.cpp @@ -4,21 +4,19 @@ #include <algorithm> #include <array> - #include "audio_core/codec.h" #include "audio_core/hle/common.h" #include "audio_core/hle/source.h" #include "audio_core/interpolate.h" - #include "common/assert.h" #include "common/logging/log.h" - #include "core/memory.h" namespace DSP { namespace HLE { -SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { +SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, + const s16_le (&adpcm_coeffs)[16]) { ParseConfig(config, adpcm_coeffs); if (state.enabled) { @@ -47,7 +45,8 @@ void Source::Reset() { state = {}; } -void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { +void Source::ParseConfig(SourceConfiguration::Configuration& config, + const s16_le (&adpcm_coeffs)[16]) { if (!config.dirty_raw) { return; } @@ -82,7 +81,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); if (state.rate_multiplier <= 0) { - LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); + LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", + source_id, state.rate_multiplier); state.rate_multiplier = 1.0f; // Note: Actual firmware starts producing garbage if this occurs. } @@ -90,37 +90,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l if (config.adpcm_coefficients_dirty) { config.adpcm_coefficients_dirty.Assign(0); - std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), - [](const auto& coeff) { return static_cast<s16>(coeff); }); + std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), + state.adpcm_coeffs.begin(), + [](const auto& coeff) { return static_cast<s16>(coeff); }); LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); } if (config.gain_0_dirty) { config.gain_0_dirty.Assign(0); std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), - [](const auto& coeff) { return static_cast<float>(coeff); }); + [](const auto& coeff) { return static_cast<float>(coeff); }); LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); } if (config.gain_1_dirty) { config.gain_1_dirty.Assign(0); std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), - [](const auto& coeff) { return static_cast<float>(coeff); }); + [](const auto& coeff) { return static_cast<float>(coeff); }); LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); } if (config.gain_2_dirty) { config.gain_2_dirty.Assign(0); std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), - [](const auto& coeff) { return static_cast<float>(coeff); }); + [](const auto& coeff) { return static_cast<float>(coeff); }); LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); } if (config.filters_enabled_dirty) { config.filters_enabled_dirty.Assign(0); - state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); - LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", - source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); + state.filters.Enable(config.simple_filter_enabled.ToBool(), + config.biquad_filter_enabled.ToBool()); + LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id, + config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); } if (config.simple_filter_dirty) { @@ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l if (config.interpolation_dirty) { config.interpolation_dirty.Assign(0); state.interpolation_mode = config.interpolation_mode; - LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); + LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, + static_cast<size_t>(state.interpolation_mode)); } if (config.format_dirty || config.embedded_buffer_dirty) { config.format_dirty.Assign(0); state.format = config.format; - LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); + LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, + static_cast<size_t>(state.format)); } if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { config.mono_or_stereo_dirty.Assign(0); state.mono_or_stereo = config.mono_or_stereo; - LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); + LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, + static_cast<size_t>(state.mono_or_stereo)); } if (config.embedded_buffer_dirty) { @@ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l config.physical_address, config.length, static_cast<u8>(config.adpcm_ps), - { config.adpcm_yn[0], config.adpcm_yn[1] }, + {config.adpcm_yn[0], config.adpcm_yn[1]}, config.adpcm_dirty.ToBool(), config.is_looping.ToBool(), config.buffer_id, state.mono_or_stereo, state.format, - false + false, }); - LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id); + LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", + config.physical_address, config.length, config.buffer_id); } if (config.buffer_queue_dirty) { @@ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l b.physical_address, b.length, static_cast<u8>(b.adpcm_ps), - { b.adpcm_yn[0], b.adpcm_yn[1] }, + {b.adpcm_yn[0], b.adpcm_yn[1]}, b.adpcm_dirty != 0, b.is_looping != 0, b.buffer_id, state.mono_or_stereo, state.format, - true + true, }); - LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id); + LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, + b.physical_address, b.length, b.buffer_id); } } config.buffers_dirty = 0; @@ -218,10 +225,13 @@ void Source::GenerateFrame() { break; } - const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); + const size_t size_to_copy = + std::min(state.current_buffer.size(), current_frame.size() - frame_position); - std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); - state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); + std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, + current_frame.begin() + frame_position); + state.current_buffer.erase(state.current_buffer.begin(), + state.current_buffer.begin() + size_to_copy); frame_position += size_to_copy; state.next_sample_number += static_cast<u32>(size_to_copy); @@ -230,9 +240,9 @@ void Source::GenerateFrame() { state.filters.ProcessFrame(current_frame); } - bool Source::DequeueBuffer() { - ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); + ASSERT_MSG(state.current_buffer.empty(), + "Shouldn't dequeue; we still have data in current_buffer"); if (state.input_queue.empty()) return false; @@ -261,29 +271,34 @@ bool Source::DequeueBuffer() { break; case Format::ADPCM: DEBUG_ASSERT(num_channels == 1); - state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); + state.current_buffer = + Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); break; default: UNIMPLEMENTED(); break; } } else { - LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", - source_id, buf.buffer_id, buf.length, buf.physical_address); + LOG_WARNING(Audio_DSP, + "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", + source_id, buf.buffer_id, buf.length, buf.physical_address); state.current_buffer.clear(); return true; } switch (state.interpolation_mode) { case InterpolationMode::None: - state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); + state.current_buffer = + AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); break; case InterpolationMode::Linear: - state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); + state.current_buffer = + AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); break; case InterpolationMode::Polyphase: // TODO(merry): Implement polyphase interpolation - state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); + state.current_buffer = + AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); break; default: UNIMPLEMENTED(); @@ -296,7 +311,8 @@ bool Source::DequeueBuffer() { state.buffer_update = buf.from_queue; LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", - source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); + source_id, buf.buffer_id, buf.from_queue ? "true" : "false", + state.current_buffer.size()); return true; } diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h index 7ee08d424..3d725f2a3 100644 --- a/src/audio_core/hle/source.h +++ b/src/audio_core/hle/source.h @@ -7,13 +7,11 @@ #include <array> #include <queue> #include <vector> - #include "audio_core/codec.h" #include "audio_core/hle/common.h" #include "audio_core/hle/dsp.h" #include "audio_core/hle/filter.h" #include "audio_core/interpolate.h" - #include "common/common_types.h" namespace DSP { @@ -40,13 +38,17 @@ public: /** * This is called once every audio frame. This performs per-source processing every frame. * @param config The new configuration we've got for this Source from the application. - * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise). - * @return The current status of this Source. This is given back to the emulated application via SharedMemory. + * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain + * invalid values otherwise). + * @return The current status of this Source. This is given back to the emulated application via + * SharedMemory. */ - SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); + SourceStatus::Status Tick(SourceConfiguration::Configuration& config, + const s16_le (&adpcm_coeffs)[16]); /** - * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer. + * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th + * intermediate mixer. * @param dest The QuadFrame32 to mix into. * @param intermediate_mix_id The id of the intermediate mix whose gains we are using. */ @@ -77,7 +79,7 @@ private: }; struct BufferOrder { - bool operator() (const Buffer& a, const Buffer& b) const { + bool operator()(const Buffer& a, const Buffer& b) const { // Lower buffer_id comes first. return a.buffer_id > b.buffer_id; } @@ -134,7 +136,8 @@ private: void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); /// INTERNAL: Generate the current audio output for this frame based on our internal state. void GenerateFrame(); - /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer. + /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it + /// into current_buffer. bool DequeueBuffer(); /// INTERNAL: Generates a SourceStatus::Status based on our internal state. SourceStatus::Status GetCurrentStatus(); diff --git a/src/audio_core/interpolate.cpp b/src/audio_core/interpolate.cpp index fcd3aa066..8a5d4181a 100644 --- a/src/audio_core/interpolate.cpp +++ b/src/audio_core/interpolate.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include "audio_core/interpolate.h" - #include "common/assert.h" #include "common/math_util.h" @@ -17,7 +16,8 @@ constexpr u64 scale_mask = scale_factor - 1; /// Here we step over the input in steps of rate_multiplier, until we consume all of the input. /// Three adjacent samples are passed to fn each step. template <typename Function> -static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { +static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, + float rate_multiplier, Function fn) { ASSERT(rate_multiplier > 0); if (input.size() < 2) @@ -63,23 +63,24 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, } StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { - return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { - return x0; - }); + return StepOverSamples( + state, input, rate_multiplier, + [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); } StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. - return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { - // This is a saturated subtraction. (Verified by black-box fuzzing.) - s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); - s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); - - return std::array<s16, 2> { - static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), - static_cast<s16>(x0[1] + fraction * delta1 / scale_factor) - }; - }); + return StepOverSamples(state, input, rate_multiplier, + [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { + // This is a saturated subtraction. (Verified by black-box fuzzing.) + s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); + s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); + + return std::array<s16, 2>{ + static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), + static_cast<s16>(x0[1] + fraction * delta1 / scale_factor), + }; + }); } } // namespace AudioInterp diff --git a/src/audio_core/interpolate.h b/src/audio_core/interpolate.h index a4c0a453d..dd06fdda9 100644 --- a/src/audio_core/interpolate.h +++ b/src/audio_core/interpolate.h @@ -6,7 +6,6 @@ #include <array> #include <vector> - #include "common/common_types.h" namespace AudioInterp { @@ -24,7 +23,8 @@ struct State { * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. * @param input Input buffer. * @param rate_multiplier Stretch factor. Must be a positive non-zero value. - * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. + * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 + * performs upsampling. * @return The resampled audio buffer. */ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); @@ -33,7 +33,8 @@ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multip * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. * @param input Input buffer. * @param rate_multiplier Stretch factor. Must be a positive non-zero value. - * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. + * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 + * performs upsampling. * @return The resampled audio buffer. */ StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index 9931c4778..e7668438c 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h @@ -5,7 +5,6 @@ #pragma once #include <cstddef> - #include "audio_core/audio_core.h" #include "audio_core/sink.h" diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp index 1d7912715..75cc0d6dd 100644 --- a/src/audio_core/sdl2_sink.cpp +++ b/src/audio_core/sdl2_sink.cpp @@ -3,16 +3,13 @@ // Refer to the license.txt file included. #include <list> +#include <numeric> #include <vector> - #include <SDL.h> - #include "audio_core/audio_core.h" #include "audio_core/sdl2_sink.h" - #include "common/assert.h" #include "common/logging/log.h" -#include <numeric> namespace AudioCore { @@ -45,7 +42,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { SDL_AudioSpec obtained_audiospec; SDL_zero(obtained_audiospec); - impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); + impl->audio_device_id = + SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); if (impl->audio_device_id <= 0) { LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); return; @@ -86,11 +84,12 @@ size_t SDL2Sink::SamplesInQueue() const { SDL_LockAudioDevice(impl->audio_device_id); - size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), - [](size_t sum, const auto& buffer) { - // Division by two because each stereo sample is made of two s16. - return sum + buffer.size() / 2; - }); + size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), + static_cast<size_t>(0), [](size_t sum, const auto& buffer) { + // Division by two because each stereo sample is made of + // two s16. + return sum + buffer.size() / 2; + }); SDL_UnlockAudioDevice(impl->audio_device_id); @@ -100,7 +99,8 @@ size_t SDL2Sink::SamplesInQueue() const { void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { Impl* impl = reinterpret_cast<Impl*>(impl_); - size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. + size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / + sizeof(s16); // Keep track of size in 16-bit increments. while (remaining_size > 0 && !impl->queue.empty()) { if (impl->queue.front().size() <= remaining_size) { @@ -111,7 +111,8 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) } else { memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); buffer += remaining_size * sizeof(s16); - impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); + impl->queue.front().erase(impl->queue.front().begin(), + impl->queue.front().begin() + remaining_size); remaining_size = 0; } } diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h index b13827214..ccd0f7c7e 100644 --- a/src/audio_core/sdl2_sink.h +++ b/src/audio_core/sdl2_sink.h @@ -6,7 +6,6 @@ #include <cstddef> #include <memory> - #include "audio_core/sink.h" namespace AudioCore { diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h index a06fc3dcc..08f3bab5b 100644 --- a/src/audio_core/sink.h +++ b/src/audio_core/sink.h @@ -5,20 +5,21 @@ #pragma once #include <vector> - #include "common/common_types.h" namespace AudioCore { /** - * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output. - * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs. + * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed + * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate. + * They are dumb outputs. */ class Sink { public: virtual ~Sink() = default; - /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) + /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: + /// samples/sec) virtual unsigned int GetNativeSampleRate() const = 0; /** diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index ba5e83d17..95ccc9e9d 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp @@ -4,10 +4,8 @@ #include <memory> #include <vector> - #include "audio_core/null_sink.h" #include "audio_core/sink_details.h" - #ifdef HAVE_SDL2 #include "audio_core/sdl2_sink.h" #endif @@ -17,9 +15,9 @@ namespace AudioCore { // g_sink_details is ordered in terms of desirability, with the best choice at the top. const std::vector<SinkDetails> g_sink_details = { #ifdef HAVE_SDL2 - { "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, + {"sdl2", []() { return std::make_unique<SDL2Sink>(); }}, #endif - { "null", []() { return std::make_unique<NullSink>(); } }, + {"null", []() { return std::make_unique<NullSink>(); }}, }; } // namespace AudioCore diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index ea38f40d0..53cb64655 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp @@ -5,12 +5,9 @@ #include <chrono> #include <cmath> #include <vector> - #include <SoundTouch.h> - #include "audio_core/audio_core.h" #include "audio_core/time_stretch.h" - #include "common/common_types.h" #include "common/logging/log.h" #include "common/math_util.h" @@ -26,8 +23,8 @@ static double ClampRatio(double ratio) { return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO); } -constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds -constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds +constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds +constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples constexpr double SMOOTHING_FACTOR = 0.007; @@ -48,7 +45,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) { double ratio = CalculateCurrentRatio(); ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); - impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; + impl->smoothed_ratio = + (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); // SoundTouch's tempo definition the inverse of our ratio definition. @@ -100,7 +98,8 @@ double TimeStretcher::CalculateCurrentRatio() { const steady_clock::time_point now = steady_clock::now(); const std::chrono::duration<double> duration = now - impl->frame_timer; - const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); + const double expected_time = + static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); const double actual_time = duration.count(); double ratio; diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index 1fde3f72a..fa81718ed 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h @@ -5,7 +5,6 @@ #include <cstddef> #include <memory> #include <vector> - #include "common/common_types.h" namespace AudioCore { @@ -37,7 +36,8 @@ public: /** * Does audio stretching and produces the time-stretched samples. * Timer calculations use sample_delay to determine how much of a margin we have. - * @param sample_delay How many samples are buffered downstream of this module and haven't been played yet. + * @param sample_delay How many samples are buffered downstream of this module and haven't been + * played yet. * @return Samples to play in interleaved stereo PCM16 format. */ std::vector<s16> Process(size_t sample_delay); @@ -48,7 +48,8 @@ private: /// INTERNAL: ratio = wallclock time / emulated time double CalculateCurrentRatio(); - /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction. + /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate + /// direction. double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; /// INTERNAL: Gets the time-stretched samples from SoundTouch. std::vector<s16> GetSamples(); |