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:
ramapcsx2 2008-10-29 13:40:20 +00:00 committed by Gregory Hainaut
parent 4da570c671
commit 88638106e6
11 changed files with 523 additions and 302 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() )
{

View File

@ -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;

View File

@ -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();

View File

@ -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:

View File

@ -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