diff --git a/aegisub/audio_player.cpp b/aegisub/audio_player.cpp index d2a213f19..ad2b3cf57 100644 --- a/aegisub/audio_player.cpp +++ b/aegisub/audio_player.cpp @@ -97,10 +97,8 @@ void AudioPlayer::SetDisplayTimer(wxTimer *timer) { // Ask to stop later void AudioPlayer::RequestStop() { wxCommandEvent event(wxEVT_STOP_AUDIO, 1000); - event.SetEventObject(this); - wxMutexGuiEnter(); - AddPendingEvent(event); - wxMutexGuiLeave(); + event.SetEventObject(this); + AddPendingEvent(event); // thread safe } diff --git a/aegisub/audio_player_dsound.cpp b/aegisub/audio_player_dsound.cpp index c451b23d8..34ba133a1 100644 --- a/aegisub/audio_player_dsound.cpp +++ b/aegisub/audio_player_dsound.cpp @@ -62,7 +62,6 @@ DirectSoundPlayer::DirectSoundPlayer() { buffer = NULL; directSound = NULL; thread = NULL; - threadRunning = false; notificationEvent = NULL; } @@ -82,12 +81,12 @@ void DirectSoundPlayer::OpenStream() { // Initialize the DirectSound object HRESULT res; - res = DirectSoundCreate8(NULL,&directSound,NULL); + res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device if (res != DS_OK) throw _T("Failed initializing DirectSound"); // Set DirectSound parameters AegisubApp *app = (AegisubApp*) wxTheApp; - directSound->SetCooperativeLevel((HWND)app->frame->GetHandle(),DSSCL_NORMAL); + directSound->SetCooperativeLevel((HWND)app->frame->GetHandle(),DSSCL_PRIORITY); // Create the wave format structure WAVEFORMATEX waveFormat; @@ -132,19 +131,25 @@ void DirectSoundPlayer::CloseStream() { // Stop it Stop(); - // Delete the DirectSound buffer -// delete buffer; - buffer = NULL; + // Unref the DirectSound buffer + if (buffer) { + buffer->Release(); + buffer = NULL; + } - // Delete the DirectSound object -// delete directSound; - directSound = NULL; + // Unref the DirectSound object + if (directSound) { + directSound->Release(); + directSound = NULL; + } } /////////////// // Fill buffer -void DirectSoundPlayer::FillBuffer(bool fill) { +bool DirectSoundPlayer::FillBuffer() { + if (playPos >= endPos) return false; + // Variables void *ptr1, *ptr2; unsigned long int size1, size2; @@ -168,7 +173,7 @@ void DirectSoundPlayer::FillBuffer(bool fill) { } // Error - if (!SUCCEEDED(res)) return; + if (FAILED(res)) return false; // Set offset offset = (offset + toWrite) % bufSize; @@ -206,6 +211,8 @@ void DirectSoundPlayer::FillBuffer(bool fill) { // Unlock buffer->Unlock(ptr1,size1,ptr2,size2); + + return true; } @@ -214,24 +221,20 @@ void DirectSoundPlayer::FillBuffer(bool fill) { void DirectSoundPlayer::Play(__int64 start,__int64 count) { // Make sure that it's stopped Stop(); + // The thread is now guaranteed dead - // Lock - wxMutexLocker locker(DSMutex); + // We sure better have a buffer + assert(buffer); - // Check if buffer is loaded - if (!buffer) return; - buffer->Stop(); - - // Create notification event - if (notificationEvent) - CloseHandle(notificationEvent); + // 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 (!SUCCEEDED(res)) return; + if (FAILED(res)) return; // Set notification DSBPOSITIONNOTIFY positionNotify[4]; @@ -253,20 +256,18 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) { offset = 0; // Fill buffer - FillBuffer(false); + FillBuffer(); + + // Start thread + thread = new DirectSoundPlayerThread(this); + thread->Create(); + thread->Run(); // Play buffer->SetCurrentPosition(0); res = buffer->Play(0,0,DSBPLAY_LOOPING); if (SUCCEEDED(res)) playing = true; - // Start thread - if (!thread) { - thread = new DirectSoundPlayerThread(this); - thread->Create(); - thread->Run(); - } - // Update timer if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15); } @@ -275,14 +276,13 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) { //////// // Stop void DirectSoundPlayer::Stop(bool timerToo) { - // Lock - wxMutexLocker locker(DSMutex); - // Stop the thread if (thread) { - thread->alive = false; + thread->Stop(); + thread->Wait(); thread = NULL; } + // The thread is now guaranteed dead and there are no concurrency problems to worry about // Stop if (buffer) buffer->Stop(); @@ -295,10 +295,8 @@ void DirectSoundPlayer::Stop(bool timerToo) { offset = 0; // Close event handle - if (notificationEvent) { - CloseHandle(notificationEvent); - notificationEvent = 0; - } + CloseHandle(notificationEvent); + notificationEvent = 0; // Stop timer if (timerToo && displayTimer) { @@ -346,72 +344,50 @@ __int64 DirectSoundPlayer::GetCurrentPosition() { // Thread constructor DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) { parent = par; - alive = true; - parent->threadRunning = true; + stopnotify = CreateSemaphore(NULL, 0, 1, NULL); } ///////////////////// // Thread destructor DirectSoundPlayerThread::~DirectSoundPlayerThread() { + CloseHandle(stopnotify); } ////////////////////// // Thread entry point wxThread::ExitCode DirectSoundPlayerThread::Entry() { - // Variables - unsigned long int playPos=0,endPos=0,bufSize=0; - bool playing; + // Objects to wait for + HANDLE notifies[2] = {stopnotify, parent->notificationEvent}; - // Wait for notification - while (alive) { - // Get variables - bool booga = true; - if (booga) { - if (!alive) break; - wxMutexLocker locker(parent->DSMutex); - if (!alive) break; - playPos = parent->GetCurrentPosition(); - endPos = parent->endPos; - bufSize = parent->bufSize; - playing = parent->playing; - if (!alive) break; - } + while (true) { + // Wait for something to happen + DWORD cause = WaitForMultipleObjects(2, notifies, false, INFINITE); - // Flag as stopped playing, but don't actually stop yet - if (playPos > endPos) { - if (!alive) break; - wxMutexLocker locker(parent->DSMutex); - if (!alive) break; - parent->playing = false; - } - - // Still playing? - if (playPos < endPos + bufSize/8) { - // Wait for signal - if (!alive) break; - WaitForSingleObject(parent->notificationEvent,1000); - if (!alive) break; - - // Fill buffer - wxMutexLocker locker(parent->DSMutex); - if (!alive) break; - parent->FillBuffer(false); - if (!alive) break; - } - - // Over, stop it - else { - if (alive) parent->Stop(); + if (cause == WAIT_OBJECT_0) { + // stopnotify break; } + else if (cause == WAIT_OBJECT_0 + 1) { + // dsound notification event + // fill the buffer + if (!parent->FillBuffer()) + break; // end of stream + } } - //wxMutexLocker locker(parent->DSMutex); - parent->threadRunning = false; - Delete(); + parent->playing = false; + parent->buffer->Stop(); return 0; } + +//////////////////////// +// Stop playback thread +void DirectSoundPlayerThread::Stop() { + // Increase the stopnotify by one, causing a wait for it to succeed + ReleaseSemaphore(stopnotify, 1, NULL); +} + #endif diff --git a/aegisub/audio_player_dsound.h b/aegisub/audio_player_dsound.h index c48d071fa..12f0489e3 100644 --- a/aegisub/audio_player_dsound.h +++ b/aegisub/audio_player_dsound.h @@ -56,9 +56,10 @@ class DirectSoundPlayer; class DirectSoundPlayerThread : public wxThread { private: DirectSoundPlayer *parent; + HANDLE stopnotify; public: - bool alive; + void Stop(); // Notify thread to stop audio playback. Thread safe. DirectSoundPlayerThread(DirectSoundPlayer *parent); ~DirectSoundPlayerThread(); @@ -72,9 +73,7 @@ class DirectSoundPlayer : public AudioPlayer { friend class DirectSoundPlayerThread; private: - wxMutex DSMutex; - - bool playing; + volatile bool playing; float volume; int offset; int bufSize; @@ -87,10 +86,9 @@ private: IDirectSoundBuffer8 *buffer; HANDLE notificationEvent; - void FillBuffer(bool fill); + bool FillBuffer(); DirectSoundPlayerThread *thread; - bool threadRunning; public: DirectSoundPlayer(); @@ -112,7 +110,7 @@ public: void SetVolume(double vol) { volume = vol; } double GetVolume() { return volume; } - wxMutex *GetMutex() { return &DSMutex; } + //wxMutex *GetMutex() { return &DSMutex; } }; #endif