/* 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 #include #include "../types.h" struct StereoOut16; struct StereoOutFloat; struct StereoOut32 { static StereoOut32 Empty; int32_t Left, 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(this->Left + right.Left, this->Right + right.Right); } StereoOut32 operator/(int src) const { return StereoOut32(this->Left / src, this->Right / src); } }; // Number of stereo samples per SndOut block. // All drivers must work in units of this size when communicating with // SndOut. const int SndOutPacketSize = 512; // edit - zeromus 23-oct-2009 // this is hardcoded differently for metaspu 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 //const int SampleRate = 48000; //const int SampleRate = 44100; // edit - nitsuja: make it use the global sample rate define #include "../SPU.h" const int SampleRate = DESMUME_SAMPLE_RATE; struct StereoOut16 { int16_t Left, Right; StereoOut16() : Left(0), Right(0) { } StereoOut16(const StereoOut32 &src) : Left(src.Left & 0xFFFF), Right(src.Right & 0xFFFF) { } StereoOut16(int16_t left, int16_t right) : Left(left), Right(right) { } void ResampleFrom(const StereoOut32 &src) { // Use StereoOut32's built in conversion *this = src.DownSample(); } }; struct StereoOutFloat { float Left, 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) { } }; // 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 std::unique_ptr sndTempBuffer; static int sndTempProgress; static std::unique_ptr 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 void _InitFail(); static void _WriteSamples(StereoOut32 *bData, int nSamples); static bool CheckUnderrunStatus(int &nSamples, int &quietSampleCount); static void soundtouchInit(); 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 Write(const StereoOut32 &Sample); // 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. int endPos = m_rpos + nSamples; 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)); } };