SPU2-X: Added a lowpass filter to the reverb output, which should produce more correct audio (still experimental). Plus many code cleanups.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@509 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-02-16 20:00:31 +00:00
parent d5b0ff7fa3
commit 340db13caa
17 changed files with 342 additions and 539 deletions

View File

@ -51,7 +51,37 @@ static __forceinline T GetClamped( T src, T min, T max )
return std::min( std::max( src, min ), max );
}
extern void SysMessage(const char *fmt, ...);
//////////////////////////////////////////////////////////////
// Dev / Debug conditionals --
// Consts for using if() statements instead of uglier #ifdef macros.
// Abbreviated macros for dev/debug only consoles and msgboxes.
#ifdef SPU2X_DEVBUILD
# define DevCon Console
# define DevMsg MsgBox
static const bool IsDevBuild = true;
#else
# define DevCon 0&&Console
# define DevMsg
static const bool IsDevBuild = false;
#endif
#ifdef _DEBUG
# define DbgCon Console
static const bool IsDebugBuild = true;
#else
# define DbgCon 0&&Console
static const bool IsDebugBuild = false;
#endif
#endif

View File

@ -187,14 +187,14 @@ void DoFullDump()
" - Value: %x\n",
Cores[c].Voices[v].ADSR.Reg_ADSR1,
Cores[c].Voices[v].ADSR.Reg_ADSR2,
Cores[c].Voices[v].ADSR.Ar,
Cores[c].Voices[v].ADSR.Am,
Cores[c].Voices[v].ADSR.Dr,
Cores[c].Voices[v].ADSR.Sl,
Cores[c].Voices[v].ADSR.Sr,
Cores[c].Voices[v].ADSR.Sm,
Cores[c].Voices[v].ADSR.Rr,
Cores[c].Voices[v].ADSR.Rm,
Cores[c].Voices[v].ADSR.AttackRate,
Cores[c].Voices[v].ADSR.AttackMode,
Cores[c].Voices[v].ADSR.DecayRate,
Cores[c].Voices[v].ADSR.SustainLevel,
Cores[c].Voices[v].ADSR.SustainRate,
Cores[c].Voices[v].ADSR.SustainMode,
Cores[c].Voices[v].ADSR.ReleaseRate,
Cores[c].Voices[v].ADSR.ReleaseMode,
Cores[c].Voices[v].ADSR.Phase,
Cores[c].Voices[v].ADSR.Value);
fprintf(dump," - Pitch: %x\n",Cores[c].Voices[v].Pitch);

View File

@ -45,14 +45,16 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD dwReason,LPVOID lpvReserved)
static void InitLibraryName()
{
#ifdef PUBLIC
if( !IsDevBuild )
{
// Public Release!
// Output a simplified string that's just our name:
strcpy( libraryName, "SPU2-X" );
#elif defined( SVN_REV_UNKNOWN )
}
else
{
#ifdef SVN_REV_UNKNOWN
// Unknown revision.
// Output a name that includes devbuild status but not
@ -67,6 +69,7 @@ static void InitLibraryName()
"-Dev"
#endif
);
#else
// Use TortoiseSVN's SubWCRev utility's output
@ -84,7 +87,7 @@ static void InitLibraryName()
SVN_MODS ? "m" : ""
);
#endif
}
}
EXPORT_C_(u32) PS2EgetLibType()
@ -309,7 +312,8 @@ bool numpad_plus = false, numpad_plus_old = false;
EXPORT_C_(void) SPU2async(u32 cycles)
{
#ifndef PUBLIC
if( IsDevBuild )
{
u32 oldClocks = lClocks;
static u32 timer=0,time1=0,time2=0;
timer++;
@ -320,18 +324,19 @@ EXPORT_C_(void) SPU2async(u32 cycles)
time2 = timeGetTime()-time1 ;
timer=0;
}
#endif
}
DspUpdate();
#ifndef PUBLIC
if( IsDevBuild )
{
/*numpad_plus = (GetAsyncKeyState(VK_ADD)&0x8000)!=0;
if(numpad_plus && !numpad_plus_old)
{
DoFullDump();
}
numpad_plus_old = numpad_plus;*/
#endif
}
if(hasPtr)
{

View File

@ -438,9 +438,9 @@ void SPU2writeDMA(int core, u16* pMem, u32 size)
return;
}
#ifndef PUBLIC
if( IsDevBuild )
DebugCores[core].lastsize = size;
#endif
Cores[core].TSA&=~7;
bool adma_enable = ((Cores[core].AutoDMACtrl&(core+1))==(core+1));

View File

