/* 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 "Mixer.h" #include "SndOut.h" // -------------------------------------------------------------------------------------- // SPU2 Memory Indexers // -------------------------------------------------------------------------------------- #define spu2Rs16(mmem) (*(s16 *)((s8 *)spu2regs + ((mmem)&0x1fff))) #define spu2Ru16(mmem) (*(u16 *)((s8 *)spu2regs + ((mmem)&0x1fff))) extern s16 *GetMemPtr(u32 addr); extern s16 spu2M_Read(u32 addr); extern void spu2M_Write(u32 addr, s16 value); extern void spu2M_Write(u32 addr, u16 value); struct V_VolumeLR { static V_VolumeLR Max; s32 Left; s32 Right; V_VolumeLR() = default; V_VolumeLR(s32 both) : Left(both) , Right(both) { } void DebugDump(FILE *dump, const char *title); }; struct V_VolumeSlide { // Holds the "original" value of the volume for this voice, prior to slides. // (ie, the volume as written to the register) s16 Reg_VOL; s32 Value; s8 Increment; s8 Mode; public: V_VolumeSlide() = default; V_VolumeSlide(s16 regval, s32 fullvol) : Reg_VOL(regval) , Value(fullvol) , Increment(0) , Mode(0) { } void Update(); void RegSet(u16 src); // used to set the volume from a register source (16 bit signed) void DebugDump(FILE *dump, const char *title, const char *nameLR); }; struct V_VolumeSlideLR { static V_VolumeSlideLR Max; V_VolumeSlide Left; V_VolumeSlide Right; public: V_VolumeSlideLR() = default; V_VolumeSlideLR(s16 regval, s32 bothval) : Left(regval, bothval) , Right(regval, bothval) { } void Update() { Left.Update(); Right.Update(); } void DebugDump(FILE *dump, const char *title); }; struct V_ADSR { union { u32 reg32; struct { u16 regADSR1; u16 regADSR2; }; struct { u32 SustainLevel : 4, DecayRate : 4, AttackRate : 7, AttackMode : 1, // 0 for linear (+lin), 1 for pseudo exponential (+exp) ReleaseRate : 5, ReleaseMode : 1, // 0 for linear (-lin), 1 for exponential (-exp) SustainRate : 7, SustainMode : 3; // 0 = +lin, 1 = -lin, 2 = +exp, 3 = -exp }; }; s32 Value; // Ranges from 0 to 0x7fffffff (signed values are clamped to 0) [Reg_ENVX] u8 Phase; // monitors current phase of ADSR envelope bool Releasing; // Ready To Release, triggered by Voice.Stop(); public: bool Calculate(); }; struct V_Voice { u32 PlayCycle; // SPU2 cycle where the Playing started V_VolumeSlideLR Volume; // Envelope V_ADSR ADSR; // Pitch (also Reg_PITCH) u16 Pitch; // Loop Start address (also Reg_LSAH/L) u32 LoopStartA; // Sound Start address (also Reg_SSAH/L) u32 StartA; // Next Read Data address (also Reg_NAXH/L) u32 NextA; // Voice Decoding State s32 Prev1; s32 Prev2; // Pitch Modulated by previous voice bool Modulated; // Source (Wave/Noise) bool Noise; s8 LoopMode; s8 LoopFlags; // Sample pointer (19:12 bit fixed point) s32 SP; // Sample pointer for Cubic Interpolation // Cubic interpolation mixes a sample behind Linear, so that it // can have sample data to either side of the end points from which // to extrapolate. This SP represents that late sample position. s32 SPc; // Previous sample values - used for interpolation // Inverted order of these members to match the access order in the // code (might improve cache hits). s32 PV4; s32 PV3; s32 PV2; s32 PV1; // Last outputted audio value, used for voice modulation. s32 OutX; s32 NextCrest; // temp value for Crest calculation // SBuffer now points directly to an ADPCM cache entry. s16 *SBuffer; // sample position within the current decoded packet. s32 SCurrent; // it takes a few ticks for voices to start on the real SPU2? void QueueStart(); bool Start(); void Stop(); }; // ** Begin Debug-only variables section ** // Separated from the V_Voice struct to improve cache performance of // the Public Release build. struct V_VoiceDebug { s8 FirstBlock; s32 SampleData; s32 PeakX; s32 displayPeak; s32 lastSetStartA; }; struct V_CoreDebug { V_VoiceDebug Voices[24]; // Last Transfer Size u32 lastsize; // draw adma waveform in the visual debugger s32 admaWaveformL[0x100]; s32 admaWaveformR[0x100]; // Enabled when a dma write starts, disabled when the visual debugger showed it once s32 dmaFlag; }; // Debug tracking information - 24 voices and 2 cores. extern V_CoreDebug DebugCores[2]; struct V_Reverb { s16 IN_COEF_L; s16 IN_COEF_R; u32 APF1_SIZE; u32 APF2_SIZE; s16 APF1_VOL; s16 APF2_VOL; u32 SAME_L_SRC; u32 SAME_R_SRC; u32 DIFF_L_SRC; u32 DIFF_R_SRC; u32 SAME_L_DST; u32 SAME_R_DST; u32 DIFF_L_DST; u32 DIFF_R_DST; s16 IIR_VOL; s16 WALL_VOL; u32 COMB1_L_SRC; u32 COMB1_R_SRC; u32 COMB2_L_SRC; u32 COMB2_R_SRC; u32 COMB3_L_SRC; u32 COMB3_R_SRC; u32 COMB4_L_SRC; u32 COMB4_R_SRC; s16 COMB1_VOL; s16 COMB2_VOL; s16 COMB3_VOL; s16 COMB4_VOL; u32 APF1_L_DST; u32 APF1_R_DST; u32 APF2_L_DST; u32 APF2_R_DST; }; struct V_ReverbBuffers { s32 SAME_L_SRC; s32 SAME_R_SRC; s32 DIFF_R_SRC; s32 DIFF_L_SRC; s32 SAME_L_DST; s32 SAME_R_DST; s32 DIFF_L_DST; s32 DIFF_R_DST; s32 COMB1_L_SRC; s32 COMB1_R_SRC; s32 COMB2_L_SRC; s32 COMB2_R_SRC; s32 COMB3_L_SRC; s32 COMB3_R_SRC; s32 COMB4_L_SRC; s32 COMB4_R_SRC; s32 APF1_L_DST; s32 APF1_R_DST; s32 APF2_L_DST; s32 APF2_R_DST; s32 SAME_L_PRV; s32 SAME_R_PRV; s32 DIFF_L_PRV; s32 DIFF_R_PRV; s32 APF1_L_SRC; s32 APF1_R_SRC; s32 APF2_L_SRC; s32 APF2_R_SRC; bool NeedsUpdated; }; struct V_SPDIF { u16 Out; u16 Info; u16 Unknown1; u16 Mode; u16 Media; u16 Unknown2; u16 Protection; }; struct V_CoreRegs { u32 PMON; u32 NON; u32 VMIXL; u32 VMIXR; u32 VMIXEL; u32 VMIXER; u32 ENDX; u16 MMIX; u16 STATX; u16 ATTR; u16 _1AC; }; struct V_VoiceGates { s16 DryL; // 'AND Gate' for Direct Output to Left Channel s16 DryR; // 'AND Gate' for Direct Output for Right Channel s16 WetL; // 'AND Gate' for Effect Output for Left Channel s16 WetR; // 'AND Gate' for Effect Output for Right Channel }; struct V_CoreGates { union { u128 v128; struct { s16 InpL; // Sound Data Input to Direct Output (Left) s16 InpR; // Sound Data Input to Direct Output (Right) s16 SndL; // Voice Data to Direct Output (Left) s16 SndR; // Voice Data to Direct Output (Right) s16 ExtL; // External Input to Direct Output (Left) s16 ExtR; // External Input to Direct Output (Right) }; }; }; struct VoiceMixSet { static const VoiceMixSet Empty; StereoOut32 Dry, Wet; VoiceMixSet() {} VoiceMixSet(const StereoOut32 &dry, const StereoOut32 &wet) : Dry(dry) , Wet(wet) { } }; struct V_Core { static const uint NumVoices = 24; int Index; // Core index identifier. // Voice Gates -- These are SSE-related values, and must always be // first to ensure 16 byte alignment V_VoiceGates VoiceGates[NumVoices]; V_CoreGates DryGate; V_CoreGates WetGate; V_VolumeSlideLR MasterVol; // Master Volume V_VolumeLR ExtVol; // Volume for External Data Input V_VolumeLR InpVol; // Volume for Sound Data Input V_VolumeLR FxVol; // Volume for Output from Effects V_Voice Voices[NumVoices]; u32 IRQA; // Interrupt Address u32 TSA; // DMA Transfer Start Address bool IRQEnable; // Interrupt Enable bool FxEnable; // Effect Enable bool Mute; // Mute bool AdmaInProgress; s8 DMABits; // DMA related? s8 NoiseClk; // Noise Clock u16 AutoDMACtrl; // AutoDMA Status s32 DMAICounter; // DMA Interrupt Counter u32 InputDataLeft; // Input Buffer u32 InputPosRead; u32 InputPosWrite; u32 InputDataProgress; V_Reverb Revb; // Reverb Registers V_ReverbBuffers RevBuffers; // buffer pointers for reverb, pre-calculated and pre-clipped. u32 EffectsStartA; u32 EffectsEndA; u32 ExtEffectsStartA; u32 ExtEffectsEndA; u32 ReverbX; // Current size of and position of the effects buffer. Pre-caculated when the effects start // or end position registers are written. CAN BE NEGATIVE OR ZERO, in which // case reverb should be disabled. s32 EffectsBufferSize; u32 EffectsBufferStart; V_CoreRegs Regs; // Registers // Preserves the channel processed last cycle StereoOut32 LastEffect; u8 CoreEnabled; u8 AttrBit0; u8 DmaMode; // new dma only bool DmaStarted; u32 AutoDmaFree; // old dma only u16 *DMAPtr; u32 MADR; u32 TADR; u32 KeyOn; // not the KON register (though maybe it is) // psxmode caches u16 psxSoundDataTransferControl; u16 psxSPUSTAT; // HACK -- This is a temp buffer which is (or isn't?) used to circumvent some memory // corruption that originates elsewhere in the plugin. >_< The actual ADMA buffer // is an area mapped to SPU2 main memory. //s16 ADMATempBuffer[0x1000]; // ---------------------------------------------------------------------------------- // V_Core Methods // ---------------------------------------------------------------------------------- // uninitialized constructor V_Core() : Index(-1) , DMAPtr(NULL) { } V_Core(int idx); // our badass constructor ~V_Core() throw(); void Init(int index); void UpdateEffectsBufferSize(); void AnalyzeReverbPreset(); s32 EffectsBufferIndexer(s32 offset) const; void WriteRegPS1(u32 mem, u16 value); u16 ReadRegPS1(u32 mem); // -------------------------------------------------------------------------------------- // Mixer Section // -------------------------------------------------------------------------------------- StereoOut32 Mix(const VoiceMixSet &inVoices, const StereoOut32 &Input, const StereoOut32 &Ext); void Reverb_AdvanceBuffer(); StereoOut32 DoReverb(const StereoOut32 &Input); s32 RevbGetIndexer(s32 offset); StereoOut32 ReadInput(); StereoOut32 ReadInput_HiFi(); // -------------------------------------------------------------------------- // DMA Section // -------------------------------------------------------------------------- // Returns the index of the DMA channel (4 for Core 0, or 7 for Core 1) int GetDmaIndex() const { return (Index == 0) ? 4 : 7; } // returns either '4' or '7' char GetDmaIndexChar() const { return 0x30 + GetDmaIndex(); } __forceinline u16 DmaRead() { const u16 ret = (u16)spu2M_Read(TSA); ++TSA; TSA &= 0xfffff; return ret; } __forceinline void DmaWrite(u16 value) { spu2M_Write(TSA, value); ++TSA; TSA &= 0xfffff; } void LogAutoDMA(FILE *fp); s32 NewDmaRead(u32 *data, u32 bytesLeft, u32 *bytesProcessed); s32 NewDmaWrite(u32 *data, u32 bytesLeft, u32 *bytesProcessed); void NewDmaInterrupt(); // old dma only void DoDMAwrite(u16 *pMem, u32 size); void DoDMAread(u16 *pMem, u32 size); void AutoDMAReadBuffer(int mode); void StartADMAWrite(u16 *pMem, u32 sz); void PlainDMAWrite(u16 *pMem, u32 sz); }; extern V_Core Cores[2]; extern V_SPDIF Spdif; // Output Buffer Writing Position (the same for all data); extern s16 OutPos; // Input Buffer Reading Position (the same for all data); extern s16 InputPos; // SPU Mixing Cycles ("Ticks mixed" counter) extern u32 Cycles; extern s16 *spu2regs; extern s16 *_spu2mem; extern int PlayMode; extern void SetIrqCall(int core); extern void StartVoices(int core, u32 value); extern void StopVoices(int core, u32 value); extern void InitADSR(); extern void CalculateADSR(V_Voice &vc); extern void UpdateSpdifMode(); namespace Savestate { struct DataBlock; extern s32 __fastcall FreezeIt(DataBlock &spud); extern s32 __fastcall ThawIt(DataBlock &spud); extern s32 __fastcall SizeIt(); } // -------------------------------------------------------------------------------------- // ADPCM Decoder Cache // -------------------------------------------------------------------------------------- // The SPU2 has a dynamic memory range which is used for several internal operations, such as // registers, CORE 1/2 mixing, AutoDMAs, and some other fancy stuff. We exclude this range // from the cache here: static const s32 SPU2_DYN_MEMLINE = 0x2800; // 8 short words per encoded PCM block. (as stored in SPU2 ram) static const int pcm_WordsPerBlock = 8; // number of cachable ADPCM blocks (any blocks above the SPU2_DYN_MEMLINE) static const int pcm_BlockCount = 0x100000 / pcm_WordsPerBlock; // 28 samples per decoded PCM block (as stored in our cache) static const int pcm_DecodedSamplesPerBlock = 28; struct PcmCacheEntry { bool Validated; s16 Sampledata[pcm_DecodedSamplesPerBlock]; }; extern PcmCacheEntry *pcm_cache_data;