From 8eb3ac69b2c63733739336bff76bae8cebc87a15 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin <stenzek@gmail.com> Date: Tue, 2 Jun 2020 01:59:44 +1000 Subject: [PATCH] SPU: Update ADSR envelope when register changes Fixes menu sounds in Final Fantasy 7 staying audible for too long. --- src/core/spu.cpp | 33 ++++++++++++++++++++++----------- src/core/spu.h | 2 +- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/core/spu.cpp b/src/core/spu.cpp index e6c8a4eb8..8c8b1465f 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -71,8 +71,11 @@ void SPU::Reset() v.current_block_samples.fill(s16(0)); v.previous_block_last_samples.fill(s16(0)); v.adpcm_last_samples.fill(s32(0)); - v.SetADSRPhase(ADSRPhase::Off); + v.adsr_envelope.Reset(0, false, false); + v.adsr_phase = ADSRPhase::Off; + v.adsr_target = 0; v.has_samples = false; + v.ignore_loop_address = false; } m_transfer_fifo.Clear(); @@ -594,21 +597,25 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value) case 0x08: // adsr low { - Log_DebugPrintf("SPU voice %u ADSR low <- 0x%04X", voice_index, value); + Log_DebugPrintf("SPU voice %u ADSR low <- 0x%04X (was 0x%04X)", voice_index, value, voice.regs.adsr.bits_low); voice.regs.adsr.bits_low = value; + if (voice.IsOn()) + voice.UpdateADSREnvelope(); } break; case 0x0A: // adsr high { - Log_DebugPrintf("SPU voice %u ADSR high <- 0x%04X", voice_index, value); + Log_DebugPrintf("SPU voice %u ADSR high <- 0x%04X (was 0x%04X)", voice_index, value, voice.regs.adsr.bits_low); voice.regs.adsr.bits_high = value; + if (voice.IsOn()) + voice.UpdateADSREnvelope(); } break; case 0x0C: // adsr volume { - Log_DebugPrintf("SPU voice %u ADSR volume <- 0x%04X", voice_index, value); + Log_DebugPrintf("SPU voice %u ADSR volume <- 0x%04X (was 0x%04X)", voice_index, value, voice.regs.adsr_volume); voice.regs.adsr_volume = value; } break; @@ -1055,7 +1062,8 @@ void SPU::Voice::KeyOn() adpcm_last_samples.fill(0); has_samples = false; ignore_loop_address = false; - SetADSRPhase(ADSRPhase::Attack); + adsr_phase = ADSRPhase::Attack; + UpdateADSREnvelope(); } void SPU::Voice::KeyOff() @@ -1063,7 +1071,8 @@ void SPU::Voice::KeyOff() if (adsr_phase == ADSRPhase::Off || adsr_phase == ADSRPhase::Release) return; - SetADSRPhase(ADSRPhase::Release); + adsr_phase = ADSRPhase::Release; + UpdateADSREnvelope(); } void SPU::Voice::ForceOff() @@ -1072,7 +1081,7 @@ void SPU::Voice::ForceOff() return; regs.adsr_volume = 0; - SetADSRPhase(ADSRPhase::Off); + adsr_phase = ADSRPhase::Off; } SPU::ADSRPhase SPU::GetNextADSRPhase(ADSRPhase phase) @@ -1216,10 +1225,9 @@ void SPU::VolumeSweep::Tick() (envelope.decreasing ? (current_level > ENVELOPE_MIN_VOLUME) : (current_level < ENVELOPE_MAX_VOLUME)); } -void SPU::Voice::SetADSRPhase(ADSRPhase phase) +void SPU::Voice::UpdateADSREnvelope() { - adsr_phase = phase; - switch (phase) + switch (adsr_phase) { case ADSRPhase::Off: adsr_target = 0; @@ -1261,7 +1269,10 @@ void SPU::Voice::TickADSR() const bool reached_target = adsr_envelope.decreasing ? (regs.adsr_volume <= adsr_target) : (regs.adsr_volume >= adsr_target); if (reached_target) - SetADSRPhase(GetNextADSRPhase(adsr_phase)); + { + adsr_phase = GetNextADSRPhase(adsr_phase); + UpdateADSREnvelope(); + } } } diff --git a/src/core/spu.h b/src/core/spu.h index fc0f73218..591cb1377 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -277,7 +277,7 @@ private: s32 Interpolate() const; // Switches to the specified phase, filling in target. - void SetADSRPhase(ADSRPhase phase); + void UpdateADSREnvelope(); // Updates the ADSR volume/phase. void TickADSR();