@ -28,8 +28,7 @@ void ADMAOutLogWrite(void *lpData, u32 ulSize);
u32 core, voice;
static const s32 ADSR_MAX_VOL = 0x7fffffff;
static const s32 f[5][2] =
static const s32 tbl_XA_Factor[5][2] =
{
{ 0, 0 },
{ 60, 0 },
@ -38,13 +37,10 @@ static const s32 f[5][2] =
{ 122, -60 }
};
static const int InvExpOffsets[] = { 0,4,6,8,9,10,11,12 };
static u32 PsxRates[160];
// Performs a 64-bit multiplication between two values and returns the
// high 32 bits as a result (discarding the fractional 32 bits).
// The combined fracional bits of both inputs must be 32 bits for this
// The combined fractional bits of both inputs must be 32 bits for this
// to work properly.
//
// This is meant to be a drop-in replacement for times when the 'div' part
@ -70,29 +66,12 @@ __forceinline s32 clamp_mix(s32 x, u8 bitshift)
return GetClamped( x, -0x8000<<bitshift, 0x7fff<<bitshift );
}
void InitADSR() // INIT ADSR
{
for (int i=0; i<(32+128); i++)
{
int shift=(i-32)>>2;
s64 rate=(i&3)+4;
if (shift<0)
rate >>= -shift;
else
rate <<= shift;
PsxRates[i] = (int)min( rate, 0x3fffffffLL );
}
}
#define VOL(x) (((s32)x)) //24.8 volume
static void __forceinline XA_decode_block(s16* buffer, const s16* block, s32& prev1, s32& prev2)
{
const s32 header = *block;
s32 shift = ((header>> 0)&0xF)+16;
s32 pred1 = f[(header>> 4)&0xF][0];
s32 pred2 = f[(header>> 4)&0xF][1];
s32 pred1 = tbl_XA_Factor[(header>> 4)&0xF][0];
s32 pred2 = tbl_XA_Factor[(header>> 4)&0xF][1];
const s8* blockbytes = (s8*)&block[1];
@ -129,8 +108,8 @@ static void __forceinline XA_decode_block_unsaturated(s16* buffer, const s16* bl
{
const s32 header = *block;
s32 shift = ((header>> 0)&0xF)+16;
s32 pred1 = f[(header>> 4)&0xF][0];
s32 pred2 = f[(header>> 4)&0xF][1];
s32 pred1 = tbl_XA_Factor[(header>> 4)&0xF][0];
s32 pred2 = tbl_XA_Factor[(header>> 4)&0xF][1];
const s8* blockbytes = (s8*)&block[1];
@ -167,9 +146,9 @@ static void __forceinline IncrementNextA( const V_Core& thiscore, V_Voice& vc )
{
if( Cores[i].IRQEnable && (vc.NextA==Cores[i].IRQA ) )
{
#ifndef PUBLIC
if( IsDevBuild )
ConLog(" * SPU2 Core %d: IRQ Called (IRQ passed).\n", i);
#endif
Spdif.Info = 4 << i;
SetIrqCall();
}
@ -184,11 +163,9 @@ static void __forceinline IncrementNextA( const V_Core& thiscore, V_Voice& vc )
// invalided when DMA transfers and memory writes are performed.
PcmCacheEntry *pcm_cache_data = NULL;
#ifndef PUBLIC
int g_counter_cache_hits = 0;
int g_counter_cache_misses = 0;
int g_counter_cache_ignores = 0;
#endif
#define XAFLAG_LOOP_END (1ul<<0)
#define XAFLAG_LOOP (1ul<<1)
@ -213,12 +190,12 @@ static void __forceinline __fastcall GetNextDataBuffered( V_Core& thiscore, V_Vo
}
else
{
VoiceStop(core,voice);
thiscore.Regs.ENDX|=1<<voice;
#ifndef PUBLIC
vc.Stop();
if( IsDevBuild )
{
if(MsgVoiceOff()) ConLog(" * SPU2: Voice Off by EndPoint: %d \n", voice);
DebugCores[core].Voices[voice].lastStopReason = 1;
#endif
}
}
}
@ -242,9 +219,8 @@ static void __forceinline __fastcall GetNextDataBuffered( V_Core& thiscore, V_Vo
//ConLog( " * SPU2 : Cache Hit! NextA=0x%x, cacheIdx=0x%x\n", vc.NextA, cacheIdx );
#ifndef PUBLIC
if( IsDevBuild )
g_counter_cache_hits++;
#endif
}
else
{
@ -252,12 +228,13 @@ static void __forceinline __fastcall GetNextDataBuffered( V_Core& thiscore, V_Vo
if( vc.NextA >= SPU2_DYN_MEMLINE )
cacheLine.Validated = true;
#ifndef PUBLIC
if( IsDevBuild )
{
if( vc.NextA < SPU2_DYN_MEMLINE )
g_counter_cache_ignores++;
else
g_counter_cache_misses++;
#endif
}
s16* sbuffer = cacheLine.Sampledata;
@ -284,166 +261,6 @@ _skipIncrement:
Data = vc.SBuffer[vc.SCurrent++];
}
// Returns the linear slide value for AR and SR inputs.
static int GetLinearSrAr( uint SrAr )
{
// The Sr/Ar settings work in quarter steps, which means
// the bottom 2 bits go on the left side of the shift, and
// the right side of the shift gets divided by 4:
const uint newSr = 0x7f - SrAr;
return ((1|(newSr&3)) << (newSr>>2));
}
static void __forceinline CalculateADSR( V_Voice& vc )
{
V_ADSR& env(vc.ADSR);
jASSUME( env.Phase != 0 );
if(env.Releasing && (env.Phase < 5))
env.Phase = 5;
switch (env.Phase)
{
case 1: // attack
if( env.Value == ADSR_MAX_VOL )
{
// Already maxed out. Progress phase and nothing more:
env.Phase++;
break;
}
// Case 1 below is for pseudo exponential below 75%.
// Pseudo Exp > 75% and Linear are the same.
if (env.Am && (env.Value>=0x60000000))
env.Value += PsxRates[(env.Ar^0x7f)-0x18+32];
else //if( env.Ar < 0x7f )
env.Value+=PsxRates[(env.Ar^0x7f)-0x10+32];
//env.Value += GetLinearSrAr( env.Ar );
if( env.Value < 0 )
{
// We hit the ceiling.
env.Phase++;
env.Value = ADSR_MAX_VOL;
}
break;
case 2: // decay
{
u32 off = InvExpOffsets[(env.Value>>28)&7];
env.Value-=PsxRates[((env.Dr^0x1f)*4)-0x18+off+32];
// calculate sustain level by mirroring the bits
// of the sustain var into the lower bits as we shift up
// (total shift, 27 bits)
//s32 suslev8 = (env.Sl << 4) | env.Sl;
//s32 suslev = (suslev8 << 8) | suslev8; // brings us to 16 bits!
//suslev = (suslev << 8) | suslev8; // 24 bits!
s32 suslev = 0x7fffffff / (0x10 - env.Sl);
if( env.Value <= suslev )
{
if (env.Value < 0)
env.Value = 0;
env.Phase++;
}
}
break;
case 3: // sustain
{
// 0x7f disables sustain (infinite sustain)
if( env.Sr == 0x7f ) return;
if (env.Sm&2) // decreasing
{
if (env.Sm&4) // exponential
{
u32 off = InvExpOffsets[(env.Value>>28)&7];
env.Value-=PsxRates[(env.Sr^0x7f)-0x1b+off+32];
}
else // linear
{
env.Value-=PsxRates[(env.Sr^0x7f)-0xf+32];
//env.Value -= GetLinearSrAr( env.Sr );
}
if( env.Value <= 0 )
{
env.Value = 0;
env.Phase++;
}
}
else // increasing
{
if( (env.Sm&4) && (env.Value>=0x60000000) )
env.Value+=PsxRates[(env.Sr^0x7f)-0x18+32];
else
{
// linear / Pseudo below 75% (they're the same)
env.Value+=PsxRates[(env.Sr^0x7f)-0x10+32];
//env.Value += GetLinearSrAr( env.Sr );
}
if( env.Value < 0 )
{
env.Value = ADSR_MAX_VOL;
env.Phase++;
}
}
}
break;
case 4: // sustain end
env.Value = (env.Sm&2) ? 0 : ADSR_MAX_VOL;
if(env.Value==0)
env.Phase=6;
break;
case 5: // release
if (env.Rm) // exponential
{
u32 off=InvExpOffsets[(env.Value>>28)&7];
env.Value-=PsxRates[((env.Rr^0x1f)*4)-0x18+off+32];
}
else // linear
{
//env.Value-=PsxRates[((env.Rr^0x1f)*4)-0xc+32];
if( env.Rr != 0x1f )
env.Value -= (1 << (0x1f-env.Rr));
}
if( env.Value <= 0 )
{
env.Value=0;
env.Phase++;
}
break;
case 6: // release end
env.Value=0;
break;
jNO_DEFAULT
}
if (env.Phase==6) {
#ifndef PUBLIC
if(MsgVoiceOff()) ConLog(" * SPU2: Voice Off by ADSR: %d \n", voice);
DebugCores[core].Voices[voice].lastStopReason = 2;
#endif
VoiceStop(core,voice);
Cores[core].Regs.ENDX|=(1<<voice);
env.Phase=0;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
@ -497,6 +314,28 @@ static void __forceinline UpdatePitch( V_Voice& vc )
vc.SP+=pitch;
}
static __forceinline void CalculateADSR( V_Core& thiscore, V_Voice& vc )
{
if( vc.ADSR.Phase==0 )
{
vc.ADSR.Value = 0;
return;
}
if( !vc.ADSR.Calculate() )
{
if( IsDevBuild )
{
if(MsgVoiceOff()) ConLog(" * SPU2: Voice Off by ADSR: %d \n", voice);
DebugCores[core].Voices[voice].lastStopReason = 2;
}
vc.Stop();
}
jASSUME( vc.ADSR.Value >= 0 ); // ADSR should never be negative...
}
// Returns a 16 bit result in Value.
static void __forceinline GetVoiceValues_Linear(V_Core& thiscore, V_Voice& vc, s32& Value)
{
@ -509,15 +348,7 @@ static void __forceinline GetVoiceValues_Linear(V_Core& thiscore, V_Voice& vc, s
vc.SP -= 4096;
}
if( vc.ADSR.Phase==0 )
{
Value = 0;
return;
}
CalculateADSR( vc );
jASSUME( vc.ADSR.Value >= 0 ); // ADSR should never be negative...
CalculateADSR( thiscore, vc );
// Note! It's very important that ADSR stay as accurate as possible. By the way
// it is used, various sound effects can end prematurely if we truncate more than
@ -549,15 +380,7 @@ static void __forceinline GetVoiceValues_Cubic(V_Core& thiscore, V_Voice& vc, s3
vc.SP-=4096;
}
if( vc.ADSR.Phase==0 )
{
Value = 0;
return;
}
CalculateADSR( vc );
jASSUME( vc.ADSR.Value >= 0 ); // ADSR should never be negative...
CalculateADSR( thiscore, vc );
s32 z0 = vc.PV3 - vc.PV4 + vc.PV1 - vc.PV2;
s32 z1 = (vc.PV4 - vc.PV3 - z0);
@ -591,7 +414,7 @@ static void __forceinline __fastcall GetNoiseValues(V_Core& thiscore, V_Voice& v
// like GetVoiceValues can. Better assert just in case though..
jASSUME( vc.ADSR.Phase != 0 );
CalculateADSR( vc );
CalculateADSR( thiscore, vc );
// Yup, ADSR applies even to noise sources...
Data = MulShr32( Data, vc.ADSR.Value );
@ -649,12 +472,13 @@ void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR)
{
FileLog("[%10d] AutoDMA%c block end.\n",Cycles, (core==0)?'4':'7');
#ifndef PUBLIC
if( IsDevBuild )
{
if(thiscore.InputDataLeft>0)
{
if(MsgAutoDMA()) ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
}
#endif
}
thiscore.InputDataLeft=0;
thiscore.DMAICounter=1;
}
@ -689,12 +513,13 @@ void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR)
{
FileLog("[%10d] Spdif AutoDMA%c block end.\n",Cycles, (core==0)?'4':'7');
#ifndef PUBLIC
if( IsDevBuild )
{
if(thiscore.InputDataLeft>0)
{
if(MsgAutoDMA()) ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
}
#endif
}
thiscore.InputDataLeft=0;
thiscore.DMAICounter=1;
}
@ -741,13 +566,15 @@ void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR)
{
thiscore.AutoDMACtrl |= ~3;
#ifndef PUBLIC
if( IsDevBuild )
{
FileLog("[%10d] AutoDMA%c block end.\n",Cycles, (core==0)?'4':'7');
if(thiscore.InputDataLeft>0)
{
if(MsgAutoDMA()) ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
}
#endif
}
thiscore.InputDataLeft = 0;
thiscore.DMAICounter = 1;
}
@ -795,57 +622,6 @@ static void __forceinline __fastcall ReadInputPV(V_Core& thiscore, s32& ValL,s32
/////////////////////////////////////////////////////////////////////////////////////////
// //
#define VOLFLAG_REVERSE_PHASE (1ul<<0)
#define VOLFLAG_DECREMENT (1ul<<1)
#define VOLFLAG_EXPONENTIAL (1ul<<2)
#define VOLFLAG_SLIDE_ENABLE (1ul<<3)
static void __fastcall UpdateVolume(V_Volume& Vol)
{
// Volume slides use the same basic logic as ADSR, but simplified (single-stage
// instead of multi-stage)
if (Vol.Mode & VOLFLAG_DECREMENT)
{
// Decrement
if(Vol.Mode & VOLFLAG_EXPONENTIAL)
{
u32 off = InvExpOffsets[(Vol.Value>>28)&7];
Vol.Value -= PsxRates[(Vol.Increment^0x7f)-0x1b+off+32];
}
else
Vol.Value -= Vol.Increment;
if (Vol.Value < 0)
{
Vol.Value = 0;
Vol.Mode = 0; // disable slide
}
}
else
{
// Increment
// Pseudo-exponential increments, as done by the SPU2 (really!)
// Above 75% slides slow, below 75% slides fast. It's exponential, pseudo'ly speaking.
if( (Vol.Mode & VOLFLAG_EXPONENTIAL) && (Vol.Value>=0x60000000))
Vol.Value += PsxRates[(Vol.Increment^0x7f)-0x18+32];
else
Vol.Value += Vol.Increment;
if( Vol.Value < 0 ) // wrapped around the "top"?
{
Vol.Value = 0x7fffffff;
Vol.Mode = 0; // disable slide
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
// writes a signed value to the SPU2 ram
// Performs no cache invalidation -- use only for dynamic memory ranges
// of the SPU2 (between 0x0000 and SPU2_DYN_MEMLINE)
@ -869,8 +645,8 @@ static __forceinline void MixVoice( V_Core& thiscore, V_Voice& vc, s32& VValL, s
// Most games don't use much volume slide effects. So only call the UpdateVolume
// methods when needed by checking the flag outside the method here...
if( vc.VolumeL.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume( vc.VolumeL );
if( vc.VolumeR.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume( vc.VolumeR );
vc.VolumeL.Update();
vc.VolumeR.Update();
if( vc.ADSR.Phase > 0 )
{
@ -889,9 +665,8 @@ static __forceinline void MixVoice( V_Core& thiscore, V_Voice& vc, s32& VValL, s
// Record the output (used for modulation effects)
vc.OutX = Value;
#ifndef PUBLIC
if( IsDevBuild )
DebugCores[core].Voices[voice].displayPeak = max(DebugCores[core].Voices[voice].displayPeak,abs(Value));
#endif
// TODO : Implement this using high-def MulShr32.
// vc.VolumeL/R are 15 bits. Value should be 32 bits (but is currently 16)
@ -1017,8 +792,8 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR)
// Apply Master Volume. The core will need this when the function returns.
if( thiscore.MasterL.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume(thiscore.MasterL);
if( thiscore.MasterR.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume(thiscore.MasterR);
thiscore.MasterL.Update();
thiscore.MasterR.Update();
}
// used to throttle the output rate of cache stat reports
@ -1107,7 +882,8 @@ void Mix()
OutPos++;
if (OutPos>=0x200) OutPos=0;
#ifndef PUBLIC
if( IsDevBuild )
{
p_cachestat_counter++;
if(p_cachestat_counter > (48000*10) )
{
@ -1121,7 +897,7 @@ void Mix()
g_counter_cache_misses =
g_counter_cache_ignores = 0;
}
#endif
}
}
/////////////////////////////////////////////////////////////////////////////////////////

View File

@ -30,7 +30,8 @@ __forceinline void RegLog(int level, char *RName,u32 mem,u32 core,u16 value)
void SPU2writeLog(u32 rmem, u16 value)
{
#ifndef PUBLIC
if( !IsDevBuild ) return;
u32 vx=0, vc=0, core=0, omem, mem;
omem=mem=rmem & 0x7FF; //FFFF;
if (mem & 0x400) { omem^=0x400; core=1; }
@ -247,6 +248,5 @@ void SPU2writeLog(u32 rmem, u16 value)
RegLog(2,"UNKNOWN",rmem,core,value); spu2Ru16(mem) = value;
}
}
#endif
}

View File

@ -21,8 +21,8 @@
#include "spu2.h"
//static LPF_data lowpass_left( 11000, SampleRate );
//static LPF_data lowpass_right( 11000, SampleRate );
static LPF_data lowpass_left( 11000, SampleRate );
static LPF_data lowpass_right( 11000, SampleRate );
static s32 EffectsBufferIndexer( V_Core& thiscore, s32 offset )
{
@ -34,14 +34,10 @@ static s32 EffectsBufferIndexer( V_Core& thiscore, s32 offset )
if( pos > thiscore.EffectsEndA )
{
pos = thiscore.EffectsStartA + ((thiscore.ReverbX + offset) % (u32)thiscore.EffectsBufferSize);
//pos -= thiscore.EffectsEndA+1;
//pos += thiscore.EffectsStartA;
}
else if( pos < thiscore.EffectsStartA )
{
pos = thiscore.EffectsEndA+1 - ((thiscore.ReverbX + offset) % (u32)thiscore.EffectsBufferSize );
//pos -= thiscore.EffectsStartA;
//pos += thiscore.EffectsEndA+1;
}
return pos;
}
@ -176,7 +172,12 @@ void DoReverb( V_Core& thiscore, s32& OutL, s32& OutR, s32 InL, s32 InR)
_spu2mem[mix_dest_b0] = clamp_mix( (MulShr32(thiscore.Revb.FB_ALPHA<<14, ACC0) - fb_xor_a0 - ((_spu2mem[fb_src_b0] * thiscore.Revb.FB_X)>>2)) >> 14 );
_spu2mem[mix_dest_b1] = clamp_mix( (MulShr32(thiscore.Revb.FB_ALPHA<<14, ACC1) - fb_xor_a1 - ((_spu2mem[fb_src_b1] * thiscore.Revb.FB_X)>>2)) >> 14 );
OutL = thiscore.LastEffectL = clamp_mix(_spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0]);
OutR = thiscore.LastEffectR = clamp_mix(_spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1]);
thiscore.LastEffectL = clamp_mix(_spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0]);
thiscore.LastEffectR = clamp_mix(_spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1]);
//OutL = thiscore.LastEffectL;
//OutR = thiscore.LastEffectR;
OutL = (s32)(lowpass_left.sample( thiscore.LastEffectL / 32768.0 ) * 32768.0);
OutR = (s32)(lowpass_right.sample( thiscore.LastEffectR / 32768.0 ) * 32768.0);
}
}

View File

@ -82,7 +82,6 @@ s32 __fastcall FreezeIt( SPU2freezeData& spud )
spud.OutPos = OutPos;
spud.InputPos = InputPos;
spud.Cycles = Cycles;
spud.uTicks = uTicks;
spud.PlayMode = PlayMode;
// Save our cache:
@ -156,7 +155,6 @@ s32 __fastcall ThawIt( SPU2freezeData& spud )
OutPos = spud.OutPos;
InputPos = spud.InputPos;
Cycles = spud.Cycles;
uTicks = spud.uTicks;
PlayMode = spud.PlayMode;
// Load the ADPCM cache:

View File

@ -51,9 +51,7 @@ u8 callirq;
HANDLE hThreadFunc;
u32 ThreadFuncID;
#ifndef PUBLIC
V_CoreDebug DebugCores[2];
#endif
V_Core Cores[2];
V_SPDIF Spdif;
@ -206,6 +204,44 @@ void V_Core::UpdateEffectsBufferSize()
EffectsBufferSize = EffectsEndA - EffectsStartA + 1;
}
void V_Voice::Start()
{
if((Cycles-PlayCycle)>=4)
{
if(StartA&7)
{
fprintf( stderr, " *** Misaligned StartA %05x!\n",StartA);
StartA=(StartA+0xFFFF8)+0x8;
}
ADSR.Releasing=false;
ADSR.Value=1;
ADSR.Phase=1;
PlayCycle=Cycles;
SCurrent=28;
LoopMode=0;
LoopFlags=0;
LoopStartA=StartA;
NextA=StartA;
Prev1=0;
Prev2=0;
PV1=PV2=0;
PV3=PV4=0;
}
else
{
printf(" *** KeyOn after less than 4 T disregarded.\n");
}
}
void V_Voice::Stop()
{
ADSR.Value = 0;
ADSR.Phase = 0;
//Cores[core].Regs.ENDX|=(1<<vc);
}
static const int TickInterval = 768;
static const int SanityInterval = 4800;
@ -368,16 +404,16 @@ void SPU_ps1_write(u32 mem, u16 value)
case 2: Cores[0].Voices[voice].Pitch=value; break;
case 3: Cores[0].Voices[voice].StartA=(u32)value<<8; break;
case 4: // ADSR1 (Envelope)
Cores[0].Voices[voice].ADSR.Am=(value & 0x8000)>>15;
Cores[0].Voices[voice].ADSR.Ar=(value & 0x7F00)>>8;
Cores[0].Voices[voice].ADSR.Dr=(value & 0xF0)>>4;
Cores[0].Voices[voice].ADSR.Sl=(value & 0xF);
Cores[0].Voices[voice].ADSR.AttackMode=(value & 0x8000)>>15;
Cores[0].Voices[voice].ADSR.AttackRate=(value & 0x7F00)>>8;
Cores[0].Voices[voice].ADSR.DecayRate=(value & 0xF0)>>4;
Cores[0].Voices[voice].ADSR.SustainLevel=(value & 0xF);
Cores[0].Voices[voice].ADSR.Reg_ADSR1 = value; break;
case 5: // ADSR2 (Envelope)
Cores[0].Voices[voice].ADSR.Sm=(value & 0xE000)>>13;
Cores[0].Voices[voice].ADSR.Sr=(value & 0x1FC0)>>6;
Cores[0].Voices[voice].ADSR.Rm=(value & 0x20)>>5;
Cores[0].Voices[voice].ADSR.Rr=(value & 0x1F);
Cores[0].Voices[voice].ADSR.SustainMode=(value & 0xE000)>>13;
Cores[0].Voices[voice].ADSR.SustainRate=(value & 0x1FC0)>>6;
Cores[0].Voices[voice].ADSR.ReleaseMode=(value & 0x20)>>5;
Cores[0].Voices[voice].ADSR.ReleaseRate=(value & 0x1F);
Cores[0].Voices[voice].ADSR.Reg_ADSR2 = value; break;
case 6:
Cores[0].Voices[voice].ADSR.Value = ((s32)value<<16) | value;
@ -614,16 +650,16 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
case 2: thisvoice.Pitch=value; break;
case 3: // ADSR1 (Envelope)
thisvoice.ADSR.Am = (value & 0x8000)>>15;
thisvoice.ADSR.Ar = (value & 0x7F00)>>8;
thisvoice.ADSR.Dr = (value & 0xF0)>>4;
thisvoice.ADSR.Sl = (value & 0xF);
thisvoice.ADSR.AttackMode = (value & 0x8000)>>15;
thisvoice.ADSR.AttackRate = (value & 0x7F00)>>8;
thisvoice.ADSR.DecayRate = (value & 0xF0)>>4;
thisvoice.ADSR.SustainLevel = (value & 0xF);
thisvoice.ADSR.Reg_ADSR1 = value; break;
case 4: // ADSR2 (Envelope)
thisvoice.ADSR.Sm = (value & 0xE000)>>13;
thisvoice.ADSR.Sr = (value & 0x1FC0)>>6;
thisvoice.ADSR.Rm = (value & 0x20)>>5;
thisvoice.ADSR.Rr = (value & 0x1F);
thisvoice.ADSR.SustainMode = (value & 0xE000)>>13;
thisvoice.ADSR.SustainRate = (value & 0x1FC0)>>6;
thisvoice.ADSR.ReleaseMode = (value & 0x20)>>5;
thisvoice.ADSR.ReleaseRate = (value & 0x1F);
thisvoice.ADSR.Reg_ADSR2 = value; break;
case 5:
// [Air] : Mysterious ADSR set code. Too bad none of my games ever use it.
@ -646,23 +682,34 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
switch (address)
{
case 0: thisvoice.StartA = ((value & 0x0F) << 16) | (thisvoice.StartA & 0xFFF8);
#ifndef PUBLIC
case 0:
thisvoice.StartA = ((value & 0x0F) << 16) | (thisvoice.StartA & 0xFFF8);
if( IsDevBuild )
DebugCores[core].Voices[voice].lastSetStartA = thisvoice.StartA;
#endif
break;
case 1: thisvoice.StartA = (thisvoice.StartA & 0x0F0000) | (value & 0xFFF8);
#ifndef PUBLIC
case 1:
thisvoice.StartA = (thisvoice.StartA & 0x0F0000) | (value & 0xFFF8);
if( IsDevBuild )
DebugCores[core].Voices[voice].lastSetStartA = thisvoice.StartA;
#endif
break;
case 2: thisvoice.LoopStartA = ((value & 0x0F) << 16) | (thisvoice.LoopStartA & 0xFFF8);
thisvoice.LoopMode = 3; break;
case 3: thisvoice.LoopStartA = (thisvoice.LoopStartA & 0x0F0000) | (value & 0xFFF8);break;
thisvoice.LoopMode = 3; break;
case 4: thisvoice.NextA = ((value & 0x0F) << 16) | (thisvoice.NextA & 0xFFF8);
case 2:
thisvoice.LoopStartA = ((value & 0x0F) << 16) | (thisvoice.LoopStartA & 0xFFF8);
thisvoice.LoopMode = 3;
break;
case 5: thisvoice.NextA = (thisvoice.NextA & 0x0F0000) | (value & 0xFFF8);
case 3:
thisvoice.LoopStartA = (thisvoice.LoopStartA & 0x0F0000) | (value & 0xFFF8);
thisvoice.LoopMode = 3;
break;
case 4:
thisvoice.NextA = ((value & 0x0F) << 16) | (thisvoice.NextA & 0xFFF8);
break;
case 5:
thisvoice.NextA = (thisvoice.NextA & 0x0F0000) | (value & 0xFFF8);
break;
}
}
@ -930,60 +977,6 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
}
void VoiceStart(int core,int vc)
{
if((Cycles-Cores[core].Voices[vc].PlayCycle)>=4)
{
if(Cores[core].Voices[vc].StartA&7)
{
fprintf( stderr, " *** Misaligned StartA %05x!\n",Cores[core].Voices[vc].StartA);
Cores[core].Voices[vc].StartA=(Cores[core].Voices[vc].StartA+0xFFFF8)+0x8;
}
Cores[core].Voices[vc].ADSR.Releasing=false;
Cores[core].Voices[vc].ADSR.Value=1;
Cores[core].Voices[vc].ADSR.Phase=1;
Cores[core].Voices[vc].PlayCycle=Cycles;
Cores[core].Voices[vc].SCurrent=28;
Cores[core].Voices[vc].LoopMode=0;
Cores[core].Voices[vc].LoopFlags=0;
Cores[core].Voices[vc].LoopStartA=Cores[core].Voices[vc].StartA;
Cores[core].Voices[vc].NextA=Cores[core].Voices[vc].StartA;
Cores[core].Voices[vc].Prev1=0;
Cores[core].Voices[vc].Prev2=0;
Cores[core].Voices[vc].PV1=Cores[core].Voices[vc].PV2=0;
Cores[core].Voices[vc].PV3=Cores[core].Voices[vc].PV4=0;
Cores[core].Regs.ENDX&=~(1<<vc);
#ifndef PUBLIC
DebugCores[core].Voices[vc].FirstBlock=1;
if(MsgKeyOnOff()) ConLog(" * SPU2: KeyOn: C%dV%02d: SSA: %8x; M: %s%s%s%s; H: %02x%02x; P: %04x V: %04x/%04x; ADSR: %04x%04x\n",
core,vc,Cores[core].Voices[vc].StartA,
(Cores[core].Voices[vc].DryL)?"+":"-",(Cores[core].Voices[vc].DryR)?"+":"-",
(Cores[core].Voices[vc].WetL)?"+":"-",(Cores[core].Voices[vc].WetR)?"+":"-",
*(u8*)GetMemPtr(Cores[core].Voices[vc].StartA),*(u8 *)GetMemPtr((Cores[core].Voices[vc].StartA)+1),
Cores[core].Voices[vc].Pitch,
Cores[core].Voices[vc].VolumeL.Value,Cores[core].Voices[vc].VolumeR.Value,
Cores[core].Voices[vc].ADSR.Reg_ADSR1,Cores[core].Voices[vc].ADSR.Reg_ADSR2);
#endif
}
else
{
printf(" *** KeyOn after less than 4 T disregarded.\n");
}
}
void VoiceStop(int core,int vc)
{
Cores[core].Voices[vc].ADSR.Value=0;
Cores[core].Voices[vc].ADSR.Phase=0;
//Cores[core].Regs.ENDX|=(1<<vc);
}
void StartVoices(int core, u32 value)
{
// Optimization: Games like to write zero to the KeyOn reg a lot, so shortcut
@ -996,7 +989,24 @@ void StartVoices(int core, u32 value)
for( u8 vc=0; vc<24; vc++ )
{
if ((value>>vc) & 1)
VoiceStart(core,vc);
{
Cores[core].Voices[vc].Start();
Cores[core].Regs.ENDX &= ~( 1 << vc );
if( IsDevBuild )
{
V_Voice& thisvc( Cores[core].Voices[vc] );
if(MsgKeyOnOff()) ConLog(" * SPU2: KeyOn: C%dV%02d: SSA: %8x; M: %s%s%s%s; H: %02x%02x; P: %04x V: %04x/%04x; ADSR: %04x%04x\n",
core,vc,thisvc.StartA,
(thisvc.DryL)?"+":"-",(thisvc.DryR)?"+":"-",
(thisvc.WetL)?"+":"-",(thisvc.WetR)?"+":"-",
*(u8*)GetMemPtr(thisvc.StartA),*(u8 *)GetMemPtr((thisvc.StartA)+1),
thisvc.Pitch,
thisvc.VolumeL.Value,thisvc.VolumeR.Value,
thisvc.ADSR.Reg_ADSR1,thisvc.ADSR.Reg_ADSR2);
}
}
}
}

View File

@ -142,9 +142,6 @@ extern void __inline __fastcall spu2M_Write( u32 addr, u16 value );
#define spu2Rs16(mmem) (*(s16 *)((s8 *)spu2regs + ((mmem) & 0x1fff)))
#define spu2Ru16(mmem) (*(u16 *)((s8 *)spu2regs + ((mmem) & 0x1fff)))
extern void VoiceStart(int core,int vc);
extern void VoiceStop(int core,int vc);
extern u8 callirq;
extern void (* _irqcallback)();
@ -162,8 +159,6 @@ extern int PlayMode;
extern int recording;
extern bool disableFreezes;
extern s32 uTicks;
extern u32 lClocks;
extern u32* cPtr;
extern bool hasPtr;
@ -192,7 +187,7 @@ extern void RecordWrite(s16 left, s16 right);
extern void UpdateSpdifMode();
extern void LowPassFilterInit();
extern void InitADSR();
extern void SndUpdateLimitMode();
extern void CalculateADSR( V_Voice& vc );
//////////////////////////////
// The Mixer Section //

View File

@ -44,7 +44,7 @@ namespace WaveDump
void Open()
{
#ifndef PUBLIC
if( !IsDevBuild ) return;
if( !WaveLog() ) return;
char wavfilename[256];
@ -69,12 +69,11 @@ namespace WaveDump
}
}
}
#endif
}
void Close()
{
#ifndef PUBLIC
if( !IsDevBuild ) return;
for( uint cidx=0; cidx<2; cidx++ )
{
for( int srcidx=0; srcidx<CoreSrc_Count; srcidx++ )
@ -82,18 +81,16 @@ namespace WaveDump
SAFE_DELETE_OBJ( m_CoreWav[cidx][srcidx] );
}
}
#endif
}
void WriteCore( uint coreidx, CoreSourceType src, s16 left, s16 right )
{
#ifndef PUBLIC
if( !IsDevBuild ) return;
if( m_CoreWav[coreidx][src] != NULL )
{
s16 buffer[2] = { left, right };
m_CoreWav[coreidx][src]->write( buffer, 2 );
}
#endif
}
}

View File

@ -36,12 +36,12 @@ static LRESULT WINAPI AboutProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
ConvertStaticToHyperlink( hDlg, IDC_LINK_WEBSITE );
wchar_t outstr[256];
#ifdef PUBLIC
if( IsDevBuild )
swprintf_s( outstr, _T("Build r%d -- Compiled on ") _T(__DATE__), SVN_REV );
else
swprintf_s( outstr, _T("Release v%d.%d -- Compiled on ") _T(__DATE__),
VersionInfo::Release, VersionInfo::Revision );
#else
swprintf_s( outstr, _T("Build r%d -- Compiled on ") _T(__DATE__), SVN_REV );
#endif
SetWindowText( GetDlgItem(hDlg, IDC_LABEL_VERSION_INFO), outstr );
ShowWindow( hDlg, true );
}

View File

@ -22,7 +22,7 @@
#include "spu2.h"
#include "dialogs.h"
#ifndef PUBLIC
#ifdef SPU2X_DEVBUILD
static const int LATENCY_MAX = 3000;
#else
static const int LATENCY_MAX = 750;

View File

@ -132,13 +132,8 @@ void EnableControls( HWND hWnd )
{
EnableMessages( hWnd );
ENABLE_CONTROL(IDC_LOGDMA, DebugEnabled);
#ifdef PUBLIC
ENABLE_CONTROL(IDC_LOGREGS, false);
ENABLE_CONTROL(IDC_LOGWAVE, false);
#else
ENABLE_CONTROL(IDC_LOGREGS, DebugEnabled);
ENABLE_CONTROL(IDC_LOGWAVE, DebugEnabled);
#endif
ENABLE_CONTROL(IDC_LOGREGS, IsDevBuild ? DebugEnabled : false);
ENABLE_CONTROL(IDC_LOGWAVE, IsDevBuild ? DebugEnabled : false);
ENABLE_CONTROL(IDC_DUMPCORE,DebugEnabled);
ENABLE_CONTROL(IDC_DUMPMEM, DebugEnabled);
ENABLE_CONTROL(IDC_DUMPREGS,DebugEnabled);
@ -174,11 +169,7 @@ static BOOL CALLBACK DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
SET_CHECK(IDC_DUMPMEM, _MemDump);
SET_CHECK(IDC_DUMPREGS,_RegDump);
#ifdef PUBLIC
ShowWindow( GetDlgItem( hWnd, IDC_MSG_PUBLIC_BUILD ), true );
#else
ShowWindow( GetDlgItem( hWnd, IDC_MSG_PUBLIC_BUILD ), false );
#endif
ShowWindow( GetDlgItem( hWnd, IDC_MSG_PUBLIC_BUILD ), !IsDevBuild );
}
break;

