From 37f2a95289baf41e58054c75685801e10b25750c Mon Sep 17 00:00:00 2001 From: ramapcsx2 Date: Thu, 23 Oct 2008 09:22:20 +0000 Subject: [PATCH] Jake.Stine "completed" his work on SPU2Ghz, I tested a whole lot of games with this and it sounds great! Savestates work very well, the mixer is fast, even the dreaded buffer over/underflows are handled quite well (even without timestretch!). All in all great additions which imo make SPU2Ghz the best SPU2 plugin to use now :) git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@229 a6443dda-0b58-4228-96e9-037be469359c --- pcsx2/GS.cpp | 5 +- .../windows/VCprojects/pcsx2_2005_x64.vcproj | 57 +++---- pcsx2/windows/VCprojects/pcsx2_2008.vcproj | 2 +- plugins/spu2ghz/SPU2ghz_vs2008.vcproj | 16 +- plugins/spu2ghz/dma.cpp | 4 - plugins/spu2ghz/mixer.cpp | 152 +++++++++--------- plugins/spu2ghz/sndout.cpp | 41 +++-- plugins/spu2ghz/spu2.cpp | 61 +++---- plugins/spu2ghz/spu2.h | 2 +- plugins/spu2ghz/waveout.cpp | 6 - 10 files changed, 166 insertions(+), 180 deletions(-) diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 7afb282e47..f4fb896440 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -106,7 +106,7 @@ void* GSThreadProc(void* idp); #endif -bool gsHasToExit=false; +static bool gsHasToExit=false; int g_FFXHack=0; #ifdef PCSX2_DEVBUILD @@ -1471,6 +1471,7 @@ int HasToExit() } #if defined(_WIN32) && !defined(WIN32_PTHREADS) +#pragma optimize ("", off) DWORD WINAPI GSThreadProc(LPVOID lpParam) { HANDLE handles[2] = { g_hGsEvent, g_hVuGSExit }; @@ -1744,7 +1745,7 @@ ExitGS: GSclose(); return 0; } - +#pragma optimize ("", on) int gsFreeze(gzFile f, int Mode) { gzfreeze(PS2MEM_GS, 0x2000); diff --git a/pcsx2/windows/VCprojects/pcsx2_2005_x64.vcproj b/pcsx2/windows/VCprojects/pcsx2_2005_x64.vcproj index 3b61dff800..0a04f4d6c0 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2005_x64.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2005_x64.vcproj @@ -1,10 +1,11 @@ - @@ -210,6 +210,8 @@ AdditionalLibraryDirectories="..\" ProgramDatabaseFile=".\Release/pcsx2.pdb" SubSystem="2" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="17" /> - @@ -311,6 +310,8 @@ GenerateDebugInformation="true" ProgramDatabaseFile=".\Debug/pcsx2.pdb" SubSystem="2" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" /> - @@ -410,6 +408,8 @@ GenerateDebugInformation="true" ProgramDatabaseFile=".\Debug/pcsx2.pdb" SubSystem="2" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="17" /> - @@ -511,6 +508,8 @@ GenerateDebugInformation="true" ProgramDatabaseFile=".\Debug TLB/pcsx2.pdb" SubSystem="2" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" /> - @@ -610,6 +606,8 @@ GenerateDebugInformation="true" ProgramDatabaseFile=".\Debug TLB/pcsx2.pdb" SubSystem="2" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="17" /> - @@ -717,6 +712,8 @@ AdditionalLibraryDirectories="..\" ProgramDatabaseFile=".\ReleaseTLB/pcsx2.pdb" SubSystem="2" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="17" /> - @@ -779,14 +773,13 @@ /> @@ -847,9 +843,6 @@ - diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 62cd061251..126e484acc 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -372,7 +372,7 @@ /> >12 ); } - Value = MulShr32su( Value, vc.ADSR.Value>>12 ); } } @@ -781,7 +780,7 @@ void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR) ///////////////////////////////////////////////////////////////////////////////////////// // // -void __fastcall ReadInputPV(V_Core& thiscore, s32& ValL,s32& ValR) +static void __forceinline __fastcall ReadInputPV(V_Core& thiscore, s32& ValL,s32& ValR) { s32 DL=0, DR=0; @@ -800,6 +799,18 @@ void __fastcall ReadInputPV(V_Core& thiscore, s32& ValL,s32& ValR) ValL=thiscore.ADMAPL; ValR=thiscore.ADMAPR; + + #ifndef PUBLIC + s32 InputPeak = max(abs(ValL),abs(ValR)); + if(DebugCores[core].AutoDMAPeak>= 1; + ValR >>= 1; + } ///////////////////////////////////////////////////////////////////////////////////////// @@ -1050,55 +1061,13 @@ static void __forceinline MixVoice( V_Core& thiscore, V_Voice& vc, s32& VValL, s static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR) { - s32 InpL=0, InpR=0; - s32 RVL,RVR; - s32 TDL=0,TDR=0,TWL=0,TWR=0; - s32 SDL=0,SDR=0,SWL=0,SWR=0; + s32 TDL=0,TDR=0; + s32 SDL=0,SDR=0; + s32 SWL=0,SWR=0; - TDL=TDR=TWL=TWR=(s32)0; - - if (core == 1) { //Core 0 doesn't have External input - spu2M_WriteFast( 0x800 + OutPos, (s16)(ExtL>>16) ); - spu2M_WriteFast( 0xA00 + OutPos, (s16)(ExtR>>16) ); - } - V_Core& thiscore( Cores[core] ); - if((core==0)&&((PlayMode&4)!=4)) - { - ReadInputPV(thiscore, InpL,InpR); // get input data from input buffers - } - if((core==1)&&((PlayMode&8)!=8)) - { - ReadInputPV(thiscore, InpL,InpR); // get input data from input buffers - } - - #ifndef PUBLIC - s32 InputPeak = max(abs(InpL),abs(InpR)); - if(DebugCores[core].AutoDMAPeak>= 1; - InpR >>= 1; - - // shift inputs by 20 collectively, so that the result is - // effectively downshifted by 12: - ExtL = MulShr32su( ExtL<<3, ((int)thiscore.ExtL)<<16); - ExtR = MulShr32su( ExtR<<3, ((int)thiscore.ExtR)<<16); - - //InpL = MulDiv(InpL,(thiscore.InpL),1<<1); - //InpR = MulDiv(InpR,(thiscore.InpR),1<<1); - //ExtL = MulDiv(ExtL,(thiscore.ExtL),1<<12); - //ExtR = MulDiv(ExtR,(thiscore.ExtR),1<<12); - SDL=SDR=SWL=SWR=(s32)0; for (voice=0;voice<24;voice++) @@ -1120,42 +1089,46 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR) spu2M_WriteFast( 0x1400 + (core<<12) + OutPos, (s16)(SWL>>16) ); spu2M_WriteFast( 0x1600 + (core<<12) + OutPos, (s16)(SWR>>16) ); + // Mix in the Input data + TDL = OutL * thiscore.InpDryL; + TDR = OutR * thiscore.InpDryR; + // Mix in the Voice data TDL += SDL * thiscore.SndDryL; TDR += SDR * thiscore.SndDryR; - TWL += SWL * thiscore.SndWetL; - TWR += SWR * thiscore.SndWetR; - - // Mix in the Input data - TDL += InpL * thiscore.InpDryL; - TDR += InpR * thiscore.InpDryR; - TWL += InpL * thiscore.InpWetL; - TWR += InpR * thiscore.InpWetR; // Mix in the External (nothing/core0) data TDL += ExtL * thiscore.ExtDryL; TDR += ExtR * thiscore.ExtDryR; - TWL += ExtL * thiscore.ExtWetL; - TWR += ExtR * thiscore.ExtWetR; if(EffectsEnabled) { + s32 TWL=0,TWR=0; + + // Mix Input, Voice, and External data: + TWL = OutL * thiscore.InpWetL; + TWR = OutR * thiscore.InpWetR; + TWL += SWL * thiscore.SndWetL; + TWR += SWR * thiscore.SndWetR; + TWL += ExtL * thiscore.ExtWetL; + TWR += ExtR * thiscore.ExtWetR; + //Apply Effects DoReverb( thiscore, RVL,RVR,TWL>>16,TWR>>16); TWL=ApplyVolume(RVL,VOL(thiscore.FxL)); TWR=ApplyVolume(RVR,VOL(thiscore.FxR)); + + //Mix Wet,Dry + OutL=(TDL + TWL); + OutR=(TDR + TWR); } else { - TWL=0; - TWR=0; + OutL = TDL; + OutR = TDR; } - //Mix Wet,Dry - OutL=(TDL + TWL); - OutR=(TDR + TWR); - //Apply Master Volume if( thiscore.MasterL.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume(thiscore.MasterL); if( thiscore.MasterR.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume(thiscore.MasterR); @@ -1170,17 +1143,6 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR) OutL=0; OutR=0; } - - if((core==1)&&(PlayMode&8)) - { - ReadInput(thiscore, OutL,OutR); - } - - if((core==0)&&(PlayMode&4)) - { - OutL=0; - OutR=0; - } } // used to throttle the output rate of cache stat reports @@ -1190,11 +1152,45 @@ void __fastcall Mix() { s32 ExtL=0, ExtR=0, OutL, OutR; + // **** CORE ZERO **** + core=0; - MixCore(ExtL,ExtR,0,0); + if( (PlayMode&4) != 4 ) + { + // get input data from input buffers + ReadInputPV(Cores[0], ExtL, ExtR); + } + + MixCore( ExtL, ExtR, 0, 0 ); + + if( PlayMode & 4 ) + { + ExtL=0; + ExtR=0; + } + + // Commit Core 0 output to ram before mixing Core 1: + ExtL>>=13; + ExtR>>=13; + spu2M_WriteFast( 0x800 + OutPos, ExtL>>3 ); + spu2M_WriteFast( 0xA00 + OutPos, ExtR>>3 ); + + // **** CORE ONE **** core=1; - MixCore(OutL,OutR,ExtL,ExtR); + if( (PlayMode&8) != 8 ) + { + ReadInputPV(Cores[1], OutL, OutR); // get input data from input buffers + } + + // Apply volume to the external (Core 0) input data. + + MixCore( OutL, OutR, ExtL*Cores[1].ExtL, ExtR*Cores[1].ExtR ); + + if( PlayMode & 8 ) + { + ReadInput(Cores[1], OutL, OutR); + } #ifndef PUBLIC static s32 Peak0,Peak1; diff --git a/plugins/spu2ghz/sndout.cpp b/plugins/spu2ghz/sndout.cpp index 07a92d8fef..264249e401 100644 --- a/plugins/spu2ghz/sndout.cpp +++ b/plugins/spu2ghz/sndout.cpp @@ -128,9 +128,9 @@ public: { //isWaiting=true; LeaveCriticalSection(&cs); - ConLog( " * SPU2 : Waiting for object... " ); + //ConLog( " * SPU2 : Waiting for object... " ); WaitForSingleObject(hSyncEvent,1000); - ConLog( " Signaled! \n" ); + //ConLog( " Signaled! \n" ); EnterCriticalSection(&cs); #ifdef DYNAMIC_BUFFER_LIMITING free = buffer_limit-data; @@ -150,14 +150,28 @@ public: // several seconds). // // Compromise: - // Same as with underruns below, an overrun can be handled by aborting - // the write operation before the writepos goes past the readpos, and then - // ignoring the rest of the incoming data. The resultant sound will have - // a single hiccup when an overflow occurs, instead of getting crapped out - // for several seconds (or in many cases, until the SPU sndout driver was - // manually reset.. grr!). + // When an overrun occurs, we adapt by discarding a portion of the buffer. + // The older portion of the buffer is discarded rather than incoming data, + // so that the overall audio synchronization is better. #ifndef DYNAMIC_BUFFER_LIMITING + + if( data+nSamples > size ) + { + // Buffer overrun! + // Dump samples from the read portion of the buffer instead of dropping + // the newly written stuff. + + // Toss half the buffer plus whatever's being written anew: + s32 comp = (size / 2) + nSamples; + comp = (comp + 128) & ~127; // probably not important but it makes the log look nicer. :P + if( comp > size ) comp = size; + + data-=comp; + rpos=(rpos+comp)%size; + ConLog(" * SPU2 > Overrun Compensation (%d samples tossed)\n", size ); + } + while(data0) { buffer[wpos] = *(bData++); @@ -213,7 +227,13 @@ public: #ifndef DYNAMIC_BUFFER_LIMITING if( underrun_freeze ) { - if( data < (int)(size * 0.85) ) + // Let's fill up 80% of our buffer. + // (I just picked 80% arbitrarily.. there might be a better value) + + int toFill = (int)(size * 0.80); + toFill = (toFill + 128) & ~127; // probably not important but it makes the log look nicer. :P + + if( data < toFill ) { while( nSamples>0 ) { @@ -225,7 +245,7 @@ public: } underrun_freeze = false; - //ConLog( " * SPU2 > Underrun Freeze Finished!\n" ); + ConLog( " * SPU2 > Underrun compensation (%d samples buffered)\n", toFill ); } while(data>0 && nSamples>0) @@ -250,7 +270,6 @@ public: if( data == 0 && !pw ) { - ConLog( " * SPU2 > Underrun compensation\n" ); underrun_freeze = true; } diff --git a/plugins/spu2ghz/spu2.cpp b/plugins/spu2ghz/spu2.cpp index 98738aedae..112f666de1 100644 --- a/plugins/spu2ghz/spu2.cpp +++ b/plugins/spu2ghz/spu2.cpp @@ -60,9 +60,8 @@ static bool spu2init=false; // has spu2init plugin interface been called? static bool resetClock = true; // Used to make spu2 more robust at loading incompatible saves. -// You won't get any sound but it won't cause mass instability either. -// (should allow players to get to their next save point more easily) -bool disableEverything=false; +// Disables re-freezing of save state data. +bool disableFreezes=false; void (* _irqcallback)(); void (* dma4callback)(); @@ -281,7 +280,7 @@ s32 CALLBACK SPU2init() #endif srand((unsigned)time(NULL)); - disableEverything=false; + disableFreezes=false; if (spu2init) { @@ -653,10 +652,10 @@ void __fastcall TimeUpdate(u32 cClocks, u32 syncType) // If for some reason our clock value seems way off base, just mix // out a little bit, skip the rest, and hope the ship "rights" itself later on. - if( dClocks > TickInterval*32 ) + if( dClocks > TickInterval*48 ) { - ConLog( " * SPU2 > TimeUpdate > Sanity Check Failed: %d (cc: %d)\n", dClocks/TickInterval, cClocks/TickInterval ); - dClocks = TickInterval*32; + ConLog( " * SPU2 > TimeUpdate Sanity Check (Tick Delta: %d) (PS2 Ticks: %d)\n", dClocks/TickInterval, cClocks/TickInterval ); + dClocks = TickInterval*48; lClocks = cClocks-dClocks; } @@ -734,8 +733,6 @@ bool numpad_minus = false; void CALLBACK SPU2async(u32 cycles) { - if( disableEverything ) return; - #ifndef PUBLIC u32 oldClocks = lClocks; static u32 timer=0,time1=0,time2=0; @@ -1251,8 +1248,6 @@ void CALLBACK SPU2writeLog(u32 rmem, u16 value) void CALLBACK SPU2write(u32 rmem, u16 value) { - if( disableEverything ) return; - #ifdef S2R_ENABLE if(!replay_mode) s2r_writereg(Cycles,rmem,value); @@ -1583,8 +1578,6 @@ void CALLBACK SPU2write(u32 rmem, u16 value) u16 CALLBACK SPU2read(u32 rmem) { - if( disableEverything ) return 0; - // if(!replay_mode) // s2r_readreg(Cycles,rmem); @@ -1677,14 +1670,12 @@ typedef struct static int getFreezeSize() { - if( disableEverything ) return 7; // length of the string id "invalid" + if( disableFreezes ) return 7; // length of the string id "invalid" int size = sizeof(SPU2freezeData); // calculate the amount of memory consumed by our cache: - //size += PCM_CACHE_BLOCK_COUNT / 8; - for( int bidx=0; bidxid != SAVE_ID || spud->version != SAVE_VERSION ) { - // [Air]: Running the SPU2 from an "empty" state this way is pretty unreliable. - // It usually didn't crash at least, but it never output sound anyway and would - // confuse the new cache system. - // - // To fix it I introduced a new global flag that disables the SPU2 logic completely. - // This is the safest way to recover from an unsupported SPU2 save, since it pretty - // well garauntees the user will have a stable enough environment to reach a save spot. - printf("\n*** SPU2Ghz Warning:\n"); - printf("The savestate you are trying to load was not made with this plugin.\n"); - printf("Sound will be disabled until the emulator is reset.\n"); - printf("Find a memorycard savespot to save your game, reset, and then continue from there.\n\n"); + printf(" The savestate you are trying to load was not made with this plugin.\n"); + printf(" The emulator will not be stable! Find a memorycard savespot to save your\n"); + printf(" game, reset, and then continue from there.\n\n"); - // Clear stuff, not that it matters: - - disableEverything=true; + disableFreezes=true; lClocks = 0; resetClock = true; - // Reset the cores. + // Do *not* reset the cores. + // We'll need some "hints" as to how the cores should be initialized, + // and the only way to get that is to use the game's existing core settings + // and hope they kinda match the settings for the savestate (IRQ enables and such). + // - CoreReset( 0 ); - CoreReset( 1 ); + //CoreReset( 0 ); + //CoreReset( 1 ); - // adpcm cache : Just clear all the cache flags, which forces the mixer - // to re-decode everything. + // adpcm cache : Clear all the cache flags and buffers. memset( pcm_cache_flags, 0, (0x200000 / (16*32)) * 4 ); memset( pcm_cache_data, 0, (0x200000 / 16) * 28 * 2 ); } else { - disableEverything=false; + disableFreezes=false; // base stuff memcpy(spu2regs, spud->unkregs, 0x010000); @@ -1795,7 +1779,7 @@ s32 CALLBACK SPU2freeze(int mode, freezeData *data) { if (data->data == NULL) return -1; - if( disableEverything ) + if( disableFreezes ) { // No point in making a save state since the SPU2 // state is completely bogus anyway... Let's just @@ -1971,7 +1955,8 @@ void StopVoices(int core, u32 value) // for now, pData is not used int CALLBACK SPU2setupRecording(int start, void* pData) { - if( disableEverything ) return 0; + // Don't record if we have a bogus state. + if( disableFreezes ) return 0; if(start==0) { diff --git a/plugins/spu2ghz/spu2.h b/plugins/spu2ghz/spu2.h index cfbd8e5fa1..aef0c1e3fa 100644 --- a/plugins/spu2ghz/spu2.h +++ b/plugins/spu2ghz/spu2.h @@ -141,7 +141,7 @@ extern u32 lClocks; extern u32* cPtr; extern bool hasPtr; -extern bool disableEverything; +extern bool disableFreezes; void __fastcall TimeUpdate(u32 cClocks, u32 syncType); diff --git a/plugins/spu2ghz/waveout.cpp b/plugins/spu2ghz/waveout.cpp index d71454e701..ae5d9226ab 100644 --- a/plugins/spu2ghz/waveout.cpp +++ b/plugins/spu2ghz/waveout.cpp @@ -127,12 +127,6 @@ public: whbuffer[i].reserved=0; waveOutPrepareHeader(hwodevice,whbuffer+i,sizeof(WAVEHDR)); whbuffer[i].dwFlags|=WHDR_DONE; //avoid deadlock - - // Feed blocks into the device. - // It'll all be empty samples, but it helps reduce some of the pop-on-init. - - //whbuffer[i].dwFlags&=~WHDR_DONE; - //waveOutWrite(hwodevice,&whbuffer[i],sizeof(WAVEHDR)); } // Start Thread