WASAPI: better pause/unpause and clear buffer behavior

See #3
This commit is contained in:
Andrew Kelley 2015-09-01 16:31:56 -07:00
parent c88e79cb7b
commit c14d61fa32
5 changed files with 86 additions and 15 deletions

View file

@ -186,6 +186,11 @@ int main(int argc, char **argv) {
return 1;
}
fprintf(stderr,
"'p\\n' - pause\n"
"'u\\n' - unpause\n"
"'c\\n' - clear buffer\n"
"'q\\n' - quit\n");
fprintf(stderr, "Output device: %s\n", device->name);
if (device->probe_error) {

View file

@ -973,12 +973,17 @@ SOUNDIO_EXPORT int soundio_outstream_end_write(struct SoundIoOutStream *outstrea
/// Clears the output stream buffer.
/// This function can be called from any thread.
/// This function can be called regardless of whether the outstream is paused
/// or not.
/// Some backends do not support clearing the buffer. On these backends this
/// function will return SoundIoErrorIncompatibleBackend.
/// Some devices do not support clearing the buffer. On these devices this
/// function might return SoundIoErrorIncompatibleDevice.
/// Possible errors:
///
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorIncompatibleBackend
/// * #SoundIoErrorIncompatibleDevice
SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
/// If the underlying device supports pausing, this pauses the stream.

View file

@ -1138,6 +1138,7 @@ static void instream_thread_run(void *arg) {
}
continue;
case SND_PCM_STATE_RUNNING:
case SND_PCM_STATE_PAUSED:
{
if ((err = instream_wait_for_poll(is)) < 0) {
if (!isa->thread_exit_flag.test_and_set())
@ -1176,7 +1177,6 @@ static void instream_thread_run(void *arg) {
continue;
case SND_PCM_STATE_OPEN:
case SND_PCM_STATE_DRAINING:
case SND_PCM_STATE_PAUSED:
case SND_PCM_STATE_DISCONNECTED:
instream->error_callback(instream, SoundIoErrorStreaming);
return;

View file

@ -1246,6 +1246,38 @@ void outstream_shared_run(SoundIoOutStreamPrivate *os) {
return;
}
soundio_os_mutex_unlock(osw->mutex);
bool reset_buffer = false;
if (!osw->clear_buffer_flag.test_and_set()) {
if (!osw->is_paused) {
if (FAILED(hr = IAudioClient_Stop(osw->audio_client))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
osw->is_paused = true;
}
if (FAILED(hr = IAudioClient_Reset(osw->audio_client))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
osw->pause_resume_flag.clear();
reset_buffer = true;
}
if (!osw->pause_resume_flag.test_and_set()) {
bool pause = osw->desired_pause_state.load();
if (pause && !osw->is_paused) {
if (FAILED(hr = IAudioClient_Stop(osw->audio_client))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
osw->is_paused = true;
} else if (!pause && osw->is_paused) {
if (FAILED(hr = IAudioClient_Start(osw->audio_client))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
osw->is_paused = false;
}
}
if (FAILED(hr = IAudioClient_GetCurrentPadding(osw->audio_client, &frames_used))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
@ -1253,7 +1285,7 @@ void outstream_shared_run(SoundIoOutStreamPrivate *os) {
}
osw->writable_frame_count = osw->buffer_frame_count - frames_used;
if (osw->writable_frame_count > 0) {
if (frames_used == 0)
if (frames_used == 0 && !reset_buffer)
outstream->underflow_callback(outstream);
outstream->write_callback(outstream, 0, osw->writable_frame_count);
}
@ -1277,6 +1309,22 @@ void outstream_raw_run(SoundIoOutStreamPrivate *os) {
WaitForSingleObject(osw->h_event, INFINITE);
if (!osw->thread_exit_flag.test_and_set())
return;
if (!osw->pause_resume_flag.test_and_set()) {
bool pause = osw->desired_pause_state.load();
if (pause && !osw->is_paused) {
if (FAILED(hr = IAudioClient_Stop(osw->audio_client))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
osw->is_paused = true;
} else if (!pause && osw->is_paused) {
if (FAILED(hr = IAudioClient_Start(osw->audio_client))) {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
osw->is_paused = false;
}
}
outstream->write_callback(outstream, osw->buffer_frame_count, osw->buffer_frame_count);
}
@ -1331,6 +1379,10 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
SoundIoDevice *device = outstream->device;
SoundIo *soundio = &si->pub;
osw->pause_resume_flag.test_and_set();
osw->clear_buffer_flag.test_and_set();
osw->desired_pause_state.store(false);
// All the COM functions are supposed to be called from the same thread. libsoundio API does not
// restrict the calling thread context in this way. Furthermore, the user might have called
// CoInitializeEx with a different threading model than Single Threaded Apartment.
@ -1385,19 +1437,19 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
return 0;
}
static int outstream_pause_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi;
HRESULT hr;
if (pause && !osw->is_paused) {
if (FAILED(hr = IAudioClient_Stop(osw->audio_client)))
return SoundIoErrorStreaming;
osw->is_paused = true;
} else if (!pause && osw->is_paused) {
if (FAILED(hr = IAudioClient_Start(osw->audio_client)))
return SoundIoErrorStreaming;
osw->is_paused = false;
osw->desired_pause_state.store(pause);
osw->pause_resume_flag.clear();
if (osw->h_event) {
SetEvent(osw->h_event);
} else {
soundio_os_mutex_lock(osw->mutex);
soundio_os_cond_signal(osw->cond, osw->mutex);
soundio_os_mutex_unlock(osw->mutex);
}
return 0;
}
@ -1450,9 +1502,15 @@ static int outstream_end_write_wasapi(struct SoundIoPrivate *si, struct SoundIoO
static int outstream_clear_buffer_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi;
HRESULT hr;
if (FAILED(hr = IAudioClient_Reset(osw->audio_client)))
return SoundIoErrorStreaming;
if (osw->h_event) {
return SoundIoErrorIncompatibleDevice;
} else {
osw->clear_buffer_flag.clear();
soundio_os_mutex_lock(osw->mutex);
soundio_os_cond_signal(osw->cond, osw->mutex);
soundio_os_mutex_unlock(osw->mutex);
}
return 0;
}

View file

@ -67,6 +67,9 @@ struct SoundIoOutStreamWasapi {
UINT32 buffer_frame_count;
int write_frame_count;
HANDLE h_event;
atomic_bool desired_pause_state;
atomic_flag pause_resume_flag;
atomic_flag clear_buffer_flag;
bool is_paused;
bool open_complete;
int open_err;