mirror of https://github.com/PCSX2/pcsx2.git
SPU2-X: Improved Real-Reverb engine algorithm.
Before trying this revision, make sure your speakers/headphones are not too loud. I have been looking at it for a long while, and I decided it couldn't be just "somewhat similar" to a Schroeder Reverberator. The SPU2 designers would have actually used a working design for a reverberator, so chances are they implemented Schroeder's. I haven't had the chance to test this code in spu2-x, but the results in the "Impulse Response Analyzer" tool I coded specifically for this, are positive. Some tweaks might be needed, because I'm not sure if Neill got the IIR part right or not. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4678 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
9af8099569
commit
948de6d452
|
@ -1086,8 +1086,9 @@ Air notes:
|
|||
buffer[MIX_DEST_B1] = (ACC1 * FB_ALPHA) + (FB_A1 * (1.0-FB_ALPHA)) - FB_B1 * FB_X;
|
||||
|
||||
Which reduces to:
|
||||
buffer[MIX_DEST_B0] = ACC0 + ((FB_A0-ACC0) * FB_ALPHA) - FB_B0 * FB_X;
|
||||
buffer[MIX_DEST_B1] = ACC1 + ((FB_A1-ACC1) * FB_ALPHA) - FB_B1 * FB_X;
|
||||
buffer[MIX_DEST_B0] = FB_A0 + ((ACC0-FB_A0) * FB_ALPHA) - FB_B0 * FB_X;
|
||||
buffer[MIX_DEST_B1] = FB_A1 + ((ACC1-FB_A1) * FB_ALPHA) - FB_B1 * FB_X;
|
||||
|
||||
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
|
|
@ -176,6 +176,13 @@ StereoOut32 V_Core::DoReverb( const StereoOut32& Input )
|
|||
INPUT_SAMPLE.Right += (downbuf[(dbpos+x)&7].Right * downcoeffs[x]);
|
||||
}
|
||||
|
||||
// This is gotta be a Schroeder Reverberator:
|
||||
// http://cnx.org/content/m15491/latest/
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Part 1: Input filter block
|
||||
// Purpose: Filter and write data to the sample queues for the echos below
|
||||
|
||||
INPUT_SAMPLE.Left >>= 16;
|
||||
INPUT_SAMPLE.Right >>= 16;
|
||||
|
||||
|
@ -187,59 +194,81 @@ StereoOut32 V_Core::DoReverb( const StereoOut32& Input )
|
|||
const s32 IIR_INPUT_B0 = ((_spu2mem[src_b0] * Revb.IIR_COEF) + input_R)>>16;
|
||||
const s32 IIR_INPUT_B1 = ((_spu2mem[src_b1] * Revb.IIR_COEF) + input_R)>>16;
|
||||
|
||||
// This section differs from Neill's doc as it uses single-mul interpolation instead
|
||||
// of 0x8000-val inversion. (same result, faster)
|
||||
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);
|
||||
_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 );
|
||||
const s32 IIR_A0 = _spu2mem[dest_a0];
|
||||
const s32 IIR_A1 = _spu2mem[dest_a1];
|
||||
const s32 IIR_B0 = _spu2mem[dest_b0];
|
||||
const s32 IIR_B1 = _spu2mem[dest_b1];
|
||||
|
||||
const s32 ACC0 = (
|
||||
_spu2mem[dest2_a0] = clamp_mix( IIR_A0 + (((IIR_INPUT_A0-IIR_A0) * Revb.IIR_ALPHA) >> 16) );
|
||||
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 + (((IIR_INPUT_A1-IIR_A1) * Revb.IIR_ALPHA) >> 16) );
|
||||
_spu2mem[dest2_b0] = clamp_mix( IIR_B0 + (((IIR_INPUT_B0-IIR_B0) * Revb.IIR_ALPHA) >> 16) );
|
||||
_spu2mem[dest2_b1] = clamp_mix( IIR_B1 + (((IIR_INPUT_B1-IIR_B1) * Revb.IIR_ALPHA) >> 16) );
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Part 2: Comb filters (echos)
|
||||
// Purpose: Create the primary reflections on the virtual walls
|
||||
|
||||
s32 ACC0 = (
|
||||
((_spu2mem[acc_src_a0] * Revb.ACC_COEF_A)) +
|
||||
((_spu2mem[acc_src_b0] * Revb.ACC_COEF_B)) +
|
||||
((_spu2mem[acc_src_c0] * Revb.ACC_COEF_C)) +
|
||||
((_spu2mem[acc_src_d0] * Revb.ACC_COEF_D))
|
||||
); // >> 16;
|
||||
|
||||
const s32 ACC1 = (
|
||||
s32 ACC1 = (
|
||||
((_spu2mem[acc_src_a1] * Revb.ACC_COEF_A)) +
|
||||
((_spu2mem[acc_src_b1] * Revb.ACC_COEF_B)) +
|
||||
((_spu2mem[acc_src_c1] * Revb.ACC_COEF_C)) +
|
||||
((_spu2mem[acc_src_d1] * Revb.ACC_COEF_D))
|
||||
); // >> 16;
|
||||
|
||||
// The following code differs from Neill's doc as it uses the more natural single-mul
|
||||
// interpolative, instead of the funky ^0x8000 stuff. (better result, faster)
|
||||
|
||||
// A hack! Why? Because gigaherz decided the other version didn't make sense. --air
|
||||
// Set to 1 to enable gigaherz hack mode, set to 0 to use Neill's version.
|
||||
#define A_HACK 0
|
||||
#if !A_HACK
|
||||
const s32 FB_A0 = _spu2mem[fb_src_a0] * Revb.FB_ALPHA;
|
||||
const s32 FB_A1 = _spu2mem[fb_src_a1] * Revb.FB_ALPHA;
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Part 3: All-pass filters
|
||||
// Purpose: Create actual reverberations
|
||||
|
||||
_spu2mem[mix_dest_a0] = clamp_mix( (ACC0 - FB_A0) >> 16 );
|
||||
_spu2mem[mix_dest_a1] = clamp_mix( (ACC1 - FB_A1) >> 16 );
|
||||
#endif
|
||||
// First
|
||||
|
||||
const s32 acc_fb_mix_a = ACC0 + ( (_spu2mem[fb_src_a0] - (ACC0>>16)) * Revb.FB_ALPHA );
|
||||
const s32 acc_fb_mix_b = ACC1 + ( (_spu2mem[fb_src_a1] - (ACC1>>16)) * Revb.FB_ALPHA );
|
||||
// Take delayed input
|
||||
s32 FB_A0 = _spu2mem[fb_src_a0]; // 16
|
||||
s32 FB_A1 = _spu2mem[fb_src_a1];
|
||||
|
||||
#if A_HACK
|
||||
_spu2mem[mix_dest_a0] = clamp_mix( acc_fb_mix_a >> 16 );
|
||||
_spu2mem[mix_dest_a1] = clamp_mix( acc_fb_mix_b >> 16 );
|
||||
#endif
|
||||
// Apply gain and add to input
|
||||
s32 MIX_A0 = (ACC0 + FB_A0 * Revb.FB_ALPHA)>>16; // 32 + 16*16 = 32
|
||||
s32 MIX_A1 = (ACC1 + FB_A1 * Revb.FB_ALPHA)>>16;
|
||||
|
||||
_spu2mem[mix_dest_b0] = clamp_mix( ( acc_fb_mix_a - (_spu2mem[fb_src_b0] * Revb.FB_X) ) >> 16 );
|
||||
_spu2mem[mix_dest_b1] = clamp_mix( ( acc_fb_mix_b - (_spu2mem[fb_src_b1] * Revb.FB_X) ) >> 16 );
|
||||
// Write to queue
|
||||
_spu2mem[mix_dest_a0] = clamp_mix(MIX_A0);
|
||||
_spu2mem[mix_dest_a1] = clamp_mix(MIX_A1);
|
||||
|
||||
// Apply second gain and add
|
||||
ACC0 = (FB_A0 << 16) - MIX_A0 * Revb.FB_ALPHA;
|
||||
ACC1 = (FB_A1 << 16) - MIX_A1 * Revb.FB_ALPHA;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
// Second
|
||||
|
||||
// Take delayed input
|
||||
s32 FB_B0 = _spu2mem[fb_src_b0]; // 16
|
||||
s32 FB_B1 = _spu2mem[fb_src_b1];
|
||||
|
||||
// Apply gain and add to input
|
||||
s32 MIX_B0 = (ACC0 + FB_B0 * Revb.FB_X)>>16; // 32 + 16*16 = 32
|
||||
s32 MIX_B1 = (ACC1 + FB_B1 * Revb.FB_X)>>16;
|
||||
|
||||
// Write to queue
|
||||
_spu2mem[mix_dest_b0] = clamp_mix(MIX_B0);
|
||||
_spu2mem[mix_dest_b1] = clamp_mix(MIX_B1);
|
||||
|
||||
// Apply second gain and add
|
||||
ACC0 = (FB_B0 << 16) - MIX_B0 * Revb.FB_X;
|
||||
ACC1 = (FB_B1 << 16) - MIX_B1 * Revb.FB_X;
|
||||
|
||||
upbuf[ubpos] = clamp_mix( StereoOut32(
|
||||
(_spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0]), // left
|
||||
(_spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1]) // right
|
||||
ACC0, // left
|
||||
ACC1 // right
|
||||
) );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue