Skip to content

Commit

Permalink
Do not directly store input sample rate, rather just store a divisor …
Browse files Browse the repository at this point in the history
…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 somehow setting a floating point sample rate (not sure if anything supports that), or resampling to an integer sample rate.
  • Loading branch information
CasualPokePlayer committed Jun 17, 2022
1 parent 107a928 commit 43616a4
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 83 deletions.
47 changes: 25 additions & 22 deletions Source/Core/AudioCommon/Mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ unsigned int Mixer::MixerFifo::Mix(short* samples, unsigned int numSamples,
// advance indexR with sample position
// remember fractional offset

float aid_sample_rate = static_cast<float>(m_input_sample_rate);
float aid_sample_rate =
FIXED_SAMPLE_RATE_DIVIDEND / static_cast<float>(m_input_sample_rate_divisor);
if (consider_framelimit && emulationspeed > 0.0f)
{
float numLeft = static_cast<float>(((indexW - indexR) & INDEX_MASK) / 2);

u32 low_waterwark = m_input_sample_rate * timing_variance / 1000;
u32 low_waterwark =
FIXED_SAMPLE_RATE_DIVIDEND / m_input_sample_rate_divisor * timing_variance / 1000;
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2);

m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1)) / CONTROL_AVG;
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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)
{
Expand All @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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) * m_mixer->m_sampleRate /
(FIXED_SAMPLE_RATE_DIVIDEND / m_input_sample_rate_divisor);
}
35 changes: 21 additions & 14 deletions Source/Core/AudioCommon/Mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ class Mixer final
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);
Expand All @@ -51,6 +51,10 @@ class Mixer final
float GetCurrentSpeed() const { return m_speed.load(); }
void UpdateSpeed(float val) { m_speed.store(val); }

static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND =
54000000 *
2; // 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does

private:
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
Expand All @@ -63,23 +67,24 @@ class Mixer final
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<s32, s32> 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<short, MAX_SAMPLES * 2> m_buffer{};
std::atomic<u32> m_indexW{0};
Expand All @@ -93,11 +98,13 @@ class Mixer final

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<MixerFifo, 4> 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<MixerFifo, 4> 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;
Expand Down
17 changes: 9 additions & 8 deletions Source/Core/AudioCommon/WaveFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later

#include "AudioCommon/WaveFile.h"
#include "AudioCommon/Mixer.h"

#include <string>

Expand All @@ -26,7 +27,7 @@ WaveFileWriter::~WaveFileWriter()
Stop();
}

bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate)
bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRateDivisor)
{
// Ask to delete file
if (File::Exists(filename))
Expand Down Expand Up @@ -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 = HLESampleRateDivisor;

// -----------------
// Write file header
Expand All @@ -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 / HLESampleRateDivisor;
Write(sample_rate);
Write(sample_rate * 2 * 2); // two channels, 16bit

Expand Down Expand Up @@ -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,
int sample_rate_divisor, int l_volume, int r_volume)
{
if (!file)
ERROR_LOG_FMT(AUDIO, "WaveFileWriter - file not open.");
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions Source/Core/AudioCommon/WaveFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class WaveFileWriter
void Stop();

void SetSkipSilence(bool skip) { skip_silence = skip; }
void AddStereoSamplesBE(const short* sample_data, u32 count, int sample_rate, int l_volume,
void AddStereoSamplesBE(const short* sample_data, u32 count, int sample_rate_divisor,
int l_volume,
int r_volume); // big endian
u32 GetAudioSize() const { return audio_size; }

Expand All @@ -48,6 +49,6 @@ class WaveFileWriter
void Write(u32 value);
void Write4(const char* ptr);
std::string basename;
int current_sample_rate;
int current_sample_rate_divisor;
int file_index = 0;
};
Loading

0 comments on commit 43616a4

Please sign in to comment.