From 9c97092efd11b0e35ebca73b559d6a533b6cf805 Mon Sep 17 00:00:00 2001 From: Gauvain 'GovanifY' Roussel-Tarbouriech Date: Thu, 24 Sep 2020 15:26:34 +0200 Subject: [PATCH] SPU2: merging some codebase callbacks --- pcsx2/CMakeLists.txt | 42 +++ pcsx2/Hw.cpp | 1 + pcsx2/HwWrite.cpp | 1 + pcsx2/IopCounters.cpp | 1 + pcsx2/IopDma.cpp | 1 + pcsx2/IopMem.cpp | 1 + pcsx2/Memory.cpp | 1 + pcsx2/SPU2/spu2.cpp | 612 ++++++++++++++++++++++++++++++++++++++ pcsx2/SPU2/spu2.h | 69 +++++ pcsx2/SPU2/spu2replay.cpp | 321 ++++++++++++++++++++ pcsx2/SPU2/spu2replay.h | 31 ++ 11 files changed, 1081 insertions(+) create mode 100644 pcsx2/SPU2/spu2.cpp create mode 100644 pcsx2/SPU2/spu2.h create mode 100644 pcsx2/SPU2/spu2replay.cpp create mode 100644 pcsx2/SPU2/spu2replay.h diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 49c3990111..b14881186a 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -222,6 +222,46 @@ set(pcsx2CDVDHeaders CDVD/zlib_indexed.h ) +# SPU2 sources +set(pcsx2SPU2Sources + SPU2/ADSR.cpp + SPU2/Debug.cpp + SPU2/DplIIdecoder.cpp + SPU2/Dma.cpp + SPU2/Lowpass.cpp + SPU2/Mixer.cpp + SPU2/spu2.cpp + SPU2/ReadInput.cpp + SPU2/RegLog.cpp + SPU2/RegTable.cpp + SPU2/Reverb.cpp + SPU2/SndOut.cpp + SPU2/SndOut_SDL.cpp + SPU2/spu2freeze.cpp + SPU2/spu2replay.cpp + SPU2/spu2sys.cpp + SPU2/Timestretcher.cpp + SPU2/Wavedump_wav.cpp + SPU2/WavFile.cpp + ) + +# SPU2 headers +set(pcsx2SPU2Headers + SPU2/Config.h + SPU2/Debug.h + SPU2/defs.h + SPU2/Dma.h + SPU2/Global.h + SPU2/Lowpass.h + SPU2/Mixer.h + SPU2/spu2.h + SPU2/regs.h + SPU2/SndOut.h + SPU2/spdif.h + SPU2/spu2replay.h + SPU2/WavFile.h +) + # DebugTools sources set(pcsx2DebugToolsSources DebugTools/DebugInterface.cpp @@ -651,6 +691,8 @@ set(Common ${pcsx2Headers} ${pcsx2CDVDSources} ${pcsx2CDVDHeaders} + ${pcsx2SPU2Sources} + ${pcsx2SPU2Headers} ${pcsx2DebugToolsSources} ${pcsx2GuiSources} ${pcsx2GuiResources} diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp index b2ec18f0e5..d2beae16cc 100644 --- a/pcsx2/Hw.cpp +++ b/pcsx2/Hw.cpp @@ -21,6 +21,7 @@ #include "IPU/IPUdma.h" #include "Gif_Unit.h" #include "IopCommon.h" +#include "SPU2/spu2.h" using namespace R5900; diff --git a/pcsx2/HwWrite.cpp b/pcsx2/HwWrite.cpp index efacbc707b..f2aa97a897 100644 --- a/pcsx2/HwWrite.cpp +++ b/pcsx2/HwWrite.cpp @@ -23,6 +23,7 @@ #include "ps2/eeHwTraceLog.inl" #include "ps2/pgif.h" +#include "SPU2/spu2.h" #include "R3000A.h" using namespace R5900; diff --git a/pcsx2/IopCounters.cpp b/pcsx2/IopCounters.cpp index 169169c728..d38e9fb264 100644 --- a/pcsx2/IopCounters.cpp +++ b/pcsx2/IopCounters.cpp @@ -20,6 +20,7 @@ #include "PrecompiledHeader.h" #include "IopCommon.h" +#include "SPU2/spu2.h" #include diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp index 1f241c74d7..2aa3df70a4 100644 --- a/pcsx2/IopDma.cpp +++ b/pcsx2/IopDma.cpp @@ -15,6 +15,7 @@ #include "PrecompiledHeader.h" #include "IopCommon.h" +#include "SPU2/spu2.h" #include "Sif.h" diff --git a/pcsx2/IopMem.cpp b/pcsx2/IopMem.cpp index 0c159b2254..a45bf93469 100644 --- a/pcsx2/IopMem.cpp +++ b/pcsx2/IopMem.cpp @@ -17,6 +17,7 @@ #include "PrecompiledHeader.h" #include "IopCommon.h" #include "ps2/pgif.h" // for PSX kernel TTY in iopMemWrite32 +#include "SPU2/spu2.h" uptr *psxMemWLUT = NULL; const uptr *psxMemRLUT = NULL; diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp index 212581c1d7..411c219784 100644 --- a/pcsx2/Memory.cpp +++ b/pcsx2/Memory.cpp @@ -44,6 +44,7 @@ BIOS #include "ps2/HwInternal.h" #include "ps2/BiosTools.h" +#include "SPU2/spu2.h" #include "Utilities/PageFaultSource.h" diff --git a/pcsx2/SPU2/spu2.cpp b/pcsx2/SPU2/spu2.cpp new file mode 100644 index 0000000000..be6c54bb52 --- /dev/null +++ b/pcsx2/SPU2/spu2.cpp @@ -0,0 +1,612 @@ +/* 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] + * + * 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 . + */ + +#include "Global.h" +#include "spu2.h" +#include "Dma.h" +#include "Dialogs.h" + +#include "svnrev.h" + +#ifdef _MSC_VER +#define snprintf sprintf_s +#endif +// PCSX2 expects ASNI, not unicode, so this MUST always be char... +static char libraryName[256]; + +int SampleRate = 48000; + +static bool IsOpened = false; +static bool IsInitialized = false; + +static u32 pClocks = 0; + +u32 *cyclePtr = NULL; +u32 lClocks = 0; + + +//static bool cpu_detected = false; + +static bool CheckSSE() +{ + return true; + +#if 0 + if( !cpu_detected ) + { + cpudetectInit(); + cpu_detected = true; + } + if( !x86caps.hasStreamingSIMDExtensions || !x86caps.hasStreamingSIMD2Extensions ) + { + SysMessage( "Your CPU does not support SSE2 instructions.\nThe SPU2-X plugin requires SSE2 to run." ); + return false; + } + return true; +#endif +} + +void SPU2configure() +{ + if (!CheckSSE()) + return; + configure(); +} + +s32 SPU2test() +{ + if (!CheckSSE()) + return -1; + + ReadSettings(); + if (SndBuffer::Test() != 0) { + // TODO : Implement a proper dialog that allows the user to test different audio out drivers. + const wchar_t *wtf = mods[OutputModule]->GetIdent(); + SysMessage(L"The '%s' driver test failed. Please configure\na different SoundOut module and try again.", wtf); + return -1; + } + + return 0; +} + +// -------------------------------------------------------------------------------------- +// DMA 4/7 Callbacks from Core Emulator +// -------------------------------------------------------------------------------------- + +u16 *DMABaseAddr; +void (*_irqcallback)(); +void (*dma4callback)(); +void (*dma7callback)(); + +u32 CALLBACK SPU2ReadMemAddr(int core) +{ + return Cores[core].MADR; +} +void CALLBACK SPU2WriteMemAddr(int core, u32 value) +{ + Cores[core].MADR = value; +} + +void CALLBACK SPU2setDMABaseAddr(uptr baseaddr) +{ + DMABaseAddr = (u16 *)baseaddr; +} + +void CALLBACK SPU2setSettingsDir(const char *dir) +{ + CfgSetSettingsDir(dir); +} + +void CALLBACK SPU2setLogDir(const char *dir) +{ + CfgSetLogDir(dir); +} + +void SPU2irqCallback(void (*SPU2callback)(), void (*DMA4callback)(), void (*DMA7callback)()) +{ + _irqcallback = SPU2callback; + dma4callback = DMA4callback; + dma7callback = DMA7callback; +} + +void CALLBACK SPU2readDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units +{ + if (cyclePtr != NULL) + TimeUpdate(*cyclePtr); + + FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1); + Cores[0].DoDMAread(pMem, size); +} + +void CALLBACK SPU2writeDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units +{ + if (cyclePtr != NULL) + TimeUpdate(*cyclePtr); + + FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA); +#ifdef S2R_ENABLE + if (!replay_mode) + s2r_writedma4(Cycles, pMem, size); +#endif + Cores[0].DoDMAwrite(pMem, size); +} + +void CALLBACK SPU2interruptDMA4() +{ + FileLog("[%10d] SPU2 interruptDMA4\n", Cycles); + Cores[0].Regs.STATX |= 0x80; + //Cores[0].Regs.ATTR &= ~0x30; +} + +void CALLBACK SPU2interruptDMA7() +{ + FileLog("[%10d] SPU2 interruptDMA7\n", Cycles); + Cores[1].Regs.STATX |= 0x80; + //Cores[1].Regs.ATTR &= ~0x30; +} + +void CALLBACK SPU2readDMA7Mem(u16 *pMem, u32 size) +{ + if (cyclePtr != NULL) + TimeUpdate(*cyclePtr); + + FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1); + Cores[1].DoDMAread(pMem, size); +} + +void CALLBACK SPU2writeDMA7Mem(u16 *pMem, u32 size) +{ + if (cyclePtr != NULL) + TimeUpdate(*cyclePtr); + + FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA); +#ifdef S2R_ENABLE + if (!replay_mode) + s2r_writedma7(Cycles, pMem, size); +#endif + Cores[1].DoDMAwrite(pMem, size); +} + +s32 SPU2reset() +{ + if (SndBuffer::Test() == 0 && SampleRate != 48000) + { + SampleRate = 48000; + SndBuffer::Cleanup(); + + try { + SndBuffer::Init(); + } + catch (std::exception& ex) { + fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what()); + SPU2close(); + return -1; + } + } + else + SampleRate = 48000; + + memset(spu2regs, 0, 0x010000); + memset(_spu2mem, 0, 0x200000); + memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free. + Cores[0].Init(0); + Cores[1].Init(1); + return 0; +} + +s32 SPU2ps1reset() +{ + printf("RESET PS1 \n"); + + if (SndBuffer::Test() == 0 && SampleRate != 44100) + { + SampleRate = 44100; + SndBuffer::Cleanup(); + + try { + SndBuffer::Init(); + } + catch (std::exception& ex) { + fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what()); + SPU2close(); + return -1; + } + } + else + SampleRate = 44100; + + /* memset(spu2regs, 0, 0x010000); + memset(_spu2mem, 0, 0x200000); + memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free. + Cores[0].Init(0); + Cores[1].Init(1);*/ + return 0; +} + +s32 SPU2init() +{ + assert(regtable[0x400] == NULL); + + if (IsInitialized) { + printf(" * SPU2-X: Already initialized - Ignoring SPU2init signal."); + return 0; + } + + IsInitialized = true; + + ReadSettings(); + +#ifdef SPU2_LOG + if (AccessLog()) { + spu2Log = OpenLog(AccessLogFileName); + setvbuf(spu2Log, NULL, _IONBF, 0); + FileLog("SPU2init\n"); + } +#endif + srand((unsigned)time(NULL)); + + spu2regs = (s16 *)malloc(0x010000); + _spu2mem = (s16 *)malloc(0x200000); + + // adpcm decoder cache: + // the cache data size is determined by taking the number of adpcm blocks + // (2MB / 16) and multiplying it by the decoded block size (28 samples). + // Thus: pcm_cache_data = 7,340,032 bytes (ouch!) + // Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio] + // Resulting in 2MB * 3.5. + + pcm_cache_data = (PcmCacheEntry *)calloc(pcm_BlockCount, sizeof(PcmCacheEntry)); + + if ((spu2regs == NULL) || (_spu2mem == NULL) || (pcm_cache_data == NULL)) { + SysMessage("SPU2-X: Error allocating Memory\n"); + return -1; + } + + // Patch up a copy of regtable that directly maps "NULLs" to SPU2 memory. + + memcpy(regtable, regtable_original, sizeof(regtable)); + + for (uint mem = 0; mem < 0x800; mem++) { + u16 *ptr = regtable[mem >> 1]; + if (!ptr) { + regtable[mem >> 1] = &(spu2Ru16(mem)); + } + } + + SPU2reset(); + + DMALogOpen(); + InitADSR(); + +#ifdef S2R_ENABLE + if (!replay_mode) + s2r_open(Cycles, "replay_dump.s2r"); +#endif + return 0; +} + +#ifdef _MSC_VER +// Bit ugly to have this here instead of in RealttimeDebugger.cpp, but meh :p +extern bool debugDialogOpen; +extern HWND hDebugDialog; + +static INT_PTR CALLBACK DebugProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int wmId; + + switch (uMsg) { + case WM_PAINT: + return FALSE; + case WM_INITDIALOG: { + debugDialogOpen = true; + } break; + + case WM_COMMAND: + wmId = LOWORD(wParam); + // Parse the menu selections: + switch (wmId) { + case IDOK: + case IDCANCEL: + debugDialogOpen = false; + EndDialog(hWnd, 0); + break; + default: + return FALSE; + } + break; + + default: + return FALSE; + } + return TRUE; +} +#endif +uptr gsWindowHandle = 0; + +s32 SPU2open(void *pDsp) +{ + if (IsOpened) + return 0; + + FileLog("[%10d] SPU2 Open\n", Cycles); + + if (pDsp != NULL) + gsWindowHandle = *(uptr *)pDsp; + else + gsWindowHandle = 0; + +#ifdef _MSC_VER +#ifdef PCSX2_DEVBUILD // Define may not be needed but not tested yet. Better make sure. + if (IsDevBuild && VisualDebug()) { + if (debugDialogOpen == 0) { + hDebugDialog = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DEBUG), 0, DebugProc, 0); + ShowWindow(hDebugDialog, SW_SHOWNORMAL); + debugDialogOpen = 1; + } + } else if (debugDialogOpen) { + DestroyWindow(hDebugDialog); + debugDialogOpen = 0; + } +#endif +#endif + + IsOpened = true; + lClocks = (cyclePtr != NULL) ? *cyclePtr : 0; + + try { + SndBuffer::Init(); + +#ifndef __POSIX__ + DspLoadLibrary(dspPlugin, dspPluginModule); +#endif + WaveDump::Open(); + } catch (std::exception &ex) { + fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what()); + SPU2close(); + return -1; + } + return 0; +} + +void SPU2close() +{ + if (!IsOpened) + return; + IsOpened = false; + + FileLog("[%10d] SPU2 Close\n", Cycles); + +#ifndef __POSIX__ + DspCloseLibrary(); +#endif + + SndBuffer::Cleanup(); +} + +void SPU2shutdown() +{ + if (!IsInitialized) + return; + IsInitialized = false; + + ConLog("* SPU2-X: Shutting down.\n"); + + SPU2close(); + +#ifdef S2R_ENABLE + if (!replay_mode) + s2r_close(); +#endif + + DoFullDump(); +#ifdef STREAM_DUMP + fclose(il0); + fclose(il1); +#endif +#ifdef EFFECTS_DUMP + fclose(el0); + fclose(el1); +#endif + WaveDump::Close(); + + DMALogClose(); + + safe_free(spu2regs); + safe_free(_spu2mem); + safe_free(pcm_cache_data); + + +#ifdef SPU2_LOG + if (!AccessLog()) + return; + FileLog("[%10d] SPU2shutdown\n", Cycles); + if (spu2Log) + fclose(spu2Log); +#endif +} + +void SPU2setClockPtr(u32 *ptr) +{ + cyclePtr = ptr; +} + +#ifdef DEBUG_KEYS +static u32 lastTicks; +static bool lState[6]; +#endif + +void SPU2async(u32 cycles) +{ + DspUpdate(); + + if (cyclePtr != NULL) { + TimeUpdate(*cyclePtr); + } else { + pClocks += cycles; + TimeUpdate(pClocks); + } + +#ifdef DEBUG_KEYS + u32 curTicks = GetTickCount(); + if ((curTicks - lastTicks) >= 50) { + int oldI = Interpolation; + bool cState[6]; + for (int i = 0; i < 6; i++) { + cState[i] = !!(GetAsyncKeyState(VK_NUMPAD0 + i) & 0x8000); + + if ((cState[i] && !lState[i]) && i != 5) + Interpolation = i; + + if ((cState[i] && !lState[i]) && i == 5) { + postprocess_filter_enabled = !postprocess_filter_enabled; + printf("Post process filters %s \n", postprocess_filter_enabled ? "enabled" : "disabled"); + } + + lState[i] = cState[i]; + } + + if (Interpolation != oldI) { + printf("Interpolation set to %d", Interpolation); + switch (Interpolation) { + case 0: + printf(" - Nearest.\n"); + break; + case 1: + printf(" - Linear.\n"); + break; + case 2: + printf(" - Cubic.\n"); + break; + case 3: + printf(" - Hermite.\n"); + break; + case 4: + printf(" - Catmull-Rom.\n"); + break; + default: + printf(" (unknown).\n"); + break; + } + } + + lastTicks = curTicks; + } +#endif +} + +u16 SPU2read(u32 rmem) +{ + // if(!replay_mode) + // s2r_readreg(Cycles,rmem); + + u16 ret = 0xDEAD; + u32 core = 0, mem = rmem & 0xFFFF, omem = mem; + if (mem & 0x400) { + omem ^= 0x400; + core = 1; + } + + if (omem == 0x1f9001AC) { + ret = Cores[core].DmaRead(); + } else { + if (cyclePtr != NULL) + TimeUpdate(*cyclePtr); + + if (rmem >> 16 == 0x1f80) { + ret = Cores[0].ReadRegPS1(rmem); + } else if (mem >= 0x800) { + ret = spu2Ru16(mem); + ConLog("* SPU2-X: Read from reg>=0x800: %x value %x\n", mem, ret); + } else { + ret = *(regtable[(mem >> 1)]); + //FileLog("[%10d] SPU2 read mem %x (core %d, register %x): %x\n",Cycles, mem, core, (omem & 0x7ff), ret); + SPU2writeLog("read", rmem, ret); + } + } + + return ret; +} + +void SPU2write(u32 rmem, u16 value) +{ +#ifdef S2R_ENABLE + if (!replay_mode) + s2r_writereg(Cycles, rmem, value); +#endif + + // Note: Reverb/Effects are very sensitive to having precise update timings. + // If the SPU2 isn't in in sync with the IOP, samples can end up playing at rather + // incorrect pitches and loop lengths. + + if (cyclePtr != NULL) + TimeUpdate(*cyclePtr); + + if (rmem >> 16 == 0x1f80) + Cores[0].WriteRegPS1(rmem, value); + else { + SPU2writeLog("write", rmem, value); + SPU2_FastWrite(rmem, value); + } +} + +// if start is 1, starts recording spu2 data, else stops +// returns a non zero value if successful +// for now, pData is not used +int SPU2setupRecording(int start, std::wstring* filename) +{ + if (start == 0) + RecordStop(); + else if (start == 1) + RecordStart(filename); + + return 0; +} + +s32 SPU2freeze(int mode, freezeData *data) +{ + pxAssume(data != NULL); + if (!data) { + printf("SPU2-X savestate null pointer!\n"); + return -1; + } + + if (mode == FREEZE_SIZE) { + data->size = Savestate::SizeIt(); + return 0; + } + + pxAssume(mode == FREEZE_LOAD || mode == FREEZE_SAVE); + + if (data->data == NULL) { + printf("SPU2-X savestate null pointer!\n"); + return -1; + } + + Savestate::DataBlock &spud = (Savestate::DataBlock &)*(data->data); + + switch (mode) { + case FREEZE_LOAD: + return Savestate::ThawIt(spud); + case FREEZE_SAVE: + return Savestate::FreezeIt(spud); + + jNO_DEFAULT; + } + + // technically unreachable, but kills a warning: + return 0; +} diff --git a/pcsx2/SPU2/spu2.h b/pcsx2/SPU2/spu2.h new file mode 100644 index 0000000000..4eabc8a0e4 --- /dev/null +++ b/pcsx2/SPU2/spu2.h @@ -0,0 +1,69 @@ +/* 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] + * + * 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 . + */ + +#pragma once + +#include "Pcsx2Defs.h" + +s32 SPU2init(); +s32 SPU2reset(); +s32 SPU2ps1reset(); +s32 SPU2open(void *pDsp); +void SPU2close(); +void SPU2shutdown(); +void SPU2write(u32 mem, u16 value); +u16 SPU2read(u32 mem); + +// extended funcs +// if start is 1, starts recording spu2 data, else stops +// returns a non zero value if successful +// for now, pData is not used +int SPU2setupRecording(int start, std::wstring* filename); + +void SPU2setClockPtr(u32 *ptr); + +void SPU2async(u32 cycles); +s32 SPU2freeze(int mode, freezeData *data); +void SPU2configure(); +void SPU2about(); +s32 SPU2test(); + +#include "Spu2replay.h" + +extern u8 callirq; + +extern void (*_irqcallback)(); + +extern void (*dma4callback)(); +extern void (*dma7callback)(); + +extern s16 *input_data; +extern u32 input_data_ptr; + +extern double srate_pv; + +extern int recording; +extern u32 lClocks; +extern u32 *cyclePtr; + +extern void SPU2writeLog(const char *action, u32 rmem, u16 value); +extern void TimeUpdate(u32 cClocks); +extern void SPU2_FastWrite(u32 rmem, u16 value); + +extern void LowPassFilterInit(); + +//#define PCM24_S1_INTERLEAVE diff --git a/pcsx2/SPU2/spu2replay.cpp b/pcsx2/SPU2/spu2replay.cpp new file mode 100644 index 0000000000..2b5d62b61a --- /dev/null +++ b/pcsx2/SPU2/spu2replay.cpp @@ -0,0 +1,321 @@ +//GiGaHeRz's SPU2 Driver +//Copyright (c) 2003-2008, David Quintana +// +//This library 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 Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This library 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 this library; if not, write to the Free Software +//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +// + +#include "Global.h" +#include "PS2E-spu2.h" + +#ifdef _MSC_VER +#include "Windows.h" +#endif + +FILE *s2rfile; + +void s2r_write16(s16 data) +{ + fwrite(&data, 2, 1, s2rfile); +} + +void s2r_write32(u32 data) +{ + fwrite(&data, 4, 1, s2rfile); +} + +static void EMITC(u32 i, u32 a) +{ + s2r_write32(((i & 0x7u) << 29u) | (a & 0x1FFFFFFFu)); +} + +int s2r_open(u32 ticks, char *filename) +{ + s2rfile = fopen(filename, "wb"); + if (s2rfile) + s2r_write32(ticks); + return s2rfile ? 0 : -1; +} + +void s2r_readreg(u32 ticks, u32 addr) +{ + if (!s2rfile) + return; + s2r_write32(ticks); + EMITC(0, addr); +} + +void s2r_writereg(u32 ticks, u32 addr, s16 value) +{ + if (!s2rfile) + return; + s2r_write32(ticks); + EMITC(1, addr); + s2r_write16(value); +} + +void s2r_writedma4(u32 ticks, u16 *data, u32 len) +{ + u32 i; + if (!s2rfile) + return; + s2r_write32(ticks); + EMITC(2, len); + for (i = 0; i < len; i++, data++) + s2r_write16(*data); +} + +void s2r_writedma7(u32 ticks, u16 *data, u32 len) +{ + u32 i; + if (!s2rfile) + return; + s2r_write32(ticks); + EMITC(3, len); + for (i = 0; i < len; i++, data++) + s2r_write16(*data); +} + +void s2r_close() +{ + if (!s2rfile) + return; + fclose(s2rfile); +} + +/////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////// +// replay code + +bool replay_mode = false; + +u16 dmabuffer[0xFFFFF]; + +const u32 IOP_CLK = 768 * 48000; +const u32 IOPCiclesPerMS = 768 * 48; +u32 CurrentIOPCycle = 0; + +u64 HighResFreq; +u64 HighResPrev; +double HighResScale; + +bool Running = false; + +#ifdef _MSC_VER + +int conprintf(const char *fmt, ...) +{ +#ifdef _WIN32 + char s[1024]; + va_list list; + + va_start(list, fmt); + vsprintf(s, fmt, list); + va_end(list); + + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (handle == INVALID_HANDLE_VALUE) + return 0; + + DWORD written = 0; + WriteConsoleA(handle, s, strlen(s), &written, 0); + FlushFileBuffers(handle); + + return written; +#else + va_list list; + va_start(list, fmt); + int ret = vsprintf(stderr, fmt, list); + va_end(list); + return ret; +#endif +} + +void dummy1() +{ +} + +void dummy4() +{ + SPU2interruptDMA4(); +} + +void dummy7() +{ + SPU2interruptDMA7(); +} + +u64 HighResFrequency() +{ + u64 freq; +#ifdef _WIN32 + QueryPerformanceFrequency((LARGE_INTEGER *)&freq); +#else +// TODO +#endif + return freq; +} + +u64 HighResCounter() +{ + u64 time; +#ifdef _WIN32 + QueryPerformanceCounter((LARGE_INTEGER *)&time); +#else +// TODO +#endif + return time; +} + +void InitWaitSync() // not extremely accurate but enough. +{ + HighResFreq = HighResFrequency(); + HighResPrev = HighResCounter(); + HighResScale = (double)HighResFreq / (double)IOP_CLK; +} + +u32 WaitSync(u32 TargetCycle) +{ + u32 WaitCycles = (TargetCycle - CurrentIOPCycle); + u32 WaitTime = WaitCycles / IOPCiclesPerMS; + if (WaitTime > 10) + WaitTime = 10; + if (WaitTime == 0) + WaitTime = 1; + SleepEx(WaitTime, TRUE); + + // Refresh current time after sleeping + u64 Current = HighResCounter(); + u32 delta = (u32)floor((Current - HighResPrev) / HighResScale + 0.5); // We lose some precision here, cycles might drift away over long periods of time ;P + + // Calculate time delta + CurrentIOPCycle += delta; + HighResPrev += (u64)floor(delta * HighResScale + 0.5); // Trying to compensate drifting mentioned above, not necessarily useful. + + return delta; +} + +#ifdef _WIN32 +BOOL WINAPI HandlerRoutine(DWORD dwCtrlType) +{ + Running = false; + return TRUE; +} +#endif + +#include "Windows/Dialogs.h" +EXPORT_C_(void) +s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow) +{ + int events = 0; + + Running = true; + +#ifdef _WIN32 + AllocConsole(); + SetConsoleCtrlHandler(HandlerRoutine, TRUE); + + conprintf("Playing %s file on %x...", filename, hwnd); +#endif + + // load file + FILE *file = fopen(filename, "rb"); + + if (!file) { + conprintf("Could not open the replay file."); + return; + } +// if successful, init the plugin + +#define TryRead(dest, size, count, file) \ + if (fread(dest, size, count, file) < count) { \ + conprintf("Error reading from file."); \ + goto Finish; /* Need to exit the while() loop and maybe also the switch */ \ + } + + TryRead(&CurrentIOPCycle, 4, 1, file); + + replay_mode = true; + + InitWaitSync(); // Initialize the WaitSync stuff + + SPU2init(); + SPU2irqCallback(dummy1, dummy4, dummy7); + SPU2setClockPtr(&CurrentIOPCycle); + SPU2open(&hwnd); + + CurrentIOPCycle = 0; + + SPU2async(0); + + while (!feof(file) && Running) { + u32 ccycle = 0; + u32 evid = 0; + u32 sval = 0; + u32 tval = 0; + + TryRead(&ccycle, 4, 1, file); + TryRead(&sval, 4, 1, file); + + evid = sval >> 29; + sval &= 0x1FFFFFFF; + + u32 TargetCycle = ccycle * 768; + + while (TargetCycle > CurrentIOPCycle) { + u32 delta = WaitSync(TargetCycle); + SPU2async(delta); + } + + switch (evid) { + case 0: + SPU2read(sval); + break; + case 1: + TryRead(&tval, 2, 1, file); + SPU2write(sval, tval); + break; + case 2: + TryRead(dmabuffer, sval, 2, file); + SPU2writeDMA4Mem(dmabuffer, sval); + break; + case 3: + TryRead(dmabuffer, sval, 2, file); + SPU2writeDMA7Mem(dmabuffer, sval); + break; + default: + // not implemented + return; + break; + } + events++; + } + +Finish: + + //shutdown + SPU2close(); + SPU2shutdown(); + fclose(file); + + conprintf("Finished playing %s file (%d cycles, %d events).", filename, CurrentIOPCycle, events); + +#ifdef _WIN32 + FreeConsole(); +#endif + + replay_mode = false; +} +#endif diff --git a/pcsx2/SPU2/spu2replay.h b/pcsx2/SPU2/spu2replay.h new file mode 100644 index 0000000000..af62856c9e --- /dev/null +++ b/pcsx2/SPU2/spu2replay.h @@ -0,0 +1,31 @@ +//GiGaHeRz's SPU2 Driver +//Copyright (c) 2003-2008, David Quintana +// +//This library 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 Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This library 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 this library; if not, write to the Free Software +//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +// + +#pragma once + +//#define S2R_ENABLE + +// s2r dumping +int s2r_open(u32 ticks, char *filename); +void s2r_readreg(u32 ticks, u32 addr); +void s2r_writereg(u32 ticks, u32 addr, s16 value); +void s2r_writedma4(u32 ticks, u16 *data, u32 len); +void s2r_writedma7(u32 ticks, u16 *data, u32 len); +void s2r_close(); + +extern bool replay_mode;