From 4234b25682ddfab6018c3c7bf410e568f527b1cd Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sun, 3 Jul 2022 15:07:06 -0700 Subject: Do not directly store input sample rate, rather just store a divisor for that sample rate, with it using a fixed dividend of 54000000 * 2. This should reduce (but not completely eliminate) gradual audio desyncs in dumps. This also allows for accurate sample rates for the GameCube. Completely eliminating gradual audio desyncs will require resampling to an integer sample rate, as nothing seems to support a non-integer sample rate. --- Source/Core/AudioCommon/Mixer.cpp | 51 +++++++++++++++++++----------------- Source/Core/AudioCommon/Mixer.h | 34 ++++++++++++++---------- Source/Core/AudioCommon/WaveFile.cpp | 17 ++++++------ Source/Core/AudioCommon/WaveFile.h | 22 +++++++++------- 4 files changed, 69 insertions(+), 55 deletions(-) (limited to 'Source/Core/AudioCommon') diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index e0b7f28768..5baa02d25e 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -76,16 +76,18 @@ unsigned int Mixer::MixerFifo::Mix(short* samples, unsigned int numSamples, // advance indexR with sample position // remember fractional offset - float aid_sample_rate = static_cast(m_input_sample_rate); + float aid_sample_rate = + FIXED_SAMPLE_RATE_DIVIDEND / static_cast(m_input_sample_rate_divisor); if (consider_framelimit && emulationspeed > 0.0f) { float numLeft = static_cast(((indexW - indexR) & INDEX_MASK) / 2); - u32 low_waterwark = m_input_sample_rate * timing_variance / 1000; - low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2); + u32 low_watermark = (FIXED_SAMPLE_RATE_DIVIDEND * timing_variance) / + (static_cast(m_input_sample_rate_divisor) * 1000); + low_watermark = std::min(low_watermark, MAX_SAMPLES / 2); m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1)) / CONTROL_AVG; - float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR; + float offset = (m_numLeftI - low_watermark) * CONTROL_FACTOR; if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; if (offset < -MAX_FREQ_SHIFT) @@ -258,9 +260,9 @@ void Mixer::PushSamples(const short* samples, unsigned int num_samples) m_dma_mixer.PushSamples(samples, num_samples); if (m_log_dsp_audio) { - int sample_rate = m_dma_mixer.GetInputSampleRate(); + int sample_rate_divisor = m_dma_mixer.GetInputSampleRateDivisor(); auto volume = m_dma_mixer.GetVolume(); - m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples, sample_rate, volume.first, + m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples, sample_rate_divisor, volume.first, volume.second); } } @@ -270,21 +272,21 @@ void Mixer::PushStreamingSamples(const short* samples, unsigned int num_samples) m_streaming_mixer.PushSamples(samples, num_samples); if (m_log_dtk_audio) { - int sample_rate = m_streaming_mixer.GetInputSampleRate(); + int sample_rate_divisor = m_streaming_mixer.GetInputSampleRateDivisor(); auto volume = m_streaming_mixer.GetVolume(); - m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples, sample_rate, volume.first, + m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples, sample_rate_divisor, volume.first, volume.second); } } void Mixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, - unsigned int sample_rate) + unsigned int sample_rate_divisor) { short samples_stereo[MAX_SAMPLES * 2]; if (num_samples < MAX_SAMPLES) { - m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate); + m_wiimote_speaker_mixer.SetInputSampleRateDivisor(sample_rate_divisor); for (unsigned int i = 0; i < num_samples; ++i) { @@ -301,19 +303,19 @@ void Mixer::PushGBASamples(int device_number, const short* samples, unsigned int m_gba_mixers[device_number].PushSamples(samples, num_samples); } -void Mixer::SetDMAInputSampleRate(unsigned int rate) +void Mixer::SetDMAInputSampleRateDivisor(unsigned int rate_divisor) { - m_dma_mixer.SetInputSampleRate(rate); + m_dma_mixer.SetInputSampleRateDivisor(rate_divisor); } -void Mixer::SetStreamInputSampleRate(unsigned int rate) +void Mixer::SetStreamInputSampleRateDivisor(unsigned int rate_divisor) { - m_streaming_mixer.SetInputSampleRate(rate); + m_streaming_mixer.SetInputSampleRateDivisor(rate_divisor); } -void Mixer::SetGBAInputSampleRates(int device_number, unsigned int rate) +void Mixer::SetGBAInputSampleRateDivisors(int device_number, unsigned int rate_divisor) { - m_gba_mixers[device_number].SetInputSampleRate(rate); + m_gba_mixers[device_number].SetInputSampleRateDivisor(rate_divisor); } void Mixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume) @@ -335,7 +337,7 @@ void Mixer::StartLogDTKAudio(const std::string& filename) { if (!m_log_dtk_audio) { - bool success = m_wave_writer_dtk.Start(filename, m_streaming_mixer.GetInputSampleRate()); + bool success = m_wave_writer_dtk.Start(filename, m_streaming_mixer.GetInputSampleRateDivisor()); if (success) { m_log_dtk_audio = true; @@ -372,7 +374,7 @@ void Mixer::StartLogDSPAudio(const std::string& filename) { if (!m_log_dsp_audio) { - bool success = m_wave_writer_dsp.Start(filename, m_dma_mixer.GetInputSampleRate()); + bool success = m_wave_writer_dsp.Start(filename, m_dma_mixer.GetInputSampleRateDivisor()); if (success) { m_log_dsp_audio = true; @@ -414,19 +416,19 @@ void Mixer::RefreshConfig() void Mixer::MixerFifo::DoState(PointerWrap& p) { - p.Do(m_input_sample_rate); + p.Do(m_input_sample_rate_divisor); p.Do(m_LVolume); p.Do(m_RVolume); } -void Mixer::MixerFifo::SetInputSampleRate(unsigned int rate) +void Mixer::MixerFifo::SetInputSampleRateDivisor(unsigned int rate_divisor) { - m_input_sample_rate = rate; + m_input_sample_rate_divisor = rate_divisor; } -unsigned int Mixer::MixerFifo::GetInputSampleRate() const +unsigned int Mixer::MixerFifo::GetInputSampleRateDivisor() const { - return m_input_sample_rate; + return m_input_sample_rate_divisor; } void Mixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume) @@ -445,5 +447,6 @@ unsigned int Mixer::MixerFifo::AvailableSamples() const unsigned int samples_in_fifo = ((m_indexW.load() - m_indexR.load()) & INDEX_MASK) / 2; if (samples_in_fifo <= 1) return 0; // Mixer::MixerFifo::Mix always keeps one sample in the buffer. - return (samples_in_fifo - 1) * m_mixer->m_sampleRate / m_input_sample_rate; + return (samples_in_fifo - 1) * static_cast(m_mixer->m_sampleRate) * + m_input_sample_rate_divisor / FIXED_SAMPLE_RATE_DIVIDEND; } diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index f58c25f98a..7191fd6ab0 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -29,14 +29,14 @@ public: void PushSamples(const short* samples, unsigned int num_samples); void PushStreamingSamples(const short* samples, unsigned int num_samples); void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, - unsigned int sample_rate); + unsigned int sample_rate_divisor); void PushGBASamples(int device_number, const short* samples, unsigned int num_samples); unsigned int GetSampleRate() const { return m_sampleRate; } - void SetDMAInputSampleRate(unsigned int rate); - void SetStreamInputSampleRate(unsigned int rate); - void SetGBAInputSampleRates(int device_number, unsigned int rate); + void SetDMAInputSampleRateDivisor(unsigned int rate_divisor); + void SetStreamInputSampleRateDivisor(unsigned int rate_divisor); + void SetGBAInputSampleRateDivisors(int device_number, unsigned int rate_divisor); void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume); void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume); @@ -51,6 +51,9 @@ public: float GetCurrentSpeed() const { return m_speed.load(); } void UpdateSpeed(float val) { m_speed.store(val); } + // 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does + static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2; + private: static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1; @@ -63,23 +66,24 @@ private: class MixerFifo final { public: - MixerFifo(Mixer* mixer, unsigned sample_rate, bool little_endian) - : m_mixer(mixer), m_input_sample_rate(sample_rate), m_little_endian(little_endian) + MixerFifo(Mixer* mixer, unsigned sample_rate_divisor, bool little_endian) + : m_mixer(mixer), m_input_sample_rate_divisor(sample_rate_divisor), + m_little_endian(little_endian) { } void DoState(PointerWrap& p); void PushSamples(const short* samples, unsigned int num_samples); unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit, float emulationspeed, int timing_variance); - void SetInputSampleRate(unsigned int rate); - unsigned int GetInputSampleRate() const; + void SetInputSampleRateDivisor(unsigned int rate_divisor); + unsigned int GetInputSampleRateDivisor() const; void SetVolume(unsigned int lvolume, unsigned int rvolume); std::pair GetVolume() const; unsigned int AvailableSamples() const; private: Mixer* m_mixer; - unsigned m_input_sample_rate; + unsigned m_input_sample_rate_divisor; bool m_little_endian; std::array m_buffer{}; std::atomic m_indexW{0}; @@ -93,11 +97,13 @@ private: void RefreshConfig(); - MixerFifo m_dma_mixer{this, 32000, false}; - MixerFifo m_streaming_mixer{this, 48000, false}; - MixerFifo m_wiimote_speaker_mixer{this, 3000, true}; - std::array m_gba_mixers{MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true}, - MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true}}; + 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}; + 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}}; unsigned int m_sampleRate; bool m_is_stretching = false; diff --git a/Source/Core/AudioCommon/WaveFile.cpp b/Source/Core/AudioCommon/WaveFile.cpp index befcf6bfbd..dc689e74dd 100644 --- a/Source/Core/AudioCommon/WaveFile.cpp +++ b/Source/Core/AudioCommon/WaveFile.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "AudioCommon/WaveFile.h" +#include "AudioCommon/Mixer.h" #include @@ -26,7 +27,7 @@ WaveFileWriter::~WaveFileWriter() Stop(); } -bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate) +bool WaveFileWriter::Start(const std::string& filename, u32 sample_rate_divisor) { // Ask to delete file if (File::Exists(filename)) @@ -65,7 +66,7 @@ bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRa if (basename.empty()) SplitPath(filename, nullptr, &basename, nullptr); - current_sample_rate = HLESampleRate; + current_sample_rate_divisor = sample_rate_divisor; // ----------------- // Write file header @@ -78,7 +79,7 @@ bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRa Write(16); // size of fmt block Write(0x00020001); // two channels, uncompressed - const u32 sample_rate = HLESampleRate; + const u32 sample_rate = Mixer::FIXED_SAMPLE_RATE_DIVIDEND / sample_rate_divisor; Write(sample_rate); Write(sample_rate * 2 * 2); // two channels, 16bit @@ -114,8 +115,8 @@ void WaveFileWriter::Write4(const char* ptr) file.WriteBytes(ptr, 4); } -void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count, int sample_rate, - int l_volume, int r_volume) +void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count, + u32 sample_rate_divisor, int l_volume, int r_volume) { if (!file) ERROR_LOG_FMT(AUDIO, "WaveFileWriter - file not open."); @@ -148,14 +149,14 @@ void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count, int conv_buffer[2 * i + 1] = conv_buffer[2 * i + 1] * r_volume / 256; } - if (sample_rate != current_sample_rate) + if (sample_rate_divisor != current_sample_rate_divisor) { Stop(); file_index++; std::ostringstream filename; filename << File::GetUserPath(D_DUMPAUDIO_IDX) << basename << file_index << ".wav"; - Start(filename.str(), sample_rate); - current_sample_rate = sample_rate; + Start(filename.str(), sample_rate_divisor); + current_sample_rate_divisor = sample_rate_divisor; } file.WriteBytes(conv_buffer.data(), count * 4); diff --git a/Source/Core/AudioCommon/WaveFile.h b/Source/Core/AudioCommon/WaveFile.h index e95ed8eb73..350e1da9af 100644 --- a/Source/Core/AudioCommon/WaveFile.h +++ b/Source/Core/AudioCommon/WaveFile.h @@ -30,24 +30,28 @@ public: WaveFileWriter(WaveFileWriter&&) = delete; WaveFileWriter& operator=(WaveFileWriter&&) = delete; - bool Start(const std::string& filename, unsigned int HLESampleRate); + bool Start(const std::string& filename, u32 sample_rate_divisor); void Stop(); void SetSkipSilence(bool skip) { skip_silence = skip; } - void AddStereoSamplesBE(const short* sample_data, u32 count, int sample_rate, int l_volume, - int r_volume); // big endian + // big endian + void AddStereoSamplesBE(const short* sample_data, u32 count, u32 sample_rate_divisor, + int l_volume, int r_volume); u32 GetAudioSize() const { return audio_size; } private: static constexpr size_t BUFFER_SIZE = 32 * 1024; - File::IOFile file; - bool skip_silence = false; - u32 audio_size = 0; - std::array conv_buffer{}; void Write(u32 value); void Write4(const char* ptr); + + File::IOFile file; std::string basename; - int current_sample_rate; - int file_index = 0; + u32 file_index = 0; + u32 audio_size = 0; + + u32 current_sample_rate_divisor; + std::array conv_buffer{}; + + bool skip_silence = false; }; -- cgit v1.2.3