From 3999f57698f290e050d7fb5ceeecd16ebedeaf61 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jul 2015 13:37:41 -0700 Subject: [PATCH] ability to pause and resume streams and some cleanup --- README.md | 24 +++++++++++++----------- src/alsa.cpp | 27 ++++++++++++++++++++++++--- src/dummy.cpp | 20 ++++++++++++++------ src/pulseaudio.cpp | 10 ++++++++++ src/soundio.cpp | 14 ++++++++++++++ src/soundio.h | 20 ++++++++++++++++---- src/soundio.hpp | 2 ++ 7 files changed, 93 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index a6f82e8..7cb5573 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,25 @@ exposed. ## Alternatives * [PortAudio](http://www.portaudio.com/) - - It does not support [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/). - - It logs messages to stdio and you can't turn that off. - - It does not support channel layouts / channel maps. - - It is not written by me. + - Does not support [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/). + - Logs messages to stdio and you can't turn that off. + - Does not support channel layouts / channel maps. + - Does not support emitting an event when available devices change. + - Not written by me. * [rtaudio](https://www.music.mcgill.ca/~gary/rtaudio/) - It is not a C library. - It uses [exceptions](http://stackoverflow.com/questions/1736146/why-is-exception-handling-bad). - It does not support channel layouts / channel maps. - - It is not written by me. + - Does not support emitting an event when available devices change. + - Not written by me. * [SDL](https://www.libsdl.org/) - - It comes with a bunch of other baggage - display, windowing, input - handling, and lots more. - - It is not designed with real-time low latency audio in mind. + - Comes with baggage: display, windowing, input handling, and lots more. + - Not designed with real-time low latency audio in mind. - Listing audio devices is [broken](https://github.com/andrewrk/node-groove/issues/13). - - It does not support recording devices. - - It does not support channel layouts / channel maps. - - It is not written by me. + - Does not support recording devices. + - Does not support channel layouts / channel maps. + - Does not support emitting an event when available devices change. + - Not written by me. ## How It Works diff --git a/src/alsa.cpp b/src/alsa.cpp index 007211a..cfd80eb 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -40,6 +40,10 @@ struct SoundIoOutStreamAlsa { SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; +struct SoundIoInStreamAlsa { + snd_pcm_t *handle; +}; + static void wakeup_device_poll(SoundIoAlsa *sia) { ssize_t amt = write(sia->notify_pipe_fd[1], "a", 1); if (amt == -1) { @@ -1013,9 +1017,8 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) // write the hardware parameters to device if ((err = snd_pcm_hw_params(osa->handle, hwparams)) < 0) { - //assert(err != -EINVAL); outstream_destroy_alsa(si, os); - return SoundIoErrorOpeningDevice; + return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice; } // set channel map @@ -1050,7 +1053,7 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) // write the software parameters to device if ((err = snd_pcm_sw_params(osa->handle, swparams)) < 0) { outstream_destroy_alsa(si, os); - return SoundIoErrorOpeningDevice; + return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice; } if ((err = snd_async_add_pcm_handler(&osa->ahandler, osa->handle, async_direct_callback, os)) < 0) { @@ -1122,6 +1125,14 @@ static void outstream_clear_buffer_alsa(SoundIoPrivate *si, soundio_panic("TODO"); } +static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { + SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data; + int err; + if ((err = snd_pcm_pause(osa->handle, pause)) < 0) + return SoundIoErrorIncompatibleDevice; + return 0; +} + static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { soundio_panic("TODO"); } @@ -1148,6 +1159,14 @@ static void instream_clear_buffer_alsa(SoundIoPrivate *si, SoundIoInStreamPrivat soundio_panic("TODO"); } +static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) { + SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data; + int err; + if ((err = snd_pcm_pause(isa->handle, pause)) < 0) + return SoundIoErrorIncompatibleDevice; + return 0; +} + int soundio_alsa_init(SoundIoPrivate *si) { int err; @@ -1234,6 +1253,7 @@ int soundio_alsa_init(SoundIoPrivate *si) { si->outstream_begin_write = outstream_begin_write_alsa; si->outstream_write = outstream_write_alsa; si->outstream_clear_buffer = outstream_clear_buffer_alsa; + si->outstream_pause = outstream_pause_alsa; si->instream_open = instream_open_alsa; si->instream_destroy = instream_destroy_alsa; @@ -1241,6 +1261,7 @@ int soundio_alsa_init(SoundIoPrivate *si) { si->instream_peek = instream_peek_alsa; si->instream_drop = instream_drop_alsa; si->instream_clear_buffer = instream_clear_buffer_alsa; + si->instream_pause = instream_pause_alsa; return 0; } diff --git a/src/dummy.cpp b/src/dummy.cpp index 9e6afcd..fa3f7fa 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -115,12 +115,10 @@ static void outstream_destroy_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate return; if (osd->thread) { - if (osd->thread) { - osd->abort_flag.clear(); - soundio_os_cond_signal(osd->cond, nullptr); - soundio_os_thread_destroy(osd->thread); - osd->thread = nullptr; - } + osd->abort_flag.clear(); + soundio_os_cond_signal(osd->cond, nullptr); + soundio_os_thread_destroy(osd->thread); + osd->thread = nullptr; } soundio_os_cond_destroy(osd->cond); osd->cond = nullptr; @@ -210,6 +208,10 @@ static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPri soundio_ring_buffer_clear(&osd->ring_buffer); } +static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { + soundio_panic("TODO"); +} + static int instream_open_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { soundio_panic("TODO"); } @@ -236,6 +238,10 @@ static void instream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoInStreamPriva soundio_panic("TODO"); } +static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) { + soundio_panic("TODO"); +} + static int set_all_device_formats(SoundIoDevice *device) { device->format_count = 18; device->formats = allocate(device->format_count); @@ -411,6 +417,7 @@ int soundio_dummy_init(SoundIoPrivate *si) { si->outstream_begin_write = outstream_begin_write_dummy; si->outstream_write = outstream_write_dummy; si->outstream_clear_buffer = outstream_clear_buffer_dummy; + si->outstream_pause = outstream_pause_dummy; si->instream_open = instream_open_dummy; si->instream_destroy = instream_destroy_dummy; @@ -418,6 +425,7 @@ int soundio_dummy_init(SoundIoPrivate *si) { si->instream_peek = instream_peek_dummy; si->instream_drop = instream_drop_dummy; si->instream_clear_buffer = instream_clear_buffer_dummy; + si->instream_pause = instream_pause_dummy; return 0; } diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index 8325640..343ce15 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -734,6 +734,10 @@ static void outstream_clear_buffer_pa(SoundIoPrivate *si, pa_threaded_mainloop_unlock(sipa->main_loop); } +static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, bool pause) { + soundio_panic("TODO"); +} + static void recording_stream_state_callback(pa_stream *stream, void *userdata) { SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; @@ -898,6 +902,10 @@ static void instream_clear_buffer_pa(SoundIoPrivate *si, SoundIoInStreamPrivate pa_threaded_mainloop_unlock(sipa->main_loop); } +static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) { + soundio_panic("TODO"); +} + int soundio_pulseaudio_init(SoundIoPrivate *si) { assert(!si->backend_data); SoundIoPulseAudio *sipa = create(); @@ -968,6 +976,7 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) { si->outstream_begin_write = outstream_begin_write_pa; si->outstream_write = outstream_write_pa; si->outstream_clear_buffer = outstream_clear_buffer_pa; + si->outstream_pause = outstream_pause_pa; si->instream_open = instream_open_pa; si->instream_destroy = instream_destroy_pa; @@ -975,6 +984,7 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) { si->instream_peek = instream_peek_pa; si->instream_drop = instream_drop_pa; si->instream_clear_buffer = instream_clear_buffer_pa; + si->instream_pause = instream_pause_pa; return 0; } diff --git a/src/soundio.cpp b/src/soundio.cpp index 4160ff1..fcd51e9 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -430,6 +430,13 @@ int soundio_outstream_start(struct SoundIoOutStream *outstream) { return si->outstream_start(si, os); } +int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause) { + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + return si->outstream_pause(si, os, pause); +} + struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) { SoundIoInStreamPrivate *is = create(); if (!is) @@ -490,6 +497,13 @@ void soundio_instream_destroy(struct SoundIoInStream *instream) { destroy(is); } +int soundio_instream_pause(struct SoundIoInStream *instream, bool pause) { + SoundIo *soundio = instream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream; + return si->instream_pause(si, is, pause); +} + void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) { if (!devices_info) return; diff --git a/src/soundio.h b/src/soundio.h index b0762eb..c5ced1c 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -304,7 +304,7 @@ struct SoundIoOutStream { // Defaults to `buffer_duration / 2` (and then clamped into range). double period_duration; - // Defaults to NULL. + // Defaults to NULL. Put whatever you want here. void *userdata; // `err` is SoundIoErrorUnderflow or SoundIoErrorStreaming. // SoundIoErrorUnderflow means that the sound device ran out of buffered @@ -312,8 +312,7 @@ struct SoundIoOutStream { // SoundIoErrorStreaming is an unrecoverable error. The stream is in an // invalid state and must be destroyed. void (*error_callback)(struct SoundIoOutStream *, int err); - // `frame_count` is the number of requested frames to write. - void (*write_callback)(struct SoundIoOutStream *, int frame_count); + void (*write_callback)(struct SoundIoOutStream *, int requested_frame_count); // computed automatically when you call soundio_outstream_open int bytes_per_frame; @@ -344,6 +343,7 @@ struct SoundIoInStream { // Defaults to `buffer_duration / 8`. double period_duration; + // Defaults to NULL. Put whatever you want here. void *userdata; void (*read_callback)(struct SoundIoInStream *); @@ -354,6 +354,7 @@ struct SoundIoInStream { // The size of this struct is not part of the API or ABI. struct SoundIo { + // Defaults to NULL. Put whatever you want here. void *userdata; void (*on_devices_change)(struct SoundIo *); void (*on_events_signal)(struct SoundIo *); @@ -380,7 +381,7 @@ const char *soundio_backend_name(enum SoundIoBackend backend); // return the number of available backends int soundio_backend_count(struct SoundIo *soundio); -// get the backend at the specified index (0 <= index < soundio_backend_count) +// get the available backend at the specified index (0 <= index < soundio_backend_count) enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index); // when you call this, the on_devices_change and on_events_signal callbacks @@ -515,6 +516,12 @@ int soundio_outstream_write(struct SoundIoOutStream *outstream, int frame_count) void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); +// If the underyling device supports pausing, this pauses the stream and +// prevents `write_callback` from being called. Otherwise this returns +// `SoundIoErrorIncompatibleDevice`. +int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause); + + // Input Streams @@ -534,6 +541,11 @@ void soundio_instream_drop(struct SoundIoInStream *instream); void soundio_instream_clear_buffer(struct SoundIoInStream *instream); +// If the underyling device supports pausing, this pauses the stream and +// prevents `read_callback` from being called. Otherwise this returns +// `SoundIoErrorIncompatibleDevice`. +int soundio_instream_pause(struct SoundIoInStream *instream, bool pause); + // Ring Buffer struct SoundIoRingBuffer; diff --git a/src/soundio.hpp b/src/soundio.hpp index efb9929..cd6c24c 100644 --- a/src/soundio.hpp +++ b/src/soundio.hpp @@ -51,6 +51,7 @@ struct SoundIoPrivate { SoundIoChannelArea **out_areas, int *frame_count); int (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count); void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause); int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); @@ -60,6 +61,7 @@ struct SoundIoPrivate { const char **data, int *frame_count); void (*instream_drop)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); void (*instream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); + int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause); }; void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);