From 99c62c650134148b55f25ab61740c071c4aa2d74 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Mon, 26 Oct 2009 02:38:27 +0000 Subject: [PATCH] SPU2-X: (experimental) New reverb implementation using a more correct downsampler, as borrowed from "sexypsf." git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2081 96395faa-99c1-11dd-bbfe-3dabce05a288 --- plugins/spu2-x/src/Mixer.cpp | 19 +++--- plugins/spu2-x/src/Mixer.h | 15 +++++ plugins/spu2-x/src/Reverb.cpp | 110 +++++++++++++++------------------- 3 files changed, 74 insertions(+), 70 deletions(-) diff --git a/plugins/spu2-x/src/Mixer.cpp b/plugins/spu2-x/src/Mixer.cpp index f2fcd24fd0..07170430da 100644 --- a/plugins/spu2-x/src/Mixer.cpp +++ b/plugins/spu2-x/src/Mixer.cpp @@ -603,16 +603,19 @@ StereoOut32 V_Core::Mix( const VoiceMixSet& inVoices, const StereoOut32& Input, WaveDump::WriteCore( Index, CoreSrc_PreReverb, TW ); + // Like all over volumes on SPU2, reverb coefficients and stuff are signed, + // range -50% to 50%, thus *2 is typically needed. The question is: boost the + // volume before going into the reverb unit, or after coming out? + StereoOut32 RV( DoReverb( TW ) ); + + // (to do pre-reverb boost, change TW above to TW*2 and remove the *2 below on RW. + // This should give a sligntly deeper reverb effect, but may cause distortion on + // some games.) + + // (fixme: this may not be true anymore with the new reverb system, needs testing) - // Volume boost after effects application. Boosting volume prior to effects - // causes slight overflows in some games, and the volume boost is required. - // (like all over volumes on SPU2, reverb coefficients and stuff are signed, - // range -50% to 50%, thus *2 is needed) - - RV.Left *= 2; - RV.Right *= 2; - + RV *= 2; WaveDump::WriteCore( Index, CoreSrc_PostReverb, RV ); // Mix Dry+Wet diff --git a/plugins/spu2-x/src/Mixer.h b/plugins/spu2-x/src/Mixer.h index 8422a17154..36eb552019 100644 --- a/plugins/spu2-x/src/Mixer.h +++ b/plugins/spu2-x/src/Mixer.h @@ -40,6 +40,21 @@ struct StereoOut32 StereoOut16 DownSample() const; + StereoOut32 operator*( const int& factor ) const + { + return StereoOut32( + Left * factor, + Right * factor + ); + } + + StereoOut32& operator*=( const int& factor ) + { + Left *= factor; + Right *= factor; + return *this; + } + StereoOut32 operator+( const StereoOut32& right ) const { return StereoOut32( diff --git a/plugins/spu2-x/src/Reverb.cpp b/plugins/spu2-x/src/Reverb.cpp index 194dfaf935..b2a4d28e23 100644 --- a/plugins/spu2-x/src/Reverb.cpp +++ b/plugins/spu2-x/src/Reverb.cpp @@ -21,8 +21,8 @@ // Low pass filters: Change these to 32 for a speedup (benchmarks needed to see if // the speed gain is worth the quality drop) -static LowPassFilter64 lowpass_left( 11000, SampleRate ); -static LowPassFilter64 lowpass_right( 11000, SampleRate ); +//static LowPassFilter64 lowpass_left( 11000, SampleRate ); +//static LowPassFilter64 lowpass_right( 11000, SampleRate ); __forceinline s32 V_Core::RevbGetIndexer( s32 offset ) { @@ -44,14 +44,8 @@ void V_Core::Reverb_AdvanceBuffer() { if( (Cycles & 1) && (EffectsBufferSize > 0) ) { - //ReverbX = RevbGetIndexer( thiscore, 1 ); ReverbX += 1; - if( ReverbX >= (u32)EffectsBufferSize ) ReverbX = 0; - - //ReverbX += 1; - //if(ReverbX >= (u32)EffectsBufferSize ) - // ReverbX %= (u32)EffectsBufferSize; } } @@ -59,38 +53,37 @@ void V_Core::Reverb_AdvanceBuffer() StereoOut32 V_Core::DoReverb( const StereoOut32& Input ) { + static StereoOut32 downbuf[8]; + static StereoOut32 upbuf[8]; + static int dbpos=0, ubpos=0; + + static const s32 downcoeffs[8] = + { + 1283, 5344, 10895, 15243, + 15243, 10895, 5344, 1283 + }; + + downbuf[dbpos] = Input; + dbpos = (dbpos+1) & 7; + // Reverb processing occurs at 24khz, so we skip processing every other sample, // and use the previous calculation for this core instead. - if( (Cycles&1)==0 ) + if( (Cycles&1) == 0 ) { - StereoOut32 retval( LastEffect ); - - // Make sure and pass input through the LPF. The result can be discarded. - // This gives the LPF a better sampling from which to kill offending frequencies. + // Important: Factor silence into the upsampler here, otherwise the reverb engine + // develops a nasty feedback loop. - lowpass_left.sample( Input.Left / 32768.0 ); - lowpass_right.sample( Input.Right / 32768.0 ); - - //LastEffect = Input; - return retval; + upbuf[ubpos] = StereoOut32::Empty; + ubpos = (ubpos+1) & 7; } - else + else { if( RevBuffers.NeedsUpdated ) UpdateEffectsBufferSize(); if( EffectsBufferSize <= 0 ) - { - // StartA is past EndA, so effects are disabled. - //ConLog( " * SPU2: Effects disabled due to leapfrogged EffectsStart." ); - - // Should we return zero here, or the input sample? - // Because reverb gets an *2 mul, returning input seems dangerous, so I opt for silence. - - //return Input; return StereoOut32::Empty; - } // Advance the current reverb buffer pointer, and cache the read/write addresses we'll be // needing for this session of reverb. @@ -131,42 +124,28 @@ StereoOut32 V_Core::DoReverb( const StereoOut32& Input ) const u32 mix_dest_b1 = RevbGetIndexer( RevBuffers.MIX_DEST_B1 ); // ----------------------------------------- - // End Buffer Pointers, Begin Reverb! + // Begin Reverb Processing ! // ----------------------------------------- - //StereoOut32 INPUT_SAMPLE( LastEffect + Input ); + StereoOut32 INPUT_SAMPLE; - // Note: LowPass on the input! Very important. Some games like DDS get terrible feedback otherwise. - // Decisions, Decisions! Should we mix in the 22khz sample skipped, or not? - // First one mixes in the 22hkz sample. Second one does not. + for( int x=0; x<8; ++x ) + { + INPUT_SAMPLE.Left += (downbuf[(dbpos+x)&7].Left * downcoeffs[x]); + INPUT_SAMPLE.Right += (downbuf[(dbpos+x)&7].Right * downcoeffs[x]); + } - /*StereoOut32 INPUT_SAMPLE( - (s32)(lowpass_left.sample( (Input.Left+LastEffect.Left) / 32768.0 ) * 32768.0), - (s32)(lowpass_right.sample( (Input.Right+LastEffect.Right) / 32768.0 ) * 32768.0) - );*/ + INPUT_SAMPLE.Left >>= 16; + INPUT_SAMPLE.Right >>= 16; - StereoOut32 INPUT_SAMPLE( - (s32)(lowpass_left.sample( Input.Left / 32768.0 ) * 32768.0), - (s32)(lowpass_right.sample( Input.Right / 32768.0 ) * 32768.0) - ); - const s32 IIR_INPUT_A0 = ((_spu2mem[src_a0] * Revb.IIR_COEF) + (INPUT_SAMPLE.Left * Revb.IN_COEF_L))>>16; const s32 IIR_INPUT_A1 = ((_spu2mem[src_a1] * Revb.IIR_COEF) + (INPUT_SAMPLE.Right * Revb.IN_COEF_R))>>16; const s32 IIR_INPUT_B0 = ((_spu2mem[src_b0] * Revb.IIR_COEF) + (INPUT_SAMPLE.Left * Revb.IN_COEF_L))>>16; const s32 IIR_INPUT_B1 = ((_spu2mem[src_b1] * Revb.IIR_COEF) + (INPUT_SAMPLE.Right * Revb.IN_COEF_R))>>16; - const s32 IIR_A0 = (IIR_INPUT_A0 * Revb.IIR_ALPHA) + (_spu2mem[dest_a0] * (0x7fff - Revb.IIR_ALPHA)); - const s32 IIR_A1 = (IIR_INPUT_A1 * Revb.IIR_ALPHA) + (_spu2mem[dest_a1] * (0x7fff - Revb.IIR_ALPHA)); - const s32 IIR_B0 = (IIR_INPUT_B0 * Revb.IIR_ALPHA) + (_spu2mem[dest_b0] * (0x7fff - Revb.IIR_ALPHA)); - const s32 IIR_B1 = (IIR_INPUT_B1 * Revb.IIR_ALPHA) + (_spu2mem[dest_b1] * (0x7fff - Revb.IIR_ALPHA)); - _spu2mem[dest2_a0] = clamp_mix( IIR_A0 >> 16 ); - _spu2mem[dest2_a1] = clamp_mix( IIR_A1 >> 16 ); - _spu2mem[dest2_b0] = clamp_mix( IIR_B0 >> 16 ); - _spu2mem[dest2_b1] = clamp_mix( IIR_B1 >> 16 ); - // Faster single-mul approach to interpolation: // (doesn't work yet -- breaks Digital Devil Saga badly) - /*const s32 IIR_A0 = IIR_INPUT_A0 + (((_spu2mem[dest_a0]-IIR_INPUT_A0) * Revb.IIR_ALPHA)>>16); + const s32 IIR_A0 = IIR_INPUT_A0 + (((_spu2mem[dest_a0]-IIR_INPUT_A0) * Revb.IIR_ALPHA)>>16); const s32 IIR_A1 = IIR_INPUT_A1 + (((_spu2mem[dest_a1]-IIR_INPUT_A1) * Revb.IIR_ALPHA)>>16); const s32 IIR_B0 = IIR_INPUT_B0 + (((_spu2mem[dest_b0]-IIR_INPUT_B0) * Revb.IIR_ALPHA)>>16); const s32 IIR_B1 = IIR_INPUT_B1 + (((_spu2mem[dest_b1]-IIR_INPUT_B1) * Revb.IIR_ALPHA)>>16); @@ -174,7 +153,7 @@ StereoOut32 V_Core::DoReverb( const StereoOut32& Input ) _spu2mem[dest2_a0] = clamp_mix( IIR_A0 ); _spu2mem[dest2_a1] = clamp_mix( IIR_A1 ); _spu2mem[dest2_b0] = clamp_mix( IIR_B0 ); - _spu2mem[dest2_b1] = clamp_mix( IIR_B1 );*/ + _spu2mem[dest2_b1] = clamp_mix( IIR_B1 ); const s32 ACC0 = ((_spu2mem[acc_src_a0] * Revb.ACC_COEF_A)) + @@ -199,14 +178,21 @@ StereoOut32 V_Core::DoReverb( const StereoOut32& Input ) _spu2mem[mix_dest_b0] = clamp_mix( (MulShr32(Revb.FB_ALPHA<<16, ACC0) - fb_xor_a0 - (_spu2mem[fb_src_b0] * Revb.FB_X)) >> 16 ); _spu2mem[mix_dest_b1] = clamp_mix( (MulShr32(Revb.FB_ALPHA<<16, ACC1) - fb_xor_a1 - (_spu2mem[fb_src_b1] * Revb.FB_X)) >> 16 ); - LastEffect.Left = _spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0]; - LastEffect.Right = _spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1]; - - clamp_mix( LastEffect ); - - //LastEffect.Left = (s32)(lowpass_left.sample( LastEffect.Left / 32768.0 ) * 32768.0); - //LastEffect.Right = (s32)(lowpass_right.sample( LastEffect.Right / 32768.0 ) * 32768.0); - - return LastEffect; + upbuf[ubpos] = clamp_mix( StereoOut32( + _spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0], // left + _spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1] // right + ) ); } + + StereoOut32 retval; + + for( int x=0; x<8; ++x ) + { + retval.Left += (upbuf[(ubpos+x)&7].Left*downcoeffs[x]); + retval.Right += (upbuf[(ubpos+x)&7].Right*downcoeffs[x]); + } + retval.Left >>= (16-1); /* -1 To adjust for the null padding. */ + retval.Right >>= (16-1); + + return retval; }