diff --git a/example/sio_sine.c b/example/sio_sine.c index fc8c240..7eb4707 100644 --- a/example/sio_sine.c +++ b/example/sio_sine.c @@ -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) { diff --git a/soundio/soundio.h b/soundio/soundio.h index cc22979..37ac3d3 100644 --- a/soundio/soundio.h +++ b/soundio/soundio.h @@ -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. diff --git a/src/alsa.cpp b/src/alsa.cpp index e255874..56a3013 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -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; diff --git a/src/wasapi.cpp b/src/wasapi.cpp index ea12254..b2a8e09 100644 --- a/src/wasapi.cpp +++ b/src/wasapi.cpp @@ -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; } diff --git a/src/wasapi.hpp b/src/wasapi.hpp index 17507ba..41d77d4 100644 --- a/src/wasapi.hpp +++ b/src/wasapi.hpp @@ -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;