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"
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_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));
v.counter.bits = 0;
v.current_block_flags.bits = 0;
v.is_first_block = 0;
v.current_block_samples.fill(s16(0));
v.adpcm_last_samples.fill(s32(0));
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.Do(&v.counter.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[0], NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK);
sw.Do(&v.adpcm_last_samples);
@ -586,8 +588,7 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value)
DebugAssert(voice_index < 24);
Voice& voice = m_voices[voice_index];
const u32 pending_key_on = m_key_on_register;
if (voice.IsOn() || pending_key_on & (1u << voice_index))
if (voice.IsOn() || m_key_on_register & (1u << voice_index))
m_tick_event->InvokeEarly();
switch (reg_index)
@ -649,11 +650,22 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value)
case 0x0E: // repeat address
{
const bool ignore_loop_address = (pending_key_on & (1u << voice_index)) == 0 && !voice.IsOn();
Log_DebugPrintf("SPU voice %u ADPCM repeat address <- 0x%04X, ignoring block address = %s", voice_index, value,
ignore_loop_address ? "yes" : "no");
// There is a short window of time here between the voice being keyed on and the first block finishing decoding
// where setting the repeat address will *NOT* ignore the block/loop start flag. Games sensitive to this are:
// - 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.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;
@ -957,6 +969,7 @@ void SPU::Voice::KeyOn()
static_cast<s16>(0));
has_samples = false;
is_first_block = true;
ignore_loop_address = false;
adsr_phase = ADSRPhase::Attack;
UpdateADSREnvelope();
@ -1374,6 +1387,7 @@ ALWAYS_INLINE_RELEASE std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
// next block
voice.counter.sample_index -= NUM_SAMPLES_PER_ADPCM_BLOCK;
voice.has_samples = false;
voice.is_first_block = false;
voice.current_address += 2;
// handle flags

View file

@ -244,6 +244,7 @@ private:
VoiceRegisters regs;
VoiceCounter counter;
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, 2> adpcm_last_samples;
s32 last_volume;