diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index ae244d871..873868575 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,4 +2,4 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 6; +static constexpr u32 SAVE_STATE_VERSION = 7; diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 526a6f5d5..570e1e176 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -30,6 +30,8 @@ void SPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_co void SPU::Reset() { + m_tick_counter = 0; + m_SPUCNT.bits = 0; m_SPUSTAT.bits = 0; m_transfer_address = 0; @@ -67,12 +69,15 @@ void SPU::Reset() v.has_samples = false; } + m_voice_key_on_off_delay.fill(0); + m_ram.fill(0); UpdateEventInterval(); } bool SPU::DoState(StateWrapper& sw) { + sw.Do(&m_tick_counter); sw.Do(&m_SPUCNT.bits); sw.Do(&m_SPUSTAT.bits); sw.Do(&m_transfer_control.bits); @@ -110,6 +115,8 @@ bool SPU::DoState(StateWrapper& sw) sw.Do(&v.has_samples); } + sw.Do(&m_voice_key_on_off_delay); + sw.DoBytes(m_ram.data(), RAM_SIZE); if (sw.IsReading()) @@ -683,6 +690,15 @@ void SPU::Execute(TickCount ticks) output_stream->EndWrite(frames_in_this_batch); remaining_frames -= frames_in_this_batch; } + + for (u32 i = 0; i < NUM_VOICES; i++) + { + const u32 delay = static_cast(m_voice_key_on_off_delay[i]); + if (delay == 0) + continue; + + m_voice_key_on_off_delay[i] -= static_cast(std::min(delay, static_cast(ticks))); + } } void SPU::UpdateEventInterval() @@ -1113,13 +1129,31 @@ std::tuple SPU::SampleVoice(u32 voice_index) void SPU::VoiceKeyOn(u32 voice_index) { Log_DebugPrintf("Voice %u key on", voice_index); + + if (m_voice_key_on_off_delay[voice_index] > 0) + { + Log_DevPrintf("Ignoring key on for voice %u due to only %u ticks passed since last write", voice_index, + m_voice_key_on_off_delay[voice_index]); + return; + } + m_voices[voice_index].KeyOn(); + m_voice_key_on_off_delay[voice_index] = MINIMUM_TICKS_BETWEEN_KEY_ON_OFF; } void SPU::VoiceKeyOff(u32 voice_index) { Log_DebugPrintf("Voice %u key off", voice_index); + + if (m_voice_key_on_off_delay[voice_index] > 0) + { + Log_DevPrintf("Ignoring key off for voice %u due to only %u ticks passed since last write", voice_index, + m_voice_key_on_off_delay[voice_index]); + return; + } + m_voices[voice_index].KeyOff(); + m_voice_key_on_off_delay[voice_index] = MINIMUM_TICKS_BETWEEN_KEY_ON_OFF; } void SPU::EnsureCDAudioSpace(u32 remaining_frames) diff --git a/src/core/spu.h b/src/core/spu.h index b86edebdb..16a267a1e 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -56,6 +56,7 @@ private: static constexpr s16 ADSR_MAX_VOLUME = 0x7FFF; static constexpr u32 CD_AUDIO_SAMPLE_BUFFER_SIZE = 44100 * 2; static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400; + static constexpr u32 MINIMUM_TICKS_BETWEEN_KEY_ON_OFF = 2; enum class RAMTransferMode : u8 { @@ -283,6 +284,7 @@ private: DMA* m_dma = nullptr; InterruptController* m_interrupt_controller = nullptr; std::unique_ptr m_tick_event; + u32 m_tick_counter = 0; SPUCNT m_SPUCNT = {}; SPUSTAT m_SPUSTAT = {}; @@ -310,6 +312,7 @@ private: TickCount m_ticks_carry = 0; std::array m_voices{}; + std::array m_voice_key_on_off_delay{}; std::array m_ram{}; InlineFIFOQueue m_cd_audio_buffer;