From 390391e119bd77f2abf4583cb91d036e7e9dc024 Mon Sep 17 00:00:00 2001 From: Ziemas Date: Sat, 13 Feb 2021 21:00:01 +0100 Subject: [PATCH] SPU2: Downsample reverb input These filter coefficients come from mednafen, I think they were originally found by Neill Corlett. I have not personally verified their accuracy. --- pcsx2/SPU2/Reverb.cpp | 74 +++++++++++++++++++++++++++++++++++++++++- pcsx2/SPU2/defs.h | 7 ++++ pcsx2/SPU2/spu2sys.cpp | 4 +++ pcsx2/SaveState.h | 2 +- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/pcsx2/SPU2/Reverb.cpp b/pcsx2/SPU2/Reverb.cpp index 66f91fe48d..e631e40bec 100644 --- a/pcsx2/SPU2/Reverb.cpp +++ b/pcsx2/SPU2/Reverb.cpp @@ -46,6 +46,73 @@ void V_Core::Reverb_AdvanceBuffer() } } + + +static constexpr u32 NUM_TAPS = 39; +// 39 tap filter, the 0's could be optimized out +static constexpr std::array filter_coefs = { + -1, + 0, + 2, + 0, + -10, + 0, + 35, + 0, + -103, + 0, + 266, + 0, + -616, + 0, + 1332, + 0, + -2960, + 0, + 10246, + 16384, + 10246, + 0, + -2960, + 0, + 1332, + 0, + -616, + 0, + 266, + 0, + -103, + 0, + 35, + 0, + -10, + 0, + 2, + 0, + -1, +}; + +s32 V_Core::ReverbDownsample(bool right) +{ + s32 out = 0; + + for (u32 i = 0; i < NUM_TAPS; i++) + { + out += RevbDownBuf[right][((RevbSampleBufPos - NUM_TAPS) + i) & 63] * filter_coefs[i]; + } + + out >>= 15; + Clampify(out, INT16_MIN, INT16_MAX); + + return out; +} + + +s32 V_Core::ReverbUpsample() +{ + return 0; +} + ///////////////////////////////////////////////////////////////////////////////////////// StereoOut32 V_Core::DoReverb(const StereoOut32& Input) @@ -55,6 +122,11 @@ StereoOut32 V_Core::DoReverb(const StereoOut32& Input) return StereoOut32::Empty; } + RevbSampleBufPos++; + + RevbDownBuf[0][RevbSampleBufPos & 63] = Input.Left; + RevbDownBuf[1][RevbSampleBufPos & 63] = Input.Right; + bool R = Cycles & 1; // Calculate the read/write addresses we'll be needing for this session of reverb. @@ -112,7 +184,7 @@ StereoOut32 V_Core::DoReverb(const StereoOut32& Input) s32 in, same, diff, apf1, apf2, out; #define MUL(x, y) ((x) * (y) >> 15) - in = MUL(R ? Revb.IN_COEF_R : Revb.IN_COEF_L, R ? Input.Right : Input.Left); + in = MUL(R ? Revb.IN_COEF_R : Revb.IN_COEF_L, ReverbDownsample(R)); same = MUL(Revb.IIR_VOL, in + MUL(Revb.WALL_VOL, _spu2mem[same_src]) - _spu2mem[same_prv]) + _spu2mem[same_prv]; diff = MUL(Revb.IIR_VOL, in + MUL(Revb.WALL_VOL, _spu2mem[diff_src]) - _spu2mem[diff_prv]) + _spu2mem[diff_prv]; diff --git a/pcsx2/SPU2/defs.h b/pcsx2/SPU2/defs.h index 90833406c0..d969174d1c 100644 --- a/pcsx2/SPU2/defs.h +++ b/pcsx2/SPU2/defs.h @@ -416,6 +416,10 @@ struct V_Core V_Reverb Revb; // Reverb Registers V_ReverbBuffers RevBuffers; // buffer pointers for reverb, pre-calculated and pre-clipped. + + s32 RevbDownBuf[2][64]; // Downsample buffer for reverb, one for each channel + s32 RevbUpBuf[2][64]; // Upsample buffer for reverb, one for each channel + u32 RevbSampleBufPos; u32 EffectsStartA; u32 EffectsEndA; u32 ExtEffectsStartA; @@ -492,6 +496,9 @@ struct V_Core StereoOut32 DoReverb(const StereoOut32& Input); s32 RevbGetIndexer(s32 offset); + s32 ReverbDownsample(bool right); + s32 ReverbUpsample(); + StereoOut32 ReadInput(); StereoOut32 ReadInput_HiFi(); diff --git a/pcsx2/SPU2/spu2sys.cpp b/pcsx2/SPU2/spu2sys.cpp index 18c1c4e4de..5bc9ff0335 100644 --- a/pcsx2/SPU2/spu2sys.cpp +++ b/pcsx2/SPU2/spu2sys.cpp @@ -222,6 +222,10 @@ void V_Core::Init(int index) Regs.ENDX = 0xffffff; // PS2 confirmed RevBuffers.NeedsUpdated = true; + RevbSampleBufPos = 0; + memset(RevbDownBuf, 0, sizeof(RevbDownBuf)); + memset(RevbUpBuf, 0, sizeof(RevbUpBuf)); + UpdateEffectsBufferSize(); } diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 7ef8789e76..d6d2ac8c01 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -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 = (0x9A1C << 16) | 0x0000; +static const u32 g_SaveVersion = (0x9A1D << 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. :)