2009-02-15 05:15:39 +00:00
|
|
|
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
|
|
|
* Developed and maintained by the Pcsx2 Development Team.
|
|
|
|
*
|
|
|
|
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
|
|
|
*
|
2009-06-21 11:00:53 +00:00
|
|
|
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
|
|
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
|
2009-02-15 05:15:39 +00:00
|
|
|
*/
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
#include "Global.h"
|
|
|
|
#include "Lowpass.h"
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
// Low pass filters: Change these to 32 for a speedup (benchmarks needed to see if
|
|
|
|
// the speed gain is worth the quality drop)
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
static LowPassFilter64 lowpass_left( 11000, SampleRate );
|
|
|
|
static LowPassFilter64 lowpass_right( 11000, SampleRate );
|
|
|
|
|
|
|
|
__forceinline s32 V_Core::RevbGetIndexer( s32 offset )
|
2009-02-15 05:15:39 +00:00
|
|
|
{
|
2009-09-29 19:18:50 +00:00
|
|
|
u32 pos = ReverbX + offset;
|
2009-02-15 05:15:39 +00:00
|
|
|
|
|
|
|
// Need to use modulus here, because games can and will drop the buffer size
|
|
|
|
// without notice, and it leads to offsets several times past the end of the buffer.
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
if( pos > EffectsEndA )
|
2009-02-15 05:15:39 +00:00
|
|
|
{
|
2009-09-29 19:18:50 +00:00
|
|
|
//pos = EffectsStartA + ((ReverbX + offset) % (u32)EffectsBufferSize);
|
|
|
|
pos -= EffectsEndA+1;
|
|
|
|
pos += EffectsStartA;
|
2009-02-15 05:15:39 +00:00
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
void V_Core::Reverb_AdvanceBuffer()
|
2009-02-15 05:15:39 +00:00
|
|
|
{
|
2009-09-29 19:18:50 +00:00
|
|
|
if( (Cycles & 1) && (EffectsBufferSize > 0) )
|
2009-02-15 05:15:39 +00:00
|
|
|
{
|
2009-09-29 19:18:50 +00:00
|
|
|
//ReverbX = RevbGetIndexer( thiscore, 1 );
|
|
|
|
ReverbX += 1;
|
2009-02-18 14:20:06 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
if( ReverbX >= (u32)EffectsBufferSize ) ReverbX = 0;
|
2009-02-18 14:20:06 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
//ReverbX += 1;
|
|
|
|
//if(ReverbX >= (u32)EffectsBufferSize )
|
|
|
|
// ReverbX %= (u32)EffectsBufferSize;
|
2009-02-15 05:15:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
StereoOut32 V_Core::DoReverb( const StereoOut32& Input )
|
2009-02-15 05:15:39 +00:00
|
|
|
{
|
|
|
|
// Reverb processing occurs at 24khz, so we skip processing every other sample,
|
|
|
|
// and use the previous calculation for this core instead.
|
|
|
|
|
2009-02-18 13:36:20 +00:00
|
|
|
if( (Cycles&1)==0 )
|
2009-02-15 05:15:39 +00:00
|
|
|
{
|
2009-09-29 19:18:50 +00:00
|
|
|
StereoOut32 retval( LastEffect );
|
2009-02-18 16:47:06 +00:00
|
|
|
|
|
|
|
// 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 );
|
|
|
|
lowpass_right.sample( Input.Right / 32768.0 );
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
//LastEffect = Input;
|
2009-02-18 13:36:20 +00:00
|
|
|
return retval;
|
2009-02-15 05:15:39 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-09-29 19:18:50 +00:00
|
|
|
if( RevBuffers.NeedsUpdated )
|
|
|
|
UpdateEffectsBufferSize();
|
2009-02-18 13:36:20 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
if( EffectsBufferSize <= 0 )
|
2009-02-18 14:20:06 +00:00
|
|
|
{
|
|
|
|
// StartA is past EndA, so effects are disabled.
|
|
|
|
//ConLog( " * SPU2: Effects disabled due to leapfrogged EffectsStart." );
|
2009-02-18 16:47:06 +00:00
|
|
|
|
|
|
|
// 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;
|
2009-02-18 14:20:06 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 05:15:39 +00:00
|
|
|
// Advance the current reverb buffer pointer, and cache the read/write addresses we'll be
|
|
|
|
// needing for this session of reverb.
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
const u32 src_a0 = RevbGetIndexer( RevBuffers.IIR_SRC_A0 );
|
|
|
|
const u32 src_a1 = RevbGetIndexer( RevBuffers.IIR_SRC_A1 );
|
|
|
|
const u32 src_b0 = RevbGetIndexer( RevBuffers.IIR_SRC_B0 );
|
|
|
|
const u32 src_b1 = RevbGetIndexer( RevBuffers.IIR_SRC_B1 );
|
|
|
|
|
|
|
|
const u32 dest_a0 = RevbGetIndexer( RevBuffers.IIR_DEST_A0 );
|
|
|
|
const u32 dest_a1 = RevbGetIndexer( RevBuffers.IIR_DEST_A1 );
|
|
|
|
const u32 dest_b0 = RevbGetIndexer( RevBuffers.IIR_DEST_B0 );
|
|
|
|
const u32 dest_b1 = RevbGetIndexer( RevBuffers.IIR_DEST_B1 );
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
const u32 dest2_a0 = RevbGetIndexer( RevBuffers.IIR_DEST_A0 + 1 );
|
|
|
|
const u32 dest2_a1 = RevbGetIndexer( RevBuffers.IIR_DEST_A1 + 1 );
|
|
|
|
const u32 dest2_b0 = RevbGetIndexer( RevBuffers.IIR_DEST_B0 + 1 );
|
|
|
|
const u32 dest2_b1 = RevbGetIndexer( RevBuffers.IIR_DEST_B1 + 1 );
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
const u32 acc_src_a0 = RevbGetIndexer( RevBuffers.ACC_SRC_A0 );
|
|
|
|
const u32 acc_src_b0 = RevbGetIndexer( RevBuffers.ACC_SRC_B0 );
|
|
|
|
const u32 acc_src_c0 = RevbGetIndexer( RevBuffers.ACC_SRC_C0 );
|
|
|
|
const u32 acc_src_d0 = RevbGetIndexer( RevBuffers.ACC_SRC_D0 );
|
|
|
|
|
|
|
|
const u32 acc_src_a1 = RevbGetIndexer( RevBuffers.ACC_SRC_A1 );
|
|
|
|
const u32 acc_src_b1 = RevbGetIndexer( RevBuffers.ACC_SRC_B1 );
|
|
|
|
const u32 acc_src_c1 = RevbGetIndexer( RevBuffers.ACC_SRC_C1 );
|
|
|
|
const u32 acc_src_d1 = RevbGetIndexer( RevBuffers.ACC_SRC_D1 );
|
|
|
|
|
|
|
|
const u32 fb_src_a0 = RevbGetIndexer( RevBuffers.FB_SRC_A0 );
|
|
|
|
const u32 fb_src_a1 = RevbGetIndexer( RevBuffers.FB_SRC_A1 );
|
|
|
|
const u32 fb_src_b0 = RevbGetIndexer( RevBuffers.FB_SRC_B0 );
|
|
|
|
const u32 fb_src_b1 = RevbGetIndexer( RevBuffers.FB_SRC_B1 );
|
|
|
|
|
|
|
|
const u32 mix_dest_a0 = RevbGetIndexer( RevBuffers.MIX_DEST_A0 );
|
|
|
|
const u32 mix_dest_a1 = RevbGetIndexer( RevBuffers.MIX_DEST_A1 );
|
|
|
|
const u32 mix_dest_b0 = RevbGetIndexer( RevBuffers.MIX_DEST_B0 );
|
|
|
|
const u32 mix_dest_b1 = RevbGetIndexer( RevBuffers.MIX_DEST_B1 );
|
2009-02-15 05:15:39 +00:00
|
|
|
|
|
|
|
// -----------------------------------------
|
|
|
|
// End Buffer Pointers, Begin Reverb!
|
|
|
|
// -----------------------------------------
|
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
//StereoOut32 INPUT_SAMPLE( LastEffect + Input );
|
2009-02-18 16:47:06 +00:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
/*StereoOut32 INPUT_SAMPLE(
|
2009-09-29 19:18:50 +00:00
|
|
|
(s32)(lowpass_left.sample( (Input.Left+LastEffect.Left) / 32768.0 ) * 32768.0),
|
|
|
|
(s32)(lowpass_right.sample( (Input.Right+LastEffect.Right) / 32768.0 ) * 32768.0)
|
2009-02-18 16:47:06 +00:00
|
|
|
);*/
|
|
|
|
|
|
|
|
StereoOut32 INPUT_SAMPLE(
|
|
|
|
(s32)(lowpass_left.sample( Input.Left / 32768.0 ) * 32768.0),
|
|
|
|
(s32)(lowpass_right.sample( Input.Right / 32768.0 ) * 32768.0)
|
|
|
|
);
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
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));
|
2009-02-18 14:20:06 +00:00
|
|
|
_spu2mem[dest2_a0] = clamp_mix( IIR_A0 >> 16 );
|
|
|
|
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 >> 16 );
|
|
|
|
_spu2mem[dest2_b0] = clamp_mix( IIR_B0 >> 16 );
|
2009-02-18 16:47:06 +00:00
|
|
|
_spu2mem[dest2_b1] = clamp_mix( IIR_B1 >> 16 );
|
2009-02-18 13:36:20 +00:00
|
|
|
|
|
|
|
// Faster single-mul approach to interpolation:
|
2009-02-18 16:47:06 +00:00
|
|
|
// (doesn't work yet -- breaks Digital Devil Saga badly)
|
2009-09-29 19:18:50 +00:00
|
|
|
/*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);
|
2009-02-18 13:36:20 +00:00
|
|
|
|
|
|
|
_spu2mem[dest2_a0] = clamp_mix( IIR_A0 );
|
|
|
|
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 );
|
|
|
|
_spu2mem[dest2_b0] = clamp_mix( IIR_B0 );
|
2009-02-18 16:47:06 +00:00
|
|
|
_spu2mem[dest2_b1] = clamp_mix( IIR_B1 );*/
|
2009-02-15 05:15:39 +00:00
|
|
|
|
|
|
|
const s32 ACC0 =
|
2009-09-29 19:18:50 +00:00
|
|
|
((_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));
|
2009-02-15 05:15:39 +00:00
|
|
|
|
|
|
|
const s32 ACC1 =
|
2009-09-29 19:18:50 +00:00
|
|
|
((_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));
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
const s32 FB_A0 = (_spu2mem[fb_src_a0] * Revb.FB_ALPHA);
|
|
|
|
const s32 FB_A1 = (_spu2mem[fb_src_a1] * Revb.FB_ALPHA);
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
const s32 fb_xor_a0 = _spu2mem[fb_src_a0] * ( Revb.FB_ALPHA ^ 0x8000 );
|
|
|
|
const s32 fb_xor_a1 = _spu2mem[fb_src_a1] * ( Revb.FB_ALPHA ^ 0x8000 );
|
2009-02-15 05:15:39 +00:00
|
|
|
|
|
|
|
_spu2mem[mix_dest_a0] = clamp_mix( (ACC0 - FB_A0) >> 16 );
|
|
|
|
_spu2mem[mix_dest_a1] = clamp_mix( (ACC1 - FB_A1) >> 16 );
|
2009-09-29 19:18:50 +00:00
|
|
|
_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 );
|
2009-02-15 05:15:39 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
LastEffect.Left = _spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0];
|
|
|
|
LastEffect.Right = _spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1];
|
2009-02-18 16:47:06 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
clamp_mix( LastEffect );
|
2009-02-16 20:00:31 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
//LastEffect.Left = (s32)(lowpass_left.sample( LastEffect.Left / 32768.0 ) * 32768.0);
|
|
|
|
//LastEffect.Right = (s32)(lowpass_right.sample( LastEffect.Right / 32768.0 ) * 32768.0);
|
2009-02-18 13:36:20 +00:00
|
|
|
|
2009-09-29 19:18:50 +00:00
|
|
|
return LastEffect;
|
2009-02-15 05:15:39 +00:00
|
|
|
}
|
|
|
|
}
|