diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp index 508f891a98..5450848bfb 100644 --- a/Source/Core/Core/HW/AudioInterface.cpp +++ b/Source/Core/Core/HW/AudioInterface.cpp @@ -335,6 +335,16 @@ u32 AudioInterfaceManager::GetAISSampleRateDivisor() const return m_ais_sample_rate_divisor; } +SampleRate AudioInterfaceManager::GetAIDSampleRate() const +{ + return m_control.AIDFR == AID_48KHz ? SampleRate::AI48KHz : SampleRate::AI32KHz; +} + +SampleRate AudioInterfaceManager::GetAISSampleRate() const +{ + return m_control.AISFR == AIS_32KHz ? SampleRate::AI32KHz : SampleRate::AI48KHz; +} + u32 AudioInterfaceManager::Get32KHzSampleRateDivisor() const { return Get48KHzSampleRateDivisor() * 3 / 2; diff --git a/Source/Core/Core/HW/AudioInterface.h b/Source/Core/Core/HW/AudioInterface.h index a9bc24be3a..10fbb11c1c 100644 --- a/Source/Core/Core/HW/AudioInterface.h +++ b/Source/Core/Core/HW/AudioInterface.h @@ -23,6 +23,12 @@ class Mapping; namespace AudioInterface { +enum class SampleRate +{ + AI32KHz, + AI48KHz, +}; + class AudioInterfaceManager { public: @@ -45,18 +51,17 @@ public: u32 GetAIDSampleRateDivisor() const; u32 GetAISSampleRateDivisor() const; + // The configured sample rate based on the control registers. Note that on GameCube, the named + // rates are slightly higher than the names would suggest due to a hardware bug. + SampleRate GetAIDSampleRate() const; + SampleRate GetAISSampleRate() const; + u32 Get32KHzSampleRateDivisor() const; u32 Get48KHzSampleRateDivisor() const; void GenerateAISInterrupt(); private: - enum class SampleRate - { - AI32KHz, - AI48KHz, - }; - // AI Control Register union AICR { diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index f933983f2d..a17ee5bd37 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -105,7 +105,7 @@ void DVDInterface::DoState(PointerWrap& p) p.Do(m_current_length); p.Do(m_next_start); p.Do(m_next_length); - p.Do(m_pending_samples); + p.Do(m_pending_blocks); p.Do(m_enable_dtk); p.Do(m_dtk_buffer_length); @@ -124,32 +124,34 @@ void DVDInterface::DoState(PointerWrap& p) m_adpcm_decoder.DoState(p); } -size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t sample_count, +size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t target_block_count, const std::vector& audio_data) { + const size_t block_count_to_process = + std::min(target_block_count, audio_data.size() / StreamADPCM::ONE_BLOCK_SIZE); size_t samples_processed = 0; size_t bytes_processed = 0; - while (samples_processed < sample_count && bytes_processed < audio_data.size()) + for (size_t i = 0; i < block_count_to_process; ++i) { m_adpcm_decoder.DecodeBlock(&target_samples[samples_processed * 2], &audio_data[bytes_processed]); - for (size_t i = 0; i < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++i) + for (size_t j = 0; j < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++j) { // TODO: Fix the mixer so it can accept non-byte-swapped samples. - s16* sample = &target_samples[samples_processed * 2 + i]; + s16* sample = &target_samples[samples_processed * 2 + j]; *sample = Common::swap16(*sample); } samples_processed += StreamADPCM::SAMPLES_PER_BLOCK; bytes_processed += StreamADPCM::ONE_BLOCK_SIZE; } - return samples_processed; + return block_count_to_process; } -u32 DVDInterface::AdvanceDTK(u32 maximum_samples, u32* samples_to_process) +u32 DVDInterface::AdvanceDTK(u32 maximum_blocks, u32* blocks_to_process) { u32 bytes_to_process = 0; - *samples_to_process = 0; - while (*samples_to_process < maximum_samples) + *blocks_to_process = 0; + while (*blocks_to_process < maximum_blocks) { if (m_audio_position >= m_current_start + m_current_length) { @@ -175,7 +177,7 @@ u32 DVDInterface::AdvanceDTK(u32 maximum_samples, u32* samples_to_process) m_audio_position += StreamADPCM::ONE_BLOCK_SIZE; bytes_to_process += StreamADPCM::ONE_BLOCK_SIZE; - *samples_to_process += StreamADPCM::SAMPLES_PER_BLOCK; + *blocks_to_process += 1; } return bytes_to_process; @@ -189,57 +191,51 @@ void DVDInterface::DTKStreamingCallback(DIInterruptType interrupt_type, // Actual games always set this to 48 KHz // but let's make sure to use GetAISSampleRateDivisor() // just in case it changes to 32 KHz + const auto sample_rate = ai.GetAISSampleRate(); const u32 sample_rate_divisor = ai.GetAISSampleRateDivisor(); // Determine which audio data to read next. // 3.5 ms of samples - const u32 maximum_samples = - ((Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 2000) * 7) / sample_rate_divisor; + constexpr u32 MAX_POSSIBLE_BLOCKS = 6; + constexpr u32 MAX_POSSIBLE_SAMPLES = MAX_POSSIBLE_BLOCKS * StreamADPCM::SAMPLES_PER_BLOCK; + const u32 maximum_blocks = sample_rate == AudioInterface::SampleRate::AI32KHz ? 4 : 6; u64 read_offset = 0; u32 read_length = 0; if (interrupt_type == DIInterruptType::TCINT) { - // maximum_samples can only be 112 (if running at 32khz) or 168 (if running at 48khz), so just - // prepare stack space for the biggest possible case. This is true even for the GameCube's not - // quite correct sample rates. - constexpr u32 MAX_POSSIBLE_SAMPLES = 168; - // Send audio to the mixer. - std::array temp_pcm; - ASSERT(m_pending_samples <= MAX_POSSIBLE_SAMPLES); - const u32 pending_samples = std::min(m_pending_samples, MAX_POSSIBLE_SAMPLES); - const size_t samples_processed = - ProcessDTKSamples(temp_pcm.data(), pending_samples, audio_data); - - // Fill unprocessed space up with zero samples. - const size_t samples_free = pending_samples - samples_processed; - std::fill_n(temp_pcm.data() + samples_processed * 2, samples_free * 2, 0); + std::array temp_pcm{}; + ASSERT(m_pending_blocks <= MAX_POSSIBLE_BLOCKS); + const u32 pending_blocks = std::min(m_pending_blocks, MAX_POSSIBLE_BLOCKS); + ProcessDTKSamples(temp_pcm.data(), pending_blocks, audio_data); SoundStream* sound_stream = m_system.GetSoundStream(); - sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), pending_samples); + sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), + pending_blocks * StreamADPCM::SAMPLES_PER_BLOCK); if (m_stream && ai.IsPlaying()) { read_offset = m_audio_position; - read_length = AdvanceDTK(maximum_samples, &m_pending_samples); + read_length = AdvanceDTK(maximum_blocks, &m_pending_blocks); } else { read_length = 0; - m_pending_samples = maximum_samples; + m_pending_blocks = maximum_blocks; } } else { read_length = 0; - m_pending_samples = maximum_samples; + m_pending_blocks = maximum_blocks; } // Read the next chunk of audio data asynchronously. - s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(m_pending_samples) * - sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND; + s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(m_pending_blocks) * + StreamADPCM::SAMPLES_PER_BLOCK * sample_rate_divisor / + Mixer::FIXED_SAMPLE_RATE_DIVIDEND; ticks_to_dtk -= cycles_late; if (read_length > 0) { @@ -297,7 +293,7 @@ void DVDInterface::ResetDrive(bool spinup) m_next_length = 0; m_current_start = 0; m_current_length = 0; - m_pending_samples = 0; + m_pending_blocks = 0; m_enable_dtk = false; m_dtk_buffer_length = 0; diff --git a/Source/Core/Core/HW/DVD/DVDInterface.h b/Source/Core/Core/HW/DVD/DVDInterface.h index d4886f3af0..f01558cc1e 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.h +++ b/Source/Core/Core/HW/DVD/DVDInterface.h @@ -178,9 +178,9 @@ public: private: void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector& audio_data, s64 cycles_late); - size_t ProcessDTKSamples(s16* target_samples, size_t sample_count, + size_t ProcessDTKSamples(s16* target_samples, size_t target_block_count, const std::vector& audio_data); - u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process); + u32 AdvanceDTK(u32 maximum_blocks, u32* blocks_to_process); void SetLidOpen(); void UpdateInterrupts(); @@ -274,7 +274,7 @@ private: u32 m_current_length = 0; u64 m_next_start = 0; u32 m_next_length = 0; - u32 m_pending_samples = 0; + u32 m_pending_blocks = 0; bool m_enable_dtk = false; u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 6345d340fe..e8ab5a85ab 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -96,7 +96,7 @@ static size_t s_state_writes_in_queue; static std::condition_variable s_state_write_queue_is_empty; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 160; // Last changed in PR 11644 +constexpr u32 STATE_VERSION = 161; // Last changed in PR 11655 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list,