Fixed #320, other applications no longer interfere with audio playback. Apparently those notifications for DSound buffers aren't reliable at all.

Still one minor problem with audio display cursor making a strange jump about one second from end of selection, this doesn't seem to affect playback though.

Originally committed to SVN as r1099.
This commit is contained in:
Niels Martin Hansen 2007-04-20 16:27:18 +00:00
parent 813a487b23
commit 4276b02f89
2 changed files with 56 additions and 62 deletions

View file

@ -105,7 +105,7 @@ void DirectSoundPlayer::OpenStream() {
bufSize = MIN(MAX(min,aim),max); bufSize = MIN(MAX(min,aim),max);
DSBUFFERDESC desc; DSBUFFERDESC desc;
desc.dwSize = sizeof(DSBUFFERDESC); desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY; desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
desc.dwBufferBytes = bufSize; desc.dwBufferBytes = bufSize;
desc.dwReserved = 0; desc.dwReserved = 0;
desc.lpwfxFormat = &waveFormat; desc.lpwfxFormat = &waveFormat;
@ -147,49 +147,67 @@ void DirectSoundPlayer::CloseStream() {
/////////////// ///////////////
// Fill buffer // Fill buffer
bool DirectSoundPlayer::FillBuffer() { bool DirectSoundPlayer::FillBuffer(bool fill) {
if (playPos >= endPos) return false; if (playPos >= endPos) return false;
// Variables // Variables
HRESULT res;
void *ptr1, *ptr2; void *ptr1, *ptr2;
unsigned long int size1, size2; unsigned long int size1, size2;
AudioProvider *provider = GetProvider(); AudioProvider *provider = GetProvider();
int bytesps = provider->GetBytesPerSample(); int bytesps = provider->GetBytesPerSample();
// To write length // To write length
//int max = bufSize/4; int toWrite = 0;
//if (fill) max = bufSize; if (fill) {
//int toWrite = MIN(max,(endPos-playPos)*bytesps); toWrite = bufSize;
int toWrite = bufSize/4; }
else {
DWORD bufplay;
res = buffer->GetCurrentPosition(&bufplay, NULL);
if (FAILED(res)) return false;
toWrite = (int)bufplay - (int)offset;
if (toWrite < 0) toWrite += bufSize;
//wxLogDebug(_T("DSound Fill Buffer: bufplay=%u, offset=%d, toWrite=%d"), bufplay, offset, toWrite);
}
if (toWrite == 0) return true;
// Lock buffer // Lock buffer
HRESULT res; RetryLock:
res = buffer->Lock(offset,toWrite,&ptr1,&size1,&ptr2,&size2,0); if (fill) {
res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);
}
else {
res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);//DSBLOCK_FROMWRITECURSOR);
}
// Buffer lost? // Buffer lost?
if (res == DSERR_BUFFERLOST) { if (res == DSERR_BUFFERLOST) {
wxLogDebug(_T("Lost DSound buffer"));
buffer->Restore(); buffer->Restore();
res = buffer->Lock(offset,toWrite,&ptr1,&size1,&ptr2,&size2,0); goto RetryLock;
} }
// Error // Error
if (FAILED(res)) return false; if (FAILED(res)) return false;
// Set 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;
// Check if it's writing over play // Check if remaining buffer is longer than remaining sound
unsigned long int totalCount = count1+count2; unsigned long int totalCount = count1+count2;
unsigned long int left = 0; unsigned long int left = 0;
if (endPos > playPos) left = endPos - playPos; if (endPos > playPos) left = endPos - playPos;
unsigned long int delta = 0; unsigned long int delta = 0;
if (totalCount > left) delta = totalCount - left; if (totalCount > left) delta = totalCount - left;
// If so, don't allow it // If so, zero-fill buffer first
if (delta) { if (delta) {
// Zero at start // Zero at start
memset(ptr1,0,size1); memset(ptr1,0,size1);
@ -204,13 +222,17 @@ bool DirectSoundPlayer::FillBuffer() {
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 += totalCount; 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,size1,ptr2,size2); buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
return true; return true;
} }
@ -223,40 +245,19 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) {
Stop(); Stop();
// The thread is now guaranteed dead // The thread is now guaranteed dead
HRESULT res;
// We sure better have a buffer // We sure better have a buffer
assert(buffer); assert(buffer);
// Create a new notification event
// It was already destroyed by Stop
notificationEvent = CreateEvent(NULL,false,false,NULL);
// Create notification interface
IDirectSoundNotify8 *notify;
HRESULT res;
res = buffer->QueryInterface(IID_IDirectSoundNotify8,(LPVOID*)&notify);
if (FAILED(res)) return;
// Set notification
DSBPOSITIONNOTIFY positionNotify[4];
positionNotify[0].dwOffset = bufSize / 8;
positionNotify[0].hEventNotify = notificationEvent;
positionNotify[1].dwOffset = 3 * bufSize / 8;
positionNotify[1].hEventNotify = notificationEvent;
positionNotify[2].dwOffset = 5 * bufSize / 8;
positionNotify[2].hEventNotify = notificationEvent;
positionNotify[3].dwOffset = 7 * bufSize / 8;
positionNotify[3].hEventNotify = notificationEvent;
res = notify->SetNotificationPositions(4,positionNotify);
notify->Release();
// Set variables // Set variables
startPos = start; startPos = start;
endPos = start+count; endPos = start+count;
playPos = start; playPos = start;
offset = 0; offset = 0;
// Fill buffer // Fill whole buffer
FillBuffer(); FillBuffer(true);
// Start thread // Start thread
thread = new DirectSoundPlayerThread(this); thread = new DirectSoundPlayerThread(this);
@ -285,7 +286,7 @@ void DirectSoundPlayer::Stop(bool timerToo) {
// The thread is now guaranteed dead and there are no concurrency problems to worry about // The thread is now guaranteed dead and there are no concurrency problems to worry about
// Stop // Stop
if (buffer) buffer->Stop(); if (buffer) buffer->Stop(); // the thread should have done this already
// Reset variables // Reset variables
playing = false; playing = false;
@ -295,7 +296,8 @@ void DirectSoundPlayer::Stop(bool timerToo) {
offset = 0; offset = 0;
// Close event handle // Close event handle
CloseHandle(notificationEvent); if (notificationEvent)
CloseHandle(notificationEvent);
notificationEvent = 0; notificationEvent = 0;
// Stop timer // Stop timer
@ -344,7 +346,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 = CreateSemaphore(NULL, 0, 1, NULL); stopnotify = CreateEvent(NULL, false, false, NULL);
} }
@ -358,25 +360,17 @@ DirectSoundPlayerThread::~DirectSoundPlayerThread() {
////////////////////// //////////////////////
// Thread entry point // Thread entry point
wxThread::ExitCode DirectSoundPlayerThread::Entry() { wxThread::ExitCode DirectSoundPlayerThread::Entry() {
// Objects to wait for // Wake up thread every half second to fill buffer as needed
HANDLE notifies[2] = {stopnotify, parent->notificationEvent}; // This more or less assumes the buffer is at least one second long
while (WaitForSingleObject(stopnotify, 500)) {
while (true) { if (!parent->FillBuffer(false))
// Wait for something to happen // FillBuffer returns true if there's more to play
DWORD cause = WaitForMultipleObjects(2, notifies, false, INFINITE); // So false means end-of-stream
if (cause == WAIT_OBJECT_0) {
// stopnotify
break; break;
}
else if (cause == WAIT_OBJECT_0 + 1) {
// dsound notification event
// fill the buffer
if (!parent->FillBuffer())
break; // end of stream
}
} }
wxLogDebug(_T("DS thread dead"));
parent->playing = false; parent->playing = false;
parent->buffer->Stop(); parent->buffer->Stop();
return 0; return 0;
@ -387,7 +381,7 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() {
// Stop playback thread // Stop playback thread
void DirectSoundPlayerThread::Stop() { void DirectSoundPlayerThread::Stop() {
// Increase the stopnotify by one, causing a wait for it to succeed // Increase the stopnotify by one, causing a wait for it to succeed
ReleaseSemaphore(stopnotify, 1, NULL); SetEvent(stopnotify);
} }
#endif #endif

View file

@ -86,7 +86,7 @@ private:
IDirectSoundBuffer8 *buffer; IDirectSoundBuffer8 *buffer;
HANDLE notificationEvent; HANDLE notificationEvent;
bool FillBuffer(); bool FillBuffer(bool fill);
DirectSoundPlayerThread *thread; DirectSoundPlayerThread *thread;