ability to pause and resume streams

and some cleanup
This commit is contained in:
Andrew Kelley 2015-07-16 13:37:41 -07:00
parent cee134eee8
commit 3999f57698
7 changed files with 93 additions and 24 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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<SoundIoFormat>(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;
}

View file

@ -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<SoundIoPulseAudio>();
@ -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;
}

View file

@ -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<SoundIoInStreamPrivate>();
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;

View file

@ -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;

View file

@ -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);