diff --git a/README.md b/README.md index 36bdc03..1d42cbe 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,9 @@ view `coverage/index.html` in a browser. 0. Figure out a way to test prebuf. I suspect prebuf not working for ALSA which is why we have to pre-fill the ring buffer with silence for the microphone example. + 0. In ALSA do we need to wake up the poll when destroying the in or out stream? + 0. Create a test for clearing the playback buffer. + 0. Create a test for pausing and resuming input and output streams. 0. implement ASIO (Windows) backend, get examples working 0. clean up API and improve documentation - make sure every function which can return an error documents which errors diff --git a/src/alsa.cpp b/src/alsa.cpp index df19b0e..8e76caf 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -909,7 +909,6 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate * if (osa->thread) { osa->thread_exit_flag.clear(); - // TODO wake up poll soundio_os_thread_destroy(osa->thread); } @@ -1007,8 +1006,6 @@ void outstream_thread_run(void *arg) { for (;;) { snd_pcm_state_t state = snd_pcm_state(osa->handle); switch (state) { - case SND_PCM_STATE_OPEN: - soundio_panic("TODO open"); case SND_PCM_STATE_SETUP: if ((err = snd_pcm_prepare(osa->handle)) < 0) { outstream->error_callback(outstream, SoundIoErrorStreaming); @@ -1050,18 +1047,18 @@ void outstream_thread_run(void *arg) { return; } continue; - case SND_PCM_STATE_DRAINING: - soundio_panic("TODO draining"); - case SND_PCM_STATE_PAUSED: - soundio_panic("TODO paused"); case SND_PCM_STATE_SUSPENDED: if ((err = xrun_recovery(os, -ESTRPIPE)) < 0) { outstream->error_callback(outstream, SoundIoErrorStreaming); return; } continue; + case SND_PCM_STATE_OPEN: + case SND_PCM_STATE_DRAINING: + case SND_PCM_STATE_PAUSED: case SND_PCM_STATE_DISCONNECTED: - soundio_panic("TODO disconnected"); + outstream->error_callback(outstream, SoundIoErrorStreaming); + return; } } } @@ -1076,8 +1073,6 @@ static void instream_thread_run(void *arg) { for (;;) { snd_pcm_state_t state = snd_pcm_state(osa->handle); switch (state) { - case SND_PCM_STATE_OPEN: - soundio_panic("TODO open"); case SND_PCM_STATE_SETUP: if ((err = snd_pcm_prepare(osa->handle)) < 0) { instream->error_callback(instream, SoundIoErrorStreaming); @@ -1119,18 +1114,18 @@ static void instream_thread_run(void *arg) { return; } continue; - case SND_PCM_STATE_DRAINING: - soundio_panic("TODO draining"); - case SND_PCM_STATE_PAUSED: - soundio_panic("TODO paused"); case SND_PCM_STATE_SUSPENDED: if ((err = instream_xrun_recovery(is, -ESTRPIPE)) < 0) { instream->error_callback(instream, SoundIoErrorStreaming); return; } continue; + case SND_PCM_STATE_OPEN: + case SND_PCM_STATE_DRAINING: + case SND_PCM_STATE_PAUSED: case SND_PCM_STATE_DISCONNECTED: - soundio_panic("TODO disconnected"); + instream->error_callback(instream, SoundIoErrorStreaming); + return; } } } @@ -1395,10 +1390,14 @@ static int outstream_end_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate return 0; } -static void outstream_clear_buffer_alsa(SoundIoPrivate *si, +static int outstream_clear_buffer_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { - soundio_panic("TODO"); + SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data; + int err; + if ((err = snd_pcm_reset(osa->handle)) < 0) + return SoundIoErrorStreaming; + return 0; } static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { @@ -1416,7 +1415,6 @@ static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is if (isa->thread) { isa->thread_exit_flag.clear(); - // TODO wake up poll soundio_os_thread_destroy(isa->thread); } diff --git a/src/dummy.cpp b/src/dummy.cpp index 1b8a64b..410642f 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -290,9 +290,11 @@ static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate return 0; } -static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { +static int outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; soundio_ring_buffer_clear(&osd->ring_buffer); + osd->prebuf_frames_left = osd->prebuf_frame_count; + return 0; } static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index f2ec933..b6d6325 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -782,7 +782,7 @@ static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *o return 0; } -static void outstream_clear_buffer_pa(SoundIoPrivate *si, +static int outstream_clear_buffer_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; @@ -791,9 +791,10 @@ static void outstream_clear_buffer_pa(SoundIoPrivate *si, pa_threaded_mainloop_lock(sipa->main_loop); pa_operation *op = pa_stream_flush(stream, NULL, NULL); if (!op) - soundio_panic("pa_stream_flush failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context))); + return SoundIoErrorStreaming; pa_operation_unref(op); pa_threaded_mainloop_unlock(sipa->main_loop); + return 0; } static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, bool pause) { diff --git a/src/soundio.h b/src/soundio.h index c5c233f..9e8af85 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -340,14 +340,14 @@ struct SoundIoOutStream { // This optional callback happens when the sound device runs out of buffered // audio data to play. After this occurs, the outstream waits until the // buffer is full to resume playback. - // This callback is called in the same thread context as `write_callback`. + // This is called from the `write_callback` thread context. void (*underflow_callback)(struct SoundIoOutStream *); // Optional callback. `err` is always SoundIoErrorStreaming. // SoundIoErrorStreaming is an unrecoverable error. The stream is in an // invalid state and must be destroyed. // If you do not supply `error_callback`, the default callback will print // a message to stderr and then call `abort`. - // This callback is called in the same thread context as `write_callback`. + // This is called fram the `write_callback` thread context. void (*error_callback)(struct SoundIoOutStream *, int err); // Name of the stream. This is used by PulseAudio. Defaults to "SoundIo". @@ -398,7 +398,7 @@ struct SoundIoInStream { // invalid state and must be destroyed. // If you do not supply `error_callback`, the default callback will print // a message to stderr and then abort(). - // This is called from the same thread context as `read_callback`. + // This is called from the `read_callback` thread context. void (*error_callback)(struct SoundIoInStream *, int err); // Name of the stream. This is used by PulseAudio. Defaults to "SoundIo". @@ -555,7 +555,7 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device); int soundio_outstream_open(struct SoundIoOutStream *outstream); -// You may not call this function from `write_callback`. +// You may not call this function from the `write_callback` thread context. void soundio_outstream_destroy(struct SoundIoOutStream *outstream); int soundio_outstream_start(struct SoundIoOutStream *outstream); @@ -569,21 +569,23 @@ int soundio_outstream_start(struct SoundIoOutStream *outstream); // It is your responsibility to call this function no more and no fewer than the // correct number of times as determined by `requested_frame_count` from // `write_callback`. See sine.c for an example. -// You must call this function only from `write_callback`. After calling this -// function, write data to `areas` and then call `soundio_outstream_end_write`. +// You must call this function only from the `write_callback` thread context. +// After calling this function, write data to `areas` and then call `soundio_outstream_end_write`. int soundio_outstream_begin_write(struct SoundIoOutStream *outstream, struct SoundIoChannelArea **areas, int *frame_count); // Commits the write that you began with `soundio_outstream_begin_write`. -// You must call this function only from `write_callback`. +// You must call this function only from the `write_callback` thread context. int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count); -void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); +// Clears the output stream buffer and the stream goes into prebuffering mode. +// You must call this function only from the `write_callback` thread context. +int 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`. -// You must call this function only from `write_callback`. +// You must call this function only from the `write_callback` thread context. int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause); @@ -613,7 +615,7 @@ int soundio_instream_start(struct SoundIoInStream *instream); // It is your responsibility to call this function no more and no fewer than the // correct number of times as determined by `available_frame_count` from // `read_callback`. See microphone.c for an example. -// You must call this function only from `read_callback`. +// You must call this function only from the `read_callback` thread context. // After calling this function, read data from `areas` and then use // `soundio_instream_end_read` to actually remove the data from the buffer // and move the read index forward. `soundio_instream_end_read` should not be @@ -623,14 +625,15 @@ int soundio_instream_begin_read(struct SoundIoInStream *instream, struct SoundIoChannelArea **areas, int *frame_count); // This will drop all of the frames from when you called // `soundio_instream_begin_read`. -// You must call this function only from `read_callback` after a successful -// call to `soundio_instream_begin_read`. +// You must call this function only from the `read_callback` thread context. +// You must call this function only after a successful call to +// `soundio_instream_begin_read`. int soundio_instream_end_read(struct SoundIoInStream *instream); // If the underyling device supports pausing, this pauses the stream and // prevents `read_callback` from being called. Otherwise this returns // `SoundIoErrorIncompatibleDevice`. -// You must call this function only from `read_callback`. +// You must call this function only from the `read_callback` thread context. int soundio_instream_pause(struct SoundIoInStream *instream, bool pause); diff --git a/src/soundio.hpp b/src/soundio.hpp index 4b48bd0..0ce1ac5 100644 --- a/src/soundio.hpp +++ b/src/soundio.hpp @@ -49,7 +49,7 @@ struct SoundIoPrivate { int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, SoundIoChannelArea **out_areas, int *frame_count); int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count); - void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);