diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index ae6f29522d..daf7e08820 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "AudioCommon/Enums.h" #include "Common/ChunkFile.h" @@ -67,8 +68,7 @@ void Mixer::MixerFifo::Mix(s16* samples, std::size_t num_samples) // We need at least a double because the index jump has 24 bits of fractional precision. const double out_sample_rate = m_mixer->m_output_sample_rate; - double in_sample_rate = - static_cast(FIXED_SAMPLE_RATE_DIVIDEND) / m_input_sample_rate_divisor; + double in_sample_rate = double(m_input_sample_rate_dividend) / m_input_sample_rate_divisor; const double emulation_speed = m_mixer->m_config_emulation_speed; if (!m_mixer->m_config_audio_preserve_pitch && 0 < emulation_speed && emulation_speed != 1.0) @@ -208,29 +208,17 @@ std::size_t Mixer::MixSurround(float* samples, std::size_t num_samples) return num_samples; } -void Mixer::MixerFifo::PushSamples(const s16* samples, std::size_t num_samples) -{ - while (num_samples-- > 0) - { - const s16 l = m_little_endian ? samples[1] : Common::swap16(samples[1]); - const s16 r = m_little_endian ? samples[0] : Common::swap16(samples[0]); - samples += 2; - - m_next_buffer[m_next_buffer_index] = StereoPair(l, r); - m_next_buffer_index = (m_next_buffer_index + 1) & GRANULE_MASK; - - // The granules overlap by 50%, so we need to enqueue the - // next buffer every time we fill half of the samples. - if (m_next_buffer_index == 0 || m_next_buffer_index == m_next_buffer.size() / 2) - Enqueue(); - } -} - void Mixer::PushSamples(const s16* samples, std::size_t num_samples) { if (IsOutputSampleRateValid()) { - m_dma_mixer.PushSamples(samples, num_samples); + // Big-endian RL-orderered stereo samples. + + while (num_samples--) + { + m_dma_mixer.PushSample(Common::swap16(samples[1]), Common::swap16(samples[0])); + samples += 2; + } } if (m_log_dsp_audio) @@ -246,7 +234,13 @@ void Mixer::PushStreamingSamples(const s16* samples, std::size_t num_samples) { if (IsOutputSampleRateValid()) { - m_streaming_mixer.PushSamples(samples, num_samples); + // Big-endian RL-orderered stereo samples. + + while (num_samples--) + { + m_streaming_mixer.PushSample(Common::swap16(samples[1]), Common::swap16(samples[0])); + samples += 2; + } } if (m_log_dtk_audio) @@ -264,24 +258,13 @@ void Mixer::PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_sample if (!IsOutputSampleRateValid()) return; - // Max 20 bytes/speaker report, may be 4-bit ADPCM so multiply by 2 - static constexpr std::size_t MAX_SPEAKER_SAMPLES = 20 * 2; - std::array samples_stereo; + // WiimoteEmu produces host-endian mono samples. - ASSERT_MSG(AUDIO, num_samples <= MAX_SPEAKER_SAMPLES, - "num_samples would overflow samples_stereo: {} > {}", num_samples, - MAX_SPEAKER_SAMPLES); - if (num_samples <= MAX_SPEAKER_SAMPLES) + m_wiimote_speaker_mixer.SetInputSampleRateDivisor(sample_rate_divisor); + + for (const s16 sample : std::span{samples, num_samples}) { - m_wiimote_speaker_mixer.SetInputSampleRateDivisor(sample_rate_divisor); - - for (std::size_t i = 0; i < num_samples; ++i) - { - samples_stereo[i * 2] = samples[i]; - samples_stereo[i * 2 + 1] = samples[i]; - } - - m_wiimote_speaker_mixer.PushSamples(samples_stereo.data(), num_samples); + m_wiimote_speaker_mixer.PushSample(sample, sample); } } @@ -292,24 +275,13 @@ void Mixer::PushSkylanderPortalSamples(const u8* samples, std::size_t num_sample // Skylander samples are always supplied as 64 bytes, 32 x 16 bit samples // The portal speaker is 1 channel, so duplicate and play as stereo audio - static constexpr std::size_t MAX_PORTAL_SPEAKER_SAMPLES = 32; - std::array samples_stereo; - ASSERT_MSG(AUDIO, num_samples <= MAX_PORTAL_SPEAKER_SAMPLES, - "num_samples is not less or equal to 32: {} > {}", num_samples, - MAX_PORTAL_SPEAKER_SAMPLES); - - if (num_samples <= MAX_PORTAL_SPEAKER_SAMPLES) + while (num_samples--) { - for (std::size_t i = 0; i < num_samples; ++i) - { - const s16 sample = - static_cast(samples[i * 2 + 1]) << 8 | static_cast(samples[i * 2]); - samples_stereo[i * 2] = sample; - samples_stereo[i * 2 + 1] = sample; - } - - m_skylander_portal_mixer.PushSamples(samples_stereo.data(), num_samples); + // Little-endian data. + const s16 sample = u16(samples[0] | u16(samples[1] << 8u)); + m_skylander_portal_mixer.PushSample(sample, sample); + samples += 2; } } @@ -318,7 +290,13 @@ void Mixer::PushGBASamples(std::size_t device_number, const s16* samples, std::s if (!IsOutputSampleRateValid()) return; - m_gba_mixers[device_number].PushSamples(samples, num_samples); + // Integrated GBA pushes host-endian LR-ordered stereo samples. + + while (num_samples--) + { + m_gba_mixers[device_number].PushSample(samples[0], samples[1]); + samples += 2; + } } void Mixer::SetDMAInputSampleRateDivisor(u32 rate_divisor) @@ -331,9 +309,9 @@ void Mixer::SetStreamInputSampleRateDivisor(u32 rate_divisor) m_streaming_mixer.SetInputSampleRateDivisor(rate_divisor); } -void Mixer::SetGBAInputSampleRateDivisors(std::size_t device_number, u32 rate_divisor) +void Mixer::SetGBAInputSampleRate(std::size_t device_number, u32 sample_rate) { - m_gba_mixers[device_number].SetInputSampleRateDivisor(rate_divisor); + m_gba_mixers[device_number].SetInputSampleRateDivisor(GBA_SAMPLE_RATE_DIVIDEND / sample_rate); } void Mixer::SetStreamingVolume(u32 lvolume, u32 rvolume) @@ -442,6 +420,16 @@ void Mixer::MixerFifo::DoState(PointerWrap& p) p.Do(m_RVolume); } +void Mixer::MixerFifo::SetInputSampleRateDividend(u32 rate_dividend) +{ + m_input_sample_rate_dividend = rate_dividend; +} + +u32 Mixer::MixerFifo::GetInputSampleRateDividend() const +{ + return m_input_sample_rate_dividend; +} + void Mixer::MixerFifo::SetInputSampleRateDivisor(u32 rate_divisor) { m_input_sample_rate_divisor = rate_divisor; diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index 87939895e2..6e5a52eecd 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -11,6 +11,7 @@ #include "AudioCommon/WaveFile.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Common/Inline.h" class PointerWrap; @@ -42,7 +43,7 @@ public: void SetDMAInputSampleRateDivisor(u32 rate_divisor); void SetStreamInputSampleRateDivisor(u32 rate_divisor); - void SetGBAInputSampleRateDivisors(std::size_t device_number, u32 rate_divisor); + void SetGBAInputSampleRate(std::size_t device_number, u32 sample_rate); void SetStreamingVolume(u32 lvolume, u32 rvolume); void SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume); @@ -100,23 +101,42 @@ private: using Granule = std::array; public: - MixerFifo(Mixer* mixer, u32 sample_rate_divisor, bool little_endian) - : m_mixer(mixer), m_input_sample_rate_divisor(sample_rate_divisor), - m_little_endian(little_endian) + MixerFifo(Mixer* mixer, u32 sample_rate_divisor, + u32 sample_rate_dividend = FIXED_SAMPLE_RATE_DIVIDEND) + : m_mixer(mixer), m_input_sample_rate_dividend(sample_rate_dividend), + m_input_sample_rate_divisor(sample_rate_divisor) { } void DoState(PointerWrap& p); - void PushSamples(const s16* samples, std::size_t num_samples); + + DOLPHIN_FORCE_INLINE void PushSample(s16 left, s16 right) + { + m_next_buffer[m_next_buffer_index] = {left, right}; + m_next_buffer_index = (m_next_buffer_index + 1) & GRANULE_MASK; + + // The granules overlap by 50%, so we need to enqueue the + // next buffer every time we fill half of the samples. + if (m_next_buffer_index == 0 || m_next_buffer_index == m_next_buffer.size() / 2) + Enqueue(); + } + void Mix(s16* samples, std::size_t num_samples); + + void SetInputSampleRateDividend(u32 rate_dividend); + u32 GetInputSampleRateDividend() const; + void SetInputSampleRateDivisor(u32 rate_divisor); u32 GetInputSampleRateDivisor() const; + void SetVolume(u32 lvolume, u32 rvolume); std::pair GetVolume() const; private: Mixer* m_mixer; + + // All non-GBA MixerFifo instances use FIXED_SAMPLE_RATE_DIVIDEND. + u32 m_input_sample_rate_dividend; u32 m_input_sample_rate_divisor; - bool m_little_endian; Granule m_next_buffer{}; std::size_t m_next_buffer_index = 0; @@ -144,14 +164,21 @@ private: void RefreshConfig(); - MixerFifo m_dma_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 32000, false}; - MixerFifo m_streaming_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, false}; - MixerFifo m_wiimote_speaker_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000, true}; - MixerFifo m_skylander_portal_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 8000, true}; - std::array m_gba_mixers{MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}, - MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}, - MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}, - MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}}; + MixerFifo m_dma_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 32000}; + MixerFifo m_streaming_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000}; + MixerFifo m_wiimote_speaker_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000}; + MixerFifo m_skylander_portal_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 8000}; + + // GBAs generally use a 65536 sample rate which is not a factor of our FIXED_SAMPLE_RATE_DIVIDEND. + static constexpr u32 GBA_SAMPLE_RATE_DIVIDEND = 0x1000000; + + std::array m_gba_mixers{ + MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND}, + MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND}, + MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND}, + MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND}, + }; + u32 m_output_sample_rate; AudioCommon::SurroundDecoder m_surround_decoder; diff --git a/Source/Core/Core/HW/GBACore.cpp b/Source/Core/Core/HW/GBACore.cpp index 6d1d14b239..d915be6a5b 100644 --- a/Source/Core/Core/HW/GBACore.cpp +++ b/Source/Core/Core/HW/GBACore.cpp @@ -435,8 +435,7 @@ void Core::SetAVStream() m_stream.audioRateChanged = [](mAVStream* stream, unsigned rate) { auto* core = static_cast(stream)->core; auto* const sound_stream = core->m_system.GetSoundStream(); - sound_stream->GetMixer()->SetGBAInputSampleRateDivisors( - core->m_device_number, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / rate); + sound_stream->GetMixer()->SetGBAInputSampleRate(core->m_device_number, rate); }; m_stream.postAudioBuffer = [](mAVStream* stream, mAudioBuffer* audio_buffer) { size_t sample_count = mAudioBufferAvailable(audio_buffer);