mirror of https://github.com/PCSX2/pcsx2.git
SPU2Ghz: Well, yet again another update from Jake.Stine :p This time around he improved the already great timestretcher to be even smarter. This and the fixed XAudio2 output module get even Vista users a near realtime sound. (Try a delay between 90 and 140ms).
Read a bit more about this version in issue 31 :) git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@247 a6443dda-0b58-4228-96e9-037be469359c
This commit is contained in:
parent
4da570c671
commit
88638106e6
|
@ -137,29 +137,29 @@ BEGIN
|
|||
PUSHBUTTON "Cancel",IDCANCEL,281,161,50,14
|
||||
COMBOBOX IDC_DS_DEVICE,5,17,142,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "DirectSound Device",IDC_STATIC,5,5,63,8
|
||||
CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,15,53,30,36
|
||||
CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,57,54,30,34
|
||||
CONTROL "",IDC_SLIDER3,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,99,54,30,34
|
||||
CONTROL "",IDC_LEFT_GAIN_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,15,53,30,36
|
||||
CONTROL "",IDC_CENTER_GAIN_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,57,54,30,34
|
||||
CONTROL "",IDC_RIGHT_GAIN_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,99,54,30,34
|
||||
GROUPBOX "Surround Volume Correction",IDC_STATIC,5,35,138,139
|
||||
CTEXT "Center",IDC_STATIC,51,46,42,8
|
||||
CTEXT "Left",IDC_STATIC,9,46,42,8
|
||||
CTEXT "Right",IDC_STATIC,93,46,42,8
|
||||
CONTROL "",IDC_SLIDER4,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,15,115,30,40
|
||||
CONTROL "",IDC_SLIDER5,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,57,115,30,40
|
||||
CONTROL "",IDC_SLIDER6,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,99,115,30,40
|
||||
CTEXT "Center",IDC_STATIC,50,46,42,8
|
||||
CTEXT "Left",IDC_STATIC,8,46,42,8
|
||||
CTEXT "Right",IDC_STATIC,92,46,42,8
|
||||
CONTROL "",IDC_RLEFT_GAIN_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,15,115,30,40
|
||||
CONTROL "",IDC_LFE_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,57,115,30,40
|
||||
CONTROL "",IDC_RRIGHT_GAIN_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,99,115,30,40
|
||||
CTEXT "LFE (sub)",IDC_STATIC,51,107,42,8
|
||||
CTEXT "Rear Left",IDC_STATIC,9,107,42,8
|
||||
CTEXT "Rear Right",IDC_STATIC,93,107,42,8
|
||||
GROUPBOX "Other Tweaks",IDC_STATIC,153,77,63,97
|
||||
CONTROL "",IDC_SLIDER7,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,169,98,30,49
|
||||
CONTROL "",IDC_LR_CENTER_SLIDER,"msctls_trackbar32",TBS_VERT | TBS_BOTH | WS_TABSTOP,169,99,30,49
|
||||
CTEXT "Center in LR",IDC_STATIC,163,90,42,8
|
||||
EDITTEXT IDC_EDIT1,15,88,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_EDIT2,57,88,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_EDIT3,99,88,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_EDIT4,15,155,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_EDIT5,57,155,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_EDIT6,99,155,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_EDIT7,169,150,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_LEFT_GAIN_EDIT,15,88,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_CENTER_GAIN_EDIT,57,88,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_RIGHT_GAIN_EDIT,99,88,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_RLEFT_GAIN_EDIT,15,155,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_LFE_EDIT,57,155,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_RRIGHT_GAIN_EDIT,99,155,30,14,ES_AUTOHSCROLL
|
||||
EDITTEXT IDC_LR_CENTER_EDIT,169,151,30,14,ES_AUTOHSCROLL
|
||||
LTEXT "Number of Buffers",IDC_STATIC,160,32,61,11
|
||||
CONTROL "",IDC_BUFFERS_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,229,40,94,10
|
||||
LTEXT "Use extra buffers if you are experiencing loopy or studdery audio even when games run at high FPS.",IDC_STATIC,226,69,102,46
|
||||
|
|
|
@ -61,11 +61,11 @@ namespace soundtouch
|
|||
/// and vice versa.
|
||||
///
|
||||
/// Increasing this value reduces computational burden & vice versa.
|
||||
#define DEFAULT_SEQUENCE_MS 40
|
||||
#define DEFAULT_SEQUENCE_MS 54
|
||||
|
||||
#define DEFAULT_SEEKWINDOW_MS 30
|
||||
#define DEFAULT_SEEKWINDOW_MS 15
|
||||
|
||||
#define DEFAULT_OVERLAP_MS 20
|
||||
#define DEFAULT_OVERLAP_MS 6
|
||||
|
||||
|
||||
/// Class that does the time-stretch (tempo change) effect for the processed
|
||||
|
@ -233,4 +233,4 @@ public:
|
|||
#endif /// ALLOW_SSE
|
||||
|
||||
}
|
||||
#endif /// TDStretch_H
|
||||
#endif /// TDStretch_H
|
||||
|
|
|
@ -183,12 +183,10 @@ int CfgReadInt(const char *Section, const char*Name,int Default) {
|
|||
|
||||
|
||||
void CfgReadStr(const char *Section, const char* Name, char *Data, int DataSize, const char *Default) {
|
||||
int sl;
|
||||
GetPrivateProfileString(Section,Name,"",Data,DataSize,CfgFile);
|
||||
|
||||
if(strlen(Data)==0) {
|
||||
sl=(int)strlen(Default);
|
||||
strncpy(Data,Default,sl>255?255:sl);
|
||||
sprintf_s( Data, DataSize, "%s", Default );
|
||||
CfgWriteStr(Section,Name,Data);
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +254,7 @@ void ReadSettings()
|
|||
);
|
||||
|
||||
SampleRate=CfgReadInt("OUTPUT","Sample_Rate",48000);
|
||||
SndOutLatencyMS=CfgReadInt("OUTPUT","Latency",140);
|
||||
SndOutLatencyMS=CfgReadInt("OUTPUT","Latency", 140);
|
||||
|
||||
//OutputModule = CfgReadInt("OUTPUT","Output_Module", OUTPUT_DSOUND );
|
||||
char omodid[128];
|
||||
|
@ -282,6 +280,8 @@ void ReadSettings()
|
|||
Config_WaveOut.NumBuffers = CfgReadInt( "WAVEOUT", "Buffer_Count", 4 );
|
||||
|
||||
// Read DSOUND51 config:
|
||||
CfgReadStr( "DSOUND51", "Device", Config_DSound51.Device, 254, "default" );
|
||||
Config_DSound51.NumBuffers = CfgReadInt( "DSOUND51", "Buffer_Count", 5 );
|
||||
Config_DSound51.GainL =CfgReadInt("DSOUND51","Channel_Gain_L", 256);
|
||||
Config_DSound51.GainR =CfgReadInt("DSOUND51","Channel_Gain_R", 256);
|
||||
Config_DSound51.GainC =CfgReadInt("DSOUND51","Channel_Gain_C", 256);
|
||||
|
@ -300,7 +300,7 @@ void ReadSettings()
|
|||
SampleRate = 48000; // Yup nothing else is supported for now.
|
||||
VolumeShiftModifier = min( max( VolumeShiftModifier, -2 ), 2 );
|
||||
SndOutVolumeShift = SndOutVolumeShiftBase - VolumeShiftModifier;
|
||||
SndOutLatencyMS = min( max( SndOutLatencyMS, 20 ), 420 );
|
||||
SndOutLatencyMS = min( max( SndOutLatencyMS, 40 ), 480 );
|
||||
|
||||
Config_DSoundOut.NumBuffers = min( max( Config_DSoundOut.NumBuffers, 2 ), 8 );
|
||||
Config_WaveOut.NumBuffers = min( max( Config_DSoundOut.NumBuffers, 3 ), 8 );
|
||||
|
@ -368,12 +368,18 @@ void WriteSettings()
|
|||
|
||||
CfgWriteInt("OUTPUT","Volume_Shift",SndOutVolumeShiftBase - SndOutVolumeShift);
|
||||
|
||||
if( strlen( Config_DSoundOut.Device ) == 0 ) strcpy( Config_DSoundOut.Device, "default" );
|
||||
if( strlen( Config_DSound51.Device ) == 0 ) strcpy( Config_DSound51.Device, "default" );
|
||||
if( strlen( Config_WaveOut.Device ) == 0 ) strcpy( Config_WaveOut.Device, "default" );
|
||||
|
||||
CfgWriteStr("DSOUNDOUT","Device",Config_DSoundOut.Device);
|
||||
CfgWriteInt("DSOUNDOUT","Buffer_Count",Config_DSoundOut.NumBuffers);
|
||||
|
||||
CfgWriteStr("WAVEOUT","Device",Config_WaveOut.Device);
|
||||
CfgWriteInt("WAVEOUT","Buffer_Count",Config_WaveOut.NumBuffers);
|
||||
|
||||
CfgWriteStr("DSOUND51","Device",Config_DSound51.Device);
|
||||
CfgWriteInt("DSOUND51","Buffer_Count", Config_DSound51.NumBuffers);
|
||||
CfgWriteInt("DSOUND51","Channel_Gain_L", Config_DSound51.GainL);
|
||||
CfgWriteInt("DSOUND51","Channel_Gain_R", Config_DSound51.GainR);
|
||||
CfgWriteInt("DSOUND51","Channel_Gain_C", Config_DSound51.GainC);
|
||||
|
@ -466,7 +472,7 @@ BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|||
}
|
||||
|
||||
//INIT_SLIDER(IDC_BUFFER,512,16384,4096,2048,512);
|
||||
INIT_SLIDER( IDC_LATENCY_SLIDER, 20, 420, 100, 20, 5 );
|
||||
INIT_SLIDER( IDC_LATENCY_SLIDER, 40, 480, 100, 20, 5 );
|
||||
|
||||
SendMessage(GetDlgItem(hWnd,IDC_LATENCY_SLIDER),TBM_SETPOS,TRUE,SndOutLatencyMS);
|
||||
sprintf_s(temp,80,"%d ms (avg)",SndOutLatencyMS);
|
||||
|
@ -514,8 +520,8 @@ BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|||
|
||||
SndOutLatencyMS = (int)SendMessage( GetDlgItem( hWnd, IDC_LATENCY_SLIDER ), TBM_GETPOS, 0, 0 );
|
||||
|
||||
if( SndOutLatencyMS > 420 ) SndOutLatencyMS = 420;
|
||||
if( SndOutLatencyMS < 20 ) SndOutLatencyMS = 20;
|
||||
if( SndOutLatencyMS > 480 ) SndOutLatencyMS = 480;
|
||||
if( SndOutLatencyMS < 40 ) SndOutLatencyMS = 40;
|
||||
|
||||
Interpolation=(int)SendMessage(GetDlgItem(hWnd,IDC_INTERPOLATE),CB_GETCURSEL,0,0);
|
||||
OutputModule=(int)SendMessage(GetDlgItem(hWnd,IDC_OUTPUT),CB_GETCURSEL,0,0);
|
||||
|
@ -579,8 +585,8 @@ BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|||
case TB_PAGEDOWN:
|
||||
wmEvent=(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
|
||||
case TB_THUMBTRACK:
|
||||
if(wmEvent<20) wmEvent=20;
|
||||
if(wmEvent>420) wmEvent=420;
|
||||
if(wmEvent<40) wmEvent=40;
|
||||
if(wmEvent>480) wmEvent=480;
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,wmEvent);
|
||||
sprintf_s(temp,80,"%d ms (avg)",wmEvent);
|
||||
SetDlgItemText(hWnd,IDC_LATENCY_LABEL,temp);
|
||||
|
|
|
@ -38,10 +38,12 @@ struct ds_device_data {
|
|||
char name[256];
|
||||
GUID guid;
|
||||
bool hasGuid;
|
||||
} extern devices[];
|
||||
extern int ndevs;
|
||||
extern GUID DevGuid;
|
||||
extern bool haveGuid;
|
||||
};
|
||||
|
||||
static ds_device_data devices[32];
|
||||
static int ndevs;
|
||||
static GUID DevGuid; // currently employed GUID.
|
||||
static bool haveGuid;
|
||||
|
||||
extern HRESULT GUIDFromString(const char *str, LPGUID guid);
|
||||
|
||||
|
@ -50,15 +52,16 @@ class DSound51: public SndOutModule
|
|||
private:
|
||||
# define MAX_BUFFER_COUNT 8
|
||||
|
||||
static const int PacketsPerBuffer = (1024 / SndOutPacketSize);
|
||||
static const int BufferSize = SndOutPacketSize*PacketsPerBuffer * 6;
|
||||
static const int PacketsPerBuffer = 1;
|
||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer * 3;
|
||||
static const int BufferSizeBytes = BufferSize << 1;
|
||||
|
||||
s32* tbuffer;
|
||||
|
||||
FILE *voicelog;
|
||||
|
||||
u32 numBuffers; // cached copy of our configuration setting.
|
||||
int channel;
|
||||
int myLastWrite;
|
||||
|
||||
bool dsound_running;
|
||||
HANDLE thread;
|
||||
|
@ -142,16 +145,19 @@ private:
|
|||
}
|
||||
#else
|
||||
|
||||
if((ValL>>8)>LMax) LMax = (ValL>>8);
|
||||
if(-(ValL>>8)>LMax) LMax = -(ValL>>8);
|
||||
if((ValR>>8)>RMax) RMax = (ValR>>8);
|
||||
if(-(ValR>>8)>RMax) RMax = -(ValR>>8);
|
||||
if(ValL>LMax) LMax = ValL;
|
||||
if(-ValL>LMax) LMax = -ValL;
|
||||
if(ValR>RMax) RMax = ValR;
|
||||
if(-ValR>RMax) RMax = -ValR;
|
||||
ANum++;
|
||||
if(ANum>=128)
|
||||
{
|
||||
// shift into a 21 bit value
|
||||
const u8 shift = SndOutVolumeShift-5;
|
||||
|
||||
ANum=0;
|
||||
LAccum = (LAccum * 224 + LMax * 32)>>8;
|
||||
RAccum = (RAccum * 224 + RMax * 32)>>8;
|
||||
LAccum = ((LAccum * 224) + (LMax>>shift))>>8;
|
||||
RAccum = ((RAccum * 224) + (RMax>>shift))>>8;
|
||||
|
||||
LMax=0;
|
||||
RMax=0;
|
||||
|
@ -174,11 +180,17 @@ private:
|
|||
}
|
||||
#endif
|
||||
|
||||
Gfr = 1; Gfl = 1;
|
||||
|
||||
s32 L,R,C,LFE,SL,SR,LL,LR;
|
||||
|
||||
extern double pow_2_31;
|
||||
LL = (s32)(LPF(&lpf_l,(ValL>>4)/pow_2_31)*pow_2_31);
|
||||
LR = (s32)(LPF(&lpf_r,(ValR>>4)/pow_2_31)*pow_2_31);
|
||||
|
||||
// shift Values into 12 bits:
|
||||
u8 shift2 = SndOutVolumeShift + 4;
|
||||
|
||||
LL = (s32)(LPF(&lpf_l,(ValL>>shift2)/pow_2_31)*pow_2_31);
|
||||
LR = (s32)(LPF(&lpf_r,(ValR>>shift2)/pow_2_31)*pow_2_31);
|
||||
LFE = (LL + LR)>>4;
|
||||
|
||||
C=(ValL+ValR)>>1; //16.8
|
||||
|
@ -186,9 +198,9 @@ private:
|
|||
ValL-=C;//16.8
|
||||
ValR-=C;//16.8
|
||||
|
||||
L=ValL>>8; //16.0
|
||||
R=ValR>>8; //16.0
|
||||
C=C>>8; //16.0
|
||||
L=ValL>>SndOutVolumeShift; //16.0
|
||||
R=ValR>>SndOutVolumeShift; //16.0
|
||||
C=C>>SndOutVolumeShift; //16.0
|
||||
|
||||
s32 VL=(ValL>>4) * Gfl; //16.12
|
||||
s32 VR=(ValR>>4) * Gfr;
|
||||
|
@ -243,7 +255,7 @@ private:
|
|||
{
|
||||
while( dsound_running )
|
||||
{
|
||||
u32 rv = WaitForMultipleObjects(Config_DSound51.NumBuffers,buffer_events,FALSE,200);
|
||||
u32 rv = WaitForMultipleObjects(numBuffers,buffer_events,FALSE,200);
|
||||
|
||||
s16* p1, *oldp1;
|
||||
LPVOID p2;
|
||||
|
@ -251,7 +263,15 @@ private:
|
|||
|
||||
u32 poffset=BufferSizeBytes * rv;
|
||||
|
||||
#ifdef _DEBUG
|
||||
verifyc(buffer->Lock(poffset,BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0));
|
||||
#else
|
||||
if( FAILED(buffer->Lock(poffset,BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0) ) )
|
||||
{
|
||||
fputs( " * SPU2 : Directsound Warning > Buffer lock failure. You may need to increase the DSound buffer count.\n", stderr );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
oldp1 = p1;
|
||||
|
||||
for(int p=0; p<PacketsPerBuffer; p++, p1+=SndOutPacketSize )
|
||||
|
@ -271,7 +291,15 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef PUBLIC
|
||||
verifyc(buffer->Unlock(oldp1,s1,p2,s2));
|
||||
#else
|
||||
buffer->Unlock(oldp1,s1,p2,s2);
|
||||
#endif
|
||||
verifyc(buffer->Unlock(oldp1,s1,p2,s2));
|
||||
|
||||
// Set the write pointer to the beginning of the next block.
|
||||
myLastWrite = (poffset + BufferSizeBytes) % (BufferSizeBytes*numBuffers);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -280,11 +308,25 @@ public:
|
|||
s32 Init(SndBuffer *sb)
|
||||
{
|
||||
buff = sb;
|
||||
numBuffers = Config_DSound51.NumBuffers;
|
||||
|
||||
//
|
||||
// Initialize DSound
|
||||
//
|
||||
verifyc(DirectSoundCreate8(NULL,&dsound,NULL));
|
||||
GUID cGuid;
|
||||
bool success = false;
|
||||
|
||||
if( (strlen(Config_DSound51.Device)>0)&&(!FAILED(GUIDFromString(Config_DSound51.Device,&cGuid))))
|
||||
{
|
||||
if( !FAILED( DirectSoundCreate8(&cGuid,&dsound,NULL) ) )
|
||||
success = true;
|
||||
}
|
||||
|
||||
// if the GUID failed, just open up the default dsound driver:
|
||||
if( !success )
|
||||
{
|
||||
verifyc(DirectSoundCreate8(NULL,&dsound,NULL));
|
||||
}
|
||||
|
||||
verifyc(dsound->SetCooperativeLevel(GetDesktopWindow(),DSSCL_PRIORITY));
|
||||
IDirectSoundBuffer* buffer_;
|
||||
|
@ -311,7 +353,7 @@ public:
|
|||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
|
||||
desc.dwBufferBytes = BufferSizeBytes * Config_DSound51.NumBuffers;
|
||||
desc.dwBufferBytes = BufferSizeBytes * numBuffers;
|
||||
desc.lpwfxFormat = &wfx.Format;
|
||||
|
||||
desc.dwFlags |=DSBCAPS_LOCSOFTWARE;
|
||||
|
@ -325,14 +367,14 @@ public:
|
|||
|
||||
DSBPOSITIONNOTIFY not[MAX_BUFFER_COUNT];
|
||||
|
||||
for(int i=0;i<Config_DSound51.NumBuffers;i++)
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
buffer_events[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
|
||||
not[i].dwOffset=(wfx.Format.nBlockAlign*10 + BufferSizeBytes*(i+1))%desc.dwBufferBytes;
|
||||
not[i].dwOffset=(wfx.Format.nBlockAlign*2 + BufferSizeBytes*(i+1))%desc.dwBufferBytes;
|
||||
not[i].hEventNotify=buffer_events[i];
|
||||
}
|
||||
|
||||
buffer_notify->SetNotificationPositions(Config_DSound51.NumBuffers,not);
|
||||
buffer_notify->SetNotificationPositions(numBuffers,not);
|
||||
|
||||
LPVOID p1=0,p2=0;
|
||||
DWORD s1=0,s2=0;
|
||||
|
@ -348,9 +390,8 @@ public:
|
|||
//Play the buffer !
|
||||
verifyc(buffer->Play(0,0,DSBPLAY_LOOPING));
|
||||
|
||||
tbuffer = new s32[SndOutPacketSize];
|
||||
|
||||
// Start Thread
|
||||
myLastWrite = 0;
|
||||
dsound_running=true;
|
||||
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
SetThreadPriority(thread,THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
@ -372,16 +413,21 @@ public:
|
|||
//
|
||||
// Clean up
|
||||
//
|
||||
buffer->Stop();
|
||||
if( buffer != NULL )
|
||||
{
|
||||
buffer->Stop();
|
||||
|
||||
for(int i=0;i<Config_DSound51.NumBuffers;i++)
|
||||
CloseHandle(buffer_events[i]);
|
||||
|
||||
buffer_notify->Release();
|
||||
buffer->Release();
|
||||
dsound->Release();
|
||||
|
||||
delete tbuffer;
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
if( buffer_events[i] == NULL ) continue;
|
||||
CloseHandle( buffer_events[i] );
|
||||
buffer_events[i] = NULL;
|
||||
}
|
||||
|
||||
SAFE_RELEASE( buffer_notify );
|
||||
SAFE_RELEASE( buffer );
|
||||
}
|
||||
SAFE_RELEASE( dsound );
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -406,11 +452,26 @@ private:
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL DoHandleScrollMessage(WPARAM wParam, LPARAM lParam, int vmin, int vmax, HWND hwndDisplay)
|
||||
static void AssignSliderValue( HWND idcwnd, HWND hwndDisplay, int value )
|
||||
{
|
||||
value = min( max( value, 0 ), 512 );
|
||||
SendMessage(idcwnd,TBM_SETPOS,TRUE,value);
|
||||
|
||||
char tbox[24];
|
||||
sprintf_s( tbox, 16, "%d", value );
|
||||
SetWindowText( hwndDisplay, tbox );
|
||||
}
|
||||
|
||||
static void AssignSliderValue( HWND hWnd, int idc, int editbox, int value )
|
||||
{
|
||||
AssignSliderValue( GetDlgItem( hWnd, idc ), GetDlgItem( hWnd, editbox ), value );
|
||||
}
|
||||
|
||||
static BOOL DoHandleScrollMessage(WPARAM wParam, LPARAM lParam, HWND hwndDisplay)
|
||||
{
|
||||
int wmId = LOWORD(wParam);
|
||||
int wmEvent = HIWORD(wParam);
|
||||
static char temp[1024];
|
||||
static char temp[64];
|
||||
switch(wmId) {
|
||||
//case TB_ENDTRACK:
|
||||
//case TB_THUMBPOSITION:
|
||||
|
@ -420,19 +481,22 @@ private:
|
|||
case TB_PAGEDOWN:
|
||||
wmEvent=(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
|
||||
case TB_THUMBTRACK:
|
||||
if(wmEvent<vmin) wmEvent=vmin;
|
||||
if(wmEvent>vmax) wmEvent=vmax;
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,wmEvent);
|
||||
sprintf_s(temp,1024,"%d",vmax-wmEvent);
|
||||
SetWindowText(hwndDisplay,temp);
|
||||
AssignSliderValue( (HWND)lParam, hwndDisplay, wmEvent );
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#define HANDLE_SCROLL_MESSAGE(idc,vmin,vmax,idcDisplay) \
|
||||
if((HWND)lParam == GetDlgItem(hWnd,idc)) return DoHandleScrollMessage(wParam,lParam,vmin,vmax,GetDlgItem(hWnd,idcDisplay))
|
||||
|
||||
static int GetSliderValue( HWND hWnd, int idc )
|
||||
{
|
||||
int retval = (int)SendMessage( GetDlgItem( hWnd, idc ), TBM_GETPOS, 0, 0 );
|
||||
return min( max( retval, 0), 512);
|
||||
}
|
||||
|
||||
#define HANDLE_SCROLL_MESSAGE(idc,idcDisplay) \
|
||||
if((HWND)lParam == GetDlgItem(hWnd,idc)) return DoHandleScrollMessage(wParam,lParam,GetDlgItem(hWnd,idcDisplay))
|
||||
|
||||
static BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
|
@ -464,21 +528,29 @@ private:
|
|||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_SETCURSEL,tSel,0);
|
||||
}
|
||||
|
||||
INIT_SLIDER(IDC_SLIDER1,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_SLIDER2,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_SLIDER3,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_SLIDER4,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_SLIDER5,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_SLIDER6,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_SLIDER7,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_LEFT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_RIGHT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_RLEFT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_RRIGHT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_CENTER_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_LFE_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_LR_CENTER_SLIDER,0,512,64,16,8);
|
||||
|
||||
AssignSliderValue( hWnd, IDC_LEFT_GAIN_SLIDER, IDC_LEFT_GAIN_EDIT, Config_DSound51.GainL );
|
||||
AssignSliderValue( hWnd, IDC_RIGHT_GAIN_SLIDER, IDC_RIGHT_GAIN_EDIT, Config_DSound51.GainR );
|
||||
AssignSliderValue( hWnd, IDC_RLEFT_GAIN_SLIDER, IDC_RLEFT_GAIN_EDIT, Config_DSound51.GainSL );
|
||||
AssignSliderValue( hWnd, IDC_RRIGHT_GAIN_SLIDER, IDC_RRIGHT_GAIN_EDIT, Config_DSound51.GainSR );
|
||||
AssignSliderValue( hWnd, IDC_CENTER_GAIN_SLIDER, IDC_CENTER_GAIN_EDIT, Config_DSound51.GainC);
|
||||
AssignSliderValue( hWnd, IDC_LFE_SLIDER, IDC_LFE_EDIT, Config_DSound51.GainLFE);
|
||||
AssignSliderValue( hWnd, IDC_LR_CENTER_SLIDER, IDC_LR_CENTER_EDIT, Config_DSound51.AddCLR );
|
||||
|
||||
char temp[128];
|
||||
INIT_SLIDER( IDC_BUFFERS_SLIDER, 2, MAX_BUFFER_COUNT, 2, 1, 1 );
|
||||
SendMessage(GetDlgItem(hWnd,IDC_BUFFERS_SLIDER),TBM_SETPOS,TRUE,Config_DSound51.NumBuffers);
|
||||
sprintf_s(temp, 128, "%d (%d ms latency)",Config_DSound51.NumBuffers, 1000 / (96000 / (Config_DSound51.NumBuffers * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL2),temp);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
|
@ -509,7 +581,14 @@ private:
|
|||
devices[i].guid.Data4[7]
|
||||
);
|
||||
|
||||
Config_DSound51.NumBuffers = (int)SendMessage( GetDlgItem( hWnd, IDC_BUFFERS_SLIDER ), TBM_GETPOS, 0, 0 );
|
||||
Config_DSound51.NumBuffers = GetSliderValue( hWnd, IDC_BUFFERS_SLIDER );
|
||||
Config_DSound51.GainL = GetSliderValue( hWnd, IDC_LEFT_GAIN_SLIDER );
|
||||
Config_DSound51.GainR = GetSliderValue( hWnd, IDC_RIGHT_GAIN_SLIDER );
|
||||
Config_DSound51.GainSL = GetSliderValue( hWnd, IDC_RLEFT_GAIN_SLIDER );
|
||||
Config_DSound51.GainSR = GetSliderValue( hWnd, IDC_RRIGHT_GAIN_SLIDER );
|
||||
Config_DSound51.GainLFE = GetSliderValue( hWnd, IDC_LFE_SLIDER );
|
||||
Config_DSound51.GainC = GetSliderValue( hWnd, IDC_CENTER_GAIN_SLIDER );
|
||||
Config_DSound51.AddCLR = GetSliderValue( hWnd, IDC_LR_CENTER_SLIDER );
|
||||
|
||||
if( Config_DSound51.NumBuffers < 2 ) Config_DSound51.NumBuffers = 2;
|
||||
if( Config_DSound51.NumBuffers > MAX_BUFFER_COUNT ) Config_DSound51.NumBuffers = MAX_BUFFER_COUNT;
|
||||
|
@ -541,7 +620,7 @@ private:
|
|||
if( wmEvent > MAX_BUFFER_COUNT ) wmEvent = MAX_BUFFER_COUNT;
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,wmEvent);
|
||||
sprintf_s(temp,128,"%d (%d ms latency)",wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL2),temp);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
|
@ -549,13 +628,13 @@ private:
|
|||
break;
|
||||
|
||||
case WM_VSCROLL:
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER1,0,512,IDC_EDIT1);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER2,0,512,IDC_EDIT2);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER3,0,512,IDC_EDIT3);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER4,0,512,IDC_EDIT4);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER5,0,512,IDC_EDIT5);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER6,0,512,IDC_EDIT6);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_SLIDER7,0,512,IDC_EDIT7);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_LEFT_GAIN_SLIDER,IDC_LEFT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_RIGHT_GAIN_SLIDER,IDC_RIGHT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_RLEFT_GAIN_SLIDER,IDC_RLEFT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_RRIGHT_GAIN_SLIDER,IDC_RRIGHT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_CENTER_GAIN_SLIDER,IDC_CENTER_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_LFE_SLIDER,IDC_LFE_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_LR_CENTER_SLIDER,IDC_LR_CENTER_EDIT);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
|
@ -584,7 +663,17 @@ public:
|
|||
|
||||
int GetEmptySampleCount() const
|
||||
{
|
||||
return 0;
|
||||
DWORD play, write;
|
||||
buffer->GetCurrentPosition( &play, &write );
|
||||
|
||||
// Note: Dsound's write cursor is bogus. Use our own instead:
|
||||
|
||||
int empty = play - myLastWrite;
|
||||
if( empty < 0 )
|
||||
empty = -empty;
|
||||
|
||||
return empty / 6;
|
||||
|
||||
}
|
||||
|
||||
const char* GetIdent() const
|
||||
|
|
|
@ -31,10 +31,12 @@ struct ds_device_data {
|
|||
char name[256];
|
||||
GUID guid;
|
||||
bool hasGuid;
|
||||
} devices[32];
|
||||
int ndevs;
|
||||
GUID DevGuid;
|
||||
bool haveGuid;
|
||||
};
|
||||
|
||||
static ds_device_data devices[32];
|
||||
static int ndevs;
|
||||
static GUID DevGuid; // currently employed GUID.
|
||||
static bool haveGuid;
|
||||
|
||||
HRESULT GUIDFromString(const char *str, LPGUID guid)
|
||||
{
|
||||
|
@ -70,13 +72,16 @@ class DSound: public SndOutModule
|
|||
private:
|
||||
# define MAX_BUFFER_COUNT 8
|
||||
|
||||
static const int PacketsPerBuffer = (1024 / SndOutPacketSize);
|
||||
static const int PacketsPerBuffer = 1;
|
||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
||||
static const int BufferSizeBytes = BufferSize << 1;
|
||||
|
||||
|
||||
FILE *voicelog;
|
||||
|
||||
u32 numBuffers; // cached copy of our configuration setting.
|
||||
int channel;
|
||||
int myLastWrite; // last write position, in bytes
|
||||
|
||||
bool dsound_running;
|
||||
HANDLE thread;
|
||||
|
@ -116,7 +121,7 @@ private:
|
|||
|
||||
while( dsound_running )
|
||||
{
|
||||
u32 rv = WaitForMultipleObjects(Config_DSoundOut.NumBuffers,buffer_events,FALSE,200);
|
||||
u32 rv = WaitForMultipleObjects(numBuffers,buffer_events,FALSE,200);
|
||||
|
||||
s16* p1, *oldp1;
|
||||
LPVOID p2;
|
||||
|
@ -124,13 +129,27 @@ private:
|
|||
|
||||
u32 poffset=BufferSizeBytes * rv;
|
||||
|
||||
#ifdef _DEBUG
|
||||
verifyc(buffer->Lock(poffset,BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0));
|
||||
#else
|
||||
if( FAILED(buffer->Lock(poffset,BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0) ) )
|
||||
{
|
||||
fputs( " * SPU2 : Directsound Warning > Buffer lock failure. You may need to increase the DSound buffer count.\n", stderr );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
oldp1 = p1;
|
||||
|
||||
for(int p=0; p<PacketsPerBuffer; p++, p1+=SndOutPacketSize )
|
||||
buff->ReadSamples( p1 );
|
||||
|
||||
|
||||
#ifndef PUBLIC
|
||||
verifyc(buffer->Unlock(oldp1,s1,p2,s2));
|
||||
#else
|
||||
buffer->Unlock(oldp1,s1,p2,s2);
|
||||
#endif
|
||||
// Set the write pointer to the beginning of the next block.
|
||||
myLastWrite = (poffset + BufferSizeBytes) & ~BufferSizeBytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -139,17 +158,22 @@ public:
|
|||
s32 Init(SndBuffer *sb)
|
||||
{
|
||||
buff = sb;
|
||||
numBuffers = Config_DSoundOut.NumBuffers;
|
||||
|
||||
//
|
||||
// Initialize DSound
|
||||
//
|
||||
GUID cGuid;
|
||||
bool success = false;
|
||||
|
||||
if((strlen(Config_DSoundOut.Device)>0)&&(!FAILED(GUIDFromString(Config_DSoundOut.Device,&cGuid))))
|
||||
if( (strlen(Config_DSoundOut.Device)>0)&&(!FAILED(GUIDFromString(Config_DSoundOut.Device,&cGuid))))
|
||||
{
|
||||
verifyc(DirectSoundCreate8(&cGuid,&dsound,NULL));
|
||||
if( !FAILED( DirectSoundCreate8(&cGuid,&dsound,NULL) ) )
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
|
||||
// if the GUID failed, just open up the default dsound driver:
|
||||
if( !success )
|
||||
{
|
||||
verifyc(DirectSoundCreate8(NULL,&dsound,NULL));
|
||||
}
|
||||
|
@ -174,7 +198,7 @@ public:
|
|||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
|
||||
desc.dwBufferBytes = BufferSizeBytes * Config_DSoundOut.NumBuffers;
|
||||
desc.dwBufferBytes = BufferSizeBytes * numBuffers;
|
||||
desc.lpwfxFormat = &wfx;
|
||||
|
||||
desc.dwFlags |=DSBCAPS_LOCSOFTWARE;
|
||||
|
@ -188,7 +212,7 @@ public:
|
|||
|
||||
DSBPOSITIONNOTIFY not[MAX_BUFFER_COUNT];
|
||||
|
||||
for(int i=0;i<Config_DSoundOut.NumBuffers;i++)
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
// [Air] note: wfx.nBlockAlign modifier was *10 -- seems excessive to me but maybe
|
||||
// it was needed for some quirky driver? Theoretically we want the notification as soon
|
||||
|
@ -199,7 +223,7 @@ public:
|
|||
not[i].hEventNotify=buffer_events[i];
|
||||
}
|
||||
|
||||
buffer_notify->SetNotificationPositions(Config_DSoundOut.NumBuffers,not);
|
||||
buffer_notify->SetNotificationPositions(numBuffers,not);
|
||||
|
||||
LPVOID p1=0,p2=0;
|
||||
DWORD s1=0,s2=0;
|
||||
|
@ -213,6 +237,7 @@ public:
|
|||
verifyc(buffer->Play(0,0,DSBPLAY_LOOPING));
|
||||
|
||||
// Start Thread
|
||||
myLastWrite = 0;
|
||||
dsound_running=true;
|
||||
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
SetThreadPriority(thread,THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
@ -222,7 +247,6 @@ public:
|
|||
|
||||
void Close()
|
||||
{
|
||||
|
||||
// Stop Thread
|
||||
fprintf(stderr," * SPU2: Waiting for DSound thread to finish...");
|
||||
dsound_running=false;
|
||||
|
@ -235,14 +259,22 @@ public:
|
|||
//
|
||||
// Clean up
|
||||
//
|
||||
buffer->Stop();
|
||||
|
||||
for(int i=0;i<Config_DSoundOut.NumBuffers;i++)
|
||||
CloseHandle(buffer_events[i]);
|
||||
|
||||
buffer_notify->Release();
|
||||
buffer->Release();
|
||||
dsound->Release();
|
||||
if( buffer != NULL )
|
||||
{
|
||||
buffer->Stop();
|
||||
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
if( buffer_events[i] != NULL )
|
||||
CloseHandle(buffer_events[i]);
|
||||
buffer_events[i] = NULL;
|
||||
}
|
||||
|
||||
SAFE_RELEASE( buffer_notify );
|
||||
SAFE_RELEASE( buffer );
|
||||
}
|
||||
|
||||
SAFE_RELEASE( dsound );
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -402,13 +434,14 @@ public:
|
|||
{
|
||||
DWORD play, write;
|
||||
buffer->GetCurrentPosition( &play, &write );
|
||||
int filled = play - write;
|
||||
if( filled < 0 )
|
||||
filled = -filled;
|
||||
else
|
||||
filled = (BufferSizeBytes * Config_DSoundOut.NumBuffers) - filled;
|
||||
|
||||
return filled;
|
||||
// Note: Dsound's write cursor is bogus. Use our own instead:
|
||||
|
||||
int empty = play - myLastWrite;
|
||||
if( empty < 0 )
|
||||
empty = -empty;
|
||||
|
||||
return empty / 2;
|
||||
}
|
||||
|
||||
const char* GetIdent() const
|
||||
|
|
|
@ -35,28 +35,43 @@
|
|||
#define IDC_TS_ENABLE 1030
|
||||
#define IDC_DS_DEVICE 1032
|
||||
#define IDC_SLIDER1 1033
|
||||
#define IDC_LEFT_GAIN_SLIDER 1033
|
||||
#define IDC_SLIDER2 1034
|
||||
#define IDC_CENTER_GAIN_SLIDER 1034
|
||||
#define IDC_SLIDER3 1035
|
||||
#define IDC_RIGHT_GAIN_SLIDER 1035
|
||||
#define IDC_EDIT1 1036
|
||||
#define IDC_LEFT_GAIN_EDIT 1036
|
||||
#define IDC_BUTTON1 1037
|
||||
#define IDC_SHOW_DEBUG 1037
|
||||
#define IDC_DBG_OVERRUNS 1038
|
||||
#define IDC_SLIDER4 1039
|
||||
#define IDC_DBG_CACHE 1039
|
||||
#define IDC_RLEFT_GAIN_SLIDER 1039
|
||||
#define IDC_SLIDER5 1040
|
||||
#define IDC_DEBUG_GROUP 1040
|
||||
#define IDC_LFE_SLIDER 1040
|
||||
#define IDC_SLIDER6 1041
|
||||
#define IDC_LATENCY_SLIDER 1041
|
||||
#define IDC_RRIGHT_GAIN_SLIDER 1041
|
||||
#define IDC_SLIDER7 1042
|
||||
#define IDC_LATENCY_LABEL 1042
|
||||
#define ICD_LR_CENTER_SLIDER 1042
|
||||
#define IDC_LR_CENTER_SLIDER 1042
|
||||
#define IDC_EDIT2 1043
|
||||
#define IDC_CENTER_GAIN_EDIT 1043
|
||||
#define IDC_EDIT3 1044
|
||||
#define IDC_SPEEDLIMIT_RUNTIME_TOGGLE 1044
|
||||
#define IDC_RIGHT_GAIN_EDIT 1044
|
||||
#define IDC_EDIT4 1045
|
||||
#define IDC_RLEFT_GAIN_EDIT 1045
|
||||
#define IDC_EDIT5 1046
|
||||
#define IDC_LFE_EDIT 1046
|
||||
#define IDC_EDIT6 1047
|
||||
#define IDC_VOLBOOST 1047
|
||||
#define IDC_RRIGHT_GAIN_EDIT 1047
|
||||
#define IDC_EDIT7 1048
|
||||
#define IDC_LR_CENTER_EDIT 1048
|
||||
#define IDC_LATENCY_LABEL2 1049
|
||||
|
||||
// Next default values for new objects
|
||||
|
|
|
@ -81,8 +81,11 @@ static __forceinline s16 SndScaleVol( s32 inval )
|
|||
|
||||
// records last buffer status (fill %, range -100 to 100, with 0 being 50% full)
|
||||
double lastPct;
|
||||
double lastEmergencyAdj;
|
||||
|
||||
float cTempo=1;
|
||||
float eTempo = 1;
|
||||
int freezeTempo = 0;
|
||||
|
||||
soundtouch::SoundTouch* pSoundTouch=NULL;
|
||||
|
||||
|
@ -127,8 +130,6 @@ public:
|
|||
underrun_freeze = false;
|
||||
predictData = 0;
|
||||
|
||||
lastPct = 0.0;
|
||||
|
||||
#ifdef DYNAMIC_BUFFER_LIMITING
|
||||
overflows=0;
|
||||
underflows=0;
|
||||
|
@ -194,10 +195,12 @@ public:
|
|||
{
|
||||
// If we overran it means the timestretcher failed. We need to speed
|
||||
// up audio playback.
|
||||
|
||||
cTempo += cTempo * 1.5f;
|
||||
if( cTempo > 5.0f ) cTempo = 5.0f;
|
||||
pSoundTouch->setTempo( cTempo );
|
||||
cTempo += cTempo * 0.10f;
|
||||
eTempo += eTempo * 0.25f;
|
||||
if( eTempo > 7.5f ) eTempo = 5.0f;
|
||||
pSoundTouch->setTempo( eTempo );
|
||||
freezeTempo = (comp / SndOutPacketSize) - 1;
|
||||
if( freezeTempo < 1 ) freezeTempo = 1;
|
||||
}
|
||||
|
||||
data-=comp;
|
||||
|
@ -236,24 +239,7 @@ public:
|
|||
bool CheckUnderrunStatus( int& nSamples, int& quietSampleCount )
|
||||
{
|
||||
quietSampleCount = 0;
|
||||
if( data < nSamples )
|
||||
{
|
||||
nSamples = data;
|
||||
quietSampleCount = SndOutPacketSize - data;
|
||||
underrun_freeze = true;
|
||||
|
||||
if( timeStretchEnabled )
|
||||
{
|
||||
// timeStretcher failed it's job. We need to slow down the audio some.
|
||||
|
||||
cTempo -= (cTempo * 0.25f);
|
||||
if( cTempo < 0.2f ) cTempo = 0.2f;
|
||||
pSoundTouch->setTempo( cTempo );
|
||||
}
|
||||
|
||||
return nSamples != 0;
|
||||
}
|
||||
else if( underrun_freeze )
|
||||
if( underrun_freeze )
|
||||
{
|
||||
int toFill = (int)(size * ( timeStretchEnabled ? 0.45 : 0.70 ) );
|
||||
toFill = GetAlignedBufferSize( toFill );
|
||||
|
@ -267,10 +253,31 @@ public:
|
|||
}
|
||||
|
||||
underrun_freeze = false;
|
||||
freezeTempo = 0;
|
||||
if( MsgOverruns() )
|
||||
ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize );
|
||||
lastPct = 0.0; // normalize timestretcher
|
||||
}
|
||||
else if( data < nSamples )
|
||||
{
|
||||
nSamples = data;
|
||||
quietSampleCount = SndOutPacketSize - data;
|
||||
underrun_freeze = true;
|
||||
|
||||
if( timeStretchEnabled )
|
||||
{
|
||||
// timeStretcher failed it's job. We need to slow down the audio some.
|
||||
|
||||
cTempo -= (cTempo * 0.10f);
|
||||
eTempo -= (eTempo * 0.30f);
|
||||
if( eTempo < 0.1f ) eTempo = 0.1f;
|
||||
pSoundTouch->setTempo( eTempo );
|
||||
freezeTempo = 3;
|
||||
}
|
||||
|
||||
return nSamples != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -399,7 +406,9 @@ public:
|
|||
// Get the buffer status of the output driver too, so that we can
|
||||
// obtain a more accurate overall buffer status.
|
||||
|
||||
int drvempty = mods[OutputModule]->GetEmptySampleCount() / 2;
|
||||
int drvempty = mods[OutputModule]->GetEmptySampleCount(); // / 2;
|
||||
|
||||
//ConLog( "Data %d >>> driver: %d predict: %d\n", data, drvempty, predictData );
|
||||
|
||||
double result = (data + predictData - drvempty) - (size/2);
|
||||
result /= (size/2);
|
||||
|
@ -409,12 +418,11 @@ public:
|
|||
|
||||
};
|
||||
|
||||
SndBufferImpl *sndBuffer;
|
||||
SndBufferImpl *sndBuffer=NULL;
|
||||
|
||||
s32* sndTempBuffer;
|
||||
s32 sndTempProgress;
|
||||
s16* sndTempBuffer16;
|
||||
//float* sndTempBufferFloat;
|
||||
s32* sndTempBuffer=NULL;
|
||||
s32 sndTempProgress=NULL;
|
||||
s16* sndTempBuffer16=NULL;
|
||||
|
||||
void ResetTempoChange()
|
||||
{
|
||||
|
@ -423,85 +431,144 @@ void ResetTempoChange()
|
|||
|
||||
void UpdateTempoChange()
|
||||
{
|
||||
if( --freezeTempo > 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double statusPct = sndBuffer->GetStatusPct();
|
||||
double pctChange = statusPct - lastPct;
|
||||
|
||||
double tempoChange;
|
||||
double emergencyAdj = 0;
|
||||
double newcee = cTempo; // workspace var. for cTempo
|
||||
|
||||
// IMPORTANT!
|
||||
// If you plan to tweak these values, make sure you're using a release build
|
||||
// OUTSIDE THE DEBUGGER to test it! The Visual Studio debugger can really cause
|
||||
// erratic behavior in the audio buffers, and makes the timestretcher seem a
|
||||
// lot more inconsistent than it really is.
|
||||
|
||||
// We have two factors.
|
||||
// * Distance from nominal buffer status (50% full)
|
||||
// * The change from previous update to this update.
|
||||
|
||||
// The most important factor is the change from update to update.
|
||||
// But the synchronization between emulator, mixer, and audio driver
|
||||
// is rarely consistent so drifting away from nominal buffer status (50%)
|
||||
// is inevitable. So we need to use the nominal buffer status to
|
||||
// help temper things.
|
||||
// Prediction based on the buffer change:
|
||||
// (linear seems to work better here)
|
||||
|
||||
double relation = statusPct / pctChange;
|
||||
tempoChange = pctChange * 0.75;
|
||||
|
||||
if( relation < 0.0 )
|
||||
if( statusPct * tempoChange < 0.0 )
|
||||
{
|
||||
// The buffer is already shrinking toward
|
||||
// nominal value, so let's not do "too much"
|
||||
// We only want to adjust if the shrink rate seems too fast
|
||||
// or slow compared to our distance from nominal (50%).
|
||||
// only apply tempo change if it is in synch with the buffer status.
|
||||
// In other words, if the buffer is high (over 0%), and is decreasing,
|
||||
// ignore it. It'll just muck things up.
|
||||
|
||||
tempoChange = ( pow( statusPct, 3.0 ) * 0.33 ) + pctChange * 0.23;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempoChange = pctChange * 0.30;
|
||||
|
||||
// Sudden spikes in framerate can cause the nominal buffer status
|
||||
// to go critical, in which case we have to enact an emergency
|
||||
// stretch. The following cubic formula does that.
|
||||
|
||||
// Constants:
|
||||
// Weight - weights the statusPct's "emergency" consideration.
|
||||
// higher values here will make the buffer perform more drastic
|
||||
// compensations.
|
||||
|
||||
// Range - scales the adjustment to the given range (more or less).
|
||||
// The actual range is dependent on the weight used, so if you increase
|
||||
// Weight you'll usually want to decrease Range somewhat to compensate.
|
||||
|
||||
const double weight = 1.55;
|
||||
const double range = 0.12;
|
||||
|
||||
double nominalAdjust = (statusPct * 0.10) + ( pow( statusPct*weight, 3.0 ) * range);
|
||||
|
||||
tempoChange = tempoChange + nominalAdjust;
|
||||
tempoChange = 0;
|
||||
}
|
||||
|
||||
// Threshold - Ignore small values between -.005 and +.005.
|
||||
// We don't need to "pollute" our timestretcher with pointless
|
||||
// tempo change overhead.
|
||||
if( abs( tempoChange ) < 0.005 ) return;
|
||||
// Sudden spikes in framerate can cause the nominal buffer status
|
||||
// to go critical, in which case we have to enact an emergency
|
||||
// stretch. The following cubic formulas do that. Values near
|
||||
// the extremeites give much larger results than those near 0.
|
||||
// And the value is added only this time, and does not accumulate.
|
||||
// (otherwise a large value like this would cause problems down the road)
|
||||
|
||||
// Constants:
|
||||
// Weight - weights the statusPct's "emergency" consideration.
|
||||
// higher values here will make the buffer perform more drastic
|
||||
// compensations at the outter edges of the buffer (at -75 or +75%
|
||||
// or beyond, for example).
|
||||
|
||||
// Range - scales the adjustment to the given range (more or less).
|
||||
// The actual range is dependent on the weight used, so if you increase
|
||||
// Weight you'll usually want to decrease Range somewhat to compensate.
|
||||
|
||||
// Prediction based on the buffer fill status:
|
||||
|
||||
const double statusWeight = 2.99;
|
||||
const double statusRange = 0.068;
|
||||
|
||||
// "non-emergency" deadzone: In this area stretching will be strongly discouraged.
|
||||
// Note: due tot he nature of timestretch latency, it's always a wee bit harder to
|
||||
// cope with low fps (underruns) tha it is high fps (overruns). So to help out a
|
||||
// little, the low-end portions of this check are less forgiving than the high-sides.
|
||||
|
||||
if( cTempo < 0.965 || cTempo > 1.060 ||
|
||||
pctChange < -0.38 || pctChange > 0.54 ||
|
||||
statusPct < -0.32 || statusPct > 0.39 ||
|
||||
eTempo < 0.89 || eTempo > 1.19 )
|
||||
{
|
||||
emergencyAdj = ( pow( statusPct*statusWeight, 3.0 ) * statusRange);
|
||||
}
|
||||
|
||||
// Smooth things out by factoring our previous adjustment into this one.
|
||||
// It helps make the system 'feel' a little smarter by giving it at least
|
||||
// one packet worth of history to help work off of:
|
||||
|
||||
emergencyAdj = (emergencyAdj * 0.75) + (lastEmergencyAdj * 0.25 );
|
||||
|
||||
lastEmergencyAdj = emergencyAdj;
|
||||
lastPct = statusPct;
|
||||
|
||||
// Accumulate a fraction of the tempo change into the tempo itself.
|
||||
// This helps the system run "smarter" to games that run consistently
|
||||
// fast or slow by altering the base tempo to something closer to the
|
||||
// game's active speed. In tests most games normalize within 2 seconds
|
||||
// at 100ms latency, which is pretty good (larger buffers normalize even
|
||||
// quicker).
|
||||
|
||||
newcee += newcee * (tempoChange+emergencyAdj) * 0.03;
|
||||
|
||||
// Apply tempoChange as a scale of cTempo. That way the effect is proportional
|
||||
// to the current tempo. (otherwise tempos would change too slowly/quickly at the extremes)
|
||||
cTempo += (float)( tempoChange * cTempo );
|
||||
|
||||
if( statusPct < -0.20 || statusPct > 0.20 || cTempo < 0.980 || cTempo > 1.020 )
|
||||
// to the current tempo. (otherwise tempos rate of change at the extremes would
|
||||
// be too drastic)
|
||||
|
||||
double newTempo = newcee + ( emergencyAdj * cTempo );
|
||||
|
||||
// ... and as a final optimization, only stretch if the new tempo is outside
|
||||
// a nominal threshold. Keep this threshold check small, because it could
|
||||
// cause some serious side effects otherwise. (enlarging the cTempo check above
|
||||
// is usually better/safer)
|
||||
if( newTempo < 0.970 || newTempo > 1.045 )
|
||||
{
|
||||
if( cTempo < 0.20f ) cTempo = 0.20f;
|
||||
else if( cTempo > 10.0f ) cTempo = 10.0f; //5.0 is suggested by soundtouch, but this is fine as well (rama)
|
||||
pSoundTouch->setTempo( cTempo );
|
||||
cTempo = (float)newcee;
|
||||
|
||||
if( newTempo < 0.10f ) newTempo = 0.10f;
|
||||
else if( newTempo > 10.0f ) newTempo = 10.0f;
|
||||
|
||||
if( cTempo < 0.15f ) cTempo = 0.15f;
|
||||
else if( cTempo > 7.5f ) cTempo = 7.5f;
|
||||
|
||||
pSoundTouch->setTempo( eTempo = (float)newTempo );
|
||||
ts_stats_stretchblocks++;
|
||||
ConLog(" %s * SPU2: TempoChange by %d%% (tempo: %d%%) (buffer: %d%%)\n",
|
||||
(relation < 0.0) ? "Normalize" : "",
|
||||
(int)(tempoChange * 100.0), (int)(cTempo * 100.0),
|
||||
|
||||
/*ConLog(" * SPU2: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n",
|
||||
//(relation < 0.0) ? "Normalize" : "",
|
||||
(int)(tempoChange * 100.0 * 0.03),
|
||||
(int)(emergencyAdj * 100.0),
|
||||
(int)(cTempo * 100.0),
|
||||
(int)(newTempo * 100.0),
|
||||
(int)(statusPct * 100.0)
|
||||
);
|
||||
);*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nominal operation -- turn off stretching.
|
||||
pSoundTouch->setTempo( 1.0f );
|
||||
ts_stats_normalblocks++;
|
||||
// note: eTempo 'slides' toward 1.0 for smoother audio and better
|
||||
// protection against spikes.
|
||||
if( cTempo != 1.0f )
|
||||
{
|
||||
cTempo = 1.0f;
|
||||
eTempo = ( 1.0f + eTempo ) * 0.5f;
|
||||
pSoundTouch->setTempo( eTempo );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( eTempo != cTempo )
|
||||
pSoundTouch->setTempo( eTempo=cTempo );
|
||||
ts_stats_normalblocks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,13 +593,21 @@ s32 SndInit()
|
|||
// initialize sound buffer
|
||||
// Buffer actually attempts to run ~50%, so allocate near double what
|
||||
// the requested latency is:
|
||||
sndBuffer = new SndBufferImpl( SndOutLatencyMS * 1.75 );
|
||||
|
||||
sndBuffer = new SndBufferImpl( SndOutLatencyMS * (timeStretchEnabled ? 2.0 : 1.5) );
|
||||
sndTempProgress = 0;
|
||||
sndTempBuffer = new s32[SndOutPacketSize];
|
||||
sndTempBuffer16 = new s16[SndOutPacketSize];
|
||||
//sndTempBufferFloat = new float[sndTempSize];
|
||||
|
||||
cTempo = 1.0;
|
||||
eTempo = 1.0;
|
||||
|
||||
lastPct = 0;
|
||||
lastEmergencyAdj = 0;
|
||||
|
||||
// just freeze tempo changes for a while at startup.
|
||||
// the driver buffers are bogus anyway.
|
||||
freezeTempo = 8;
|
||||
soundtouchInit();
|
||||
|
||||
ResetTempoChange();
|
||||
|
@ -546,18 +621,22 @@ s32 SndInit()
|
|||
spdif_set51(mods[OutputModule]->Is51Out());
|
||||
|
||||
// initialize module
|
||||
return mods[OutputModule]->Init(sndBuffer);
|
||||
if( mods[OutputModule]->Init(sndBuffer) == -1 )
|
||||
{
|
||||
OutputModule = FindOutputModuleById( NullOut.GetIdent() );
|
||||
return mods[OutputModule]->Init( sndBuffer );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SndClose()
|
||||
{
|
||||
mods[OutputModule]->Close();
|
||||
|
||||
delete sndBuffer;
|
||||
delete sndTempBuffer;
|
||||
delete sndTempBuffer16;
|
||||
|
||||
delete pSoundTouch;
|
||||
SAFE_DELETE_OBJ( sndBuffer );
|
||||
SAFE_DELETE_ARRAY( sndTempBuffer );
|
||||
SAFE_DELETE_ARRAY( sndTempBuffer16 );
|
||||
SAFE_DELETE_OBJ( pSoundTouch );
|
||||
}
|
||||
|
||||
void SndUpdateLimitMode()
|
||||
|
@ -612,15 +691,12 @@ s32 SndWrite(s32 ValL, s32 ValR)
|
|||
}
|
||||
|
||||
static int equalized = 0;
|
||||
//use this to debug buffer usage in near realtime (rama)
|
||||
/*if ((sndBuffer->GetStatusPct() < -0.3) || (sndBuffer->GetStatusPct() > 1.3))
|
||||
printf("Buffer beyond save zone! Usage: %f\n",sndBuffer->GetStatusPct());*/
|
||||
if(timeStretchEnabled)
|
||||
{
|
||||
bool progress = false;
|
||||
|
||||
// data prediction helps keep the tempo adjustments more accurate.
|
||||
sndBuffer->PredictDataWrite( (int)( sndTempProgress / cTempo ) );
|
||||
sndBuffer->PredictDataWrite( (int)( sndTempProgress / eTempo ) );
|
||||
for(int i=0;i<sndTempProgress;i++) { ((float*)sndTempBuffer)[i] = sndTempBuffer[i]/2147483648.0f; }
|
||||
|
||||
pSoundTouch->putSamples((float*)sndTempBuffer, sndTempProgress>>1);
|
||||
|
@ -642,9 +718,10 @@ s32 SndWrite(s32 ValL, s32 ValR)
|
|||
progress = true;
|
||||
}
|
||||
|
||||
UpdateTempoChange();
|
||||
|
||||
if( progress )
|
||||
{
|
||||
UpdateTempoChange();
|
||||
|
||||
if( MsgOverruns() )
|
||||
{
|
||||
|
|
|
@ -56,6 +56,9 @@ public:
|
|||
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 char* GetIdent() const=0;
|
||||
|
|
|
@ -83,6 +83,19 @@ default: \
|
|||
break; \
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Helper macros
|
||||
//--------------------------------------------------------------------------------------
|
||||
#ifndef SAFE_DELETE_ARRAY
|
||||
# define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef SAFE_DELETE
|
||||
# define SAFE_DELETE_OBJ(p) { if(p) { delete (p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef SAFE_RELEASE
|
||||
# define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
|
||||
#endif
|
||||
|
||||
|
||||
extern void spdif_set51(u32 is_5_1_out);
|
||||
extern u32 spdif_init();
|
||||
|
|
|
@ -29,6 +29,7 @@ private:
|
|||
static const int BufferSize = SndOutPacketSize*PacketsPerBuffer;
|
||||
static const int BufferSizeBytes = BufferSize << 1;
|
||||
|
||||
u32 numBuffers;
|
||||
HWAVEOUT hwodevice;
|
||||
WAVEFORMATEX wformat;
|
||||
WAVEHDR whbuffer[MAX_BUFFER_COUNT];
|
||||
|
@ -57,7 +58,7 @@ private:
|
|||
while( waveout_running )
|
||||
{
|
||||
bool didsomething = false;
|
||||
for(int i=0;i<MAX_BUFFER_COUNT;i++)
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
if(!(whbuffer[i].dwFlags & WHDR_DONE) ) continue;
|
||||
|
||||
|
@ -86,6 +87,7 @@ public:
|
|||
s32 Init(SndBuffer *sb)
|
||||
{
|
||||
buff = sb;
|
||||
numBuffers = Config_WaveOut.NumBuffers;
|
||||
|
||||
MMRESULT woores;
|
||||
|
||||
|
@ -99,7 +101,7 @@ public:
|
|||
wformat.nAvgBytesPerSec=(wformat.nSamplesPerSec * wformat.nBlockAlign);
|
||||
wformat.cbSize=0;
|
||||
|
||||
qbuffer=new s16[BufferSize*MAX_BUFFER_COUNT];
|
||||
qbuffer=new s16[BufferSize*numBuffers];
|
||||
|
||||
woores = waveOutOpen(&hwodevice,WAVE_MAPPER,&wformat,0,0,0);
|
||||
if (woores != MMSYSERR_NOERROR)
|
||||
|
@ -109,7 +111,7 @@ public:
|
|||
return -1;
|
||||
}
|
||||
|
||||
for(int i=0;i<MAX_BUFFER_COUNT;i++)
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
whbuffer[i].dwBufferLength=BufferSizeBytes;
|
||||
whbuffer[i].dwBytesRecorded=BufferSizeBytes;
|
||||
|
@ -126,7 +128,8 @@ public:
|
|||
// Start Thread
|
||||
// [Air]: The waveout code does not use wait objects, so setting a time critical
|
||||
// priority level is a bad idea. Standard priority will do fine. The buffer will get the
|
||||
// love it needs and won't suck resources idling pointlessly.
|
||||
// love it needs and won't suck resources idling pointlessly. Just don't try to
|
||||
// run it in uber-low-latency mode.
|
||||
waveout_running=true;
|
||||
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
//SetThreadPriority( thread, THREAD_PRIORITY_TIME_CRITICAL );
|
||||
|
@ -149,13 +152,13 @@ public:
|
|||
// Clean up
|
||||
//
|
||||
waveOutReset(hwodevice);
|
||||
for(int i=0;i<MAX_BUFFER_COUNT;i++)
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
waveOutUnprepareHeader(hwodevice,&whbuffer[i],sizeof(WAVEHDR));
|
||||
}
|
||||
waveOutClose(hwodevice);
|
||||
|
||||
delete qbuffer;
|
||||
SAFE_DELETE_ARRAY( qbuffer );
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -24,56 +24,55 @@
|
|||
#include <mmsystem.h>
|
||||
#include <conio.h>
|
||||
|
||||
// turn off warning C4355: 'this' : used in base member initializer list
|
||||
#pragma warning( disable: 4355 )
|
||||
|
||||
class XAudio2Mod: public SndOutModule
|
||||
{
|
||||
private:
|
||||
static const int BufferSize = SndOutPacketSize;
|
||||
static const int PacketsPerBuffer = 1;
|
||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
||||
static const int BufferSizeBytes = BufferSize * 2;
|
||||
//#define BufferSize (SndOutPacketSize<<1)
|
||||
//#define BufferSizeBytes (BufferSize<<1)
|
||||
|
||||
s16* qbuffer;
|
||||
|
||||
s32 out_num;
|
||||
|
||||
#define QBUFFER(num) (qbuffer+(BufferSize*(num)))
|
||||
|
||||
SndBuffer *buff;
|
||||
|
||||
bool xaudio2_running;
|
||||
HANDLE thread;
|
||||
DWORD tid;
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Helper macros
|
||||
//--------------------------------------------------------------------------------------
|
||||
#ifndef SAFE_DELETE_ARRAY
|
||||
# define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef SAFE_RELEASE
|
||||
# define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
|
||||
#endif
|
||||
|
||||
#define MAX_BUFFER_COUNT 3
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Callback structure
|
||||
//--------------------------------------------------------------------------------------
|
||||
struct StreamingVoiceContext : public IXAudio2VoiceCallback
|
||||
class StreamingVoiceContext : public IXAudio2VoiceCallback
|
||||
{
|
||||
public:
|
||||
SndBuffer* sndout;
|
||||
IXAudio2SourceVoice* pSourceVoice;
|
||||
|
||||
protected:
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { };
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
STDMETHOD_(void, OnBufferStart) ( void* ) {}
|
||||
STDMETHOD_(void, OnBufferEnd) ( void* ) { SetEvent( hBufferEndEvent ); }
|
||||
STDMETHOD_(void, OnBufferEnd) ( void* context )
|
||||
{
|
||||
s16* qb = (s16*)context;
|
||||
|
||||
for(int p=0; p<PacketsPerBuffer; p++, qb+=SndOutPacketSize )
|
||||
sndout->ReadSamples( qb );
|
||||
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buf.AudioBytes = BufferSizeBytes;
|
||||
buf.pAudioData=(const BYTE*)context;
|
||||
buf.pContext=context;
|
||||
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
}
|
||||
STDMETHOD_(void, OnLoopEnd) ( void* ) {}
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) { };
|
||||
|
||||
HANDLE hBufferEndEvent;
|
||||
|
||||
StreamingVoiceContext(): hBufferEndEvent( CreateEvent( NULL, FALSE, FALSE, NULL ) ){}
|
||||
~StreamingVoiceContext(){ CloseHandle( hBufferEndEvent ); }
|
||||
} voiceContext;
|
||||
|
||||
IXAudio2* pXAudio2;
|
||||
|
@ -82,42 +81,14 @@ private:
|
|||
|
||||
WAVEFORMATEX wfx;
|
||||
|
||||
|
||||
static DWORD CALLBACK RThread(XAudio2Mod*obj)
|
||||
{
|
||||
return obj->Thread();
|
||||
}
|
||||
|
||||
DWORD CALLBACK Thread()
|
||||
{
|
||||
while( xaudio2_running )
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
|
||||
{
|
||||
WaitForSingleObject( voiceContext.hBufferEndEvent, INFINITE );
|
||||
}
|
||||
|
||||
s16 *qb=QBUFFER(out_num);
|
||||
out_num=(out_num+1)%MAX_BUFFER_COUNT;
|
||||
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buff->ReadSamples(qb);
|
||||
|
||||
buf.AudioBytes = BufferSizeBytes;
|
||||
buf.pAudioData=(const BYTE*)qb;
|
||||
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
s32 Init(SndBuffer *sb)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
buff=sb;
|
||||
voiceContext.sndout = buff;
|
||||
|
||||
//
|
||||
// Initialize XAudio2
|
||||
|
@ -164,50 +135,57 @@ public:
|
|||
SAFE_RELEASE( pXAudio2 );
|
||||
return -1;
|
||||
}
|
||||
voiceContext.pSourceVoice = pSourceVoice;
|
||||
pSourceVoice->Start( 0, 0 );
|
||||
|
||||
//tbuffer = new s32[BufferSize];
|
||||
qbuffer = new s16[BufferSize*MAX_BUFFER_COUNT];
|
||||
ZeroMemory(qbuffer,BufferSize*MAX_BUFFER_COUNT*2);
|
||||
ZeroMemory(qbuffer,BufferSizeBytes*MAX_BUFFER_COUNT);
|
||||
|
||||
// Start Thread
|
||||
xaudio2_running=true;
|
||||
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
// Start two buffers.
|
||||
// Frankly two buffers is all we should ever need since the buffer fill code
|
||||
// is tied directly to the XAudio2 engine.
|
||||
|
||||
if(thread==INVALID_HANDLE_VALUE) return -1;
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buf.AudioBytes = BufferSizeBytes;
|
||||
buf.pContext=qbuffer;
|
||||
buf.pAudioData=(BYTE*)buf.pContext;
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
|
||||
SetThreadPriority(thread,THREAD_PRIORITY_TIME_CRITICAL);
|
||||
buf.pContext=&qbuffer[BufferSize];
|
||||
buf.pAudioData=(BYTE*)buf.pContext;
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
// Stop Thread
|
||||
fprintf(stderr," * SPU2: Waiting for XAudio2 thread to finish...");
|
||||
xaudio2_running=false;
|
||||
|
||||
WaitForSingleObject(thread,INFINITE);
|
||||
CloseHandle(thread);
|
||||
|
||||
fprintf(stderr," Done.\n");
|
||||
|
||||
//
|
||||
// Clean up
|
||||
//
|
||||
pSourceVoice->Stop( 0 );
|
||||
pSourceVoice->DestroyVoice();
|
||||
if( pSourceVoice != NULL )
|
||||
{
|
||||
pSourceVoice->Stop( 0 );
|
||||
pSourceVoice->DestroyVoice();
|
||||
pSourceVoice = NULL;
|
||||
}
|
||||
|
||||
Sleep(100);
|
||||
Sleep(50);
|
||||
|
||||
//
|
||||
// Cleanup XAudio2
|
||||
//
|
||||
|
||||
// All XAudio2 interfaces are released when the engine is destroyed, but being tidy
|
||||
pMasteringVoice->DestroyVoice();
|
||||
// All XAudio2 interfaces are released when the engine is destroyed,
|
||||
// but being tidy never hurt.
|
||||
|
||||
if( pMasteringVoice != NULL )
|
||||
pMasteringVoice->DestroyVoice();
|
||||
pMasteringVoice = NULL;
|
||||
|
||||
SAFE_RELEASE( pXAudio2 );
|
||||
SAFE_DELETE_ARRAY( qbuffer );
|
||||
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
|
@ -224,7 +202,11 @@ public:
|
|||
|
||||
int GetEmptySampleCount() const
|
||||
{
|
||||
return 0;
|
||||
// I think this code works.
|
||||
// It's kind of hard to know for sure.
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
pSourceVoice->GetState( &state );
|
||||
return state.SamplesPlayed & (BufferSize-1);
|
||||
}
|
||||
|
||||
const char* GetIdent() const
|
||||
|
|
Loading…
Reference in New Issue