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.
This commit is contained in:
CasualPokePlayer 2022-07-03 15:07:06 -07:00
parent edd89e343c
commit 4234b25682
11 changed files with 121 additions and 93 deletions

View File

@ -76,16 +76,18 @@ unsigned int Mixer::MixerFifo::Mix(short* samples, unsigned int numSamples,
// advance indexR with sample position // advance indexR with sample position
// remember fractional offset // 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) if (consider_framelimit && emulationspeed > 0.0f)
{ {
float numLeft = static_cast<float>(((indexW - indexR) & INDEX_MASK) / 2); float numLeft = static_cast<float>(((indexW - indexR) & INDEX_MASK) / 2);
u32 low_waterwark = m_input_sample_rate * timing_variance / 1000; u32 low_watermark = (FIXED_SAMPLE_RATE_DIVIDEND * timing_variance) /
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2); (static_cast<u64>(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; 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) if (offset > MAX_FREQ_SHIFT)
offset = MAX_FREQ_SHIFT; offset = MAX_FREQ_SHIFT;
if (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); m_dma_mixer.PushSamples(samples, num_samples);
if (m_log_dsp_audio) 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(); 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); volume.second);
} }
} }
@ -270,21 +272,21 @@ void Mixer::PushStreamingSamples(const short* samples, unsigned int num_samples)
m_streaming_mixer.PushSamples(samples, num_samples); m_streaming_mixer.PushSamples(samples, num_samples);
if (m_log_dtk_audio) 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(); 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); volume.second);
} }
} }
void Mixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, 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]; short samples_stereo[MAX_SAMPLES * 2];
if (num_samples < MAX_SAMPLES) 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) 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); 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) 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) 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) if (success)
{ {
m_log_dtk_audio = true; m_log_dtk_audio = true;
@ -372,7 +374,7 @@ void Mixer::StartLogDSPAudio(const std::string& filename)
{ {
if (!m_log_dsp_audio) 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) if (success)
{ {
m_log_dsp_audio = true; m_log_dsp_audio = true;
@ -414,19 +416,19 @@ void Mixer::RefreshConfig()
void Mixer::MixerFifo::DoState(PointerWrap& p) 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_LVolume);
p.Do(m_RVolume); 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) 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; unsigned int samples_in_fifo = ((m_indexW.load() - m_indexR.load()) & INDEX_MASK) / 2;
if (samples_in_fifo <= 1) if (samples_in_fifo <= 1)
return 0; // Mixer::MixerFifo::Mix always keeps one sample in the buffer. 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<u64>(m_mixer->m_sampleRate) *
m_input_sample_rate_divisor / FIXED_SAMPLE_RATE_DIVIDEND;
} }

View File

@ -29,14 +29,14 @@ public:
void PushSamples(const short* samples, unsigned int num_samples); void PushSamples(const short* samples, unsigned int num_samples);
void PushStreamingSamples(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, 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); void PushGBASamples(int device_number, const short* samples, unsigned int num_samples);
unsigned int GetSampleRate() const { return m_sampleRate; } unsigned int GetSampleRate() const { return m_sampleRate; }
void SetDMAInputSampleRate(unsigned int rate); void SetDMAInputSampleRateDivisor(unsigned int rate_divisor);
void SetStreamInputSampleRate(unsigned int rate); void SetStreamInputSampleRateDivisor(unsigned int rate_divisor);
void SetGBAInputSampleRates(int device_number, unsigned int rate); void SetGBAInputSampleRateDivisors(int device_number, unsigned int rate_divisor);
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume); void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(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(); } float GetCurrentSpeed() const { return m_speed.load(); }
void UpdateSpeed(float val) { m_speed.store(val); } 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: private:
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1; static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
@ -63,23 +66,24 @@ private:
class MixerFifo final class MixerFifo final
{ {
public: public:
MixerFifo(Mixer* mixer, unsigned sample_rate, bool little_endian) MixerFifo(Mixer* mixer, unsigned sample_rate_divisor, bool little_endian)
: m_mixer(mixer), m_input_sample_rate(sample_rate), m_little_endian(little_endian) : m_mixer(mixer), m_input_sample_rate_divisor(sample_rate_divisor),
m_little_endian(little_endian)
{ {
} }
void DoState(PointerWrap& p); void DoState(PointerWrap& p);
void PushSamples(const short* samples, unsigned int num_samples); void PushSamples(const short* samples, unsigned int num_samples);
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit, unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit,
float emulationspeed, int timing_variance); float emulationspeed, int timing_variance);
void SetInputSampleRate(unsigned int rate); void SetInputSampleRateDivisor(unsigned int rate_divisor);
unsigned int GetInputSampleRate() const; unsigned int GetInputSampleRateDivisor() const;
void SetVolume(unsigned int lvolume, unsigned int rvolume); void SetVolume(unsigned int lvolume, unsigned int rvolume);
std::pair<s32, s32> GetVolume() const; std::pair<s32, s32> GetVolume() const;
unsigned int AvailableSamples() const; unsigned int AvailableSamples() const;
private: private:
Mixer* m_mixer; Mixer* m_mixer;
unsigned m_input_sample_rate; unsigned m_input_sample_rate_divisor;
bool m_little_endian; bool m_little_endian;
std::array<short, MAX_SAMPLES * 2> m_buffer{}; std::array<short, MAX_SAMPLES * 2> m_buffer{};
std::atomic<u32> m_indexW{0}; std::atomic<u32> m_indexW{0};
@ -93,11 +97,13 @@ private:
void RefreshConfig(); void RefreshConfig();
MixerFifo m_dma_mixer{this, 32000, false}; MixerFifo m_dma_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 32000, false};
MixerFifo m_streaming_mixer{this, 48000, false}; MixerFifo m_streaming_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, false};
MixerFifo m_wiimote_speaker_mixer{this, 3000, true}; MixerFifo m_wiimote_speaker_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000, true};
std::array<MixerFifo, 4> m_gba_mixers{MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true}, std::array<MixerFifo, 4> m_gba_mixers{MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true},
MixerFifo{this, 48000, true}, MixerFifo{this, 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; unsigned int m_sampleRate;
bool m_is_stretching = false; bool m_is_stretching = false;

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "AudioCommon/WaveFile.h" #include "AudioCommon/WaveFile.h"
#include "AudioCommon/Mixer.h"
#include <string> #include <string>
@ -26,7 +27,7 @@ WaveFileWriter::~WaveFileWriter()
Stop(); 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 // Ask to delete file
if (File::Exists(filename)) if (File::Exists(filename))
@ -65,7 +66,7 @@ bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRa
if (basename.empty()) if (basename.empty())
SplitPath(filename, nullptr, &basename, nullptr); SplitPath(filename, nullptr, &basename, nullptr);
current_sample_rate = HLESampleRate; current_sample_rate_divisor = sample_rate_divisor;
// ----------------- // -----------------
// Write file header // Write file header
@ -78,7 +79,7 @@ bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRa
Write(16); // size of fmt block Write(16); // size of fmt block
Write(0x00020001); // two channels, uncompressed 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);
Write(sample_rate * 2 * 2); // two channels, 16bit Write(sample_rate * 2 * 2); // two channels, 16bit
@ -114,8 +115,8 @@ void WaveFileWriter::Write4(const char* ptr)
file.WriteBytes(ptr, 4); file.WriteBytes(ptr, 4);
} }
void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count, int sample_rate, void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count,
int l_volume, int r_volume) u32 sample_rate_divisor, int l_volume, int r_volume)
{ {
if (!file) if (!file)
ERROR_LOG_FMT(AUDIO, "WaveFileWriter - file not open."); 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; 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(); Stop();
file_index++; file_index++;
std::ostringstream filename; std::ostringstream filename;
filename << File::GetUserPath(D_DUMPAUDIO_IDX) << basename << file_index << ".wav"; filename << File::GetUserPath(D_DUMPAUDIO_IDX) << basename << file_index << ".wav";
Start(filename.str(), sample_rate); Start(filename.str(), sample_rate_divisor);
current_sample_rate = sample_rate; current_sample_rate_divisor = sample_rate_divisor;
} }
file.WriteBytes(conv_buffer.data(), count * 4); file.WriteBytes(conv_buffer.data(), count * 4);

