From fe4a04d93cfc54a9b7e59f758497a0fe148af83c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Sep 2015 14:43:50 -0700 Subject: [PATCH] ALSA: better pause/unpause behavior --- example/sio_sine.c | 20 ++++++++++++++++++-- soundio/soundio.h | 19 +++++++++++++------ src/alsa.cpp | 18 ++++++++++++++++-- src/alsa.hpp | 2 ++ src/coreaudio.cpp | 2 +- src/jack.cpp | 2 +- 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/example/sio_sine.c b/example/sio_sine.c index 04a56a6..253e843 100644 --- a/example/sio_sine.c +++ b/example/sio_sine.c @@ -229,8 +229,24 @@ int main(int argc, char **argv) { return 1; } - for (;;) - soundio_wait_events(soundio); + for (;;) { + soundio_flush_events(soundio); + int c = getc(stdin); + if (c == 'p') { + fprintf(stderr, "pausing result: %s\n", + soundio_strerror(soundio_outstream_pause(outstream, true))); + } else if (c == 'u') { + fprintf(stderr, "unpausing result: %s\n", + soundio_strerror(soundio_outstream_pause(outstream, false))); + } else if (c == 'c') { + fprintf(stderr, "clear buffer result: %s\n", + soundio_strerror(soundio_outstream_clear_buffer(outstream))); + } else if (c == '\r' || c == '\n') { + // ignore + } else { + fprintf(stderr, "Unrecognized command: %c\n", c); + } + } soundio_outstream_destroy(outstream); soundio_device_unref(device); diff --git a/soundio/soundio.h b/soundio/soundio.h index 7e75582..cc22979 100644 --- a/soundio/soundio.h +++ b/soundio/soundio.h @@ -972,23 +972,30 @@ SOUNDIO_EXPORT int soundio_outstream_begin_write(struct SoundIoOutStream *outstr SOUNDIO_EXPORT int soundio_outstream_end_write(struct SoundIoOutStream *outstream); /// Clears the output stream buffer. -/// You must call this function only from the SoundIoOutStream::write_callback thread context. +/// This function can be called from any thread. +/// Some backends do not support clearing the buffer. On these backends this +/// function will return SoundIoErrorIncompatibleBackend. /// Possible errors: /// /// * #SoundIoErrorStreaming +/// * #SoundIoErrorIncompatibleBackend SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); -/// If the underyling device supports pausing, this pauses the stream and -/// prevents SoundIoOutStream::write_callback from being called. Otherwise this returns -/// #SoundIoErrorIncompatibleDevice. -/// You must call this function only from the SoundIoOutStream::write_callback thread context. +/// If the underlying device supports pausing, this pauses the stream. +/// SoundIoOutStream::write_callback may be called a few more times if the +/// buffer is not full. +/// Pausing might put the hardware into a low power state which is ideal if your +/// software is silent for some time. +/// This function may be called any thread. /// Pausing when already paused or unpausing when already unpaused has no /// effect and always returns SoundIoErrorNone. /// /// Possible errors: /// * #SoundIoErrorBackendDisconnected /// * #SoundIoErrorStreaming -/// * #SoundIoErrorIncompatibleDevice - device does not support pausing/unpausing +/// * #SoundIoErrorIncompatibleDevice - device does not support +/// pausing/unpausing. This error code might not be returned even if the +/// device does not support pausing/unpausing. SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause); diff --git a/src/alsa.cpp b/src/alsa.cpp index 307df65..0d34ef2 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -1054,6 +1054,7 @@ void outstream_thread_run(void *arg) { continue; } case SND_PCM_STATE_RUNNING: + case SND_PCM_STATE_PAUSED: { if ((err = outstream_wait_for_poll(os)) < 0) { if (!osa->thread_exit_flag.test_and_set()) @@ -1090,7 +1091,6 @@ void outstream_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: outstream->error_callback(outstream, SoundIoErrorStreaming); return; @@ -1437,9 +1437,17 @@ static int outstream_clear_buffer_alsa(SoundIoPrivate *si, static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { SoundIoOutStreamAlsa *osa = &os->backend_data.alsa; + + if (osa->is_paused == pause) + return 0; + int err; - if ((err = snd_pcm_pause(osa->handle, pause)) < 0) + if ((err = snd_pcm_pause(osa->handle, pause)) < 0) { + fprintf(stderr, "alsa pause result: %s\n", snd_strerror(err)); return SoundIoErrorIncompatibleDevice; + } + + osa->is_paused = pause; return 0; } @@ -1701,9 +1709,15 @@ static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) { SoundIoInStreamAlsa *isa = &is->backend_data.alsa; + + if (isa->is_paused == pause) + return 0; + int err; if ((err = snd_pcm_pause(isa->handle, pause)) < 0) return SoundIoErrorIncompatibleDevice; + + isa->is_paused = pause; return 0; } diff --git a/src/alsa.hpp b/src/alsa.hpp index 1156b7d..8a74487 100644 --- a/src/alsa.hpp +++ b/src/alsa.hpp @@ -58,6 +58,7 @@ struct SoundIoOutStreamAlsa { atomic_flag thread_exit_flag; int period_size; int write_frame_count; + bool is_paused; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; @@ -75,6 +76,7 @@ struct SoundIoInStreamAlsa { atomic_flag thread_exit_flag; int period_size; int read_frame_count; + bool is_paused; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; diff --git a/src/coreaudio.cpp b/src/coreaudio.cpp index 2be7d0c..b225396 100644 --- a/src/coreaudio.cpp +++ b/src/coreaudio.cpp @@ -1049,7 +1049,7 @@ static int outstream_end_write_ca(struct SoundIoPrivate *si, struct SoundIoOutSt } static int outstream_clear_buffer_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { - return 0; + return SoundIoErrorIncompatibleBackend; } diff --git a/src/jack.cpp b/src/jack.cpp index bf5c187..b1df8db 100644 --- a/src/jack.cpp +++ b/src/jack.cpp @@ -561,7 +561,7 @@ static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOut } static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { - return 0; + return SoundIoErrorIncompatibleBackend; }