Skip to content

Commit

Permalink
WASAPI: better pause/unpause and clear buffer behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrk committed Sep 1, 2015
1 parent c88e79c commit c14d61f
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 15 deletions.
5 changes: 5 additions & 0 deletions example/sio_sine.c
Expand Up @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions soundio/soundio.h
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/alsa.cpp
Expand Up @@ -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())
Expand Down Expand Up @@ -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;
Expand Down
86 changes: 72 additions & 14 deletions src/wasapi.cpp
Expand Up @@ -1246,14 +1246,46 @@ 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);
return;
}
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);
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions src/wasapi.hpp
Expand Up @@ -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;
Expand Down

0 comments on commit c14d61f

Please sign in to comment.