Fix race conditions in the dsound2 player that occasionally resulted in audio playback stopping instantly

Originally committed to SVN as r5856.
This commit is contained in:
Thomas Goyne 2011-11-16 19:55:31 +00:00
parent b16f1a0698
commit 672f420d6c

View file

@ -46,8 +46,8 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_dsound2.h" #include "audio_player_dsound2.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
@ -279,7 +279,13 @@ unsigned int __stdcall DirectSoundPlayer2Thread::ThreadProc(void *parameter)
} }
/// Macro used to set error_message, error_happened and end the thread /// Macro used to set error_message, error_happened and end the thread
#define REPORT_ERROR(msg) { error_message = "DirectSoundPlayer2Thread: " msg; SetEvent(error_happened); return; } #define REPORT_ERROR(msg) \
{ \
ResetEvent(is_playing); \
error_message = "DirectSoundPlayer2Thread: " msg; \
SetEvent(error_happened); \
return; \
}
void DirectSoundPlayer2Thread::Run() void DirectSoundPlayer2Thread::Run()
{ {
@ -364,7 +370,6 @@ void DirectSoundPlayer2Thread::Run()
{ {
// Start or restart playback // Start or restart playback
bfr->Stop(); bfr->Stop();
ResetEvent(is_playing);
next_input_frame = start_frame; next_input_frame = start_frame;
@ -425,57 +430,41 @@ void DirectSoundPlayer2Thread::Run()
} }
case WAIT_OBJECT_0+1: case WAIT_OBJECT_0+1:
{ stop_playback:
// Stop playing // Stop playing
bfr->Stop(); bfr->Stop();
ResetEvent(is_playing); ResetEvent(is_playing);
playback_should_be_running = false; playback_should_be_running = false;
break; break;
}
case WAIT_OBJECT_0+2: case WAIT_OBJECT_0+2:
// Set end frame
if (end_frame <= next_input_frame)
{ {
// Set end frame goto stop_playback;
if (end_frame <= next_input_frame)
{
bfr->Stop();
ResetEvent(is_playing);
playback_should_be_running = false;
}
else
{
// If the user is dragging the start or end point in the audio display
// the set end frame events might come in faster than the timeouts happen
// and then new data never get filled into the buffer. See bug #915.
goto do_fill_buffer;
}
break;
} }
// If the user is dragging the start or end point in the audio display
// the set end frame events might come in faster than the timeouts happen
// and then new data never get filled into the buffer. See bug #915.
goto do_fill_buffer;
case WAIT_OBJECT_0+3: case WAIT_OBJECT_0+3:
{ // Change volume
// Change volume // We aren't thread safe right now, filling the buffers grabs volume directly
// We aren't thread safe right now, filling the buffers grabs volume directly // from the field set by the controlling thread, but it shouldn't be a major
// from the field set by the controlling thread, but it shouldn't be a major // problem if race conditions do occur, just some momentary distortion.
// problem if race conditions do occur, just some momentary distortion. goto do_fill_buffer;
goto do_fill_buffer;
}
case WAIT_OBJECT_0+4: case WAIT_OBJECT_0+4:
{ // Perform suicide
// Perform suicide running = false;
bfr->Stop(); goto stop_playback;
ResetEvent(is_playing);
playback_should_be_running = false;
running = false;
break;
}
case WAIT_TIMEOUT: case WAIT_TIMEOUT:
do_fill_buffer: do_fill_buffer:
{ {
// Time to fill more into buffer // Time to fill more into buffer
if (!playback_should_be_running) if (!playback_should_be_running)
break; break;
@ -487,10 +476,7 @@ do_fill_buffer:
{ {
// Not looping playback... // Not looping playback...
// hopefully we only triggered timeout after being done with the buffer // hopefully we only triggered timeout after being done with the buffer
bfr->Stop(); goto stop_playback;
ResetEvent(is_playing);
playback_should_be_running = false;
break;
} }
DWORD play_cursor; DWORD play_cursor;
@ -716,6 +702,20 @@ void DirectSoundPlayer2Thread::Play(int64_t start, int64_t count)
SetEvent(event_start_playback); SetEvent(event_start_playback);
last_playback_restart = GetTickCount(); last_playback_restart = GetTickCount();
// Block until playback actually begins to avoid race conditions with
// checking if playback is in progress
HANDLE events_to_wait[] = { is_playing, error_happened };
switch (WaitForMultipleObjects(2, events_to_wait, FALSE, INFINITE))
{
case WAIT_OBJECT_0+0: // Playing
LOG_D("audio/player/dsound") << "Playback begun";
break;
case WAIT_OBJECT_0+1: // Error
throw error_message;
default:
throw "Unexpected result from WaitForMultipleObjects in DirectSoundPlayer2Thread::Play";
}
} }
void DirectSoundPlayer2Thread::Stop() void DirectSoundPlayer2Thread::Stop()