From 5b3fd175f87cf0d740b1d0dd7f52a049b4472cfa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Jul 2015 09:17:20 -0700 Subject: [PATCH] stream API update; expose multiple channel layouts --- example/list_devices.c | 16 +- example/microphone.c | 52 +++-- example/sine.c | 31 +-- src/alsa.cpp | 459 +++++++++++++++++++++-------------------- src/alsa.hpp | 2 +- src/channel_layout.cpp | 2 +- src/dummy.cpp | 252 +++++++++++----------- src/dummy.hpp | 2 +- src/pulseaudio.cpp | 375 ++++++++++++++++----------------- src/pulseaudio.hpp | 2 +- src/soundio.cpp | 441 ++++++++++++++++++++++++--------------- src/soundio.h | 165 ++++++++------- src/soundio.hpp | 45 ++++ test/unit_tests.cpp | 19 +- 14 files changed, 1043 insertions(+), 820 deletions(-) diff --git a/example/list_devices.c b/example/list_devices.c index 84dc9ea..64eff5b 100644 --- a/example/list_devices.c +++ b/example/list_devices.c @@ -27,7 +27,6 @@ static void print_channel_layout(const struct SoundIoChannelLayout *layout) { fprintf(stderr, ", %s", soundio_get_channel_name(layout->channels[i])); } } - } static void print_device(struct SoundIoDevice *device, bool is_default) { @@ -39,9 +38,18 @@ static void print_device(struct SoundIoDevice *device, bool is_default) { if (device->probe_error) { fprintf(stderr, " probe error: %s\n", soundio_strerror(device->probe_error)); } else { - fprintf(stderr, " channel layout: "); - print_channel_layout(&device->channel_layout); - fprintf(stderr, "\n"); + fprintf(stderr, " channel layouts:\n"); + for (int i = 0; i < device->layout_count; i += 1) { + fprintf(stderr, " "); + print_channel_layout(&device->layouts[i]); + fprintf(stderr, "\n"); + } + if (device->current_layout.channel_count > 0) { + fprintf(stderr, " current layout: "); + print_channel_layout(&device->current_layout); + fprintf(stderr, "\n"); + } + fprintf(stderr, " min sample rate: %d\n", device->sample_rate_min); fprintf(stderr, " max sample rate: %d\n", device->sample_rate_max); if (device->sample_rate_current) diff --git a/example/microphone.c b/example/microphone.c index 9b948c9..7f05a6d 100644 --- a/example/microphone.c +++ b/example/microphone.c @@ -25,18 +25,18 @@ static void panic(const char *format, ...) { abort(); } -static void read_callback(struct SoundIoInStream *in_stream) { +static void read_callback(struct SoundIoInStream *instream) { fprintf(stderr, "read_callback\n"); } -static void write_callback(struct SoundIoOutStream *out_stream, int requested_frame_count) { +static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) { fprintf(stderr, "write_callback\n"); } -static void underrun_callback(struct SoundIoOutStream *out_stream) { +static void underrun_callback(struct SoundIoOutStream *outstream) { static int count = 0; fprintf(stderr, "underrun %d\n", count++); - soundio_out_stream_fill_with_silence(out_stream); + soundio_outstream_fill_with_silence(outstream); } int main(int argc, char **argv) { @@ -67,30 +67,50 @@ int main(int argc, char **argv) { fprintf(stderr, "Input device: %s\n", in_device->description); fprintf(stderr, "Output device: %s\n", out_device->description); - if (!soundio_channel_layout_equal(&in_device->channel_layout, &out_device->channel_layout)) + soundio_device_sort_channel_layouts(out_device); + const struct SoundIoChannelLayout *layout = soundio_best_matching_channel_layout( + out_device->layouts, out_device->layout_count, + in_device->layouts, in_device->layout_count); + + if (!layout) panic("channel layouts not compatible"); - double latency = 0.1; + struct SoundIoInStream *instream = soundio_instream_create(in_device); + if (!instream) + panic("out of memory"); + instream->format = SoundIoFormatFloat32NE; // TODO pick compatible ones + instream->sample_rate = 48000; // TODO pick compatible ones + instream->layout = *layout; + instream->latency = 0.1; + instream->read_callback = read_callback; - struct SoundIoInStream *in_stream; - soundio_in_stream_create(in_device, SoundIoFormatFloat32NE, 48000, latency, NULL, - read_callback, &in_stream); + if ((err = soundio_instream_open(instream))) + panic("unable to open input stream: %s", soundio_strerror(err)); - struct SoundIoOutStream *out_stream; - soundio_out_stream_create(out_device, SoundIoFormatFloat32NE, 48000, latency, NULL, - write_callback, underrun_callback, &out_stream); + struct SoundIoOutStream *outstream = soundio_outstream_create(out_device); + if (!outstream) + panic("out of memory"); + outstream->format = SoundIoFormatFloat32NE; + outstream->sample_rate = 48000; + outstream->layout = *layout; + outstream->latency = 0.1; + outstream->write_callback = write_callback; + outstream->underrun_callback = underrun_callback; - if ((err = soundio_in_stream_start(in_stream))) + if ((err = soundio_outstream_open(outstream))) + panic("unable to open output stream: %s", soundio_strerror(err)); + + if ((err = soundio_instream_start(instream))) panic("unable to start input device: %s", soundio_strerror(err)); - if ((err = soundio_out_stream_start(out_stream))) + if ((err = soundio_outstream_start(outstream))) panic("unable to start output device: %s", soundio_strerror(err)); for (;;) soundio_wait_events(soundio); - soundio_out_stream_destroy(out_stream); - soundio_in_stream_destroy(in_stream); + soundio_outstream_destroy(outstream); + soundio_instream_destroy(instream); soundio_device_unref(in_device); soundio_device_unref(out_device); soundio_destroy(soundio); diff --git a/example/sine.c b/example/sine.c index 892a894..da1aafb 100644 --- a/example/sine.c +++ b/example/sine.c @@ -28,20 +28,20 @@ static void panic(const char *format, ...) { static const float PI = 3.1415926535f; static float seconds_offset = 0.0f; -static void write_callback(struct SoundIoOutStream *out_stream, int requested_frame_count) { +static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) { //device->bytes_per_frame; - float float_sample_rate = out_stream->sample_rate; + float float_sample_rate = outstream->sample_rate; float seconds_per_frame = 1.0f / float_sample_rate; while (requested_frame_count > 0) { char *data; int frame_count = requested_frame_count; - soundio_out_stream_begin_write(out_stream, &data, &frame_count); + soundio_outstream_begin_write(outstream, &data, &frame_count); // clear everything to 0 - memset(data, 0, frame_count * out_stream->bytes_per_frame); + memset(data, 0, frame_count * outstream->bytes_per_frame); - const struct SoundIoChannelLayout *channel_layout = &out_stream->device->channel_layout; + const struct SoundIoChannelLayout *layout = &outstream->layout; float *ptr = (float *)data; @@ -50,14 +50,14 @@ static void write_callback(struct SoundIoOutStream *out_stream, int requested_fr float radians_per_second = pitch * 2.0f * PI; for (int frame = 0; frame < frame_count; frame += 1) { float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second); - for (int channel = 0; channel < channel_layout->channel_count; channel += 1) { + for (int channel = 0; channel < layout->channel_count; channel += 1) { *ptr += sample; ptr += 1; } } seconds_offset += seconds_per_frame * frame_count; - soundio_out_stream_write(out_stream, data, frame_count); + soundio_outstream_write(outstream, data, frame_count); requested_frame_count -= frame_count; } @@ -87,17 +87,24 @@ int main(int argc, char **argv) { fprintf(stderr, "Output device: %s: %s\n", device->name, device->description); - struct SoundIoOutStream *out_stream; - soundio_out_stream_create(device, SoundIoFormatFloat32NE, 48000, - 0.1, NULL, write_callback, underrun_callback, &out_stream); + struct SoundIoOutStream *outstream = soundio_outstream_create(device); + outstream->format = SoundIoFormatFloat32NE; + outstream->sample_rate = 48000; // TODO let this be anything + outstream->layout = device->layouts[0]; + outstream->latency = 0.1; + outstream->write_callback = write_callback; + outstream->underrun_callback = underrun_callback; - if ((err = soundio_out_stream_start(out_stream))) + if ((err = soundio_outstream_open(outstream))) + panic("unable to open device: %s", soundio_strerror(err)); + + if ((err = soundio_outstream_start(outstream))) panic("unable to start device: %s", soundio_strerror(err)); for (;;) soundio_wait_events(soundio); - soundio_out_stream_destroy(out_stream); + soundio_outstream_destroy(outstream); soundio_device_unref(device); soundio_destroy(soundio); return 0; diff --git a/src/alsa.cpp b/src/alsa.cpp index 47cdd8f..a4167ef 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -30,6 +30,10 @@ struct SoundIoAlsa { struct SoundIoDevicesInfo *ready_devices_info; }; +struct SoundIoOutStreamAlsa { + +}; + static void wakeup_device_poll(SoundIoAlsa *sia) { ssize_t amt = write(sia->notify_pipe_fd[1], "a", 1); if (amt == -1) { @@ -41,8 +45,8 @@ static void wakeup_device_poll(SoundIoAlsa *sia) { } } -static void destroy_alsa(SoundIo *soundio) { - SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; +static void destroy_alsa(SoundIoPrivate *si) { + SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; if (!sia) return; @@ -67,7 +71,7 @@ static void destroy_alsa(SoundIo *soundio) { close(sia->notify_fd); destroy(sia); - soundio->backend_data = nullptr; + si->backend_data = nullptr; } static char * str_partition_on_char(char *str, char c) { @@ -132,28 +136,43 @@ static SoundIoChannelId from_alsa_chmap_pos(unsigned int pos) { return SoundIoChannelIdInvalid; } -static void get_channel_layout(SoundIoDevice *device, snd_pcm_chmap_t *chmap) { +static void get_channel_layout(SoundIoChannelLayout *dest, snd_pcm_chmap_t *chmap) { int channel_count = min((unsigned int)SOUNDIO_MAX_CHANNELS, chmap->channels); - device->channel_layout.channel_count = channel_count; - device->channel_layout.name = nullptr; + dest->channel_count = channel_count; for (int i = 0; i < channel_count; i += 1) { - device->channel_layout.channels[i] = from_alsa_chmap_pos(chmap->pos[i]); + dest->channels[i] = from_alsa_chmap_pos(chmap->pos[i]); } - soundio_channel_layout_detect_builtin(&device->channel_layout); + soundio_channel_layout_detect_builtin(dest); } -static void handle_channel_maps(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { +static int handle_channel_maps(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { if (!maps) - return; + return 0; + snd_pcm_chmap_query_t **p; snd_pcm_chmap_query_t *v; - snd_pcm_chmap_t *best = nullptr; - for (p = maps; (v = *p); p += 1) { - if (!best || v->map.channels > best->channels) - best = &v->map; + + // one iteration to count + int layout_count = 0; + for (p = maps; (v = *p) && layout_count < SOUNDIO_MAX_CHANNELS; p += 1, layout_count += 1) { } + device->layouts = allocate(layout_count); + if (!device->layouts) { + snd_pcm_free_chmaps(maps); + return SoundIoErrorNoMem; + } + device->layout_count = layout_count; + + // iterate again to collect data + int layout_index; + for (p = maps, layout_index = 0; + (v = *p) && layout_index < layout_count; + p += 1, layout_index += 1) + { + get_channel_layout(&device->layouts[layout_index], &v->map); } - get_channel_layout(device, best); snd_pcm_free_chmaps(maps); + + return 0; } static snd_pcm_format_t to_alsa_fmt(SoundIoFormat fmt) { @@ -196,68 +215,40 @@ static void test_fmt_mask(SoundIoDevice *device, const snd_pcm_format_mask_t *fm // * hw period time // * sw start threshold // * sw avail min +// TODO: device->default_latency -static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { + +// this function does not override device->formats, so if you want it to, deallocate and set it to NULL +static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, + snd_pcm_hw_params_t *hwparams, int resample) +{ int err; - snd_pcm_t *handle; - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - - snd_pcm_hw_params_alloca(&hwparams); - snd_pcm_sw_params_alloca(&swparams); - - snd_pcm_stream_t stream = purpose_to_stream(device->purpose); - - if ((err = snd_pcm_open(&handle, device->name, stream, 0)) < 0) { - handle_channel_maps(device, maps); + if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) return SoundIoErrorOpeningDevice; - } - if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); + if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0) return SoundIoErrorOpeningDevice; - } - // disable hardware resampling because we're trying to probe - if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, 0)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); + if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) return SoundIoErrorOpeningDevice; - } - - if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); - return SoundIoErrorOpeningDevice; - } unsigned int channel_count; - if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); + if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0) return SoundIoErrorOpeningDevice; - } unsigned int min_sample_rate; unsigned int max_sample_rate; int min_dir; int max_dir; - if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &max_sample_rate, &max_dir)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); + if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &max_sample_rate, &max_dir)) < 0) return SoundIoErrorOpeningDevice; - } if (max_dir < 0) max_sample_rate -= 1; - if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &min_sample_rate, &min_dir)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); + if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &min_sample_rate, &min_dir)) < 0) return SoundIoErrorOpeningDevice; - } if (min_dir > 0) min_sample_rate += 1; @@ -283,69 +274,100 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_LE); snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_BE); - if ((err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask)) < 0) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); + if ((err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask)) < 0) return SoundIoErrorOpeningDevice; - } - snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask); - device->formats = allocate(18); if (!device->formats) { - handle_channel_maps(device, maps); - snd_pcm_close(handle); - return SoundIoErrorNoMem; + snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask); + device->formats = allocate(18); + if (!device->formats) + return SoundIoErrorNoMem; + + device->format_count = 0; + test_fmt_mask(device, fmt_mask, SoundIoFormatS8); + test_fmt_mask(device, fmt_mask, SoundIoFormatU8); + test_fmt_mask(device, fmt_mask, SoundIoFormatS16LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS16BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU16LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU16BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS24LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS24BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU24LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU24BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS32LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS32BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU32LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU32BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64BE); } - device->format_count = 0; - test_fmt_mask(device, fmt_mask, SoundIoFormatS8); - test_fmt_mask(device, fmt_mask, SoundIoFormatU8); - test_fmt_mask(device, fmt_mask, SoundIoFormatS16LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatS16BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatU16LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatU16BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatS24LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatS24BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatU24LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatU24BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatS32LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatS32BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatU32LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatU32BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE); - test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE); - test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64BE); - - - snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle); - if (chmap) { - get_channel_layout(device, chmap); - free(chmap); - } else if (!maps) { - maps = snd_pcm_query_chmaps(handle); - } - handle_channel_maps(device, maps); - - device->sample_rate_min = min_sample_rate; device->sample_rate_max = max_sample_rate; - // TODO can we figure out what sample rate dmix is currently playing at, - // if dmix applies to this device? - device->sample_rate_current = 0; + + return 0; +} + +static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { + int err; + snd_pcm_t *handle; + + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + snd_pcm_stream_t stream = purpose_to_stream(device->purpose); + + if ((err = snd_pcm_open(&handle, device->name, stream, 0)) < 0) { + handle_channel_maps(device, maps); + return SoundIoErrorOpeningDevice; + } + + if ((err = probe_open_device(device, handle, hwparams, 0))) { + handle_channel_maps(device, maps); + snd_pcm_close(handle); + return err; + } + + if (!maps) + maps = snd_pcm_query_chmaps(handle); + + snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle); + if (chmap) { + get_channel_layout(&device->current_layout, chmap); + free(chmap); + } + if ((err = handle_channel_maps(device, maps))) { + snd_pcm_close(handle); + return err; + } + maps = nullptr; + + if (device->sample_rate_min == device->sample_rate_max && !device->is_raw) { + device->sample_rate_current = device->sample_rate_min; + + // now say that resampling is OK and see what the real min and max is. + if ((err = probe_open_device(device, handle, hwparams, 1)) < 0) { + snd_pcm_close(handle); + return SoundIoErrorOpeningDevice; + } + } snd_pcm_close(handle); return 0; - - // TODO: device->default_latency } static inline bool str_has_prefix(const char *big_str, const char *prefix) { return strncmp(big_str, prefix, strlen(prefix)) == 0; } -static int refresh_devices(SoundIo *soundio) { - SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; +static int refresh_devices(SoundIoPrivate *si) { + SoundIo *soundio = &si->pub; + SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoDevicesInfo *devices_info = create(); if (!devices_info) @@ -585,8 +607,8 @@ static int refresh_devices(SoundIo *soundio) { } static void device_thread_run(void *arg) { - SoundIo *soundio = (SoundIo *)arg; - SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; + SoundIoPrivate *si = (SoundIoPrivate *)arg; + SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; // Some systems cannot read integer variables if they are not // properly aligned. On other systems, incorrect alignment may @@ -672,7 +694,7 @@ static void device_thread_run(void *arg) { } } if (got_rescan_event) { - if ((err = refresh_devices(soundio))) + if ((err = refresh_devices(si))) soundio_panic("error refreshing devices: %s", soundio_strerror(err)); } } @@ -687,8 +709,9 @@ static void block_until_have_devices(SoundIoAlsa *sia) { soundio_os_mutex_unlock(sia->mutex); } -static void flush_events(SoundIo *soundio) { - SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; +static void flush_events(SoundIoPrivate *si) { + SoundIo *soundio = &si->pub; + SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; block_until_have_devices(sia); bool change = false; @@ -697,8 +720,8 @@ static void flush_events(SoundIo *soundio) { soundio_os_mutex_lock(sia->mutex); if (sia->ready_devices_info) { - old_devices_info = soundio->safe_devices_info; - soundio->safe_devices_info = sia->ready_devices_info; + old_devices_info = si->safe_devices_info; + si->safe_devices_info = sia->ready_devices_info; sia->ready_devices_info = nullptr; change = true; } @@ -711,109 +734,103 @@ static void flush_events(SoundIo *soundio) { soundio_destroy_devices_info(old_devices_info); } -static void wait_events(SoundIo *soundio) { - SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; - flush_events(soundio); +static void wait_events(SoundIoPrivate *si) { + SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; + flush_events(si); soundio_os_mutex_lock(sia->mutex); soundio_os_cond_wait(sia->cond, sia->mutex); soundio_os_mutex_unlock(sia->mutex); } -static void wakeup(SoundIo *soundio) { - SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; +static void wakeup(SoundIoPrivate *si) { + SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; soundio_os_mutex_lock(sia->mutex); soundio_os_cond_signal(sia->cond, sia->mutex); soundio_os_mutex_unlock(sia->mutex); } -static void out_stream_destroy_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - soundio_panic("TODO"); +static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data; + if (!osa) + return; + + destroy(osa); + os->backend_data = nullptr; } -static int out_stream_init_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - soundio_panic("TODO"); -} - -static int out_stream_start_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - soundio_panic("TODO"); -} - -static int out_stream_free_count_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - soundio_panic("TODO"); -} - -static void out_stream_begin_write_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream, char **data, int *frame_count) -{ - soundio_panic("TODO"); -} - -static void out_stream_write_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream, char *data, int frame_count) -{ - soundio_panic("TODO"); -} - -static void out_stream_clear_buffer_alsa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - soundio_panic("TODO"); -} - -static int in_stream_init_alsa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -static void in_stream_destroy_alsa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -static int in_stream_start_alsa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -static void in_stream_peek_alsa(SoundIo *soundio, - SoundIoInStream *in_stream, const char **data, int *frame_count) -{ - soundio_panic("TODO"); -} - -static void in_stream_drop_alsa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -static void in_stream_clear_buffer_alsa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -int soundio_alsa_init(SoundIo *soundio) { - int err; - - assert(!soundio->backend_data); - SoundIoAlsa *sia = create(); - if (!sia) { - destroy_alsa(soundio); +static int outstream_init_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStreamAlsa *osa = create(); + if (!osa) { + outstream_destroy_alsa(si, os); return SoundIoErrorNoMem; } - soundio->backend_data = sia; + os->backend_data = osa; + + return 0; +} + +static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + soundio_panic("TODO"); +} + +static int outstream_free_count_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + soundio_panic("TODO"); +} + +static void outstream_begin_write_alsa(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os, char **data, int *frame_count) +{ + soundio_panic("TODO"); +} + +static void outstream_write_alsa(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os, char *data, int frame_count) +{ + soundio_panic("TODO"); +} + +static void outstream_clear_buffer_alsa(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os) +{ + soundio_panic("TODO"); +} + +static int instream_init_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static void instream_peek_alsa(SoundIoPrivate *si, + SoundIoInStreamPrivate *is, const char **data, int *frame_count) +{ + soundio_panic("TODO"); +} + +static void instream_drop_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static void instream_clear_buffer_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +int soundio_alsa_init(SoundIoPrivate *si) { + int err; + + assert(!si->backend_data); + SoundIoAlsa *sia = create(); + if (!sia) { + destroy_alsa(si); + return SoundIoErrorNoMem; + } + si->backend_data = sia; sia->notify_fd = -1; sia->notify_wd = -1; sia->have_devices_flag.store(false); @@ -821,13 +838,13 @@ int soundio_alsa_init(SoundIo *soundio) { sia->mutex = soundio_os_mutex_create(); if (!sia->mutex) { - destroy_alsa(soundio); + destroy_alsa(si); return SoundIoErrorNoMem; } sia->cond = soundio_os_cond_create(); if (!sia->cond) { - destroy_alsa(soundio); + destroy_alsa(si); return SoundIoErrorNoMem; } @@ -837,7 +854,7 @@ int soundio_alsa_init(SoundIo *soundio) { if (sia->notify_fd == -1) { err = errno; assert(err != EINVAL); - destroy_alsa(soundio); + destroy_alsa(si); if (err == EMFILE || err == ENFILE) { return SoundIoErrorSystemResources; } else { @@ -855,7 +872,7 @@ int soundio_alsa_init(SoundIo *soundio) { assert(err != EINVAL); assert(err != ENAMETOOLONG); assert(err != ENOENT); - destroy_alsa(soundio); + destroy_alsa(si); if (err == ENOSPC) { return SoundIoErrorSystemResources; } else { @@ -873,30 +890,30 @@ int soundio_alsa_init(SoundIo *soundio) { wakeup_device_poll(sia); - if ((err = soundio_os_thread_create(device_thread_run, soundio, false, &sia->thread))) { - destroy_alsa(soundio); + if ((err = soundio_os_thread_create(device_thread_run, si, false, &sia->thread))) { + destroy_alsa(si); return err; } - soundio->destroy = destroy_alsa; - soundio->flush_events = flush_events; - soundio->wait_events = wait_events; - soundio->wakeup = wakeup; + si->destroy = destroy_alsa; + si->flush_events = flush_events; + si->wait_events = wait_events; + si->wakeup = wakeup; - soundio->out_stream_init = out_stream_init_alsa; - soundio->out_stream_destroy = out_stream_destroy_alsa; - soundio->out_stream_start = out_stream_start_alsa; - soundio->out_stream_free_count = out_stream_free_count_alsa; - soundio->out_stream_begin_write = out_stream_begin_write_alsa; - soundio->out_stream_write = out_stream_write_alsa; - soundio->out_stream_clear_buffer = out_stream_clear_buffer_alsa; + si->outstream_init = outstream_init_alsa; + si->outstream_destroy = outstream_destroy_alsa; + si->outstream_start = outstream_start_alsa; + si->outstream_free_count = outstream_free_count_alsa; + si->outstream_begin_write = outstream_begin_write_alsa; + si->outstream_write = outstream_write_alsa; + si->outstream_clear_buffer = outstream_clear_buffer_alsa; - soundio->in_stream_init = in_stream_init_alsa; - soundio->in_stream_destroy = in_stream_destroy_alsa; - soundio->in_stream_start = in_stream_start_alsa; - soundio->in_stream_peek = in_stream_peek_alsa; - soundio->in_stream_drop = in_stream_drop_alsa; - soundio->in_stream_clear_buffer = in_stream_clear_buffer_alsa; + si->instream_init = instream_init_alsa; + si->instream_destroy = instream_destroy_alsa; + si->instream_start = instream_start_alsa; + si->instream_peek = instream_peek_alsa; + si->instream_drop = instream_drop_alsa; + si->instream_clear_buffer = instream_clear_buffer_alsa; return 0; } diff --git a/src/alsa.hpp b/src/alsa.hpp index 680321f..e753d55 100644 --- a/src/alsa.hpp +++ b/src/alsa.hpp @@ -8,7 +8,7 @@ #ifndef SOUNDIO_ALSA_HPP #define SOUNDIO_ALSA_HPP -int soundio_alsa_init(struct SoundIo *soundio); +int soundio_alsa_init(struct SoundIoPrivate *si); #endif diff --git a/src/channel_layout.cpp b/src/channel_layout.cpp index 8148f65..d0b1979 100644 --- a/src/channel_layout.cpp +++ b/src/channel_layout.cpp @@ -310,7 +310,6 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = { const char *soundio_get_channel_name(enum SoundIoChannelId id) { switch (id) { case SoundIoChannelIdInvalid: return "(Invalid Channel)"; - case SoundIoChannelIdCount: return "(Invalid Channel)"; case SoundIoChannelIdFrontLeft: return "Front Left"; case SoundIoChannelIdFrontRight: return "Front Right"; @@ -405,5 +404,6 @@ bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout) return true; } } + layout->name = nullptr; return false; } diff --git a/src/dummy.cpp b/src/dummy.cpp index 19a605a..e26fae7 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -34,13 +34,14 @@ struct SoundIoDummy { }; static void playback_thread_run(void *arg) { - SoundIoOutStream *out_stream = (SoundIoOutStream *)arg; - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg; + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; double start_time = soundio_os_get_time(); long frames_consumed = 0; - double time_per_frame = 1.0 / (double)out_stream->sample_rate; + double time_per_frame = 1.0 / (double)outstream->sample_rate; while (osd->abort_flag.test_and_set()) { soundio_os_cond_timed_wait(osd->cond, nullptr, osd->period); @@ -49,32 +50,32 @@ static void playback_thread_run(void *arg) { long total_frames = total_time / time_per_frame; int frames_to_kill = total_frames - frames_consumed; int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer); - int frames_in_buffer = fill_count / out_stream->bytes_per_frame; + int frames_in_buffer = fill_count / outstream->bytes_per_frame; int read_count = min(frames_to_kill, frames_in_buffer); int frames_left = frames_to_kill - read_count; - int byte_count = read_count * out_stream->bytes_per_frame; + int byte_count = read_count * outstream->bytes_per_frame; soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count); frames_consumed += read_count; if (frames_left > 0) { - out_stream->underrun_callback(out_stream); + outstream->underrun_callback(outstream); } else if (read_count > 0) { - out_stream->write_callback(out_stream, read_count); + outstream->write_callback(outstream, read_count); } } } /* static void recording_thread_run(void *arg) { - SoundIoInStream *in_stream = (SoundIoInStream *)arg; - SoundIoDevice *device = in_stream->device; + SoundIoInStream *instream = (SoundIoInStream *)arg; + SoundIoDevice *device = instream->device; SoundIo *soundio = device->soundio; // TODO } */ -static void destroy_dummy(SoundIo *soundio) { - SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; +static void destroy_dummy(SoundIoPrivate *si) { + SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; if (!sid) return; @@ -85,32 +86,31 @@ static void destroy_dummy(SoundIo *soundio) { soundio_os_mutex_destroy(sid->mutex); destroy(sid); - soundio->backend_data = nullptr; + si->backend_data = nullptr; } -static void flush_events(SoundIo *soundio) { - SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; +static void flush_events(SoundIoPrivate *si) { + SoundIo *soundio = &si->pub; + SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; if (sid->devices_emitted) return; sid->devices_emitted = true; soundio->on_devices_change(soundio); } -static void wait_events(SoundIo *soundio) { - SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; - flush_events(soundio); +static void wait_events(SoundIoPrivate *si) { + SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; + flush_events(si); soundio_os_cond_wait(sid->cond, nullptr); } -static void wakeup(SoundIo *soundio) { - SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; +static void wakeup(SoundIoPrivate *si) { + SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; soundio_os_cond_signal(sid->cond, nullptr); } -static void out_stream_destroy_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; +static void outstream_destroy_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; if (!osd) return; @@ -128,120 +128,106 @@ static void out_stream_destroy_dummy(SoundIo *soundio, soundio_ring_buffer_deinit(&osd->ring_buffer); destroy(osd); - out_stream->backend_data = nullptr; + os->backend_data = nullptr; } -static int out_stream_init_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - +static int outstream_init_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStream *outstream = &os->pub; SoundIoOutStreamDummy *osd = create(); if (!osd) { - out_stream_destroy_dummy(soundio, out_stream); + outstream_destroy_dummy(si, os); return SoundIoErrorNoMem; } - out_stream->backend_data = osd; + os->backend_data = osd; - int buffer_frame_count = out_stream->latency * out_stream->sample_rate; - osd->buffer_size = out_stream->bytes_per_frame * buffer_frame_count; - osd->period = out_stream->latency * 0.5; + int buffer_frame_count = outstream->latency * outstream->sample_rate; + osd->buffer_size = outstream->bytes_per_frame * buffer_frame_count; + osd->period = outstream->latency * 0.5; soundio_ring_buffer_init(&osd->ring_buffer, osd->buffer_size); osd->cond = soundio_os_cond_create(); if (!osd->cond) { - out_stream_destroy_dummy(soundio, out_stream); + outstream_destroy_dummy(si, os); return SoundIoErrorNoMem; } return 0; } -static int out_stream_start_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; +static int outstream_start_dummy(SoundIoPrivate *soundio, SoundIoOutStreamPrivate *os) { + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; - soundio_out_stream_fill_with_silence(out_stream); + soundio_outstream_fill_with_silence(outstream); assert(soundio_ring_buffer_fill_count(&osd->ring_buffer) == osd->buffer_size); osd->abort_flag.test_and_set(); int err; - if ((err = soundio_os_thread_create(playback_thread_run, out_stream, true, &osd->thread))) { + if ((err = soundio_os_thread_create(playback_thread_run, os, true, &osd->thread))) { return err; } return 0; } -static int out_stream_free_count_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; +static int outstream_free_count_dummy(SoundIoPrivate *soundio, SoundIoOutStreamPrivate *os) { + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer); int bytes_free_count = osd->buffer_size - fill_count; - return bytes_free_count / out_stream->bytes_per_frame; + return bytes_free_count / outstream->bytes_per_frame; } -static void out_stream_begin_write_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream, char **data, int *frame_count) +static void outstream_begin_write_dummy(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os, char **data, int *frame_count) { - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; - int byte_count = *frame_count * out_stream->bytes_per_frame; + int byte_count = *frame_count * outstream->bytes_per_frame; assert(byte_count <= osd->buffer_size); *data = osd->ring_buffer.address; } -static void out_stream_write_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream, char *data, int frame_count) +static void outstream_write_dummy(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os, char *data, int frame_count) { - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; + SoundIoOutStream *outstream = &os->pub; assert(data == osd->ring_buffer.address); - int byte_count = frame_count * out_stream->bytes_per_frame; + int byte_count = frame_count * outstream->bytes_per_frame; soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count); } -static void out_stream_clear_buffer_dummy(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; +static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; soundio_ring_buffer_clear(&osd->ring_buffer); } -static int in_stream_init_dummy(SoundIo *soundio, - SoundIoInStream *in_stream) +static int instream_init_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + soundio_panic("TODO"); +} + +static void instream_peek_dummy(SoundIoPrivate *si, + SoundIoInStreamPrivate *is, const char **data, int *frame_count) { soundio_panic("TODO"); } -static void in_stream_destroy_dummy(SoundIo *soundio, - SoundIoInStream *in_stream) -{ +static void instream_drop_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { soundio_panic("TODO"); } -static int in_stream_start_dummy(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -static void in_stream_peek_dummy(SoundIo *soundio, - SoundIoInStream *in_stream, const char **data, int *frame_count) -{ - soundio_panic("TODO"); -} - -static void in_stream_drop_dummy(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - soundio_panic("TODO"); -} - -static void in_stream_clear_buffer_dummy(SoundIo *soundio, - SoundIoInStream *in_stream) -{ +static void instream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { soundio_panic("TODO"); } @@ -273,42 +259,43 @@ static int set_all_device_formats(SoundIoDevice *device) { return 0; } -int soundio_dummy_init(SoundIo *soundio) { - assert(!soundio->backend_data); +int soundio_dummy_init(SoundIoPrivate *si) { + SoundIo *soundio = &si->pub; + assert(!si->backend_data); SoundIoDummy *sid = create(); if (!sid) { - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } - soundio->backend_data = sid; + si->backend_data = sid; sid->mutex = soundio_os_mutex_create(); if (!sid->mutex) { - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } sid->cond = soundio_os_cond_create(); if (!sid->cond) { - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } - assert(!soundio->safe_devices_info); - soundio->safe_devices_info = create(); - if (!soundio->safe_devices_info) { - destroy_dummy(soundio); + assert(!si->safe_devices_info); + si->safe_devices_info = create(); + if (!si->safe_devices_info) { + destroy_dummy(si); return SoundIoErrorNoMem; } - soundio->safe_devices_info->default_input_index = 0; - soundio->safe_devices_info->default_output_index = 0; + si->safe_devices_info->default_input_index = 0; + si->safe_devices_info->default_output_index = 0; // create output device { SoundIoDevice *device = create(); if (!device) { - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } @@ -318,14 +305,22 @@ int soundio_dummy_init(SoundIo *soundio) { device->description = strdup("Dummy Output Device"); if (!device->name || !device->description) { soundio_device_unref(device); - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } - device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); + device->layout_count = 1; + device->layouts = allocate(1); + if (!device->layouts) { + soundio_device_unref(device); + destroy_dummy(si); + return SoundIoErrorNoMem; + } + device->layouts[0] = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); + int err; if ((err = set_all_device_formats(device))) { soundio_device_unref(device); - destroy_dummy(soundio); + destroy_dummy(si); return err; } @@ -335,9 +330,9 @@ int soundio_dummy_init(SoundIo *soundio) { device->sample_rate_current = 48000; device->purpose = SoundIoDevicePurposeOutput; - if (soundio->safe_devices_info->output_devices.append(device)) { + if (si->safe_devices_info->output_devices.append(device)) { soundio_device_unref(device); - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } } @@ -346,7 +341,7 @@ int soundio_dummy_init(SoundIo *soundio) { { SoundIoDevice *device = create(); if (!device) { - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } @@ -356,14 +351,23 @@ int soundio_dummy_init(SoundIo *soundio) { device->description = strdup("Dummy input device"); if (!device->name || !device->description) { soundio_device_unref(device); - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } - device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); + + device->layout_count = 1; + device->layouts = allocate(1); + if (!device->layouts) { + soundio_device_unref(device); + destroy_dummy(si); + return SoundIoErrorNoMem; + } + device->layouts[0] = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); + int err; if ((err = set_all_device_formats(device))) { soundio_device_unref(device); - destroy_dummy(soundio); + destroy_dummy(si); return err; } device->default_latency = 0.01; @@ -372,33 +376,33 @@ int soundio_dummy_init(SoundIo *soundio) { device->sample_rate_current = 48000; device->purpose = SoundIoDevicePurposeInput; - if (soundio->safe_devices_info->input_devices.append(device)) { + if (si->safe_devices_info->input_devices.append(device)) { soundio_device_unref(device); - destroy_dummy(soundio); + destroy_dummy(si); return SoundIoErrorNoMem; } } - soundio->destroy = destroy_dummy; - soundio->flush_events = flush_events; - soundio->wait_events = wait_events; - soundio->wakeup = wakeup; + si->destroy = destroy_dummy; + si->flush_events = flush_events; + si->wait_events = wait_events; + si->wakeup = wakeup; - soundio->out_stream_init = out_stream_init_dummy; - soundio->out_stream_destroy = out_stream_destroy_dummy; - soundio->out_stream_start = out_stream_start_dummy; - soundio->out_stream_free_count = out_stream_free_count_dummy; - soundio->out_stream_begin_write = out_stream_begin_write_dummy; - soundio->out_stream_write = out_stream_write_dummy; - soundio->out_stream_clear_buffer = out_stream_clear_buffer_dummy; + si->outstream_init = outstream_init_dummy; + si->outstream_destroy = outstream_destroy_dummy; + si->outstream_start = outstream_start_dummy; + si->outstream_free_count = outstream_free_count_dummy; + si->outstream_begin_write = outstream_begin_write_dummy; + si->outstream_write = outstream_write_dummy; + si->outstream_clear_buffer = outstream_clear_buffer_dummy; - soundio->in_stream_init = in_stream_init_dummy; - soundio->in_stream_destroy = in_stream_destroy_dummy; - soundio->in_stream_start = in_stream_start_dummy; - soundio->in_stream_peek = in_stream_peek_dummy; - soundio->in_stream_drop = in_stream_drop_dummy; - soundio->in_stream_clear_buffer = in_stream_clear_buffer_dummy; + si->instream_init = instream_init_dummy; + si->instream_destroy = instream_destroy_dummy; + si->instream_start = instream_start_dummy; + si->instream_peek = instream_peek_dummy; + si->instream_drop = instream_drop_dummy; + si->instream_clear_buffer = instream_clear_buffer_dummy; return 0; } diff --git a/src/dummy.hpp b/src/dummy.hpp index 603832e..f6f7637 100644 --- a/src/dummy.hpp +++ b/src/dummy.hpp @@ -8,6 +8,6 @@ #ifndef SOUNDIO_DUMMY_HPP #define SOUNDIO_DUMMY_HPP -int soundio_dummy_init(struct SoundIo *soundio); +int soundio_dummy_init(struct SoundIoPrivate *si); #endif diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index dae0ccf..d950fdd 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -55,26 +55,26 @@ struct SoundIoPulseAudio { static void subscribe_callback(pa_context *context, pa_subscription_event_type_t event_bits, uint32_t index, void *userdata) { - SoundIo *soundio = (SoundIo *)userdata; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPrivate *si = (SoundIoPrivate *)userdata; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; sipa->device_scan_queued = true; pa_threaded_mainloop_signal(sipa->main_loop, 0); } -static void subscribe_to_events(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void subscribe_to_events(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_subscription_mask_t events = (pa_subscription_mask_t)( PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER); pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context, - events, nullptr, soundio); + events, nullptr, si); if (!subscribe_op) soundio_panic("pa_context_subscribe failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context))); pa_operation_unref(subscribe_op); } static void context_state_callback(pa_context *context, void *userdata) { - SoundIo *soundio = (SoundIo *)userdata; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPrivate *si = (SoundIoPrivate *)userdata; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; switch (pa_context_get_state(context)) { case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet. @@ -87,7 +87,7 @@ static void context_state_callback(pa_context *context, void *userdata) { return; case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations. sipa->device_scan_queued = true; - subscribe_to_events(soundio); + subscribe_to_events(si); sipa->ready_flag = true; pa_threaded_mainloop_signal(sipa->main_loop, 0); return; @@ -107,8 +107,8 @@ static void context_state_callback(pa_context *context, void *userdata) { } } -static void destroy_pa(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void destroy_pa(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (!sipa) return; @@ -131,7 +131,7 @@ static void destroy_pa(SoundIo *soundio) { free(sipa->default_source_name); destroy(sipa); - soundio->backend_data = nullptr; + si->backend_data = nullptr; } static double usec_to_sec(pa_usec_t usec) { @@ -166,6 +166,7 @@ static int sample_rate_from_pulseaudio(pa_sample_spec sample_spec) { return sample_spec.rate; } +/* TODO static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) { switch (pos) { case PA_CHANNEL_POSITION_MONO: return SoundIoChannelIdFrontCenter; @@ -208,9 +209,10 @@ static void set_from_pulseaudio_channel_map(pa_channel_map channel_map, SoundIoC } } } +*/ -static int perform_operation(SoundIo *soundio, pa_operation *op) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static int perform_operation(SoundIoPrivate *si, pa_operation *op) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; for (;;) { switch (pa_operation_get_state(op)) { case PA_OPERATION_RUNNING: @@ -226,8 +228,9 @@ static int perform_operation(SoundIo *soundio, pa_operation *op) { } } -static void finish_device_query(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void finish_device_query(SoundIoPrivate *si) { + SoundIo *soundio = &si->pub; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (!sipa->have_sink_list || !sipa->have_source_list || @@ -263,11 +266,12 @@ static void finish_device_query(SoundIo *soundio) { } static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *info, int eol, void *userdata) { - SoundIo *soundio = (SoundIo *)userdata; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPrivate *si = (SoundIoPrivate *)userdata; + SoundIo *soundio = &si->pub; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (eol) { sipa->have_sink_list = true; - finish_device_query(soundio); + finish_device_query(si); } else { SoundIoDevice *device = create(); if (!device) @@ -279,8 +283,9 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in device->description = strdup(info->description); if (!device->name || !device->description) soundio_panic("out of memory"); - set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout); // TODO determine the list of supported formats and the min and max sample rate + // TODO determine the channel layouts supported + //TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout); device->current_format = format_from_pulseaudio(info->sample_spec); device->default_latency = usec_to_sec(info->configured_latency); device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); @@ -293,11 +298,12 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in } static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) { - SoundIo *soundio = (SoundIo *)userdata; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPrivate *si = (SoundIoPrivate *)userdata; + SoundIo *soundio = &si->pub; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (eol) { sipa->have_source_list = true; - finish_device_query(soundio); + finish_device_query(si); } else { SoundIoDevice *device = create(); if (!device) @@ -309,8 +315,9 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info device->description = strdup(info->description); if (!device->name || !device->description) soundio_panic("out of memory"); - set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout); // TODO determine the list of supported formats and the min and max sample rate + // TODO determine the channel layouts supported + // TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout); device->current_format = format_from_pulseaudio(info->sample_spec); device->default_latency = usec_to_sec(info->configured_latency); device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); @@ -323,9 +330,9 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info } static void server_info_callback(pa_context *pulse_context, const pa_server_info *info, void *userdata) { - SoundIo *soundio = (SoundIo *)userdata; - assert(soundio); - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPrivate *si = (SoundIoPrivate *)userdata; + assert(si); + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; free(sipa->default_sink_name); free(sipa->default_source_name); @@ -337,12 +344,12 @@ static void server_info_callback(pa_context *pulse_context, const pa_server_info soundio_panic("out of memory"); sipa->have_default_sink = true; - finish_device_query(soundio); + finish_device_query(si); pa_threaded_mainloop_signal(sipa->main_loop, 0); } -static void scan_devices(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void scan_devices(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; sipa->have_sink_list = false; sipa->have_default_sink = false; @@ -356,17 +363,17 @@ static void scan_devices(SoundIo *soundio) { pa_threaded_mainloop_lock(sipa->main_loop); pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context, - sink_info_callback, soundio); + sink_info_callback, si); pa_operation *list_source_op = pa_context_get_source_info_list(sipa->pulse_context, - source_info_callback, soundio); + source_info_callback, si); pa_operation *server_info_op = pa_context_get_server_info(sipa->pulse_context, - server_info_callback, soundio); + server_info_callback, si); - if (perform_operation(soundio, list_sink_op)) + if (perform_operation(si, list_sink_op)) soundio_panic("list sinks failed"); - if (perform_operation(soundio, list_source_op)) + if (perform_operation(si, list_source_op)) soundio_panic("list sources failed"); - if (perform_operation(soundio, server_info_op)) + if (perform_operation(si, server_info_op)) soundio_panic("get server info failed"); pa_threaded_mainloop_signal(sipa->main_loop, 0); @@ -374,8 +381,8 @@ static void scan_devices(SoundIo *soundio) { pa_threaded_mainloop_unlock(sipa->main_loop); } -static void block_until_have_devices(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void block_until_have_devices(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (sipa->have_devices_flag) return; pa_threaded_mainloop_lock(sipa->main_loop); @@ -385,8 +392,8 @@ static void block_until_have_devices(SoundIo *soundio) { pa_threaded_mainloop_unlock(sipa->main_loop); } -static void block_until_ready(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void block_until_ready(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (sipa->ready_flag) return; pa_threaded_mainloop_lock(sipa->main_loop); @@ -396,14 +403,15 @@ static void block_until_ready(SoundIo *soundio) { pa_threaded_mainloop_unlock(sipa->main_loop); } -static void flush_events(SoundIo *soundio) { - block_until_ready(soundio); +static void flush_events(SoundIoPrivate *si) { + SoundIo *soundio = &si->pub; + block_until_ready(si); - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; if (sipa->device_scan_queued) { sipa->device_scan_queued = false; - scan_devices(soundio); + scan_devices(si); } SoundIoDevicesInfo *old_devices_info = nullptr; @@ -412,8 +420,8 @@ static void flush_events(SoundIo *soundio) { pa_threaded_mainloop_lock(sipa->main_loop); if (sipa->ready_devices_info) { - old_devices_info = soundio->safe_devices_info; - soundio->safe_devices_info = sipa->ready_devices_info; + old_devices_info = si->safe_devices_info; + si->safe_devices_info = sipa->ready_devices_info; sipa->ready_devices_info = nullptr; change = true; } @@ -425,17 +433,17 @@ static void flush_events(SoundIo *soundio) { soundio_destroy_devices_info(old_devices_info); - block_until_have_devices(soundio); + block_until_have_devices(si); } -static void wait_events(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; - flush_events(soundio); +static void wait_events(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; + flush_events(si); pa_threaded_mainloop_wait(sipa->main_loop); } -static void wakeup(SoundIo *soundio) { - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static void wakeup(SoundIoPrivate *si) { + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_threaded_mainloop_signal(sipa->main_loop, 0); } @@ -487,7 +495,6 @@ static pa_channel_position_t to_pulseaudio_channel_pos(SoundIoChannelId channel_ case SoundIoChannelIdTopBackCenter: return PA_CHANNEL_POSITION_TOP_REAR_CENTER; case SoundIoChannelIdTopBackRight: return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - case SoundIoChannelIdCount: case SoundIoChannelIdInvalid: case SoundIoChannelIdBackLeftCenter: case SoundIoChannelIdBackRightCenter: @@ -524,10 +531,12 @@ static pa_channel_map to_pulseaudio_channel_map(const SoundIoChannelLayout *chan } static void playback_stream_state_callback(pa_stream *stream, void *userdata) { - SoundIoOutStream *out_stream = (SoundIoOutStream*) userdata; - SoundIo *soundio = out_stream->device->soundio; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate*) userdata; + SoundIoOutStream *outstream = &os->pub; + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; switch (pa_stream_get_state(stream)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: @@ -544,25 +553,23 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) { } static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) { - SoundIoOutStream *out_stream = (SoundIoOutStream*)userdata; - out_stream->underrun_callback(out_stream); + SoundIoOutStream *outstream = (SoundIoOutStream*)userdata; + outstream->underrun_callback(outstream); } static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) { - SoundIoOutStream *out_stream = (SoundIoOutStream*)(userdata); - int frame_count = ((int)nbytes) / out_stream->bytes_per_frame; - out_stream->write_callback(out_stream, frame_count); + SoundIoOutStream *outstream = (SoundIoOutStream*)(userdata); + int frame_count = ((int)nbytes) / outstream->bytes_per_frame; + outstream->write_callback(outstream, frame_count); } -static void out_stream_destroy_pa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; +static void outstream_destroy_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; if (!ospa) return; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_stream *stream = ospa->stream; if (stream) { pa_threaded_mainloop_lock(sipa->main_loop); @@ -580,21 +587,19 @@ static void out_stream_destroy_pa(SoundIo *soundio, } destroy(ospa); - out_stream->backend_data = nullptr; + os->backend_data = nullptr; } -static int out_stream_init_pa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ +static int outstream_init_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStream *outstream = &os->pub; SoundIoOutStreamPulseAudio *ospa = create(); if (!ospa) { - out_stream_destroy_pa(soundio, out_stream); + outstream_destroy_pa(si, os); return SoundIoErrorNoMem; } - out_stream->backend_data = ospa; + os->backend_data = ospa; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; - SoundIoDevice *device = out_stream->device; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; ospa->stream_ready = false; assert(sipa->pulse_context); @@ -602,24 +607,26 @@ static int out_stream_init_pa(SoundIo *soundio, pa_threaded_mainloop_lock(sipa->main_loop); pa_sample_spec sample_spec; - sample_spec.format = to_pulseaudio_format(out_stream->format); - sample_spec.rate = out_stream->sample_rate; - sample_spec.channels = device->channel_layout.channel_count; - pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout); + sample_spec.format = to_pulseaudio_format(outstream->format); + sample_spec.rate = outstream->sample_rate; + sample_spec.channels = outstream->layout.channel_count; + pa_channel_map channel_map = to_pulseaudio_channel_map(&outstream->layout); + + // TODO make this value ("SoundIo") configurable ospa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map); if (!ospa->stream) { pa_threaded_mainloop_unlock(sipa->main_loop); - out_stream_destroy_pa(soundio, out_stream); + outstream_destroy_pa(si, os); return SoundIoErrorNoMem; } - pa_stream_set_state_callback(ospa->stream, playback_stream_state_callback, out_stream); - pa_stream_set_write_callback(ospa->stream, playback_stream_write_callback, out_stream); - pa_stream_set_underflow_callback(ospa->stream, playback_stream_underflow_callback, out_stream); + pa_stream_set_state_callback(ospa->stream, playback_stream_state_callback, os); + pa_stream_set_write_callback(ospa->stream, playback_stream_write_callback, os); + pa_stream_set_underflow_callback(ospa->stream, playback_stream_underflow_callback, outstream); - int bytes_per_second = out_stream->bytes_per_frame * out_stream->sample_rate; - int buffer_length = out_stream->bytes_per_frame * - ceil(out_stream->latency * bytes_per_second / (double)out_stream->bytes_per_frame); + int bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate; + int buffer_length = outstream->bytes_per_frame * + ceil(outstream->latency * bytes_per_second / (double)outstream->bytes_per_frame); ospa->buffer_attr.maxlength = buffer_length; ospa->buffer_attr.tlength = buffer_length; @@ -632,17 +639,16 @@ static int out_stream_init_pa(SoundIo *soundio, return 0; } -static int out_stream_start_pa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; +static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStream *outstream = &os->pub; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; pa_threaded_mainloop_lock(sipa->main_loop); int err = pa_stream_connect_playback(ospa->stream, - out_stream->device->name, &ospa->buffer_attr, + outstream->device->name, &ospa->buffer_attr, PA_STREAM_ADJUST_LATENCY, nullptr, nullptr); if (err) { pa_threaded_mainloop_unlock(sipa->main_loop); @@ -652,50 +658,51 @@ static int out_stream_start_pa(SoundIo *soundio, while (!ospa->stream_ready) pa_threaded_mainloop_wait(sipa->main_loop); - soundio_out_stream_fill_with_silence(out_stream); + soundio_outstream_fill_with_silence(outstream); pa_threaded_mainloop_unlock(sipa->main_loop); return 0; } -static int out_stream_free_count_pa(SoundIo *soundio, - SoundIoOutStream *out_stream) -{ - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; - return pa_stream_writable_size(ospa->stream) / out_stream->bytes_per_frame; +static int outstream_free_count_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; + return pa_stream_writable_size(ospa->stream) / outstream->bytes_per_frame; } -static void out_stream_begin_write_pa(SoundIo *soundio, - SoundIoOutStream *out_stream, char **data, int *frame_count) +static void outstream_begin_write_pa(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os, char **data, int *frame_count) { - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_stream *stream = ospa->stream; - size_t byte_count = *frame_count * out_stream->bytes_per_frame; + size_t byte_count = *frame_count * outstream->bytes_per_frame; if (pa_stream_begin_write(stream, (void**)data, &byte_count)) soundio_panic("pa_stream_begin_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context))); - *frame_count = byte_count / out_stream->bytes_per_frame; + *frame_count = byte_count / outstream->bytes_per_frame; } -static void out_stream_write_pa(SoundIo *soundio, - SoundIoOutStream *out_stream, char *data, int frame_count) +static void outstream_write_pa(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os, char *data, int frame_count) { - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_stream *stream = ospa->stream; - size_t byte_count = frame_count * out_stream->bytes_per_frame; + size_t byte_count = frame_count * outstream->bytes_per_frame; if (pa_stream_write(stream, data, byte_count, NULL, 0, PA_SEEK_RELATIVE)) soundio_panic("pa_stream_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context))); } -static void out_stream_clear_buffer_pa(SoundIo *soundio, - SoundIoOutStream *out_stream) +static void outstream_clear_buffer_pa(SoundIoPrivate *si, + SoundIoOutStreamPrivate *os) { - SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_stream *stream = ospa->stream; pa_threaded_mainloop_lock(sipa->main_loop); pa_operation *op = pa_stream_flush(stream, NULL, NULL); @@ -706,8 +713,8 @@ static void out_stream_clear_buffer_pa(SoundIo *soundio, } static void recording_stream_state_callback(pa_stream *stream, void *userdata) { - SoundIoInStream *in_stream = (SoundIoInStream*)userdata; - SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; + SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata; + SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; switch (pa_stream_get_state(stream)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: @@ -724,18 +731,17 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) { } static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) { - SoundIoInStream *in_stream = (SoundIoInStream*)userdata; - in_stream->read_callback(in_stream); + SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata; + SoundIoInStream *instream = &is->pub; + instream->read_callback(instream); } -static void in_stream_destroy_pa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; +static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *instream) { + SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)instream->backend_data; if (!ispa) return; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_stream *stream = ispa->stream; if (stream) { pa_threaded_mainloop_lock(sipa->main_loop); @@ -751,44 +757,43 @@ static void in_stream_destroy_pa(SoundIo *soundio, } } -static int in_stream_init_pa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ +static int instream_init_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + SoundIoInStream *instream = &is->pub; SoundIoInStreamPulseAudio *ispa = create(); if (!ispa) { - in_stream_destroy_pa(soundio, in_stream); + instream_destroy_pa(si, is); return SoundIoErrorNoMem; } - in_stream->backend_data = ispa; + is->backend_data = ispa; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; - SoundIoDevice *device = in_stream->device; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; ispa->stream_ready = false; pa_threaded_mainloop_lock(sipa->main_loop); pa_sample_spec sample_spec; - sample_spec.format = to_pulseaudio_format(in_stream->format); - sample_spec.rate = in_stream->sample_rate; - sample_spec.channels = device->channel_layout.channel_count; + sample_spec.format = to_pulseaudio_format(instream->format); + sample_spec.rate = instream->sample_rate; + sample_spec.channels = instream->layout.channel_count; - pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout); + pa_channel_map channel_map = to_pulseaudio_channel_map(&instream->layout); + // TODO make this value ("SoundIo") private ispa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map); - if (!in_stream) { + if (!ispa->stream) { pa_threaded_mainloop_unlock(sipa->main_loop); - in_stream_destroy_pa(soundio, in_stream); + instream_destroy_pa(si, is); return SoundIoErrorNoMem; } pa_stream *stream = ispa->stream; - pa_stream_set_state_callback(stream, recording_stream_state_callback, in_stream); - pa_stream_set_read_callback(stream, recording_stream_read_callback, in_stream); + pa_stream_set_state_callback(stream, recording_stream_state_callback, is); + pa_stream_set_read_callback(stream, recording_stream_read_callback, is); - int bytes_per_second = in_stream->bytes_per_frame * in_stream->sample_rate; - int buffer_length = in_stream->bytes_per_frame * - ceil(in_stream->latency * bytes_per_second / (double)in_stream->bytes_per_frame); + int bytes_per_second = instream->bytes_per_frame * instream->sample_rate; + int buffer_length = instream->bytes_per_frame * + ceil(instream->latency * bytes_per_second / (double)instream->bytes_per_frame); ispa->buffer_attr.maxlength = UINT32_MAX; ispa->buffer_attr.tlength = UINT32_MAX; @@ -801,15 +806,14 @@ static int in_stream_init_pa(SoundIo *soundio, return 0; } -static int in_stream_start_pa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; +static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + SoundIoInStream *instream = &is->pub; + SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_threaded_mainloop_lock(sipa->main_loop); int err = pa_stream_connect_record(ispa->stream, - in_stream->device->name, + instream->device->name, &ispa->buffer_attr, PA_STREAM_ADJUST_LATENCY); if (err) { pa_threaded_mainloop_unlock(sipa->main_loop); @@ -820,40 +824,37 @@ static int in_stream_start_pa(SoundIo *soundio, return 0; } -static void in_stream_peek_pa(SoundIo *soundio, - SoundIoInStream *in_stream, const char **data, int *frame_count) +static void instream_peek_pa(SoundIoPrivate *si, + SoundIoInStreamPrivate *is, const char **data, int *frame_count) { - SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; + SoundIoInStream *instream = &is->pub; + SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; pa_stream *stream = ispa->stream; if (ispa->stream_ready) { size_t nbytes; if (pa_stream_peek(stream, (const void **)data, &nbytes)) soundio_panic("pa_stream_peek error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream)))); - *frame_count = ((int)nbytes) / in_stream->bytes_per_frame; + *frame_count = ((int)nbytes) / instream->bytes_per_frame; } else { *data = nullptr; *frame_count = 0; } } -static void in_stream_drop_pa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; +static void instream_drop_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; pa_stream *stream = ispa->stream; if (pa_stream_drop(stream)) soundio_panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream)))); } -static void in_stream_clear_buffer_pa(SoundIo *soundio, - SoundIoInStream *in_stream) -{ - SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; +static void instream_clear_buffer_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { + SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; if (!ispa->stream_ready) return; pa_stream *stream = ispa->stream; - SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; + SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; pa_threaded_mainloop_lock(sipa->main_loop); @@ -873,14 +874,14 @@ static void in_stream_clear_buffer_pa(SoundIo *soundio, pa_threaded_mainloop_unlock(sipa->main_loop); } -int soundio_pulseaudio_init(SoundIo *soundio) { - assert(!soundio->backend_data); +int soundio_pulseaudio_init(SoundIoPrivate *si) { + assert(!si->backend_data); SoundIoPulseAudio *sipa = create(); if (!sipa) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorNoMem; } - soundio->backend_data = sipa; + si->backend_data = sipa; sipa->connection_refused = false; sipa->device_scan_queued = false; @@ -889,7 +890,7 @@ int soundio_pulseaudio_init(SoundIo *soundio) { sipa->main_loop = pa_threaded_mainloop_new(); if (!sipa->main_loop) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorNoMem; } @@ -897,7 +898,7 @@ int soundio_pulseaudio_init(SoundIo *soundio) { sipa->props = pa_proplist_new(); if (!sipa->props) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorNoMem; } @@ -908,48 +909,48 @@ int soundio_pulseaudio_init(SoundIo *soundio) { sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, "SoundIo", sipa->props); if (!sipa->pulse_context) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorNoMem; } - pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, soundio); - pa_context_set_state_callback(sipa->pulse_context, context_state_callback, soundio); + pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, si); + pa_context_set_state_callback(sipa->pulse_context, context_state_callback, si); int err = pa_context_connect(sipa->pulse_context, NULL, (pa_context_flags_t)0, NULL); if (err) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorInitAudioBackend; } if (sipa->connection_refused) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorInitAudioBackend; } if (pa_threaded_mainloop_start(sipa->main_loop)) { - destroy_pa(soundio); + destroy_pa(si); return SoundIoErrorNoMem; } - soundio->destroy = destroy_pa; - soundio->flush_events = flush_events; - soundio->wait_events = wait_events; - soundio->wakeup = wakeup; + si->destroy = destroy_pa; + si->flush_events = flush_events; + si->wait_events = wait_events; + si->wakeup = wakeup; - soundio->out_stream_init = out_stream_init_pa; - soundio->out_stream_destroy = out_stream_destroy_pa; - soundio->out_stream_start = out_stream_start_pa; - soundio->out_stream_free_count = out_stream_free_count_pa; - soundio->out_stream_begin_write = out_stream_begin_write_pa; - soundio->out_stream_write = out_stream_write_pa; - soundio->out_stream_clear_buffer = out_stream_clear_buffer_pa; + si->outstream_init = outstream_init_pa; + si->outstream_destroy = outstream_destroy_pa; + si->outstream_start = outstream_start_pa; + si->outstream_free_count = outstream_free_count_pa; + si->outstream_begin_write = outstream_begin_write_pa; + si->outstream_write = outstream_write_pa; + si->outstream_clear_buffer = outstream_clear_buffer_pa; - soundio->in_stream_init = in_stream_init_pa; - soundio->in_stream_destroy = in_stream_destroy_pa; - soundio->in_stream_start = in_stream_start_pa; - soundio->in_stream_peek = in_stream_peek_pa; - soundio->in_stream_drop = in_stream_drop_pa; - soundio->in_stream_clear_buffer = in_stream_clear_buffer_pa; + si->instream_init = instream_init_pa; + si->instream_destroy = instream_destroy_pa; + si->instream_start = instream_start_pa; + si->instream_peek = instream_peek_pa; + si->instream_drop = instream_drop_pa; + si->instream_clear_buffer = instream_clear_buffer_pa; return 0; } diff --git a/src/pulseaudio.hpp b/src/pulseaudio.hpp index f1480f3..dbeb4b3 100644 --- a/src/pulseaudio.hpp +++ b/src/pulseaudio.hpp @@ -8,6 +8,6 @@ #ifndef SOUNDIO_PULSEAUDIO_HPP #define SOUNDIO_PULSEAUDIO_HPP -int soundio_pulseaudio_init(struct SoundIo *soundio); +int soundio_pulseaudio_init(struct SoundIoPrivate *si); #endif diff --git a/src/soundio.cpp b/src/soundio.cpp index a805575..9647d79 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -22,6 +22,16 @@ #include #include +static const SoundIoBackend available_backends[] = { +#ifdef SOUNDIO_HAVE_PULSEAUDIO + SoundIoBackendPulseAudio, +#endif +#ifdef SOUNDIO_HAVE_ALSA + SoundIoBackendAlsa, +#endif + SoundIoBackendDummy, +}; + const char *soundio_strerror(int error) { switch ((enum SoundIoError)error) { case SoundIoErrorNone: return "(no error)"; @@ -29,6 +39,8 @@ const char *soundio_strerror(int error) { case SoundIoErrorInitAudioBackend: return "unable to initialize audio backend"; case SoundIoErrorSystemResources: return "system resource not available"; case SoundIoErrorOpeningDevice: return "unable to open device"; + case SoundIoErrorInvalid: return "invalid value"; + case SoundIoErrorBackendUnavailable: return "backend unavailable"; } soundio_panic("invalid error enum value: %d", error); } @@ -99,12 +111,13 @@ const char *soundio_backend_name(enum SoundIoBackend backend) { } void soundio_destroy(struct SoundIo *soundio) { - if (!soundio) + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + if (!si) return; soundio_disconnect(soundio); - destroy(soundio); + destroy(si); } static void default_on_devices_change(struct SoundIo *) { } @@ -112,126 +125,156 @@ static void default_on_events_signal(struct SoundIo *) { } struct SoundIo * soundio_create(void) { soundio_os_init(); - struct SoundIo *soundio = create(); - if (!soundio) { - soundio_destroy(soundio); + struct SoundIoPrivate *si = create(); + if (!si) return NULL; - } + SoundIo *soundio = &si->pub; soundio->on_devices_change = default_on_devices_change; soundio->on_events_signal = default_on_events_signal; return soundio; } int soundio_connect(struct SoundIo *soundio) { + int err = 0; + + for (int i = 0; i < array_length(available_backends); i += 1) { + SoundIoBackend backend = available_backends[i]; + err = soundio_connect_backend(soundio, backend); + if (!err) + return 0; + if (err != SoundIoErrorInitAudioBackend) + return err; + } + + return err; +} + +int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) { + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + + if (si->current_backend) + return SoundIoErrorInvalid; + int err; - + switch (backend) { + case SoundIoBackendPulseAudio: #ifdef SOUNDIO_HAVE_PULSEAUDIO - soundio->current_backend = SoundIoBackendPulseAudio; - err = soundio_pulseaudio_init(soundio); - if (!err) + si->current_backend = SoundIoBackendPulseAudio; + if ((err = soundio_pulseaudio_init(si))) { + soundio_disconnect(soundio); + return err; + } return 0; - if (err != SoundIoErrorInitAudioBackend) { - soundio_disconnect(soundio); - return err; - } +#else + return SoundIoErrorBackendUnavailable; #endif - + case SoundIoBackendAlsa: #ifdef SOUNDIO_HAVE_ALSA - soundio->current_backend = SoundIoBackendAlsa; - err = soundio_alsa_init(soundio); - if (!err) + si->current_backend = SoundIoBackendAlsa; + if ((err = soundio_alsa_init(si))) { + soundio_disconnect(soundio); + return err; + } return 0; - if (err != SoundIoErrorInitAudioBackend) { - soundio_disconnect(soundio); - return err; - } +#else + return SoundIoErrorBackendUnavailable; #endif - - soundio->current_backend = SoundIoBackendDummy; - err = soundio_dummy_init(soundio); - if (err) { - soundio_disconnect(soundio); + case SoundIoBackendDummy: + si->current_backend = SoundIoBackendDummy; + err = soundio_dummy_init(si); + if (err) + soundio_disconnect(soundio); return err; + case SoundIoBackendNone: + return SoundIoErrorInvalid; } - - return 0; + return SoundIoErrorInvalid; } void soundio_disconnect(struct SoundIo *soundio) { - if (soundio->destroy) - soundio->destroy(soundio); - assert(!soundio->backend_data); + SoundIoPrivate *si = (SoundIoPrivate *)soundio; - soundio->current_backend = SoundIoBackendNone; + if (si->destroy) + si->destroy(si); + assert(!si->backend_data); - soundio_destroy_devices_info(soundio->safe_devices_info); - soundio->safe_devices_info = nullptr; + si->current_backend = SoundIoBackendNone; - soundio->destroy = nullptr; - soundio->flush_events = nullptr; - soundio->wait_events = nullptr; - soundio->wakeup = nullptr; + soundio_destroy_devices_info(si->safe_devices_info); + si->safe_devices_info = nullptr; - soundio->out_stream_init = nullptr; - soundio->out_stream_destroy = nullptr; - soundio->out_stream_start = nullptr; - soundio->out_stream_free_count = nullptr; - soundio->out_stream_begin_write = nullptr; - soundio->out_stream_write = nullptr; - soundio->out_stream_clear_buffer = nullptr; + si->destroy = nullptr; + si->flush_events = nullptr; + si->wait_events = nullptr; + si->wakeup = nullptr; - soundio->in_stream_init = nullptr; - soundio->in_stream_destroy = nullptr; - soundio->in_stream_start = nullptr; - soundio->in_stream_peek = nullptr; - soundio->in_stream_drop = nullptr; - soundio->in_stream_clear_buffer = nullptr; + si->outstream_init = nullptr; + si->outstream_destroy = nullptr; + si->outstream_start = nullptr; + si->outstream_free_count = nullptr; + si->outstream_begin_write = nullptr; + si->outstream_write = nullptr; + si->outstream_clear_buffer = nullptr; + + si->instream_init = nullptr; + si->instream_destroy = nullptr; + si->instream_start = nullptr; + si->instream_peek = nullptr; + si->instream_drop = nullptr; + si->instream_clear_buffer = nullptr; } void soundio_flush_events(struct SoundIo *soundio) { - assert(soundio->flush_events); - if (soundio->flush_events) - soundio->flush_events(soundio); + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + assert(si->flush_events); + if (si->flush_events) + si->flush_events(si); } int soundio_get_input_device_count(struct SoundIo *soundio) { + SoundIoPrivate *si = (SoundIoPrivate *)soundio; soundio_flush_events(soundio); - assert(soundio->safe_devices_info); - return soundio->safe_devices_info->input_devices.length; + assert(si->safe_devices_info); + return si->safe_devices_info->input_devices.length; } int soundio_get_output_device_count(struct SoundIo *soundio) { + SoundIoPrivate *si = (SoundIoPrivate *)soundio; soundio_flush_events(soundio); - assert(soundio->safe_devices_info); - return soundio->safe_devices_info->output_devices.length; + assert(si->safe_devices_info); + return si->safe_devices_info->output_devices.length; } int soundio_get_default_input_device_index(struct SoundIo *soundio) { + SoundIoPrivate *si = (SoundIoPrivate *)soundio; soundio_flush_events(soundio); - assert(soundio->safe_devices_info); - return soundio->safe_devices_info->default_input_index; + assert(si->safe_devices_info); + return si->safe_devices_info->default_input_index; } int soundio_get_default_output_device_index(struct SoundIo *soundio) { + SoundIoPrivate *si = (SoundIoPrivate *)soundio; soundio_flush_events(soundio); - assert(soundio->safe_devices_info); - return soundio->safe_devices_info->default_output_index; + assert(si->safe_devices_info); + return si->safe_devices_info->default_output_index; } struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index) { - assert(soundio->safe_devices_info); + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + assert(si->safe_devices_info); assert(index >= 0); - assert(index < soundio->safe_devices_info->input_devices.length); - SoundIoDevice *device = soundio->safe_devices_info->input_devices.at(index); + assert(index < si->safe_devices_info->input_devices.length); + SoundIoDevice *device = si->safe_devices_info->input_devices.at(index); soundio_device_ref(device); return device; } struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) { - assert(soundio->safe_devices_info); + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + assert(si->safe_devices_info); assert(index >= 0); - assert(index < soundio->safe_devices_info->output_devices.length); - SoundIoDevice *device = soundio->safe_devices_info->output_devices.at(index); + assert(index < si->safe_devices_info->output_devices.length); + SoundIoDevice *device = si->safe_devices_info->output_devices.at(index); soundio_device_ref(device); return device; } @@ -250,10 +293,6 @@ enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *dev return device->purpose; } -const struct SoundIoChannelLayout *soundio_device_channel_layout(const struct SoundIoDevice *device) { - return &device->channel_layout; -} - void soundio_device_unref(struct SoundIoDevice *device) { if (!device) return; @@ -274,151 +313,140 @@ void soundio_device_ref(struct SoundIoDevice *device) { } void soundio_wait_events(struct SoundIo *soundio) { - soundio->wait_events(soundio); + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + si->wait_events(si); } void soundio_wakeup(struct SoundIo *soundio) { - soundio->wakeup(soundio); + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + si->wakeup(si); } -void soundio_out_stream_fill_with_silence(struct SoundIoOutStream *out_stream) { +void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) { char *buffer; - int requested_frame_count = soundio_out_stream_free_count(out_stream); + int requested_frame_count = soundio_outstream_free_count(outstream); while (requested_frame_count > 0) { int frame_count = requested_frame_count; - soundio_out_stream_begin_write(out_stream, &buffer, &frame_count); - memset(buffer, 0, frame_count * out_stream->bytes_per_frame); - soundio_out_stream_write(out_stream, buffer, frame_count); + soundio_outstream_begin_write(outstream, &buffer, &frame_count); + memset(buffer, 0, frame_count * outstream->bytes_per_frame); + soundio_outstream_write(outstream, buffer, frame_count); requested_frame_count -= frame_count; } } -int soundio_out_stream_free_count(struct SoundIoOutStream *out_stream) { - SoundIo *soundio = out_stream->device->soundio; - return soundio->out_stream_free_count(soundio, out_stream); +int soundio_outstream_free_count(struct SoundIoOutStream *outstream) { + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + return si->outstream_free_count(si, os); } -void soundio_out_stream_begin_write(struct SoundIoOutStream *out_stream, +void soundio_outstream_begin_write(struct SoundIoOutStream *outstream, char **data, int *frame_count) { - SoundIo *soundio = out_stream->device->soundio; - soundio->out_stream_begin_write(soundio, out_stream, data, frame_count); + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + si->outstream_begin_write(si, os, data, frame_count); } -void soundio_out_stream_write(struct SoundIoOutStream *out_stream, +void soundio_outstream_write(struct SoundIoOutStream *outstream, char *data, int frame_count) { - SoundIo *soundio = out_stream->device->soundio; - soundio->out_stream_write(soundio, out_stream, data, frame_count); + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + si->outstream_write(si, os, data, frame_count); } -int soundio_out_stream_create(struct SoundIoDevice *device, - enum SoundIoFormat format, int sample_rate, - double latency, void *userdata, - void (*write_callback)(struct SoundIoOutStream *, int frame_count), - void (*underrun_callback)(struct SoundIoOutStream *), - struct SoundIoOutStream **out_out_stream) -{ - *out_out_stream = nullptr; - - SoundIoOutStream *out_stream = create(); - if (!out_stream) { - soundio_out_stream_destroy(out_stream); - return SoundIoErrorNoMem; - } +struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) { + SoundIoOutStreamPrivate *os = create(); + if (!os) + return nullptr; + SoundIoOutStream *outstream = &os->pub; + outstream->device = device; soundio_device_ref(device); - out_stream->device = device; - out_stream->userdata = userdata; - out_stream->write_callback = write_callback; - out_stream->underrun_callback = underrun_callback; - out_stream->format = format; - out_stream->sample_rate = sample_rate; - out_stream->latency = latency; - out_stream->bytes_per_frame = soundio_get_bytes_per_frame(format, - device->channel_layout.channel_count); - SoundIo *soundio = device->soundio; - int err = soundio->out_stream_init(soundio, out_stream); - if (err) { - soundio_out_stream_destroy(out_stream); - return err; - } + // TODO set defaults - *out_out_stream = out_stream; - return 0; + return outstream; } -void soundio_out_stream_destroy(SoundIoOutStream *out_stream) { - if (!out_stream) +int soundio_outstream_open(struct SoundIoOutStream *outstream) { + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count); + + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + return si->outstream_init(si, os); +} + +void soundio_outstream_destroy(SoundIoOutStream *outstream) { + if (!outstream) return; - SoundIo *soundio = out_stream->device->soundio; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; - if (soundio->out_stream_destroy) - soundio->out_stream_destroy(soundio, out_stream); + if (si->outstream_destroy) + si->outstream_destroy(si, os); - soundio_device_unref(out_stream->device); - destroy(out_stream); + soundio_device_unref(outstream->device); + destroy(os); } -int soundio_out_stream_start(struct SoundIoOutStream *out_stream) { - SoundIo *soundio = out_stream->device->soundio; - return soundio->out_stream_start(soundio, out_stream); +int soundio_outstream_start(struct SoundIoOutStream *outstream) { + SoundIo *soundio = outstream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; + return si->outstream_start(si, os); } -int soundio_in_stream_create(struct SoundIoDevice *device, - enum SoundIoFormat format, int sample_rate, - double latency, void *userdata, - void (*read_callback)(struct SoundIoInStream *), - struct SoundIoInStream **out_in_stream) -{ - *out_in_stream = nullptr; - - SoundIoInStream *sid = create(); - if (!sid) { - soundio_in_stream_destroy(sid); - return SoundIoErrorNoMem; - } +struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) { + SoundIoInStreamPrivate *is = create(); + if (!is) + return nullptr; + SoundIoInStream *instream = &is->pub; + instream->device = device; soundio_device_ref(device); - sid->device = device; - sid->userdata = userdata; - sid->read_callback = read_callback; - sid->format = format; - sid->latency = latency; - sid->sample_rate = sample_rate; - sid->bytes_per_frame = soundio_get_bytes_per_frame(format, - device->channel_layout.channel_count); - SoundIo *soundio = device->soundio; - int err = soundio->in_stream_init(soundio, sid); - if (err) { - soundio_in_stream_destroy(sid); - return err; - } + // TODO set defaults - *out_in_stream = sid; - return 0; + return instream; } -int soundio_in_stream_start(struct SoundIoInStream *in_stream) { - SoundIo *soundio = in_stream->device->soundio; - return soundio->in_stream_start(soundio, in_stream); +int soundio_instream_open(struct SoundIoInStream *instream) { + instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count); + SoundIo *soundio = instream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream; + return si->instream_init(si, is); } -void soundio_in_stream_destroy(struct SoundIoInStream *in_stream) { - if (!in_stream) +int soundio_instream_start(struct SoundIoInStream *instream) { + SoundIo *soundio = instream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; + SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream; + return si->instream_start(si, is); +} + +void soundio_instream_destroy(struct SoundIoInStream *instream) { + if (!instream) return; - SoundIo *soundio = in_stream->device->soundio; + SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream; + SoundIo *soundio = instream->device->soundio; + SoundIoPrivate *si = (SoundIoPrivate *)soundio; - if (soundio->in_stream_destroy) - soundio->in_stream_destroy(soundio, in_stream); + if (si->instream_destroy) + si->instream_destroy(si, is); - soundio_device_unref(in_stream->device); - destroy(in_stream); + soundio_device_unref(instream->device); + destroy(is); } void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) { @@ -432,3 +460,78 @@ void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) { destroy(devices_info); } + +bool soundio_have_backend(SoundIoBackend backend) { + switch (backend) { + case SoundIoBackendPulseAudio: +#ifdef SOUNDIO_HAVE_PULSEAUDIO + return true; +#else + return false; +#endif + case SoundIoBackendAlsa: +#ifdef SOUNDIO_HAVE_ALSA + return true; +#else + return false; +#endif + case SoundIoBackendDummy: + return true; + case SoundIoBackendNone: + return false; + } + return false; +} + +int soundio_backend_count(struct SoundIo *soundio) { + return array_length(available_backends); +} + +SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index) { + return available_backends[index]; +} + +static bool layout_contains(const SoundIoChannelLayout *available_layouts, int available_layouts_count, + const SoundIoChannelLayout *target_layout) +{ + for (int i = 0; i < available_layouts_count; i += 1) { + const SoundIoChannelLayout *available_layout = &available_layouts[i]; + if (soundio_channel_layout_equal(target_layout, available_layout)) + return true; + } + return false; +} + +const struct SoundIoChannelLayout *soundio_best_matching_channel_layout( + const struct SoundIoChannelLayout *preferred_layouts, int preferred_layouts_count, + const struct SoundIoChannelLayout *available_layouts, int available_layouts_count) +{ + for (int i = 0; i < preferred_layouts_count; i += 1) { + const SoundIoChannelLayout *preferred_layout = &preferred_layouts[i]; + if (layout_contains(available_layouts, available_layouts_count, preferred_layout)) + return preferred_layout; + } + return nullptr; +} + +static int compare_layouts(const void *a, const void *b) { + const SoundIoChannelLayout *layout_a = *((SoundIoChannelLayout **)a); + const SoundIoChannelLayout *layout_b = *((SoundIoChannelLayout **)b); + if (layout_a->channel_count > layout_b->channel_count) + return -1; + else if (layout_a->channel_count < layout_b->channel_count) + return 1; + else + return 0; +} + +void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layouts_count) { + if (!layouts) + return; + + qsort(layouts, layouts_count, sizeof(SoundIoChannelLayout), compare_layouts); +} + +void soundio_device_sort_channel_layouts(struct SoundIoDevice *device) { + soundio_sort_channel_layouts(device->layouts, device->layout_count); +} diff --git a/src/soundio.h b/src/soundio.h index 7cd5412..5df6db9 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -14,7 +14,7 @@ #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ +#endif struct SoundIo; struct SoundIoDevicesInfo; @@ -25,6 +25,8 @@ enum SoundIoError { SoundIoErrorInitAudioBackend, SoundIoErrorSystemResources, SoundIoErrorOpeningDevice, + SoundIoErrorInvalid, + SoundIoErrorBackendUnavailable, }; enum SoundIoChannelId { @@ -64,15 +66,6 @@ enum SoundIoChannelId { SoundIoChannelIdBottomCenter, SoundIoChannelIdBottomLeftCenter, SoundIoChannelIdBottomRightCenter, - - SoundIoChannelIdCount, -}; - -#define SOUNDIO_MAX_CHANNELS 32 -struct SoundIoChannelLayout { - const char *name; - int channel_count; - enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS]; }; enum SoundIoChannelLayoutId { @@ -160,7 +153,17 @@ enum SoundIoFormat { #error unknown byte order #endif +// The size of this struct is OK to use. +#define SOUNDIO_MAX_CHANNELS 32 +struct SoundIoChannelLayout { + const char *name; + int channel_count; + enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS]; +}; + +// The size of this struct is not part of the API or ABI. struct SoundIoDevice { + // Read-only. Set automatically. struct SoundIo *soundio; // `name` uniquely identifies this device. `description` is user-friendly @@ -168,9 +171,14 @@ struct SoundIoDevice { char *name; char *description; - // If this information is missing due to a `probe_error`, the number of - // channels will be zero. - struct SoundIoChannelLayout channel_layout; + // Channel layouts are handled similarly to sample format; see those docs. + // If this information is missing due to a `probe_error`, `layouts` + // will be NULL. It's OK to modify this data, for example calling + // soundio_sort_channel_layouts on it. + // Devices are guaranteed to have at least 1 channel layout. + struct SoundIoChannelLayout *layouts; + int layout_count; + struct SoundIoChannelLayout current_layout; // A device is either a raw device or it is a virtual device that is // provided by a software mixing service such as dmix or PulseAudio (see @@ -187,6 +195,7 @@ struct SoundIoDevice { // `format_count`. If this information is missing due to a probe error, // `formats` will be `NULL`. If `current_format` is unavailable, it will be // set to `SoundIoFormatInvalid`. + // Devices are guaranteed to have at least 1 format available. enum SoundIoFormat *formats; int format_count; enum SoundIoFormat current_format; @@ -194,6 +203,7 @@ struct SoundIoDevice { // Sample rate is handled very similar to sample format; see those docs. // If sample rate information is missing due to a probe error, the field // will be set to zero. + // Devices are guaranteed to have at least 1 sample rate available. int sample_rate_min; int sample_rate_max; int sample_rate_current; @@ -222,66 +232,42 @@ struct SoundIoDevice { int probe_error; }; +// The size of this struct is not part of the API or ABI. struct SoundIoOutStream { - void *backend_data; struct SoundIoDevice *device; enum SoundIoFormat format; int sample_rate; + struct SoundIoChannelLayout layout; double latency; - int bytes_per_frame; void *userdata; void (*underrun_callback)(struct SoundIoOutStream *); void (*write_callback)(struct SoundIoOutStream *, int frame_count); + + // computed automatically when you call soundio_outstream_open + int bytes_per_frame; }; +// The size of this struct is not part of the API or ABI. struct SoundIoInStream { - void *backend_data; struct SoundIoDevice *device; enum SoundIoFormat format; int sample_rate; + struct SoundIoChannelLayout layout; double latency; - int bytes_per_frame; void *userdata; void (*read_callback)(struct SoundIoInStream *); + + // computed automatically when you call soundio_instream_open + int bytes_per_frame; }; +// The size of this struct is not part of the API or ABI. struct SoundIo { - enum SoundIoBackend current_backend; - - // safe to read without a mutex from a single thread - struct SoundIoDevicesInfo *safe_devices_info; - void *userdata; void (*on_devices_change)(struct SoundIo *); void (*on_events_signal)(struct SoundIo *); - - - void *backend_data; - void (*destroy)(struct SoundIo *); - void (*flush_events)(struct SoundIo *); - void (*wait_events)(struct SoundIo *); - void (*wakeup)(struct SoundIo *); - - int (*out_stream_init)(struct SoundIo *, struct SoundIoOutStream *); - void (*out_stream_destroy)(struct SoundIo *, struct SoundIoOutStream *); - int (*out_stream_start)(struct SoundIo *, struct SoundIoOutStream *); - int (*out_stream_free_count)(struct SoundIo *, struct SoundIoOutStream *); - void (*out_stream_begin_write)(struct SoundIo *, struct SoundIoOutStream *, - char **data, int *frame_count); - void (*out_stream_write)(struct SoundIo *, struct SoundIoOutStream *, - char *data, int frame_count); - void (*out_stream_clear_buffer)(struct SoundIo *, struct SoundIoOutStream *); - - - int (*in_stream_init)(struct SoundIo *, struct SoundIoInStream *); - void (*in_stream_destroy)(struct SoundIo *, struct SoundIoInStream *); - int (*in_stream_start)(struct SoundIo *, struct SoundIoInStream *); - void (*in_stream_peek)(struct SoundIo *, struct SoundIoInStream *, - const char **data, int *frame_count); - void (*in_stream_drop)(struct SoundIo *, struct SoundIoInStream *); - void (*in_stream_clear_buffer)(struct SoundIo *, struct SoundIoInStream *); }; // Main Context @@ -291,12 +277,23 @@ struct SoundIo { struct SoundIo * soundio_create(void); void soundio_destroy(struct SoundIo *soundio); + +// Provided these backends were compiled in, this tries JACK, then PulseAudio, +// then ALSA, then CoreAudio, then ASIO, then DirectSound, then OSS, then Dummy. int soundio_connect(struct SoundIo *soundio); +// Instead of calling `soundio_connect` you may call this function to try a +// specific backend. +int soundio_connect_backend(struct SoundIo *soundio, enum SoundIoBackend backend); void soundio_disconnect(struct SoundIo *soundio); const char *soundio_strerror(int error); 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) +enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index); + // when you call this, the on_devices_change and on_events_signal callbacks // might be called. This is the only time those functions will be called. void soundio_flush_events(struct SoundIo *soundio); @@ -312,7 +309,10 @@ void soundio_wakeup(struct SoundIo *soundio); // Channel Layouts -bool soundio_channel_layout_equal(const struct SoundIoChannelLayout *a, +// Returns whether the channel count field and each channel id matches in +// the supplied channel layouts. +bool soundio_channel_layout_equal( + const struct SoundIoChannelLayout *a, const struct SoundIoChannelLayout *b); const char *soundio_get_channel_name(enum SoundIoChannelId id); @@ -320,15 +320,25 @@ const char *soundio_get_channel_name(enum SoundIoChannelId id); int soundio_channel_layout_builtin_count(void); const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index); +// TODO remove this API or have it write to a `char *`? void soundio_debug_print_channel_layout(const struct SoundIoChannelLayout *layout); int soundio_channel_layout_find_channel( const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel); -// merely populates the name field of layout if it matches a builtin one. +// Populates the name field of layout if it matches a builtin one. // returns whether it found a match bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout); +// Iterates over preferred_layouts. Returns the first channel layout in +// preferred_layouts which matches one of the channel layouts in +// available_layouts. Returns NULL if none matches. +const struct SoundIoChannelLayout *soundio_best_matching_channel_layout( + const struct SoundIoChannelLayout *preferred_layouts, int preferred_layout_count, + const struct SoundIoChannelLayout *available_layouts, int available_layout_count); + +// Sorts by channel count, descending. +void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layout_count); // Sample Formats @@ -375,51 +385,52 @@ bool soundio_device_equal( const struct SoundIoDevice *b); enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device); +// Sorts channel layouts by channel count, descending. +void soundio_device_sort_channel_layouts(struct SoundIoDevice *device); -// Output Devices -int soundio_out_stream_create(struct SoundIoDevice *device, - enum SoundIoFormat format, int sample_rate, - double latency, void *userdata, - void (*write_callback)(struct SoundIoOutStream *, int frame_count), - void (*underrun_callback)(struct SoundIoOutStream *), - struct SoundIoOutStream **out_out_stream); -void soundio_out_stream_destroy(struct SoundIoOutStream *out_stream); +// Output Streams +// allocates memory and sets defaults. Next you should fill out the struct fields +// and then call soundio_outstream_open +struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device); -int soundio_out_stream_start(struct SoundIoOutStream *out_stream); +int soundio_outstream_open(struct SoundIoOutStream *outstream); -void soundio_out_stream_fill_with_silence(struct SoundIoOutStream *out_stream); +void soundio_outstream_destroy(struct SoundIoOutStream *outstream); + +int soundio_outstream_start(struct SoundIoOutStream *outstream); + +void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream); // number of frames available to write -int soundio_out_stream_free_count(struct SoundIoOutStream *out_stream); -void soundio_out_stream_begin_write(struct SoundIoOutStream *out_stream, +int soundio_outstream_free_count(struct SoundIoOutStream *outstream); +void soundio_outstream_begin_write(struct SoundIoOutStream *outstream, char **data, int *frame_count); -void soundio_out_stream_write(struct SoundIoOutStream *out_stream, +void soundio_outstream_write(struct SoundIoOutStream *outstream, char *data, int frame_count); -void soundio_out_stream_clear_buffer(struct SoundIoOutStream *out_stream); +void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); -// Input Devices +// Input Streams +// allocates memory and sets defaults. Next you should fill out the struct fields +// and then call soundio_instream_open +struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device); +void soundio_instream_destroy(struct SoundIoInStream *instream); -int soundio_in_stream_create(struct SoundIoDevice *device, - enum SoundIoFormat format, int sample_rate, - double latency, void *userdata, - void (*read_callback)(struct SoundIoInStream *), - struct SoundIoInStream **out_in_stream); -void soundio_in_stream_destroy(struct SoundIoInStream *in_stream); +int soundio_instream_open(struct SoundIoInStream *instream); -int soundio_in_stream_start(struct SoundIoInStream *in_stream); +int soundio_instream_start(struct SoundIoInStream *instream); -void soundio_in_stream_peek(struct SoundIoInStream *in_stream, +void soundio_instream_peek(struct SoundIoInStream *instream, const char **data, int *out_frame_count); -// this will drop all of the frames from when you called soundio_in_stream_peek -void soundio_in_stream_drop(struct SoundIoInStream *in_stream); +// this will drop all of the frames from when you called soundio_instream_peek +void soundio_instream_drop(struct SoundIoInStream *instream); -void soundio_in_stream_clear_buffer(struct SoundIoInStream *in_stream); +void soundio_instream_clear_buffer(struct SoundIoInStream *instream); // Ring Buffer @@ -448,6 +459,6 @@ void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer); #ifdef __cplusplus } -#endif /* __cplusplus */ +#endif #endif diff --git a/src/soundio.hpp b/src/soundio.hpp index 4081720..6d425f9 100644 --- a/src/soundio.hpp +++ b/src/soundio.hpp @@ -19,6 +19,51 @@ struct SoundIoDevicesInfo { int default_input_index; }; +struct SoundIoOutStreamPrivate { + struct SoundIoOutStream pub; + void *backend_data; +}; + +struct SoundIoInStreamPrivate { + struct SoundIoInStream pub; + void *backend_data; +}; + +struct SoundIoPrivate { + struct SoundIo pub; + + enum SoundIoBackend current_backend; + + // safe to read without a mutex from a single thread + struct SoundIoDevicesInfo *safe_devices_info; + + void *backend_data; + void (*destroy)(struct SoundIoPrivate *); + void (*flush_events)(struct SoundIoPrivate *); + void (*wait_events)(struct SoundIoPrivate *); + void (*wakeup)(struct SoundIoPrivate *); + + int (*outstream_init)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + void (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, + char **data, int *frame_count); + void (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, + char *data, int frame_count); + void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + + + int (*instream_init)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); + void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); + int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); + void (*instream_peek)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, + const char **data, int *frame_count); + void (*instream_drop)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); + void (*instream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); +}; + void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info); + #endif diff --git a/test/unit_tests.cpp b/test/unit_tests.cpp index 15b4f9f..f876326 100644 --- a/test/unit_tests.cpp +++ b/test/unit_tests.cpp @@ -26,7 +26,7 @@ static void test_os_get_time(void) { static void write_callback(struct SoundIoOutStream *device, int frame_count) { } static void underrun_callback(struct SoundIoOutStream *device) { } -static void test_create_out_stream(void) { +static void test_create_outstream(void) { struct SoundIo *soundio = soundio_create(); assert(soundio); ok_or_panic(soundio_connect(soundio)); @@ -34,10 +34,17 @@ static void test_create_out_stream(void) { assert(default_out_device_index >= 0); struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index); assert(device); - struct SoundIoOutStream *out_stream; - soundio_out_stream_create(device, SoundIoFormatFloat32NE, 48000, 0.1, NULL, - write_callback, underrun_callback, &out_stream); - soundio_out_stream_destroy(out_stream); + struct SoundIoOutStream *outstream = soundio_outstream_create(device); + outstream->format = SoundIoFormatFloat32NE; + outstream->sample_rate = 48000; + outstream->layout = device->layouts[0]; + outstream->latency = 0.1; + outstream->write_callback = write_callback; + outstream->underrun_callback = underrun_callback; + + ok_or_panic(soundio_outstream_open(outstream)); + + soundio_outstream_destroy(outstream); soundio_device_unref(device); soundio_destroy(soundio); } @@ -161,7 +168,7 @@ struct Test { static struct Test tests[] = { {"os_get_time", test_os_get_time}, - {"create output stream", test_create_out_stream}, + {"create output stream", test_create_outstream}, {"ring buffer basic", test_ring_buffer_basic}, {"ring buffer threaded", test_ring_buffer_threaded}, {NULL, NULL},