ALSA implement clear buffer

This commit is contained in:
Andrew Kelley 2015-07-24 14:03:49 -07:00
parent ab8a2c2ffe
commit 5906bc93d9
6 changed files with 42 additions and 35 deletions

View file

@ -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 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 which is why we have to pre-fill the ring buffer with silence for
the microphone example. 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. implement ASIO (Windows) backend, get examples working
0. clean up API and improve documentation 0. clean up API and improve documentation
- make sure every function which can return an error documents which errors - make sure every function which can return an error documents which errors

View file

@ -909,7 +909,6 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
if (osa->thread) { if (osa->thread) {
osa->thread_exit_flag.clear(); osa->thread_exit_flag.clear();
// TODO wake up poll
soundio_os_thread_destroy(osa->thread); soundio_os_thread_destroy(osa->thread);
} }
@ -1007,8 +1006,6 @@ void outstream_thread_run(void *arg) {
for (;;) { for (;;) {
snd_pcm_state_t state = snd_pcm_state(osa->handle); snd_pcm_state_t state = snd_pcm_state(osa->handle);
switch (state) { switch (state) {
case SND_PCM_STATE_OPEN:
soundio_panic("TODO open");
case SND_PCM_STATE_SETUP: case SND_PCM_STATE_SETUP:
if ((err = snd_pcm_prepare(osa->handle)) < 0) { if ((err = snd_pcm_prepare(osa->handle)) < 0) {
outstream->error_callback(outstream, SoundIoErrorStreaming); outstream->error_callback(outstream, SoundIoErrorStreaming);
@ -1050,18 +1047,18 @@ void outstream_thread_run(void *arg) {
return; return;
} }
continue; continue;
case SND_PCM_STATE_DRAINING:
soundio_panic("TODO draining");
case SND_PCM_STATE_PAUSED:
soundio_panic("TODO paused");
case SND_PCM_STATE_SUSPENDED: case SND_PCM_STATE_SUSPENDED:
if ((err = xrun_recovery(os, -ESTRPIPE)) < 0) { if ((err = xrun_recovery(os, -ESTRPIPE)) < 0) {
outstream->error_callback(outstream, SoundIoErrorStreaming); outstream->error_callback(outstream, SoundIoErrorStreaming);
return; return;
} }
continue; continue;
case SND_PCM_STATE_OPEN:
case SND_PCM_STATE_DRAINING:
case SND_PCM_STATE_PAUSED:
case SND_PCM_STATE_DISCONNECTED: 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 (;;) { for (;;) {
snd_pcm_state_t state = snd_pcm_state(osa->handle); snd_pcm_state_t state = snd_pcm_state(osa->handle);
switch (state) { switch (state) {
case SND_PCM_STATE_OPEN:
soundio_panic("TODO open");
case SND_PCM_STATE_SETUP: case SND_PCM_STATE_SETUP:
if ((err = snd_pcm_prepare(osa->handle)) < 0) { if ((err = snd_pcm_prepare(osa->handle)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming); instream->error_callback(instream, SoundIoErrorStreaming);
@ -1119,18 +1114,18 @@ static void instream_thread_run(void *arg) {
return; return;
} }
continue; continue;
case SND_PCM_STATE_DRAINING:
soundio_panic("TODO draining");
case SND_PCM_STATE_PAUSED:
soundio_panic("TODO paused");
case SND_PCM_STATE_SUSPENDED: case SND_PCM_STATE_SUSPENDED:
if ((err = instream_xrun_recovery(is, -ESTRPIPE)) < 0) { if ((err = instream_xrun_recovery(is, -ESTRPIPE)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming); instream->error_callback(instream, SoundIoErrorStreaming);
return; return;
} }
continue; continue;
case SND_PCM_STATE_OPEN:
case SND_PCM_STATE_DRAINING:
case SND_PCM_STATE_PAUSED:
case SND_PCM_STATE_DISCONNECTED: 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; return 0;
} }
static void outstream_clear_buffer_alsa(SoundIoPrivate *si, static int outstream_clear_buffer_alsa(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os) 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) { 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) { if (isa->thread) {
isa->thread_exit_flag.clear(); isa->thread_exit_flag.clear();
// TODO wake up poll
soundio_os_thread_destroy(isa->thread); soundio_os_thread_destroy(isa->thread);
} }

View file

@ -290,9 +290,11 @@ static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate
return 0; 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; SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
soundio_ring_buffer_clear(&osd->ring_buffer); 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) { static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {

View file

@ -782,7 +782,7 @@ static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *o
return 0; return 0;
} }
static void outstream_clear_buffer_pa(SoundIoPrivate *si, static int outstream_clear_buffer_pa(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os) SoundIoOutStreamPrivate *os)
{ {
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; 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_threaded_mainloop_lock(sipa->main_loop);
pa_operation *op = pa_stream_flush(stream, NULL, NULL); pa_operation *op = pa_stream_flush(stream, NULL, NULL);
if (!op) if (!op)
soundio_panic("pa_stream_flush failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context))); return SoundIoErrorStreaming;
pa_operation_unref(op); pa_operation_unref(op);
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
return 0;
} }
static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, bool pause) { static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, bool pause) {

View file

@ -340,14 +340,14 @@ struct SoundIoOutStream {
// This optional callback happens when the sound device runs out of buffered // This optional callback happens when the sound device runs out of buffered
// audio data to play. After this occurs, the outstream waits until the // audio data to play. After this occurs, the outstream waits until the
// buffer is full to resume playback. // 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 *); void (*underflow_callback)(struct SoundIoOutStream *);
// Optional callback. `err` is always SoundIoErrorStreaming. // Optional callback. `err` is always SoundIoErrorStreaming.
// SoundIoErrorStreaming is an unrecoverable error. The stream is in an // SoundIoErrorStreaming is an unrecoverable error. The stream is in an
// invalid state and must be destroyed. // invalid state and must be destroyed.
// If you do not supply `error_callback`, the default callback will print // If you do not supply `error_callback`, the default callback will print
// a message to stderr and then call `abort`. // 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); void (*error_callback)(struct SoundIoOutStream *, int err);
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo". // Name of the stream. This is used by PulseAudio. Defaults to "SoundIo".
@ -398,7 +398,7 @@ struct SoundIoInStream {
// invalid state and must be destroyed. // invalid state and must be destroyed.
// If you do not supply `error_callback`, the default callback will print // If you do not supply `error_callback`, the default callback will print
// a message to stderr and then abort(). // 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); void (*error_callback)(struct SoundIoInStream *, int err);
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo". // 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); 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); void soundio_outstream_destroy(struct SoundIoOutStream *outstream);
int soundio_outstream_start(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 // 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 // correct number of times as determined by `requested_frame_count` from
// `write_callback`. See sine.c for an example. // `write_callback`. See sine.c for an example.
// You must call this function only from `write_callback`. After calling this // You must call this function only from the `write_callback` thread context.
// function, write data to `areas` and then call `soundio_outstream_end_write`. // After calling this function, write data to `areas` and then call `soundio_outstream_end_write`.
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream, int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
struct SoundIoChannelArea **areas, int *frame_count); struct SoundIoChannelArea **areas, int *frame_count);
// Commits the write that you began with `soundio_outstream_begin_write`. // 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); 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 // If the underyling device supports pausing, this pauses the stream and
// prevents `write_callback` from being called. Otherwise this returns // prevents `write_callback` from being called. Otherwise this returns
// `SoundIoErrorIncompatibleDevice`. // `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); 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 // 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 // correct number of times as determined by `available_frame_count` from
// `read_callback`. See microphone.c for an example. // `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 // After calling this function, read data from `areas` and then use
// `soundio_instream_end_read` to actually remove the data from the buffer // `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 // 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); struct SoundIoChannelArea **areas, int *frame_count);
// This will drop all of the frames from when you called // This will drop all of the frames from when you called
// `soundio_instream_begin_read`. // `soundio_instream_begin_read`.
// You must call this function only from `read_callback` after a successful // You must call this function only from the `read_callback` thread context.
// call to `soundio_instream_begin_read`. // You must call this function only after a successful call to
// `soundio_instream_begin_read`.
int soundio_instream_end_read(struct SoundIoInStream *instream); int soundio_instream_end_read(struct SoundIoInStream *instream);
// If the underyling device supports pausing, this pauses the stream and // If the underyling device supports pausing, this pauses the stream and
// prevents `read_callback` from being called. Otherwise this returns // prevents `read_callback` from being called. Otherwise this returns
// `SoundIoErrorIncompatibleDevice`. // `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); int soundio_instream_pause(struct SoundIoInStream *instream, bool pause);

View file

@ -49,7 +49,7 @@ struct SoundIoPrivate {
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count); SoundIoChannelArea **out_areas, int *frame_count);
int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, 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); int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);