/* 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