View File

@ -30,24 +30,28 @@ public:
WaveFileWriter(WaveFileWriter&&) = delete; WaveFileWriter(WaveFileWriter&&) = delete;
WaveFileWriter& operator=(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 Stop();
void SetSkipSilence(bool skip) { skip_silence = skip; } void SetSkipSilence(bool skip) { skip_silence = skip; }
void AddStereoSamplesBE(const short* sample_data, u32 count, int sample_rate, int l_volume, // big endian
int r_volume); // 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; } u32 GetAudioSize() const { return audio_size; }
private: private:
static constexpr size_t BUFFER_SIZE = 32 * 1024; static constexpr size_t BUFFER_SIZE = 32 * 1024;
File::IOFile file;
bool skip_silence = false;
u32 audio_size = 0;
std::array<short, BUFFER_SIZE> conv_buffer{};
void Write(u32 value); void Write(u32 value);
void Write4(const char* ptr); void Write4(const char* ptr);
File::IOFile file;
std::string basename; std::string basename;
int current_sample_rate; u32 file_index = 0;
int file_index = 0; u32 audio_size = 0;
u32 current_sample_rate_divisor;
std::array<short, BUFFER_SIZE> conv_buffer{};
bool skip_silence = false;
}; };

View File

@ -112,8 +112,8 @@ static u32 s_interrupt_timing = 0;
static u64 s_last_cpu_time = 0; static u64 s_last_cpu_time = 0;
static u64 s_cpu_cycles_per_sample = 0; static u64 s_cpu_cycles_per_sample = 0;
static u32 s_ais_sample_rate = 48000; static u32 s_ais_sample_rate_divisor = Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 48000;
static u32 s_aid_sample_rate = 32000; static u32 s_aid_sample_rate_divisor = Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 32000;
void DoState(PointerWrap& p) void DoState(PointerWrap& p)
{ {
@ -122,8 +122,8 @@ void DoState(PointerWrap& p)
p.Do(s_sample_counter); p.Do(s_sample_counter);
p.Do(s_interrupt_timing); p.Do(s_interrupt_timing);
p.Do(s_last_cpu_time); p.Do(s_last_cpu_time);
p.Do(s_ais_sample_rate); p.Do(s_ais_sample_rate_divisor);
p.Do(s_aid_sample_rate); p.Do(s_aid_sample_rate_divisor);
p.Do(s_cpu_cycles_per_sample); p.Do(s_cpu_cycles_per_sample);
g_sound_stream->GetMixer()->DoState(p); g_sound_stream->GetMixer()->DoState(p);
@ -148,14 +148,15 @@ void Init()
s_last_cpu_time = 0; s_last_cpu_time = 0;
s_ais_sample_rate = Get48KHzSampleRate(); s_ais_sample_rate_divisor = Get48KHzSampleRateDivisor();
s_aid_sample_rate = Get32KHzSampleRate(); s_aid_sample_rate_divisor = Get32KHzSampleRateDivisor();
s_cpu_cycles_per_sample = SystemTimers::GetTicksPerSecond() / s_ais_sample_rate; s_cpu_cycles_per_sample = static_cast<u64>(SystemTimers::GetTicksPerSecond()) *
s_ais_sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
event_type_ai = CoreTiming::RegisterEvent("AICallback", Update); event_type_ai = CoreTiming::RegisterEvent("AICallback", Update);
g_sound_stream->GetMixer()->SetDMAInputSampleRate(GetAIDSampleRate()); g_sound_stream->GetMixer()->SetDMAInputSampleRateDivisor(GetAIDSampleRateDivisor());
g_sound_stream->GetMixer()->SetStreamInputSampleRate(GetAISSampleRate()); g_sound_stream->GetMixer()->SetStreamInputSampleRateDivisor(GetAISSampleRateDivisor());
} }
void Shutdown() void Shutdown()
@ -188,9 +189,11 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AISFR to {}", DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AISFR to {}",
tmp_ai_ctrl.AISFR ? "48khz" : "32khz"); tmp_ai_ctrl.AISFR ? "48khz" : "32khz");
s_control.AISFR = tmp_ai_ctrl.AISFR; s_control.AISFR = tmp_ai_ctrl.AISFR;
s_ais_sample_rate = tmp_ai_ctrl.AISFR ? Get48KHzSampleRate() : Get32KHzSampleRate(); s_ais_sample_rate_divisor =
g_sound_stream->GetMixer()->SetStreamInputSampleRate(s_ais_sample_rate); tmp_ai_ctrl.AISFR ? Get48KHzSampleRateDivisor() : Get32KHzSampleRateDivisor();
s_cpu_cycles_per_sample = SystemTimers::GetTicksPerSecond() / s_ais_sample_rate; g_sound_stream->GetMixer()->SetStreamInputSampleRateDivisor(s_ais_sample_rate_divisor);
s_cpu_cycles_per_sample = static_cast<u64>(SystemTimers::GetTicksPerSecond()) *
s_ais_sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
} }
// Set frequency of DMA // Set frequency of DMA
if (tmp_ai_ctrl.AIDFR != s_control.AIDFR) if (tmp_ai_ctrl.AIDFR != s_control.AIDFR)
@ -198,8 +201,9 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIDFR to {}", DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIDFR to {}",
tmp_ai_ctrl.AIDFR ? "32khz" : "48khz"); tmp_ai_ctrl.AIDFR ? "32khz" : "48khz");
s_control.AIDFR = tmp_ai_ctrl.AIDFR; s_control.AIDFR = tmp_ai_ctrl.AIDFR;
s_aid_sample_rate = tmp_ai_ctrl.AIDFR ? Get32KHzSampleRate() : Get48KHzSampleRate(); s_aid_sample_rate_divisor =
g_sound_stream->GetMixer()->SetDMAInputSampleRate(s_aid_sample_rate); tmp_ai_ctrl.AIDFR ? Get32KHzSampleRateDivisor() : Get48KHzSampleRateDivisor();
g_sound_stream->GetMixer()->SetDMAInputSampleRateDivisor(s_aid_sample_rate_divisor);
} }
// Streaming counter // Streaming counter
@ -301,24 +305,24 @@ bool IsPlaying()
return (s_control.PSTAT == 1); return (s_control.PSTAT == 1);
} }
u32 GetAIDSampleRate() u32 GetAIDSampleRateDivisor()
{ {
return s_aid_sample_rate; return s_aid_sample_rate_divisor;
} }
u32 GetAISSampleRate() u32 GetAISSampleRateDivisor()
{ {
return s_ais_sample_rate; return s_ais_sample_rate_divisor;
} }
u32 Get32KHzSampleRate() u32 Get32KHzSampleRateDivisor()
{ {
return SConfig::GetInstance().bWii ? 32000 : 32029; return Get48KHzSampleRateDivisor() * 3 / 2;
} }
u32 Get48KHzSampleRate() u32 Get48KHzSampleRateDivisor()
{ {
return SConfig::GetInstance().bWii ? 48000 : 48043; return (SConfig::GetInstance().bWii ? 1125 : 1124) * 2;
} }
static void Update(u64 userdata, s64 cycles_late) static void Update(u64 userdata, s64 cycles_late)
@ -339,7 +343,8 @@ static void Update(u64 userdata, s64 cycles_late)
int GetAIPeriod() int GetAIPeriod()
{ {
u64 period = s_cpu_cycles_per_sample * (s_interrupt_timing - s_sample_counter); u64 period = s_cpu_cycles_per_sample * (s_interrupt_timing - s_sample_counter);
u64 s_period = s_cpu_cycles_per_sample * s_ais_sample_rate; u64 s_period =
s_cpu_cycles_per_sample * Mixer::FIXED_SAMPLE_RATE_DIVIDEND / s_ais_sample_rate_divisor;
if (period == 0) if (period == 0)
return static_cast<int>(s_period); return static_cast<int>(s_period);
return static_cast<int>(std::min(period, s_period)); return static_cast<int>(std::min(period, s_period));

View File

@ -22,12 +22,13 @@ bool IsPlaying();
void RegisterMMIO(MMIO::Mapping* mmio, u32 base); void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
// Get the audio rates (48000 or 32000 only) // Get the audio rate divisors (divisors for 48KHz or 32KHz only)
u32 GetAIDSampleRate(); // Mixer::FIXED_SAMPLE_RATE_DIVIDEND will be the dividend used for these divisors
u32 GetAISSampleRate(); u32 GetAIDSampleRateDivisor();
u32 GetAISSampleRateDivisor();
u32 Get32KHzSampleRate(); u32 Get32KHzSampleRateDivisor();
u32 Get48KHzSampleRate(); u32 Get48KHzSampleRateDivisor();
void GenerateAISInterrupt(); void GenerateAISInterrupt();

View File

@ -294,12 +294,16 @@ static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data, static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
s64 cycles_late) s64 cycles_late)
{ {
// TODO: Should we use GetAISSampleRate instead of a fixed 48 KHz? The audio mixer is using // Actual games always set this to 48 KHz
// GetAISSampleRate. (This doesn't affect any actual games, since they all set it to 48 KHz.) // but let's make sure to use GetAISSampleRateDivisor()
const u32 sample_rate = AudioInterface::Get48KHzSampleRate(); // just in case it changes to 32 KHz
const u32 sample_rate_divisor = AudioInterface::GetAISSampleRateDivisor();
// Determine which audio data to read next. // Determine which audio data to read next.
const u32 maximum_samples = sample_rate / 2000 * 7; // 3.5 ms of samples
// 3.5 ms of samples
const u32 maximum_samples =
((Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 2000) * 7) / sample_rate_divisor;
u64 read_offset = 0; u64 read_offset = 0;
u32 read_length = 0; u32 read_length = 0;
@ -328,7 +332,8 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect
} }
// Read the next chunk of audio data asynchronously. // Read the next chunk of audio data asynchronously.
s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(s_pending_samples) / sample_rate; s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(s_pending_samples) *
sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
ticks_to_dtk -= cycles_late; ticks_to_dtk -= cycles_late;
if (read_length > 0) if (read_length > 0)
{ {

View File

@ -405,7 +405,8 @@ void Core::SetSampleRates()
m_core->setAudioBufferSize(m_core, SAMPLES); m_core->setAudioBufferSize(m_core, SAMPLES);
blip_set_rates(m_core->getAudioChannel(m_core, 0), m_core->frequency(m_core), SAMPLE_RATE); blip_set_rates(m_core->getAudioChannel(m_core, 0), m_core->frequency(m_core), SAMPLE_RATE);
blip_set_rates(m_core->getAudioChannel(m_core, 1), m_core->frequency(m_core), SAMPLE_RATE); blip_set_rates(m_core->getAudioChannel(m_core, 1), m_core->frequency(m_core), SAMPLE_RATE);
g_sound_stream->GetMixer()->SetGBAInputSampleRates(m_device_number, SAMPLE_RATE); g_sound_stream->GetMixer()->SetGBAInputSampleRateDivisors(
m_device_number, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / SAMPLE_RATE);
} }
void Core::AddCallbacks() void Core::AddCallbacks()

