SPU: Ignore manually set loop address only on first block

Fixes occasional missing dialogue in Valkyrie Profile.
This commit is contained in:
Connor McLaughlin 2020-12-06 00:31:07 +10:00
parent d7bd21c06d
commit a90c25d0e7
3 changed files with 21 additions and 6 deletions

View file

@ -2,7 +2,7 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 46; static constexpr u32 SAVE_STATE_VERSION = 47;
static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION); static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);

View file

@ -91,6 +91,7 @@ void SPU::Reset()
std::fill_n(v.regs.index, NUM_VOICE_REGISTERS, u16(0)); std::fill_n(v.regs.index, NUM_VOICE_REGISTERS, u16(0));
v.counter.bits = 0; v.counter.bits = 0;
v.current_block_flags.bits = 0; v.current_block_flags.bits = 0;
v.is_first_block = 0;
v.current_block_samples.fill(s16(0)); v.current_block_samples.fill(s16(0));
v.adpcm_last_samples.fill(s32(0)); v.adpcm_last_samples.fill(s32(0));
v.adsr_envelope.Reset(0, false, false); v.adsr_envelope.Reset(0, false, false);
@ -150,6 +151,7 @@ bool SPU::DoState(StateWrapper& sw)
sw.DoArray(v.regs.index, NUM_VOICE_REGISTERS); sw.DoArray(v.regs.index, NUM_VOICE_REGISTERS);
sw.Do(&v.counter.bits); sw.Do(&v.counter.bits);
sw.Do(&v.current_block_flags.bits); sw.Do(&v.current_block_flags.bits);
sw.DoEx(&v.is_first_block, 47, false);
sw.DoArray(&v.current_block_samples[NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK], NUM_SAMPLES_PER_ADPCM_BLOCK); sw.DoArray(&v.current_block_samples[NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK], NUM_SAMPLES_PER_ADPCM_BLOCK);
sw.DoArray(&v.current_block_samples[0], NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK); sw.DoArray(&v.current_block_samples[0], NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK);
sw.Do(&v.adpcm_last_samples); sw.Do(&v.adpcm_last_samples);
@ -586,8 +588,7 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value)
DebugAssert(voice_index < 24); DebugAssert(voice_index < 24);
Voice& voice = m_voices[voice_index]; Voice& voice = m_voices[voice_index];
const u32 pending_key_on = m_key_on_register; if (voice.IsOn() || m_key_on_register & (1u << voice_index))
if (voice.IsOn() || pending_key_on & (1u << voice_index))
m_tick_event->InvokeEarly(); m_tick_event->InvokeEarly();
switch (reg_index) switch (reg_index)
@ -649,11 +650,22 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value)
case 0x0E: // repeat address case 0x0E: // repeat address
{ {
const bool ignore_loop_address = (pending_key_on & (1u << voice_index)) == 0 && !voice.IsOn(); // There is a short window of time here between the voice being keyed on and the first block finishing decoding
Log_DebugPrintf("SPU voice %u ADPCM repeat address <- 0x%04X, ignoring block address = %s", voice_index, value, // where setting the repeat address will *NOT* ignore the block/loop start flag. Games sensitive to this are:
ignore_loop_address ? "yes" : "no"); // - The Misadventures of Tron Bonne
// - Re-Loaded - The Hardcore Sequel
// - Valkyrie Profile
const bool ignore_loop_address = voice.IsOn() && !voice.is_first_block;
Log_DebugPrintf("SPU voice %u ADPCM repeat address <- 0x%04X", voice_index, value);
voice.regs.adpcm_repeat_address = value; voice.regs.adpcm_repeat_address = value;
voice.ignore_loop_address |= ignore_loop_address; voice.ignore_loop_address |= ignore_loop_address;
if (!ignore_loop_address)
{
Log_DevPrintf("Not ignoring loop address, the ADPCM repeat address of 0x%04X for voice %u will be overwritten",
value, voice_index);
}
} }
break; break;
@ -957,6 +969,7 @@ void SPU::Voice::KeyOn()
static_cast<s16>(0)); static_cast<s16>(0));
has_samples = false; has_samples = false;
is_first_block = true;
ignore_loop_address = false; ignore_loop_address = false;
adsr_phase = ADSRPhase::Attack; adsr_phase = ADSRPhase::Attack;
UpdateADSREnvelope(); UpdateADSREnvelope();
@ -1374,6 +1387,7 @@ ALWAYS_INLINE_RELEASE std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
// next block // next block
voice.counter.sample_index -= NUM_SAMPLES_PER_ADPCM_BLOCK; voice.counter.sample_index -= NUM_SAMPLES_PER_ADPCM_BLOCK;
voice.has_samples = false; voice.has_samples = false;
voice.is_first_block = false;
voice.current_address += 2; voice.current_address += 2;
// handle flags // handle flags

View file

@ -244,6 +244,7 @@ private:
VoiceRegisters regs; VoiceRegisters regs;
VoiceCounter counter; VoiceCounter counter;
ADPCMFlags current_block_flags; ADPCMFlags current_block_flags;
bool is_first_block;
std::array<s16, NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK + NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples; std::array<s16, NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK + NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples;
std::array<s16, 2> adpcm_last_samples; std::array<s16, 2> adpcm_last_samples;
s32 last_volume; s32 last_volume;