Rewrote lots of the DirectSound audio code, hopefully it's more stable now.

Originally committed to SVN as r1098.
This commit is contained in:
Niels Martin Hansen 2007-04-19 23:38:54 +00:00
parent 877c4bf1b1
commit 813a487b23
3 changed files with 67 additions and 95 deletions

View file

@ -97,10 +97,8 @@ void AudioPlayer::SetDisplayTimer(wxTimer *timer) {
// Ask to stop later // Ask to stop later
void AudioPlayer::RequestStop() { void AudioPlayer::RequestStop() {
wxCommandEvent event(wxEVT_STOP_AUDIO, 1000); wxCommandEvent event(wxEVT_STOP_AUDIO, 1000);
event.SetEventObject(this); event.SetEventObject(this);
wxMutexGuiEnter(); AddPendingEvent(event); // thread safe
AddPendingEvent(event);
wxMutexGuiLeave();
} }

View file

@ -62,7 +62,6 @@ DirectSoundPlayer::DirectSoundPlayer() {
buffer = NULL; buffer = NULL;
directSound = NULL; directSound = NULL;
thread = NULL; thread = NULL;
threadRunning = false;
notificationEvent = NULL; notificationEvent = NULL;
} }
@ -82,12 +81,12 @@ void DirectSoundPlayer::OpenStream() {
// Initialize the DirectSound object // Initialize the DirectSound object
HRESULT res; 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"); if (res != DS_OK) throw _T("Failed initializing DirectSound");
// Set DirectSound parameters // Set DirectSound parameters
AegisubApp *app = (AegisubApp*) wxTheApp; 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 // Create the wave format structure
WAVEFORMATEX waveFormat; WAVEFORMATEX waveFormat;
@ -132,19 +131,25 @@ void DirectSoundPlayer::CloseStream() {
// Stop it // Stop it
Stop(); Stop();
// Delete the DirectSound buffer // Unref the DirectSound buffer
// delete buffer; if (buffer) {
buffer = NULL; buffer->Release();
buffer = NULL;
}
// Delete the DirectSound object // Unref the DirectSound object
// delete directSound; if (directSound) {
directSound = NULL; directSound->Release();
directSound = NULL;
}
} }
/////////////// ///////////////
// Fill buffer // Fill buffer
void DirectSoundPlayer::FillBuffer(bool fill) { bool DirectSoundPlayer::FillBuffer() {
if (playPos >= endPos) return false;
// Variables // Variables
void *ptr1, *ptr2; void *ptr1, *ptr2;
unsigned long int size1, size2; unsigned long int size1, size2;
@ -168,7 +173,7 @@ void DirectSoundPlayer::FillBuffer(bool fill) {
} }
// Error // Error
if (!SUCCEEDED(res)) return; if (FAILED(res)) return false;
// Set offset // Set offset
offset = (offset + toWrite) % bufSize; offset = (offset + toWrite) % bufSize;
@ -206,6 +211,8 @@ void DirectSoundPlayer::FillBuffer(bool fill) {
// Unlock // Unlock
buffer->Unlock(ptr1,size1,ptr2,size2); buffer->Unlock(ptr1,size1,ptr2,size2);
return true;
} }
@ -214,24 +221,20 @@ void DirectSoundPlayer::FillBuffer(bool fill) {
void DirectSoundPlayer::Play(__int64 start,__int64 count) { void DirectSoundPlayer::Play(__int64 start,__int64 count) {
// Make sure that it's stopped // Make sure that it's stopped
Stop(); Stop();
// The thread is now guaranteed dead
// Lock // We sure better have a buffer
wxMutexLocker locker(DSMutex); assert(buffer);
// Check if buffer is loaded // Create a new notification event
if (!buffer) return; // It was already destroyed by Stop
buffer->Stop();
// Create notification event
if (notificationEvent)
CloseHandle(notificationEvent);
notificationEvent = CreateEvent(NULL,false,false,NULL); notificationEvent = CreateEvent(NULL,false,false,NULL);
// Create notification interface // Create notification interface
IDirectSoundNotify8 *notify; IDirectSoundNotify8 *notify;
HRESULT res; HRESULT res;
res = buffer->QueryInterface(IID_IDirectSoundNotify8,(LPVOID*)&notify); res = buffer->QueryInterface(IID_IDirectSoundNotify8,(LPVOID*)&notify);
if (!SUCCEEDED(res)) return; if (FAILED(res)) return;
// Set notification // Set notification
DSBPOSITIONNOTIFY positionNotify[4]; DSBPOSITIONNOTIFY positionNotify[4];
@ -253,20 +256,18 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) {
offset = 0; offset = 0;
// Fill buffer // Fill buffer
FillBuffer(false); FillBuffer();
// Start thread
thread = new DirectSoundPlayerThread(this);
thread->Create();
thread->Run();
// Play // Play
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;
// Start thread
if (!thread) {
thread = new DirectSoundPlayerThread(this);
thread->Create();
thread->Run();
}
// Update timer // Update timer
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15); if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
} }
@ -275,14 +276,13 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) {
//////// ////////
// Stop // Stop
void DirectSoundPlayer::Stop(bool timerToo) { void DirectSoundPlayer::Stop(bool timerToo) {
// Lock
wxMutexLocker locker(DSMutex);
// Stop the thread // Stop the thread
if (thread) { if (thread) {
thread->alive = false; thread->Stop();
thread->Wait();
thread = NULL; thread = NULL;
} }
// 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();
@ -295,10 +295,8 @@ void DirectSoundPlayer::Stop(bool timerToo) {
offset = 0; offset = 0;
// Close event handle // Close event handle
if (notificationEvent) { CloseHandle(notificationEvent);
CloseHandle(notificationEvent); notificationEvent = 0;
notificationEvent = 0;
}
// Stop timer // Stop timer
if (timerToo && displayTimer) { if (timerToo && displayTimer) {
@ -346,72 +344,50 @@ __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;
alive = true; stopnotify = CreateSemaphore(NULL, 0, 1, NULL);
parent->threadRunning = true;
} }
///////////////////// /////////////////////
// Thread destructor // Thread destructor
DirectSoundPlayerThread::~DirectSoundPlayerThread() { DirectSoundPlayerThread::~DirectSoundPlayerThread() {
CloseHandle(stopnotify);
} }
////////////////////// //////////////////////
// Thread entry point // Thread entry point
wxThread::ExitCode DirectSoundPlayerThread::Entry() { wxThread::ExitCode DirectSoundPlayerThread::Entry() {
// Variables // Objects to wait for
unsigned long int playPos=0,endPos=0,bufSize=0; HANDLE notifies[2] = {stopnotify, parent->notificationEvent};
bool playing;
// Wait for notification while (true) {
while (alive) { // Wait for something to happen
// Get variables DWORD cause = WaitForMultipleObjects(2, notifies, false, INFINITE);
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;
}
// Flag as stopped playing, but don't actually stop yet if (cause == WAIT_OBJECT_0) {
if (playPos > endPos) { // stopnotify
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();
break; 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->playing = false;
parent->threadRunning = false; parent->buffer->Stop();
Delete();
return 0; 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 #endif

View file

@ -56,9 +56,10 @@ class DirectSoundPlayer;
class DirectSoundPlayerThread : public wxThread { class DirectSoundPlayerThread : public wxThread {
private: private:
DirectSoundPlayer *parent; DirectSoundPlayer *parent;
HANDLE stopnotify;
public: public:
bool alive; void Stop(); // Notify thread to stop audio playback. Thread safe.
DirectSoundPlayerThread(DirectSoundPlayer *parent); DirectSoundPlayerThread(DirectSoundPlayer *parent);
~DirectSoundPlayerThread(); ~DirectSoundPlayerThread();
@ -72,9 +73,7 @@ class DirectSoundPlayer : public AudioPlayer {
friend class DirectSoundPlayerThread; friend class DirectSoundPlayerThread;
private: private:
wxMutex DSMutex; volatile bool playing;
bool playing;
float volume; float volume;
int offset; int offset;
int bufSize; int bufSize;
@ -87,10 +86,9 @@ private:
IDirectSoundBuffer8 *buffer; IDirectSoundBuffer8 *buffer;
HANDLE notificationEvent; HANDLE notificationEvent;
void FillBuffer(bool fill); bool FillBuffer();
DirectSoundPlayerThread *thread; DirectSoundPlayerThread *thread;
bool threadRunning;
public: public:
DirectSoundPlayer(); DirectSoundPlayer();
@ -112,7 +110,7 @@ public:
void SetVolume(double vol) { volume = vol; } void SetVolume(double vol) { volume = vol; }
double GetVolume() { return volume; } double GetVolume() { return volume; }
wxMutex *GetMutex() { return &DSMutex; } //wxMutex *GetMutex() { return &DSMutex; }
}; };
#endif #endif