SPU2: Get rid of the need for delay cycles

Properly handle KeyOn/KeyOff happening in quick succession.
This commit is contained in:
refractionpcsx2 2020-11-29 04:23:52 +00:00
parent f929d79473
commit 465aa00fc7
5 changed files with 33 additions and 53 deletions

View File

@ -57,7 +57,6 @@ float VolumeAdjustBR;
float VolumeAdjustSL; float VolumeAdjustSL;
float VolumeAdjustSR; float VolumeAdjustSR;
float VolumeAdjustLFE; float VolumeAdjustLFE;
unsigned int delayCycles;
bool postprocess_filter_enabled = true; bool postprocess_filter_enabled = true;
bool postprocess_filter_dealias = false; bool postprocess_filter_dealias = false;
@ -109,7 +108,6 @@ void ReadSettings()
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10); VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10); VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10); VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
wxString temp; wxString temp;
@ -207,7 +205,6 @@ void WriteSettings()
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS); CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode); CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers); CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
#ifdef SPU2X_PORTAUDIO #ifdef SPU2X_PORTAUDIO
PortaudioOut->WriteSettings(); PortaudioOut->WriteSettings();

View File

@ -43,7 +43,6 @@ extern float VolumeAdjustBR;
extern float VolumeAdjustSL; extern float VolumeAdjustSL;
extern float VolumeAdjustSR; extern float VolumeAdjustSR;
extern float VolumeAdjustLFE; extern float VolumeAdjustLFE;
extern unsigned int delayCycles;
struct Stereo51Out16DplII; struct Stereo51Out16DplII;
struct Stereo51Out32DplII; struct Stereo51Out32DplII;

View File

@ -58,7 +58,6 @@ float VolumeAdjustBR;
float VolumeAdjustSL; float VolumeAdjustSL;
float VolumeAdjustSR; float VolumeAdjustSR;
float VolumeAdjustLFE; float VolumeAdjustLFE;
unsigned int delayCycles;
bool postprocess_filter_enabled = 1; bool postprocess_filter_enabled = 1;
bool postprocess_filter_dealias = false; bool postprocess_filter_dealias = false;
@ -102,7 +101,6 @@ void ReadSettings()
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0); VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0); VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0); VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10); VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10); VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10); VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
@ -185,7 +183,6 @@ void WriteSettings()
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode); CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers); CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
CfgWriteInt(L"OUTPUT", L"DplDecodingLevel", dplLevel); CfgWriteInt(L"OUTPUT", L"DplDecodingLevel", dplLevel);
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
if (Config_WaveOut.Device.empty()) if (Config_WaveOut.Device.empty())
Config_WaveOut.Device = L"default"; Config_WaveOut.Device = L"default";

View File

@ -189,7 +189,6 @@ struct V_Voice
s32 SCurrent; s32 SCurrent;
// it takes a few ticks for voices to start on the real SPU2? // it takes a few ticks for voices to start on the real SPU2?
void QueueStart();
bool Start(); bool Start();
void Stop(); void Stop();
}; };

View File

