From 4276b02f89fdee06da6621d155e6219b47dc9e70 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Fri, 20 Apr 2007 16:27:18 +0000 Subject: [PATCH] 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. --- aegisub/audio_player_dsound.cpp | 116 +++++++++++++++----------------- aegisub/audio_player_dsound.h | 2 +- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/aegisub/audio_player_dsound.cpp b/aegisub/audio_player_dsound.cpp index 34ba133a1..a1df5bfeb 100644 --- a/aegisub/audio_player_dsound.cpp +++ b/aegisub/audio_player_dsound.cpp @@ -105,7 +105,7 @@ void DirectSoundPlayer::OpenStream() { bufSize = MIN(MAX(min,aim),max); DSBUFFERDESC desc; 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.dwReserved = 0; desc.lpwfxFormat = &waveFormat; @@ -147,49 +147,67 @@ void DirectSoundPlayer::CloseStream() { /////////////// // Fill buffer -bool DirectSoundPlayer::FillBuffer() { +bool DirectSoundPlayer::FillBuffer(bool fill) { if (playPos >= endPos) return false; // Variables + HRESULT res; void *ptr1, *ptr2; unsigned long int size1, size2; AudioProvider *provider = GetProvider(); int bytesps = provider->GetBytesPerSample(); // To write length - //int max = bufSize/4; - //if (fill) max = bufSize; - //int toWrite = MIN(max,(endPos-playPos)*bytesps); - int toWrite = bufSize/4; + int toWrite = 0; + if (fill) { + toWrite = bufSize; + } + 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 - HRESULT res; - res = buffer->Lock(offset,toWrite,&ptr1,&size1,&ptr2,&size2,0); +RetryLock: + 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? if (res == DSERR_BUFFERLOST) { + wxLogDebug(_T("Lost DSound buffer")); buffer->Restore(); - res = buffer->Lock(offset,toWrite,&ptr1,&size1,&ptr2,&size2,0); + goto RetryLock; } // Error if (FAILED(res)) return false; - // Set offset + // Update offset 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 unsigned long int count1 = size1 / 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 left = 0; if (endPos > playPos) left = endPos - playPos; unsigned long int delta = 0; if (totalCount > left) delta = totalCount - left; - // If so, don't allow it + // If so, zero-fill buffer first if (delta) { // Zero at start memset(ptr1,0,size1); @@ -204,13 +222,17 @@ bool DirectSoundPlayer::FillBuffer() { delta -= temp; } + //wxLogDebug(_T("DSound Fill Buffer, playpos=%d, count1=%u, count2=%u"), (int)playPos, count1, count2); + // Get source wave - if (count1) provider->GetAudioWithVolume(ptr1,playPos,count1,volume); - if (count2) provider->GetAudioWithVolume(ptr2,playPos+count1,count2,volume); - playPos += totalCount; + if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume); + if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume); + playPos += count1+count2; + + //wxLogDebug(_T("DSound Fill Buffer post-fill, playpos=%d, count1=%u, count2=%u"), (int)playPos, count1, count2); // Unlock - buffer->Unlock(ptr1,size1,ptr2,size2); + buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps); return true; } @@ -223,40 +245,19 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) { Stop(); // The thread is now guaranteed dead + HRESULT res; + // We sure better have a 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*)¬ify); - 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 startPos = start; endPos = start+count; playPos = start; offset = 0; - // Fill buffer - FillBuffer(); + // Fill whole buffer + FillBuffer(true); // Start thread 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 // Stop - if (buffer) buffer->Stop(); + if (buffer) buffer->Stop(); // the thread should have done this already // Reset variables playing = false; @@ -295,7 +296,8 @@ void DirectSoundPlayer::Stop(bool timerToo) { offset = 0; // Close event handle - CloseHandle(notificationEvent); + if (notificationEvent) + CloseHandle(notificationEvent); notificationEvent = 0; // Stop timer @@ -344,7 +346,7 @@ __int64 DirectSoundPlayer::GetCurrentPosition() { // Thread constructor DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) { parent = par; - stopnotify = CreateSemaphore(NULL, 0, 1, NULL); + stopnotify = CreateEvent(NULL, false, false, NULL); } @@ -358,25 +360,17 @@ DirectSoundPlayerThread::~DirectSoundPlayerThread() { ////////////////////// // Thread entry point wxThread::ExitCode DirectSoundPlayerThread::Entry() { - // Objects to wait for - HANDLE notifies[2] = {stopnotify, parent->notificationEvent}; - - while (true) { - // Wait for something to happen - DWORD cause = WaitForMultipleObjects(2, notifies, false, INFINITE); - - if (cause == WAIT_OBJECT_0) { - // stopnotify + // Wake up thread every half second to fill buffer as needed + // This more or less assumes the buffer is at least one second long + while (WaitForSingleObject(stopnotify, 500)) { + if (!parent->FillBuffer(false)) + // FillBuffer returns true if there's more to play + // So false means end-of-stream 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->buffer->Stop(); return 0; @@ -387,7 +381,7 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() { // Stop playback thread void DirectSoundPlayerThread::Stop() { // Increase the stopnotify by one, causing a wait for it to succeed - ReleaseSemaphore(stopnotify, 1, NULL); + SetEvent(stopnotify); } #endif diff --git a/aegisub/audio_player_dsound.h b/aegisub/audio_player_dsound.h index 12f0489e3..c8ecf22f4 100644 --- a/aegisub/audio_player_dsound.h +++ b/aegisub/audio_player_dsound.h @@ -86,7 +86,7 @@ private: IDirectSoundBuffer8 *buffer; HANDLE notificationEvent; - bool FillBuffer(); + bool FillBuffer(bool fill); DirectSoundPlayerThread *thread;