SPU2: Switch to a more accurate noise algorithm

This algorithm comes from pcsxr and is an implementation of Dr. Hell's
research. It's supposed to be very accurate.

Bumps savestate because of new SPU core struct members.
This commit is contained in:
Ziemas 2021-01-12 14:31:33 +01:00 committed by refractionpcsx2
parent f5d89062e0
commit 6c81bb54f7
4 changed files with 45 additions and 29 deletions

View File

@ -267,18 +267,6 @@ static __forceinline void GetNextDataDummy(V_Core& thiscore, uint voiceidx)
vc.SCurrent += 4 - (vc.SCurrent & 3);
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
static s32 __forceinline GetNoiseValues()
{
static u16 lfsr = 0xC0FEu;
u16 bit = lfsr ^ (lfsr << 3) ^ (lfsr << 4) ^ (lfsr << 5);
lfsr = (lfsr << 1) | (bit >> 15);
return (s16)lfsr;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
@ -460,26 +448,48 @@ static __forceinline s32 GetVoiceValues(V_Core& thiscore, uint voiceidx)
return 0; // technically unreachable!
}
// Noise values need to be mixed without going through interpolation, since it
// can wreak havoc on the noise (causing muffling or popping). Not that this noise
// generator is accurate in its own right.. but eh, ah well :)
static __forceinline s32 GetNoiseValues(V_Core& thiscore, uint voiceidx)
// This is Dr. Hell's noise algorithm as implemented in pcsxr
// Supposedly this is 100% accurate
static __forceinline void UpdateNoise(V_Core& thiscore)
{
// V_Voice &vc(thiscore.Voices[voiceidx]);
static const uint8_t noise_add[64] = {
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1};
s32 retval = GetNoiseValues();
static const uint16_t noise_freq_add[5] = {
0, 84, 140, 180, 210};
/*while(vc.SP>=4096)
u32 level = 0x8000 >> (thiscore.NoiseClk >> 2);
level <<= 16;
thiscore.NoiseCnt += 0x10000;
thiscore.NoiseCnt += noise_freq_add[thiscore.NoiseClk & 3];
if ((thiscore.NoiseCnt & 0xffff) >= noise_freq_add[4])
{
retval = GetNoiseValues();
vc.SP-=4096;
}*/
thiscore.NoiseCnt += 0x10000;
thiscore.NoiseCnt -= noise_freq_add[thiscore.NoiseClk & 3];
}
// GetNoiseValues can't set the phase zero on us unexpectedly
// like GetVoiceValues can. Better assert just in case though..
// pxAssume(vc.ADSR.Phase != 0);
if (thiscore.NoiseCnt >= level)
{
while (thiscore.NoiseCnt >= level)
thiscore.NoiseCnt -= level;
return retval;
thiscore.NoiseOut = (thiscore.NoiseOut << 1) | noise_add[(thiscore.NoiseOut >> 10) & 63];
}
}
static __forceinline s32 GetNoiseValues(V_Core& thiscore)
{
return (s16)thiscore.NoiseOut;
}
/////////////////////////////////////////////////////////////////////////////////////////
@ -534,7 +544,7 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
s32 Value = 0;
if (vc.Noise)
Value = GetNoiseValues(thiscore, voiceidx);
Value = GetNoiseValues(thiscore);
else
{
// Optimization : Forceinline'd Templated Dispatch Table. Any halfwit compiler will
@ -641,6 +651,8 @@ static __forceinline void MixCoreVoices(VoiceMixSet& dest, const uint coreidx)
StereoOut32 V_Core::Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, const StereoOut32& Ext)
{
MasterVol.Update();
UpdateNoise(*this);
// Saturate final result to standard 16 bit range.
const VoiceMixSet Voices(clamp_mix(inVoices.Dry), clamp_mix(inVoices.Wet));

View File

@ -399,7 +399,9 @@ struct V_Core
bool AdmaInProgress;
s8 DMABits; // DMA related?
s8 NoiseClk; // Noise Clock
u8 NoiseClk; // Noise Clock
u32 NoiseCnt; // Noise Counter
u32 NoiseOut; // Noise Output
u16 AutoDMACtrl; // AutoDMA Status
s32 DMAICounter; // DMA Interrupt Counter
u32 LastClock; // DMA Interrupt Clock Cycle Counter

View File

@ -130,6 +130,8 @@ void V_Core::Init(int index)
Mute = false;
DMABits = 0;
NoiseClk = 0;
NoiseCnt = 0;
NoiseOut = 0;
AutoDMACtrl = 0;
InputDataLeft = 0;
InputPosRead = 0;

View File

@ -24,7 +24,7 @@
// the lower 16 bit value. IF the change is breaking of all compatibility with old
// states, increment the upper 16 bit value, and clear the lower 16 bits to 0.
static const u32 g_SaveVersion = (0x9A17 << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A18 << 16) | 0x0000;
// this function is meant to be used in the place of GSfreeze, and provides a safe layer
// between the GS saving function and the MTGS's needs. :)