stream API update; expose multiple channel layouts

This commit is contained in:
Andrew Kelley 2015-07-13 09:17:20 -07:00
parent f0c8c68592
commit 5b3fd175f8
14 changed files with 1043 additions and 820 deletions

View file

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

View file

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

View file

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

View file

@ -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<SoundIoChannelLayout>(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<SoundIoFormat>(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<SoundIoFormat>(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<SoundIoDevicesInfo>();
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<SoundIoAlsa>();
if (!sia) {
destroy_alsa(soundio);
static int outstream_init_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
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<SoundIoAlsa>();
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;
}

View file

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

View file

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

View file

@ -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<SoundIoOutStreamDummy>();
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<SoundIoDummy>();
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<SoundIoDevicesInfo>();
if (!soundio->safe_devices_info) {
destroy_dummy(soundio);
assert(!si->safe_devices_info);
si->safe_devices_info = create<SoundIoDevicesInfo>();
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<SoundIoDevice>();
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<SoundIoChannelLayout>(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<SoundIoDevice>();
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<SoundIoChannelLayout>(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;
}

View file

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

View file

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

View file

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

View file

@ -22,6 +22,16 @@
#include <string.h>
#include <assert.h>
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<SoundIo>();
if (!soundio) {
soundio_destroy(soundio);
struct SoundIoPrivate *si = create<SoundIoPrivate>();
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<SoundIoOutStream>();
if (!out_stream) {
soundio_out_stream_destroy(out_stream);
return SoundIoErrorNoMem;
}
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
SoundIoOutStreamPrivate *os = create<SoundIoOutStreamPrivate>();
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<SoundIoInStream>();
if (!sid) {
soundio_in_stream_destroy(sid);
return SoundIoErrorNoMem;
}
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
SoundIoInStreamPrivate *is = create<SoundIoInStreamPrivate>();
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);
}

View file

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

View file

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

View file

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