View File

@ -64,7 +64,7 @@ static BOOL CALLBACK DebugProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
return TRUE;
}
#ifndef PUBLIC
#ifdef SPU2X_DEVBUILD
int FillRectangle(HDC dc, int left, int top, int width, int height)
{

View File

@ -53,7 +53,7 @@
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="FLOAT_SAMPLES;NDEBUG;_USRDLL"
PreprocessorDefinitions="SPU2X_DEVBUILD;FLOAT_SAMPLES;NDEBUG;_USRDLL"
StringPooling="true"
RuntimeLibrary="0"
StructMemberAlignment="5"
@ -148,7 +148,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="FLOAT_SAMPLES;_DEBUG;_USRDLL"
PreprocessorDefinitions="SPU2X_DEVBUILD;FLOAT_SAMPLES;_DEBUG;_USRDLL"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
@ -245,7 +245,7 @@
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="FLOAT_SAMPLES;NDEBUG;PUBLIC;_USRDLL"
PreprocessorDefinitions="FLOAT_SAMPLES;NDEBUG;_USRDLL"
StringPooling="true"
RuntimeLibrary="0"
StructMemberAlignment="5"
@ -344,7 +344,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="FLOAT_SAMPLES;_DEBUG_FAST;_DEBUG;_USRDLL"
PreprocessorDefinitions="SPU2X_DEVBUILD;FLOAT_SAMPLES;_DEBUG_FAST;_DEBUG;_USRDLL"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
@ -727,6 +727,10 @@
<Filter
Name="SPU2"
>
<File
RelativePath="..\ADSR.cpp"
>
</File>
<File
RelativePath="..\defs.h"
>

View File

@ -31,6 +31,9 @@ struct V_Volume
s32 Value;
s8 Increment;
s8 Mode;
public:
void Update();
};
struct V_ADSR
@ -38,29 +41,22 @@ struct V_ADSR
u16 Reg_ADSR1;
u16 Reg_ADSR2;
//also Reg_ENVX
s32 Value; // Ranges from 0 to 0x7fffffff (signed values are clamped to 0)
// Phase
s32 Value; // Ranges from 0 to 0x7fffffff (signed values are clamped to 0) [Reg_ENVX]
u8 Phase;
//Attack Rate
u8 Ar;
//Attack Mode
u8 Am;
//Decay Rate
u8 Dr;
//Sustain Level
u8 Sl;
//Sustain Rate
u8 Sr;
//Sustain Mode
u8 Sm;
//Release Rate
u8 Rr;
//Release Mode
u8 Rm;
u8 AttackRate; // Ar
u8 AttackMode; // Am
u8 DecayRate; // Dr
u8 SustainLevel; // Sl
u8 SustainRate; // Sr
u8 SustainMode; // Sm
u8 ReleaseRate; // Rr
u8 ReleaseMode; // Rm
//Ready To Release
bool Releasing;
bool Releasing; // Ready To Release, triggered by Voice.Stop();
public:
bool Calculate();
};
@ -133,9 +129,10 @@ struct V_Voice
// sample position within the current decoded packet.
s32 SCurrent;
void Start();
void Stop();
};
#ifndef PUBLIC
// ** Begin Debug-only variables section **
// Separated from the V_Voice struct to improve cache performance of
// the Public Release build.
@ -158,7 +155,6 @@ struct V_CoreDebug
// Debug tracking information - 24 voices and 2 cores.
extern V_CoreDebug DebugCores[2];
#endif
struct V_Reverb
{