@ -327,44 +327,29 @@ void V_Core::UpdateEffectsBufferSize()
RevBuffers.APF2_R_SRC = EffectsBufferIndexer(Revb.APF2_R_DST - Revb.APF2_SIZE); RevBuffers.APF2_R_SRC = EffectsBufferIndexer(Revb.APF2_R_DST - Revb.APF2_SIZE);
} }
void V_Voice::QueueStart()
{
if (Cycles - PlayCycle < delayCycles)
{
// Required by The Legend of Spyro: The Eternal Night (probably the other two legend games too)
ConLog(" *** KeyOn after less than %d T disregarded.\n", delayCycles);
return;
}
PlayCycle = Cycles;
}
bool V_Voice::Start() bool V_Voice::Start()
{ {
if ((Cycles - PlayCycle) >= delayCycles) if (StartA & 7)
{ {
if (StartA & 7) fprintf(stderr, " *** Misaligned StartA %05x!\n", StartA);
{ StartA = (StartA + 0xFFFF8) + 0x8;
fprintf(stderr, " *** Misaligned StartA %05x!\n", StartA);
StartA = (StartA + 0xFFFF8) + 0x8;
}
ADSR.Releasing = false;
ADSR.Value = 1;
ADSR.Phase = 1;
SCurrent = 28;
LoopMode = 0;
LoopFlags = 0;
NextA = StartA | 1;
Prev1 = 0;
Prev2 = 0;
PV1 = PV2 = 0;
PV3 = PV4 = 0;
NextCrest = -0x8000;
return true;
} }
else
return false; ADSR.Releasing = false;
ADSR.Value = 1;
ADSR.Phase = 1;
SCurrent = 28;
LoopMode = 0;
LoopFlags = 0;
NextA = StartA | 1;
Prev1 = 0;
Prev2 = 0;
PV1 = PV2 = 0;
PV3 = PV4 = 0;
NextCrest = -0x8000;
PlayCycle = Cycles;
return true;
} }
void V_Voice::Stop() void V_Voice::Stop()
@ -471,13 +456,6 @@ __forceinline void TimeUpdate(u32 cClocks)
lClocks += TickInterval; lClocks += TickInterval;
Cycles++; Cycles++;
for (int i = 0; i < 2; i++)
if (Cores[i].KeyOn)
for (int j = 0; j < 24; j++)
if (Cores[i].KeyOn >> j & 1)
if (Cores[i].Voices[j].Start())
Cores[i].KeyOn &= ~(1 << j);
// Note: IOP does not use MMX regs, so no need to save them. // Note: IOP does not use MMX regs, so no need to save them.
//SaveMMXRegs(); //SaveMMXRegs();
Mix(); Mix();
@ -1857,22 +1835,23 @@ void SPU2_FastWrite(u32 rmem, u16 value)
void StartVoices(int core, u32 value) void StartVoices(int core, u32 value)
{ {
ConLog("KeyOn Write %x\n", value);
// Optimization: Games like to write zero to the KeyOn reg a lot, so shortcut // Optimization: Games like to write zero to the KeyOn reg a lot, so shortcut
// this loop if value is zero. // this loop if value is zero.
if (value == 0) if (value == 0)
return; return;
Cores[core].Regs.ENDX &= ~value;
Cores[core].KeyOn |= value; Cores[core].KeyOn |= value;
Cores[core].Regs.ENDX &= ~value;
for (u8 vc = 0; vc < V_Core::NumVoices; vc++) for (u8 vc = 0; vc < V_Core::NumVoices; vc++)
{ {
if (!((value >> vc) & 1)) if (!((value >> vc) & 1))
continue; continue;
Cores[core].Voices[vc].QueueStart(); if (Cores[core].Voices[vc].Start())
Cores[core].KeyOn &= ~(1 << vc);
if (IsDevBuild) if (IsDevBuild)
{ {
@ -1893,13 +1872,22 @@ void StartVoices(int core, u32 value)
void StopVoices(int core, u32 value) void StopVoices(int core, u32 value)
{ {
ConLog("KeyOff Write %x\n", value);
if (value == 0) if (value == 0)
return; return;
for (u8 vc = 0; vc < V_Core::NumVoices; vc++) for (u8 vc = 0; vc < V_Core::NumVoices; vc++)
{ {
if (!((value >> vc) & 1)) if (!((value >> vc) & 1))
continue; continue;
if (Cycles - Cores[core].Voices[vc].PlayCycle < 2)
{
ConLog("Attempt to stop voice %d on core %d in less than 2T since KeyOn\n", vc, core);
continue;
}
Cores[core].Voices[vc].ADSR.Releasing = true; Cores[core].Voices[vc].ADSR.Releasing = true;
if (MsgKeyOnOff()) if (MsgKeyOnOff())
ConLog("* SPU2: KeyOff: Core %d; Voice %d.\n", core, vc); ConLog("* SPU2: KeyOff: Core %d; Voice %d.\n", core, vc);