View File

@ -48,6 +48,7 @@ IPC_HLE_PERIOD: For the Wii Remote this is the call schedule:
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
#include "AudioCommon/Mixer.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/Thread.h" #include "Common/Thread.h"
@ -110,7 +111,8 @@ void DSPCallback(u64 userdata, s64 cyclesLate)
int GetAudioDMACallbackPeriod() int GetAudioDMACallbackPeriod()
{ {
// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA // System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
return s_cpu_core_clock / (AudioInterface::GetAIDSampleRate() * 4 / 32); return static_cast<u64>(s_cpu_core_clock) * AudioInterface::GetAIDSampleRateDivisor() /
(Mixer::FIXED_SAMPLE_RATE_DIVIDEND * 4 / 32);
} }
void AudioDMACallback(u64 userdata, s64 cyclesLate) void AudioDMACallback(u64 userdata, s64 cyclesLate)

View File

@ -145,8 +145,8 @@ void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
// ADPCM sample rate is thought to be x2.(3000 x2 = 6000). // ADPCM sample rate is thought to be x2.(3000 x2 = 6000).
const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate; const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate;
g_sound_stream->GetMixer()->PushWiimoteSpeakerSamples(samples.get(), sample_length, g_sound_stream->GetMixer()->PushWiimoteSpeakerSamples(
sample_rate * 2); samples.get(), sample_length, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / (sample_rate * 2));
#ifdef WIIMOTE_SPEAKER_DUMP #ifdef WIIMOTE_SPEAKER_DUMP
static int num = 0; static int num = 0;

View File

@ -74,7 +74,7 @@ static std::recursive_mutex g_save_thread_mutex;
static std::thread g_save_thread; static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system // Don't forget to increase this after doing changes on the savestate system
constexpr u32 STATE_VERSION = 143; // Last changed in PR 10784 constexpr u32 STATE_VERSION = 144; // Last changed in PR 10762
// Maps savestate versions to Dolphin versions. // Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list, // Versions after 42 don't need to be added to this list,