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); 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! return 0; // technically unreachable!
} }
// Noise values need to be mixed without going through interpolation, since it // This is Dr. Hell's noise algorithm as implemented in pcsxr
// can wreak havoc on the noise (causing muffling or popping). Not that this noise // Supposedly this is 100% accurate
// generator is accurate in its own right.. but eh, ah well :) static __forceinline void UpdateNoise(V_Core& thiscore)
static __forceinline s32 GetNoiseValues(V_Core& thiscore, uint voiceidx)
{ {
// 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(); thiscore.NoiseCnt += 0x10000;
vc.SP-=4096; thiscore.NoiseCnt -= noise_freq_add[thiscore.NoiseClk & 3];
}*/ }
// GetNoiseValues can't set the phase zero on us unexpectedly if (thiscore.NoiseCnt >= level)
// like GetVoiceValues can. Better assert just in case though.. {
// pxAssume(vc.ADSR.Phase != 0); 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; s32 Value = 0;
if (vc.Noise) if (vc.Noise)
Value = GetNoiseValues(thiscore, voiceidx); Value = GetNoiseValues(thiscore);
else else
{ {
// Optimization : Forceinline'd Templated Dispatch Table. Any halfwit compiler will // 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) StereoOut32 V_Core::Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, const StereoOut32& Ext)
{ {
MasterVol.Update(); MasterVol.Update();
UpdateNoise(*this);
// Saturate final result to standard 16 bit range. // Saturate final result to standard 16 bit range.
const VoiceMixSet Voices(clamp_mix(inVoices.Dry), clamp_mix(inVoices.Wet)); const VoiceMixSet Voices(clamp_mix(inVoices.Dry), clamp_mix(inVoices.Wet));

View File

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

View File

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

View File

@ -24,7 +24,7 @@
// the lower 16 bit value. IF the change is breaking of all compatibility with old // 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. // 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 // 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. :) // between the GS saving function and the MTGS's needs. :)