Stable, correct and uncut audio playback GET!

Originally committed to SVN as r1100.
This commit is contained in:
Niels Martin Hansen 2007-04-20 18:07:44 +00:00
parent 4276b02f89
commit c1cb6e4367
2 changed files with 34 additions and 27 deletions

View file

@ -62,7 +62,6 @@ DirectSoundPlayer::DirectSoundPlayer() {
buffer = NULL; buffer = NULL;
directSound = NULL; directSound = NULL;
thread = NULL; thread = NULL;
notificationEvent = NULL;
} }
@ -82,7 +81,7 @@ void DirectSoundPlayer::OpenStream() {
// Initialize the DirectSound object // Initialize the DirectSound object
HRESULT res; HRESULT res;
res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device
if (res != DS_OK) throw _T("Failed initializing DirectSound"); if (FAILED(res)) throw _T("Failed initializing DirectSound");
// Set DirectSound parameters // Set DirectSound parameters
AegisubApp *app = (AegisubApp*) wxTheApp; AegisubApp *app = (AegisubApp*) wxTheApp;
@ -99,7 +98,7 @@ void DirectSoundPlayer::OpenStream() {
waveFormat.cbSize = 0; waveFormat.cbSize = 0;
// Create the buffer initializer // Create the buffer initializer
int aim = 0x20000; int aim = waveFormat.nAvgBytesPerSec + waveFormat.nAvgBytesPerSec/2; // one and a half second of buffer
int min = DSBSIZE_MIN; int min = DSBSIZE_MIN;
int max = DSBSIZE_MAX; int max = DSBSIZE_MAX;
bufSize = MIN(MAX(min,aim),max); bufSize = MIN(MAX(min,aim),max);
@ -168,7 +167,6 @@ bool DirectSoundPlayer::FillBuffer(bool fill) {
if (FAILED(res)) return false; if (FAILED(res)) return false;
toWrite = (int)bufplay - (int)offset; toWrite = (int)bufplay - (int)offset;
if (toWrite < 0) toWrite += bufSize; if (toWrite < 0) toWrite += bufSize;
//wxLogDebug(_T("DSound Fill Buffer: bufplay=%u, offset=%d, toWrite=%d"), bufplay, offset, toWrite);
} }
if (toWrite == 0) return true; if (toWrite == 0) return true;
@ -194,8 +192,6 @@ RetryLock:
// Update offset // Update offset
offset = (offset + toWrite) % bufSize; offset = (offset + toWrite) % bufSize;
//wxLogDebug(_T("DSound Fill Buffer: offset=%d, toWrite=%d, size1=%u, size2=%u"), offset, toWrite, size1, size2);
// Convert size to number of samples // Convert size to number of samples
unsigned long int count1 = size1 / bytesps; unsigned long int count1 = size1 / bytesps;
unsigned long int count2 = size2 / bytesps; unsigned long int count2 = size2 / bytesps;
@ -207,12 +203,8 @@ RetryLock:
unsigned long int delta = 0; unsigned long int delta = 0;
if (totalCount > left) delta = totalCount - left; if (totalCount > left) delta = totalCount - left;
// If so, zero-fill buffer first // And only write the remaining samples
if (delta) { if (delta) {
// Zero at start
memset(ptr1,0,size1);
memset(ptr2,0,size2);
// Lower counts // Lower counts
int temp = MIN(delta,count2); int temp = MIN(delta,count2);
count2 -= temp; count2 -= temp;
@ -222,19 +214,15 @@ RetryLock:
delta -= temp; delta -= temp;
} }
//wxLogDebug(_T("DSound Fill Buffer, playpos=%d, count1=%u, count2=%u"), (int)playPos, count1, count2);
// Get source wave // Get source wave
if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume); if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume);
if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume); if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
playPos += count1+count2; playPos += count1+count2;
//wxLogDebug(_T("DSound Fill Buffer post-fill, playpos=%d, count1=%u, count2=%u"), (int)playPos, count1, count2);
// Unlock // Unlock
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps); buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
return true; return delta==0; // If delta>0 we hit end of stream
} }
@ -268,6 +256,7 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) {
buffer->SetCurrentPosition(0); buffer->SetCurrentPosition(0);
res = buffer->Play(0,0,DSBPLAY_LOOPING); res = buffer->Play(0,0,DSBPLAY_LOOPING);
if (SUCCEEDED(res)) playing = true; if (SUCCEEDED(res)) playing = true;
startTime = GetTickCount();
// Update timer // Update timer
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15); if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
@ -295,11 +284,6 @@ void DirectSoundPlayer::Stop(bool timerToo) {
endPos = 0; endPos = 0;
offset = 0; offset = 0;
// Close event handle
if (notificationEvent)
CloseHandle(notificationEvent);
notificationEvent = 0;
// Stop timer // Stop timer
if (timerToo && displayTimer) { if (timerToo && displayTimer) {
displayTimer->Stop(); displayTimer->Stop();
@ -327,6 +311,10 @@ __int64 DirectSoundPlayer::GetCurrentPosition() {
// Check if buffer is loaded // Check if buffer is loaded
if (!buffer || !playing) return 0; if (!buffer || !playing) return 0;
DWORD curtime = GetTickCount();
__int64 tdiff = curtime - startTime;
return startPos + tdiff * provider->GetSampleRate() / 1000;
// Read position // Read position
unsigned long int play,write; unsigned long int play,write;
HRESULT res = buffer->GetCurrentPosition(&play,NULL); HRESULT res = buffer->GetCurrentPosition(&play,NULL);
@ -346,7 +334,7 @@ __int64 DirectSoundPlayer::GetCurrentPosition() {
// Thread constructor // Thread constructor
DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) { DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
parent = par; parent = par;
stopnotify = CreateEvent(NULL, false, false, NULL); stopnotify = CreateEvent(NULL, true, false, NULL);
} }
@ -363,10 +351,29 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() {
// Wake up thread every half second to fill buffer as needed // Wake up thread every half second to fill buffer as needed
// This more or less assumes the buffer is at least one second long // This more or less assumes the buffer is at least one second long
while (WaitForSingleObject(stopnotify, 500)) { while (WaitForSingleObject(stopnotify, 500)) {
if (!parent->FillBuffer(false)) if (!parent->FillBuffer(false)) {
// FillBuffer returns true if there's more to play // FillBuffer returns false when end of stream is reached
// So false means end-of-stream wxLogDebug(_T("DS thread hit end of stream"));
break; break;
}
}
// Now fill buffer with silence
DWORD bytesFilled = 0;
while (WaitForSingleObject(stopnotify, 500)) {
void *buf1, *buf2;
DWORD size1, size2;
DWORD playpos;
HRESULT res;
res = parent->buffer->GetCurrentPosition(&playpos, NULL);
if (FAILED(res)) break;
res = parent->buffer->Lock(parent->offset, (playpos-parent->offset)%parent->bufSize, &buf1, &size1, &buf2, &size2, 0);
if (FAILED(res)) break;
if (size1) memset(buf1, 0, size1);
if (size2) memset(buf2, 0, size2);
bytesFilled += size1 + size2;
parent->buffer->Unlock(buf1, size1, buf2, size2);
if (bytesFilled >= parent->bufSize) break;
} }
wxLogDebug(_T("DS thread dead")); wxLogDebug(_T("DS thread dead"));

View file

@ -79,12 +79,12 @@ private:
int bufSize; int bufSize;
volatile __int64 playPos; volatile __int64 playPos;
volatile __int64 startPos; __int64 startPos;
volatile __int64 endPos; volatile __int64 endPos;
DWORD startTime;
IDirectSound8 *directSound; IDirectSound8 *directSound;
IDirectSoundBuffer8 *buffer; IDirectSoundBuffer8 *buffer;
HANDLE notificationEvent;
bool FillBuffer(bool fill); bool FillBuffer(bool fill);