/* 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 struct StereoOut16; //struct StereoOut32; struct StereoOutFloat; struct StereoOut32 { static StereoOut32 Empty; int32_t Left; int32_t Right; StereoOut32() : Left(0), Right(0) { } StereoOut32(int32_t left, int32_t right) : Left(left), Right(right) { } StereoOut32(const StereoOut16 &src); explicit StereoOut32(const StereoOutFloat &src); StereoOut16 DownSample() const; StereoOut32 operator+(const StereoOut32 &right) const { return StereoOut32(Left + right.Left, Right + right.Right); } StereoOut32 operator/(int src) const { return StereoOut32(Left / src, Right / src); } }; // Number of stereo samples per SndOut block. // All drivers must work in units of this size when communicating with // SndOut. static const int SndOutPacketSize = 512; // Overall master volume shift. // Converts the mixer's 32 bit value into a 16 bit value. //static const int SndOutVolumeShift = 13; //edit - zeromus 23-oct-2009 //this is hardcoded differently for metaspu static const int SndOutVolumeShift = 0; // Samplerate of the SPU2. For accurate playback we need to match this // exactly. Trying to scale samplerates and maintain SPU2's Ts timing accuracy // is too problematic. :) //this is hardcoded differently for metaspu //edit - zeromus 23-oct-2009 ////static const int SampleRate = 48000; //static const int SampleRate = 44100; //edit - nitsuja: make it use the global sample rate define #include "../SPU.h" static const int SampleRate = DESMUME_SAMPLE_RATE; //extern int FindOutputModuleById(const wchar_t *omodid); struct StereoOut16 { int16_t Left; int16_t Right; StereoOut16() : Left(0), Right(0) { } StereoOut16(const StereoOut32 &src) : Left(static_cast(src.Left)), Right(static_cast(src.Right)) { } StereoOut16(int16_t left, int16_t right) : Left(left), Right(right) { } //StereoOut32 UpSample() const; void ResampleFrom(const StereoOut32 &src) { // Use StereoOut32's built in conversion *this = src.DownSample(); } }; struct StereoOutFloat { float Left; float Right; StereoOutFloat() : Left(0.0f), Right(0.0f) { } explicit StereoOutFloat(const StereoOut32 &src) : Left(src.Left / 2147483647.0f), Right(src.Right / 2147483647.0f) { } explicit StereoOutFloat(int32_t left, int32_t right) : Left(left / 2147483647.0f), Right(right / 2147483647.0f) { } StereoOutFloat(float left, float right) : Left(left), Right(right) { } }; //struct Stereo21Out16 //{ // int16_t Left; // int16_t Right; // int16_t LFE; // // void ResampleFrom( const StereoOut32& src ) // { // Left = src.Left >> SndOutVolumeShift; // Right = src.Right >> SndOutVolumeShift; // LFE = (src.Left + src.Right) >> (SndOutVolumeShift + 1); // } //}; // //struct StereoQuadOut16 //{ // int16_t Left; // int16_t Right; // int16_t LeftBack; // int16_t RightBack; // // void ResampleFrom( const StereoOut32& src ) // { // Left = src.Left >> SndOutVolumeShift; // Right = src.Right >> SndOutVolumeShift; // LeftBack = src.Left >> SndOutVolumeShift; // RightBack = src.Right >> SndOutVolumeShift; // } //}; // //struct Stereo41Out16 //{ // int16_t Left; // int16_t Right; // int16_t LFE; // int16_t LeftBack; // int16_t RightBack; // // void ResampleFrom( const StereoOut32& src ) // { // Left = src.Left >> SndOutVolumeShift; // Right = src.Right >> SndOutVolumeShift; // LFE = (src.Left + src.Right) >> (SndOutVolumeShift + 1); // LeftBack = src.Left >> SndOutVolumeShift; // RightBack = src.Right >> SndOutVolumeShift; // } //}; // //struct Stereo51Out16 //{ // int16_t Left; // int16_t Right; // int16_t Center; // int16_t LFE; // int16_t LeftBack; // int16_t RightBack; // // // Implementation Note: Center and Subwoofer/LFE --> // // This method is simple and sounds nice. It relies on the speaker/soundcard // // systems do to their own low pass / crossover. Manual lowpass is wasted effort // // and can't match solid state results anyway. // // void ResampleFrom( const StereoOut32& src ) // { // Left = src.Left >> SndOutVolumeShift; // Right = src.Right >> SndOutVolumeShift; // Center = (src.Left + src.Right) >> (SndOutVolumeShift + 1); // LFE = Center; // LeftBack = src.Left >> SndOutVolumeShift; // RightBack = src.Right >> SndOutVolumeShift; // } //}; // //struct Stereo51Out16DplII //{ // int16_t Left; // int16_t Right; // int16_t Center; // int16_t LFE; // int16_t LeftBack; // int16_t RightBack; // // void ResampleFrom( const StereoOut32& src ) // { // static const u8 sLogTable[256] = { // 0x00,0x3C,0x60,0x78,0x8C,0x9C,0xA8,0xB4,0xBE,0xC8,0xD0,0xD8,0xDE,0xE4,0xEA,0xF0, // 0xF6,0xFA,0xFE,0x04,0x08,0x0C,0x10,0x14,0x16,0x1A,0x1E,0x20,0x24,0x26,0x2A,0x2C, // 0x2E,0x32,0x34,0x36,0x38,0x3A,0x3E,0x40,0x42,0x44,0x46,0x48,0x4A,0x4C,0x4E,0x50, // 0x50,0x52,0x54,0x56,0x58,0x5A,0x5A,0x5C,0x5E,0x60,0x60,0x62,0x64,0x66,0x66,0x68, // 0x6A,0x6A,0x6C,0x6E,0x6E,0x70,0x70,0x72,0x74,0x74,0x76,0x76,0x78,0x7A,0x7A,0x7C, // 0x7C,0x7E,0x7E,0x80,0x80,0x82,0x82,0x84,0x84,0x86,0x86,0x88,0x88,0x8A,0x8A,0x8C, // 0x8C,0x8C,0x8E,0x8E,0x90,0x90,0x92,0x92,0x92,0x94,0x94,0x96,0x96,0x96,0x98,0x98, // 0x9A,0x9A,0x9A,0x9C,0x9C,0x9C,0x9E,0x9E,0xA0,0xA0,0xA0,0xA2,0xA2,0xA2,0xA4,0xA4, // 0xA4,0xA6,0xA6,0xA6,0xA8,0xA8,0xA8,0xAA,0xAA,0xAA,0xAC,0xAC,0xAC,0xAC,0xAE,0xAE, // 0xAE,0xB0,0xB0,0xB0,0xB2,0xB2,0xB2,0xB2,0xB4,0xB4,0xB4,0xB6,0xB6,0xB6,0xB6,0xB8, // 0xB8,0xB8,0xB8,0xBA,0xBA,0xBA,0xBC,0xBC,0xBC,0xBC,0xBE,0xBE,0xBE,0xBE,0xC0,0xC0, // 0xC0,0xC0,0xC2,0xC2,0xC2,0xC2,0xC2,0xC4,0xC4,0xC4,0xC4,0xC6,0xC6,0xC6,0xC6,0xC8, // 0xC8,0xC8,0xC8,0xC8,0xCA,0xCA,0xCA,0xCA,0xCC,0xCC,0xCC,0xCC,0xCC,0xCE,0xCE,0xCE, // 0xCE,0xCE,0xD0,0xD0,0xD0,0xD0,0xD0,0xD2,0xD2,0xD2,0xD2,0xD2,0xD4,0xD4,0xD4,0xD4, // 0xD4,0xD6,0xD6,0xD6,0xD6,0xD6,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xDA,0xDA,0xDA,0xDA, // 0xDA,0xDC,0xDC,0xDC,0xDC,0xDC,0xDC,0xDE,0xDE,0xDE,0xDE,0xDE,0xDE,0xE0,0xE0,0xE0, // }; // // static int32_t Gfl=0,Gfr=0; // static int32_t LMax=0,RMax=0; // // static int32_t LAccum; // static int32_t RAccum; // static int32_t ANum; // // int32_t ValL = src.Left >> (SndOutVolumeShift-8); // int32_t ValR = src.Right >> (SndOutVolumeShift-8); // // int32_t XL = abs(ValL>>8); // int32_t XR = abs(ValR>>8); // // if(XL>LMax) LMax = XL; // if(XR>RMax) RMax = XR; // // ANum++; // if(ANum>=128) // { // ANum=0; // LAccum = 1+((LAccum * 224 + LMax * 31)>>8); // RAccum = 1+((RAccum * 224 + RMax * 31)>>8); // // LMax = 0; // RMax = 0; // // int32_t Tfl=(RAccum)*255/(LAccum); // int32_t Tfr=(LAccum)*255/(RAccum); // // int gMax = std::max(Tfl,Tfr); // Tfl = Tfl*255/gMax; // Tfr = Tfr*255/gMax; // // if(Tfl>255) Tfl=255; // if(Tfr>255) Tfr=255; // if(Tfl<1) Tfl=1; // if(Tfr<1) Tfr=1; // // Gfl = (Gfl * 200 + Tfl * 56)>>8; // Gfr = (Gfr * 200 + Tfr * 56)>>8; // // } // // int32_t L,R,C,SUB,SL,SR; // // C=(ValL+ValR)>>1; //16.8 // // ValL-=C;//16.8 // ValR-=C;//16.8 // // L=ValL>>8; //16.0 // R=ValR>>8; //16.0 // C=C>>8; //16.0 // SUB = C; // // { // int32_t Cfl = 1+sLogTable[Gfl]; // int32_t Cfr = 1+sLogTable[Gfr]; // // int32_t VL=(ValL>>4) * Cfl; //16.12 // int32_t VR=(ValR>>4) * Cfr; // // //int32_t SC = (VL-VR)>>15; // // SL = (((VR/148 - VL/209)>>4)*Cfr)>>8; // SR = (((VR/209 - VL/148)>>4)*Cfl)>>8; // // } // // // Random-ish values to get it to compile // int GainL = 200; // int GainR = 200; // int GainC = 180; // int GainSL = 230; // int GainSR = 230; // int GainLFE = 200; // int AddCLR = 55; // // int AddCX = (C * AddCLR)>>8; // // Left = (((L * GainL ))>>8) + AddCX; // Right = (((R * GainR ))>>8) + AddCX; // Center = (((C * GainC ))>>8); // LFE = (((SUB * GainLFE))>>8); // LeftBack = (((SL * GainSL ))>>8); // RightBack = (((SR * GainSR ))>>8); // } //}; // //struct Stereo71Out16 //{ // int16_t Left; // int16_t Right; // int16_t Center; // int16_t LFE; // int16_t LeftBack; // int16_t RightBack; // int16_t LeftSide; // int16_t RightSide; // // void ResampleFrom( const StereoOut32& src ) // { // Left = src.Left >> SndOutVolumeShift; // Right = src.Right >> SndOutVolumeShift; // Center = (src.Left + src.Right) >> (SndOutVolumeShift + 1); // LFE = Center; // LeftBack = src.Left >> SndOutVolumeShift; // RightBack = src.Right >> SndOutVolumeShift; // // LeftSide = src.Left >> (SndOutVolumeShift+1); // RightSide = src.Right >> (SndOutVolumeShift+1); // } //}; // //struct Stereo21Out32 //{ // int32_t Left; // int32_t Right; // int32_t LFE; //}; // //struct Stereo41Out32 //{ // int32_t Left; // int32_t Right; // int32_t LFE; // int32_t LeftBack; // int32_t RightBack; //}; // //struct Stereo51Out32 //{ // int32_t Left; // int32_t Right; // int32_t Center; // int32_t LFE; // int32_t LeftBack; // int32_t RightBack; //}; // Developer Note: This is a static class only (all static members). class SndBuffer { private: static bool m_underrun_freeze; static int32_t m_predictData; static float lastPct; static StereoOut32 *sndTempBuffer; static StereoOut16 *sndTempBuffer16; static int sndTempProgress; //static int m_dsp_progress; //static int m_timestretch_progress; static int m_timestretch_writepos; static StereoOut32 *m_buffer; static int32_t m_size; static int32_t m_rpos; static int32_t m_wpos; static int32_t m_data; static float lastEmergencyAdj; static float cTempo; static float eTempo; static int freezeTempo; //static int ssFreeze; static void _InitFail(); static void _WriteSamples(StereoOut32 *bData, int nSamples); static bool CheckUnderrunStatus(int &nSamples, int &quietSampleCount); static void soundtouchInit(); static void soundtouchClearContents(); static void soundtouchCleanup(); static void timeStretchWrite(); static void timeStretchUnderrun(); static int32_t timeStretchOverrun(); static void PredictDataWrite(int samples); static float GetStatusPct(); static void UpdateTempoChange(); public: static void Init(); //static void Cleanup(); static void Write(const StereoOut32 &Sample); //static int32_t Test(); //static void ClearContents(); // Note: When using with 32 bit output buffers, the user of this function is responsible // for shifting the values to where they need to be manually. The fixed point depth of // the sample output is determined by the SndOutVolumeShift, which is the number of bits // to shift right to get a 16 bit result. template static void ReadSamples(T *bData) { int nSamples = SndOutPacketSize; // Problem: // If the SPU2 gets even the least bit out of sync with the SndOut device, // the readpos of the circular buffer will overtake the writepos, // leading to a prolonged period of hopscotching read/write accesses (ie, // lots of staticy crap sound for several seconds). // // Fix: // If the read position overtakes the write position, abort the // transfer immediately and force the SndOut driver to wait until // the read buffer has filled up again before proceeding. // This will cause one brief hiccup that can never exceed the user's // set buffer length in duration. int quietSamples; if (CheckUnderrunStatus(nSamples, quietSamples)) { assert(nSamples <= SndOutPacketSize); // [Air] [TODO]: This loop is probably a candidate for SSE2 optimization. const int endPos = m_rpos + nSamples; const int secondCopyLen = endPos - m_size; const StereoOut32 *rposbuffer = &m_buffer[m_rpos]; m_data -= nSamples; if (secondCopyLen > 0) { nSamples -= secondCopyLen; for (int i = 0; i < secondCopyLen; ++i) bData[nSamples + i].ResampleFrom(m_buffer[i]); m_rpos = secondCopyLen; } else m_rpos += nSamples; for (int i = 0; i < nSamples; ++i) bData[i].ResampleFrom(rposbuffer[i]); } // If quietSamples != 0 it means we have an underrun... // Let's just dull out some silence, because that's usually the least // painful way of dealing with underruns: memset(bData, 0, quietSamples * sizeof(T)); } }; //class SndOutModule //{ //public: // // Virtual destructor, because it helps fight C+++ funny-business. // virtual ~SndOutModule() {} // // // Returns a unique identification string for this driver. // // (usually just matches the driver's cpp filename) // virtual const wchar_t* GetIdent() const=0; // // // Returns the long name / description for this driver. // // (for use in configuration screen) // virtual const wchar_t* GetLongName() const=0; // // virtual int32_t Init()=0; // virtual void Close()=0; // virtual int32_t Test() const=0; // // // Gui function: Used to open the configuration box for this driver. // virtual void Configure(uptr parent)=0; // // // Loads settings from the INI file for this driver // virtual void ReadSettings()=0; // // // Saves settings to the INI file for this driver // virtual void WriteSettings() const=0; // // virtual bool Is51Out() const=0; // // // Returns the number of empty samples in the output buffer. // // (which is effectively the amount of data played since the last update) // virtual int GetEmptySampleCount() const=0; //}; // // //#ifdef _MSC_VER ////internal //extern SndOutModule* WaveOut; //extern SndOutModule* DSoundOut; //extern SndOutModule* XAudio2Out; //#endif // //extern SndOutModule* mods[]; // //// ===================================================================================================== // //extern void RecordStart(); //extern void RecordStop(); //extern void RecordWrite( const StereoOut16& sample ); // //extern int32_t DspLoadLibrary(wchar_t *fileName, int modNum); //extern void DspCloseLibrary(); //extern int DspProcess(int16_t *buffer, int samples); //extern void DspUpdate(); // to let the Dsp process window messages