mirror of https://github.com/PCSX2/pcsx2.git
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
This commit is contained in:
parent
eea8ff9767
commit
99c62c6501
|
@ -603,16 +603,19 @@ StereoOut32 V_Core::Mix( const VoiceMixSet& inVoices, const StereoOut32& Input,
|
||||||
|
|
||||||
WaveDump::WriteCore( Index, CoreSrc_PreReverb, TW );
|
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 ) );
|
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
|
RV *= 2;
|
||||||
// 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;
|
|
||||||
|
|
||||||
WaveDump::WriteCore( Index, CoreSrc_PostReverb, RV );
|
WaveDump::WriteCore( Index, CoreSrc_PostReverb, RV );
|
||||||
|
|
||||||
// Mix Dry+Wet
|
// Mix Dry+Wet
|
||||||
|
|
|
@ -40,6 +40,21 @@ struct StereoOut32
|
||||||
|
|
||||||
StereoOut16 DownSample() const;
|
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
|
StereoOut32 operator+( const StereoOut32& right ) const
|
||||||
{
|
{
|
||||||
return StereoOut32(
|
return StereoOut32(
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
// Low pass filters: Change these to 32 for a speedup (benchmarks needed to see if
|
// Low pass filters: Change these to 32 for a speedup (benchmarks needed to see if
|
||||||
// the speed gain is worth the quality drop)
|
// the speed gain is worth the quality drop)
|
||||||
|
|
||||||
static LowPassFilter64 lowpass_left( 11000, SampleRate );
|
//static LowPassFilter64 lowpass_left( 11000, SampleRate );
|
||||||
static LowPassFilter64 lowpass_right( 11000, SampleRate );
|
//static LowPassFilter64 lowpass_right( 11000, SampleRate );
|
||||||
|
|
||||||
__forceinline s32 V_Core::RevbGetIndexer( s32 offset )
|
__forceinline s32 V_Core::RevbGetIndexer( s32 offset )
|
||||||
{
|
{
|
||||||
|
@ -44,14 +44,8 @@ void V_Core::Reverb_AdvanceBuffer()
|
||||||
{
|
{
|
||||||
if( (Cycles & 1) && (EffectsBufferSize > 0) )
|
if( (Cycles & 1) && (EffectsBufferSize > 0) )
|
||||||
{
|
{
|
||||||
//ReverbX = RevbGetIndexer( thiscore, 1 );
|
|
||||||
ReverbX += 1;
|
ReverbX += 1;
|
||||||
|
|
||||||
if( ReverbX >= (u32)EffectsBufferSize ) ReverbX = 0;
|
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 )
|
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,
|
// Reverb processing occurs at 24khz, so we skip processing every other sample,
|
||||||
// and use the previous calculation for this core instead.
|
// and use the previous calculation for this core instead.
|
||||||
|
|
||||||
if( (Cycles&1)==0 )
|
if( (Cycles&1) == 0 )
|
||||||
{
|
{
|
||||||
StereoOut32 retval( LastEffect );
|
// Important: Factor silence into the upsampler here, otherwise the reverb engine
|
||||||
|
// develops a nasty feedback loop.
|
||||||
// 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.
|
|
||||||
|
|
||||||
lowpass_left.sample( Input.Left / 32768.0 );
|
upbuf[ubpos] = StereoOut32::Empty;
|
||||||
lowpass_right.sample( Input.Right / 32768.0 );
|
ubpos = (ubpos+1) & 7;
|
||||||
|
|
||||||
//LastEffect = Input;
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if( RevBuffers.NeedsUpdated )
|
if( RevBuffers.NeedsUpdated )
|
||||||
UpdateEffectsBufferSize();
|
UpdateEffectsBufferSize();
|
||||||
|
|
||||||
if( EffectsBufferSize <= 0 )
|
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;
|
return StereoOut32::Empty;
|
||||||
}
|
|
||||||
|
|
||||||
// Advance the current reverb buffer pointer, and cache the read/write addresses we'll be
|
// Advance the current reverb buffer pointer, and cache the read/write addresses we'll be
|
||||||
// needing for this session of reverb.
|
// 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 );
|
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.
|
for( int x=0; x<8; ++x )
|
||||||
// Decisions, Decisions! Should we mix in the 22khz sample skipped, or not?
|
{
|
||||||
// First one mixes in the 22hkz sample. Second one does not.
|
INPUT_SAMPLE.Left += (downbuf[(dbpos+x)&7].Left * downcoeffs[x]);
|
||||||
|
INPUT_SAMPLE.Right += (downbuf[(dbpos+x)&7].Right * downcoeffs[x]);
|
||||||
|
}
|
||||||
|
|
||||||
/*StereoOut32 INPUT_SAMPLE(
|
INPUT_SAMPLE.Left >>= 16;
|
||||||
(s32)(lowpass_left.sample( (Input.Left+LastEffect.Left) / 32768.0 ) * 32768.0),
|
INPUT_SAMPLE.Right >>= 16;
|
||||||
(s32)(lowpass_right.sample( (Input.Right+LastEffect.Right) / 32768.0 ) * 32768.0)
|
|
||||||
);*/
|
|
||||||
|
|
||||||
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_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_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_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_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:
|
// Faster single-mul approach to interpolation:
|
||||||
// (doesn't work yet -- breaks Digital Devil Saga badly)
|
// (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_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_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);
|
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_a0] = clamp_mix( IIR_A0 );
|
||||||
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 );
|
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 );
|
||||||
_spu2mem[dest2_b0] = clamp_mix( IIR_B0 );
|
_spu2mem[dest2_b0] = clamp_mix( IIR_B0 );
|
||||||
_spu2mem[dest2_b1] = clamp_mix( IIR_B1 );*/
|
_spu2mem[dest2_b1] = clamp_mix( IIR_B1 );
|
||||||
|
|
||||||
const s32 ACC0 =
|
const s32 ACC0 =
|
||||||
((_spu2mem[acc_src_a0] * Revb.ACC_COEF_A)) +
|
((_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_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 );
|
_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];
|
upbuf[ubpos] = clamp_mix( StereoOut32(
|
||||||
LastEffect.Right = _spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1];
|
_spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0], // left
|
||||||
|
_spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1] // right
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue