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])); fprintf(stderr, ", %s", soundio_get_channel_name(layout->channels[i]));
} }
} }
} }
static void print_device(struct SoundIoDevice *device, bool is_default) { 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) { if (device->probe_error) {
fprintf(stderr, " probe error: %s\n", soundio_strerror(device->probe_error)); fprintf(stderr, " probe error: %s\n", soundio_strerror(device->probe_error));
} else { } else {
fprintf(stderr, " channel layout: "); fprintf(stderr, " channel layouts:\n");
print_channel_layout(&device->channel_layout); for (int i = 0; i < device->layout_count; i += 1) {
fprintf(stderr, " ");
print_channel_layout(&device->layouts[i]);
fprintf(stderr, "\n"); 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, " min sample rate: %d\n", device->sample_rate_min);
fprintf(stderr, " max sample rate: %d\n", device->sample_rate_max); fprintf(stderr, " max sample rate: %d\n", device->sample_rate_max);
if (device->sample_rate_current) if (device->sample_rate_current)

View file

@ -25,18 +25,18 @@ static void panic(const char *format, ...) {
abort(); abort();
} }
static void read_callback(struct SoundIoInStream *in_stream) { static void read_callback(struct SoundIoInStream *instream) {
fprintf(stderr, "read_callback\n"); 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"); fprintf(stderr, "write_callback\n");
} }
static void underrun_callback(struct SoundIoOutStream *out_stream) { static void underrun_callback(struct SoundIoOutStream *outstream) {
static int count = 0; static int count = 0;
fprintf(stderr, "underrun %d\n", count++); 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) { 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, "Input device: %s\n", in_device->description);
fprintf(stderr, "Output device: %s\n", out_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"); 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; if ((err = soundio_instream_open(instream)))
soundio_in_stream_create(in_device, SoundIoFormatFloat32NE, 48000, latency, NULL, panic("unable to open input stream: %s", soundio_strerror(err));
read_callback, &in_stream);
struct SoundIoOutStream *out_stream; struct SoundIoOutStream *outstream = soundio_outstream_create(out_device);
soundio_out_stream_create(out_device, SoundIoFormatFloat32NE, 48000, latency, NULL, if (!outstream)
write_callback, underrun_callback, &out_stream); 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)); 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)); panic("unable to start output device: %s", soundio_strerror(err));
for (;;) for (;;)
soundio_wait_events(soundio); soundio_wait_events(soundio);
soundio_out_stream_destroy(out_stream); soundio_outstream_destroy(outstream);
soundio_in_stream_destroy(in_stream); soundio_instream_destroy(instream);
soundio_device_unref(in_device); soundio_device_unref(in_device);
soundio_device_unref(out_device); soundio_device_unref(out_device);
soundio_destroy(soundio); soundio_destroy(soundio);

View file

@ -28,20 +28,20 @@ static void panic(const char *format, ...) {
static const float PI = 3.1415926535f; static const float PI = 3.1415926535f;
static float seconds_offset = 0.0f; 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; //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; float seconds_per_frame = 1.0f / float_sample_rate;
while (requested_frame_count > 0) { while (requested_frame_count > 0) {
char *data; char *data;
int frame_count = requested_frame_count; 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 // 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; 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; float radians_per_second = pitch * 2.0f * PI;
for (int frame = 0; frame < frame_count; frame += 1) { for (int frame = 0; frame < frame_count; frame += 1) {
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second); 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 += sample;
ptr += 1; ptr += 1;
} }
} }
seconds_offset += seconds_per_frame * frame_count; 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; 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); fprintf(stderr, "Output device: %s: %s\n", device->name, device->description);
struct SoundIoOutStream *out_stream; struct SoundIoOutStream *outstream = soundio_outstream_create(device);
soundio_out_stream_create(device, SoundIoFormatFloat32NE, 48000, outstream->format = SoundIoFormatFloat32NE;
0.1, NULL, write_callback, underrun_callback, &out_stream); 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)); panic("unable to start device: %s", soundio_strerror(err));
for (;;) for (;;)
soundio_wait_events(soundio); soundio_wait_events(soundio);
soundio_out_stream_destroy(out_stream); soundio_outstream_destroy(outstream);
soundio_device_unref(device); soundio_device_unref(device);
soundio_destroy(soundio); soundio_destroy(soundio);
return 0; return 0;

View file

@ -30,6 +30,10 @@ struct SoundIoAlsa {
struct SoundIoDevicesInfo *ready_devices_info; struct SoundIoDevicesInfo *ready_devices_info;
}; };
struct SoundIoOutStreamAlsa {
};
static void wakeup_device_poll(SoundIoAlsa *sia) { static void wakeup_device_poll(SoundIoAlsa *sia) {
ssize_t amt = write(sia->notify_pipe_fd[1], "a", 1); ssize_t amt = write(sia->notify_pipe_fd[1], "a", 1);
if (amt == -1) { if (amt == -1) {
@ -41,8 +45,8 @@ static void wakeup_device_poll(SoundIoAlsa *sia) {
} }
} }
static void destroy_alsa(SoundIo *soundio) { static void destroy_alsa(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
if (!sia) if (!sia)
return; return;
@ -67,7 +71,7 @@ static void destroy_alsa(SoundIo *soundio) {
close(sia->notify_fd); close(sia->notify_fd);
destroy(sia); destroy(sia);
soundio->backend_data = nullptr; si->backend_data = nullptr;
} }
static char * str_partition_on_char(char *str, char c) { 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; 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); int channel_count = min((unsigned int)SOUNDIO_MAX_CHANNELS, chmap->channels);
device->channel_layout.channel_count = channel_count; dest->channel_count = channel_count;
device->channel_layout.name = nullptr;
for (int i = 0; i < channel_count; i += 1) { 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) if (!maps)
return; return 0;
snd_pcm_chmap_query_t **p; snd_pcm_chmap_query_t **p;
snd_pcm_chmap_query_t *v; snd_pcm_chmap_query_t *v;
snd_pcm_chmap_t *best = nullptr;
for (p = maps; (v = *p); p += 1) { // one iteration to count
if (!best || v->map.channels > best->channels) int layout_count = 0;
best = &v->map; for (p = maps; (v = *p) && layout_count < SOUNDIO_MAX_CHANNELS; p += 1, layout_count += 1) { }
} device->layouts = allocate<SoundIoChannelLayout>(layout_count);
get_channel_layout(device, best); if (!device->layouts) {
snd_pcm_free_chmaps(maps); 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);
}
snd_pcm_free_chmaps(maps);
return 0;
} }
static snd_pcm_format_t to_alsa_fmt(SoundIoFormat fmt) { 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 // * hw period time
// * sw start threshold // * sw start threshold
// * sw avail min // * 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; int err;
snd_pcm_t *handle;
snd_pcm_hw_params_t *hwparams; if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0)
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; return SoundIoErrorOpeningDevice;
}
if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) { if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0)
handle_channel_maps(device, maps);
snd_pcm_close(handle);
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
}
// disable hardware resampling because we're trying to probe if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, 0)) < 0) {
handle_channel_maps(device, maps);
snd_pcm_close(handle);
return SoundIoErrorOpeningDevice; 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; unsigned int channel_count;
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0) { if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
handle_channel_maps(device, maps);
snd_pcm_close(handle);
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
}
unsigned int min_sample_rate; unsigned int min_sample_rate;
unsigned int max_sample_rate; unsigned int max_sample_rate;
int min_dir; int min_dir;
int max_dir; int max_dir;
if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &max_sample_rate, &max_dir)) < 0) { 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);
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
}
if (max_dir < 0) if (max_dir < 0)
max_sample_rate -= 1; max_sample_rate -= 1;
if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &min_sample_rate, &min_dir)) < 0) { 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);
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
}
if (min_dir > 0) if (min_dir > 0)
min_sample_rate += 1; min_sample_rate += 1;
@ -283,19 +274,14 @@ 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_LE);
snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_BE); 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) { if ((err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask)) < 0)
handle_channel_maps(device, maps);
snd_pcm_close(handle);
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
}
if (!device->formats) {
snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask); snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask);
device->formats = allocate<SoundIoFormat>(18); device->formats = allocate<SoundIoFormat>(18);
if (!device->formats) { if (!device->formats)
handle_channel_maps(device, maps);
snd_pcm_close(handle);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
}
device->format_count = 0; device->format_count = 0;
test_fmt_mask(device, fmt_mask, SoundIoFormatS8); test_fmt_mask(device, fmt_mask, SoundIoFormatS8);
@ -316,36 +302,72 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE); test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE);
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE); test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE);
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64BE); 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_min = min_sample_rate;
device->sample_rate_max = max_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? return 0;
device->sample_rate_current = 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); snd_pcm_close(handle);
return 0; return 0;
// TODO: device->default_latency
} }
static inline bool str_has_prefix(const char *big_str, const char *prefix) { static inline bool str_has_prefix(const char *big_str, const char *prefix) {
return strncmp(big_str, prefix, strlen(prefix)) == 0; return strncmp(big_str, prefix, strlen(prefix)) == 0;
} }
static int refresh_devices(SoundIo *soundio) { static int refresh_devices(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; SoundIo *soundio = &si->pub;
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>(); SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
if (!devices_info) if (!devices_info)
@ -585,8 +607,8 @@ static int refresh_devices(SoundIo *soundio) {
} }
static void device_thread_run(void *arg) { static void device_thread_run(void *arg) {
SoundIo *soundio = (SoundIo *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
// Some systems cannot read integer variables if they are not // Some systems cannot read integer variables if they are not
// properly aligned. On other systems, incorrect alignment may // properly aligned. On other systems, incorrect alignment may
@ -672,7 +694,7 @@ static void device_thread_run(void *arg) {
} }
} }
if (got_rescan_event) { if (got_rescan_event) {
if ((err = refresh_devices(soundio))) if ((err = refresh_devices(si)))
soundio_panic("error refreshing devices: %s", soundio_strerror(err)); 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); soundio_os_mutex_unlock(sia->mutex);
} }
static void flush_events(SoundIo *soundio) { static void flush_events(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; SoundIo *soundio = &si->pub;
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
block_until_have_devices(sia); block_until_have_devices(sia);
bool change = false; bool change = false;
@ -697,8 +720,8 @@ static void flush_events(SoundIo *soundio) {
soundio_os_mutex_lock(sia->mutex); soundio_os_mutex_lock(sia->mutex);
if (sia->ready_devices_info) { if (sia->ready_devices_info) {
old_devices_info = soundio->safe_devices_info; old_devices_info = si->safe_devices_info;
soundio->safe_devices_info = sia->ready_devices_info; si->safe_devices_info = sia->ready_devices_info;
sia->ready_devices_info = nullptr; sia->ready_devices_info = nullptr;
change = true; change = true;
} }
@ -711,109 +734,103 @@ static void flush_events(SoundIo *soundio) {
soundio_destroy_devices_info(old_devices_info); soundio_destroy_devices_info(old_devices_info);
} }
static void wait_events(SoundIo *soundio) { static void wait_events(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
flush_events(soundio); flush_events(si);
soundio_os_mutex_lock(sia->mutex); soundio_os_mutex_lock(sia->mutex);
soundio_os_cond_wait(sia->cond, sia->mutex); soundio_os_cond_wait(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex); soundio_os_mutex_unlock(sia->mutex);
} }
static void wakeup(SoundIo *soundio) { static void wakeup(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data; SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
soundio_os_mutex_lock(sia->mutex); soundio_os_mutex_lock(sia->mutex);
soundio_os_cond_signal(sia->cond, sia->mutex); soundio_os_cond_signal(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex); soundio_os_mutex_unlock(sia->mutex);
} }
static void out_stream_destroy_alsa(SoundIo *soundio, static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
{ if (!osa)
soundio_panic("TODO"); return;
destroy(osa);
os->backend_data = nullptr;
} }
static int out_stream_init_alsa(SoundIo *soundio, static int outstream_init_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
{ if (!osa) {
soundio_panic("TODO"); outstream_destroy_alsa(si, os);
}
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);
return SoundIoErrorNoMem; 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_fd = -1;
sia->notify_wd = -1; sia->notify_wd = -1;
sia->have_devices_flag.store(false); sia->have_devices_flag.store(false);
@ -821,13 +838,13 @@ int soundio_alsa_init(SoundIo *soundio) {
sia->mutex = soundio_os_mutex_create(); sia->mutex = soundio_os_mutex_create();
if (!sia->mutex) { if (!sia->mutex) {
destroy_alsa(soundio); destroy_alsa(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
sia->cond = soundio_os_cond_create(); sia->cond = soundio_os_cond_create();
if (!sia->cond) { if (!sia->cond) {
destroy_alsa(soundio); destroy_alsa(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
@ -837,7 +854,7 @@ int soundio_alsa_init(SoundIo *soundio) {
if (sia->notify_fd == -1) { if (sia->notify_fd == -1) {
err = errno; err = errno;
assert(err != EINVAL); assert(err != EINVAL);
destroy_alsa(soundio); destroy_alsa(si);
if (err == EMFILE || err == ENFILE) { if (err == EMFILE || err == ENFILE) {
return SoundIoErrorSystemResources; return SoundIoErrorSystemResources;
} else { } else {
@ -855,7 +872,7 @@ int soundio_alsa_init(SoundIo *soundio) {
assert(err != EINVAL); assert(err != EINVAL);
assert(err != ENAMETOOLONG); assert(err != ENAMETOOLONG);
assert(err != ENOENT); assert(err != ENOENT);
destroy_alsa(soundio); destroy_alsa(si);
if (err == ENOSPC) { if (err == ENOSPC) {
return SoundIoErrorSystemResources; return SoundIoErrorSystemResources;
} else { } else {
@ -873,30 +890,30 @@ int soundio_alsa_init(SoundIo *soundio) {
wakeup_device_poll(sia); wakeup_device_poll(sia);
if ((err = soundio_os_thread_create(device_thread_run, soundio, false, &sia->thread))) { if ((err = soundio_os_thread_create(device_thread_run, si, false, &sia->thread))) {
destroy_alsa(soundio); destroy_alsa(si);
return err; return err;
} }
soundio->destroy = destroy_alsa; si->destroy = destroy_alsa;
soundio->flush_events = flush_events; si->flush_events = flush_events;
soundio->wait_events = wait_events; si->wait_events = wait_events;
soundio->wakeup = wakeup; si->wakeup = wakeup;
soundio->out_stream_init = out_stream_init_alsa; si->outstream_init = outstream_init_alsa;
soundio->out_stream_destroy = out_stream_destroy_alsa; si->outstream_destroy = outstream_destroy_alsa;
soundio->out_stream_start = out_stream_start_alsa; si->outstream_start = outstream_start_alsa;
soundio->out_stream_free_count = out_stream_free_count_alsa; si->outstream_free_count = outstream_free_count_alsa;
soundio->out_stream_begin_write = out_stream_begin_write_alsa; si->outstream_begin_write = outstream_begin_write_alsa;
soundio->out_stream_write = out_stream_write_alsa; si->outstream_write = outstream_write_alsa;
soundio->out_stream_clear_buffer = out_stream_clear_buffer_alsa; si->outstream_clear_buffer = outstream_clear_buffer_alsa;
soundio->in_stream_init = in_stream_init_alsa; si->instream_init = instream_init_alsa;
soundio->in_stream_destroy = in_stream_destroy_alsa; si->instream_destroy = instream_destroy_alsa;
soundio->in_stream_start = in_stream_start_alsa; si->instream_start = instream_start_alsa;
soundio->in_stream_peek = in_stream_peek_alsa; si->instream_peek = instream_peek_alsa;
soundio->in_stream_drop = in_stream_drop_alsa; si->instream_drop = instream_drop_alsa;
soundio->in_stream_clear_buffer = in_stream_clear_buffer_alsa; si->instream_clear_buffer = instream_clear_buffer_alsa;
return 0; return 0;
} }

View file

@ -8,7 +8,7 @@
#ifndef SOUNDIO_ALSA_HPP #ifndef SOUNDIO_ALSA_HPP
#define SOUNDIO_ALSA_HPP #define SOUNDIO_ALSA_HPP
int soundio_alsa_init(struct SoundIo *soundio); int soundio_alsa_init(struct SoundIoPrivate *si);
#endif #endif

View file

@ -310,7 +310,6 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = {
const char *soundio_get_channel_name(enum SoundIoChannelId id) { const char *soundio_get_channel_name(enum SoundIoChannelId id) {
switch (id) { switch (id) {
case SoundIoChannelIdInvalid: return "(Invalid Channel)"; case SoundIoChannelIdInvalid: return "(Invalid Channel)";
case SoundIoChannelIdCount: return "(Invalid Channel)";
case SoundIoChannelIdFrontLeft: return "Front Left"; case SoundIoChannelIdFrontLeft: return "Front Left";
case SoundIoChannelIdFrontRight: return "Front Right"; case SoundIoChannelIdFrontRight: return "Front Right";
@ -405,5 +404,6 @@ bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout)
return true; return true;
} }
} }
layout->name = nullptr;
return false; return false;
} }

View file

@ -34,13 +34,14 @@ struct SoundIoDummy {
}; };
static void playback_thread_run(void *arg) { static void playback_thread_run(void *arg) {
SoundIoOutStream *out_stream = (SoundIoOutStream *)arg; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data; SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
double start_time = soundio_os_get_time(); double start_time = soundio_os_get_time();
long frames_consumed = 0; 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()) { while (osd->abort_flag.test_and_set()) {
soundio_os_cond_timed_wait(osd->cond, nullptr, osd->period); 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; long total_frames = total_time / time_per_frame;
int frames_to_kill = total_frames - frames_consumed; int frames_to_kill = total_frames - frames_consumed;
int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer); 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 read_count = min(frames_to_kill, frames_in_buffer);
int frames_left = frames_to_kill - read_count; 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); soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
frames_consumed += read_count; frames_consumed += read_count;
if (frames_left > 0) { if (frames_left > 0) {
out_stream->underrun_callback(out_stream); outstream->underrun_callback(outstream);
} else if (read_count > 0) { } 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) { static void recording_thread_run(void *arg) {
SoundIoInStream *in_stream = (SoundIoInStream *)arg; SoundIoInStream *instream = (SoundIoInStream *)arg;
SoundIoDevice *device = in_stream->device; SoundIoDevice *device = instream->device;
SoundIo *soundio = device->soundio; SoundIo *soundio = device->soundio;
// TODO // TODO
} }
*/ */
static void destroy_dummy(SoundIo *soundio) { static void destroy_dummy(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
if (!sid) if (!sid)
return; return;
@ -85,32 +86,31 @@ static void destroy_dummy(SoundIo *soundio) {
soundio_os_mutex_destroy(sid->mutex); soundio_os_mutex_destroy(sid->mutex);
destroy(sid); destroy(sid);
soundio->backend_data = nullptr; si->backend_data = nullptr;
} }
static void flush_events(SoundIo *soundio) { static void flush_events(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; SoundIo *soundio = &si->pub;
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
if (sid->devices_emitted) if (sid->devices_emitted)
return; return;
sid->devices_emitted = true; sid->devices_emitted = true;
soundio->on_devices_change(soundio); soundio->on_devices_change(soundio);
} }
static void wait_events(SoundIo *soundio) { static void wait_events(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
flush_events(soundio); flush_events(si);
soundio_os_cond_wait(sid->cond, nullptr); soundio_os_cond_wait(sid->cond, nullptr);
} }
static void wakeup(SoundIo *soundio) { static void wakeup(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data; SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
soundio_os_cond_signal(sid->cond, nullptr); soundio_os_cond_signal(sid->cond, nullptr);
} }
static void out_stream_destroy_dummy(SoundIo *soundio, static void outstream_destroy_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
{
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
if (!osd) if (!osd)
return; return;
@ -128,120 +128,106 @@ static void out_stream_destroy_dummy(SoundIo *soundio,
soundio_ring_buffer_deinit(&osd->ring_buffer); soundio_ring_buffer_deinit(&osd->ring_buffer);
destroy(osd); destroy(osd);
out_stream->backend_data = nullptr; os->backend_data = nullptr;
} }
static int out_stream_init_dummy(SoundIo *soundio, static int outstream_init_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStream *outstream = &os->pub;
{
SoundIoOutStreamDummy *osd = create<SoundIoOutStreamDummy>(); SoundIoOutStreamDummy *osd = create<SoundIoOutStreamDummy>();
if (!osd) { if (!osd) {
out_stream_destroy_dummy(soundio, out_stream); outstream_destroy_dummy(si, os);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
out_stream->backend_data = osd; os->backend_data = osd;
int buffer_frame_count = out_stream->latency * out_stream->sample_rate; int buffer_frame_count = outstream->latency * outstream->sample_rate;
osd->buffer_size = out_stream->bytes_per_frame * buffer_frame_count; osd->buffer_size = outstream->bytes_per_frame * buffer_frame_count;
osd->period = out_stream->latency * 0.5; osd->period = outstream->latency * 0.5;
soundio_ring_buffer_init(&osd->ring_buffer, osd->buffer_size); soundio_ring_buffer_init(&osd->ring_buffer, osd->buffer_size);
osd->cond = soundio_os_cond_create(); osd->cond = soundio_os_cond_create();
if (!osd->cond) { if (!osd->cond) {
out_stream_destroy_dummy(soundio, out_stream); outstream_destroy_dummy(si, os);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
return 0; return 0;
} }
static int out_stream_start_dummy(SoundIo *soundio, static int outstream_start_dummy(SoundIoPrivate *soundio, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStream *outstream = &os->pub;
{ SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->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); assert(soundio_ring_buffer_fill_count(&osd->ring_buffer) == osd->buffer_size);
osd->abort_flag.test_and_set(); osd->abort_flag.test_and_set();
int err; 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 err;
} }
return 0; return 0;
} }
static int out_stream_free_count_dummy(SoundIo *soundio, static int outstream_free_count_dummy(SoundIoPrivate *soundio, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStream *outstream = &os->pub;
{ SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer); int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer);
int bytes_free_count = osd->buffer_size - fill_count; 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, static void outstream_begin_write_dummy(SoundIoPrivate *si,
SoundIoOutStream *out_stream, char **data, int *frame_count) 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); assert(byte_count <= osd->buffer_size);
*data = osd->ring_buffer.address; *data = osd->ring_buffer.address;
} }
static void out_stream_write_dummy(SoundIo *soundio, static void outstream_write_dummy(SoundIoPrivate *si,
SoundIoOutStream *out_stream, char *data, int frame_count) 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); 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); soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
} }
static void out_stream_clear_buffer_dummy(SoundIo *soundio, static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
{
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
soundio_ring_buffer_clear(&osd->ring_buffer); soundio_ring_buffer_clear(&osd->ring_buffer);
} }
static int in_stream_init_dummy(SoundIo *soundio, static int instream_init_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *in_stream) 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"); soundio_panic("TODO");
} }
static void in_stream_destroy_dummy(SoundIo *soundio, static void instream_drop_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *in_stream)
{
soundio_panic("TODO"); soundio_panic("TODO");
} }
static int in_stream_start_dummy(SoundIo *soundio, static void instream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
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)
{
soundio_panic("TODO"); soundio_panic("TODO");
} }
@ -273,42 +259,43 @@ static int set_all_device_formats(SoundIoDevice *device) {
return 0; return 0;
} }
int soundio_dummy_init(SoundIo *soundio) { int soundio_dummy_init(SoundIoPrivate *si) {
assert(!soundio->backend_data); SoundIo *soundio = &si->pub;
assert(!si->backend_data);
SoundIoDummy *sid = create<SoundIoDummy>(); SoundIoDummy *sid = create<SoundIoDummy>();
if (!sid) { if (!sid) {
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
soundio->backend_data = sid; si->backend_data = sid;
sid->mutex = soundio_os_mutex_create(); sid->mutex = soundio_os_mutex_create();
if (!sid->mutex) { if (!sid->mutex) {
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
sid->cond = soundio_os_cond_create(); sid->cond = soundio_os_cond_create();
if (!sid->cond) { if (!sid->cond) {
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
assert(!soundio->safe_devices_info); assert(!si->safe_devices_info);
soundio->safe_devices_info = create<SoundIoDevicesInfo>(); si->safe_devices_info = create<SoundIoDevicesInfo>();
if (!soundio->safe_devices_info) { if (!si->safe_devices_info) {
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
soundio->safe_devices_info->default_input_index = 0; si->safe_devices_info->default_input_index = 0;
soundio->safe_devices_info->default_output_index = 0; si->safe_devices_info->default_output_index = 0;
// create output device // create output device
{ {
SoundIoDevice *device = create<SoundIoDevice>(); SoundIoDevice *device = create<SoundIoDevice>();
if (!device) { if (!device) {
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
@ -318,14 +305,22 @@ int soundio_dummy_init(SoundIo *soundio) {
device->description = strdup("Dummy Output Device"); device->description = strdup("Dummy Output Device");
if (!device->name || !device->description) { if (!device->name || !device->description) {
soundio_device_unref(device); soundio_device_unref(device);
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; 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; int err;
if ((err = set_all_device_formats(device))) { if ((err = set_all_device_formats(device))) {
soundio_device_unref(device); soundio_device_unref(device);
destroy_dummy(soundio); destroy_dummy(si);
return err; return err;
} }
@ -335,9 +330,9 @@ int soundio_dummy_init(SoundIo *soundio) {
device->sample_rate_current = 48000; device->sample_rate_current = 48000;
device->purpose = SoundIoDevicePurposeOutput; 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); soundio_device_unref(device);
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
} }
@ -346,7 +341,7 @@ int soundio_dummy_init(SoundIo *soundio) {
{ {
SoundIoDevice *device = create<SoundIoDevice>(); SoundIoDevice *device = create<SoundIoDevice>();
if (!device) { if (!device) {
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
@ -356,14 +351,23 @@ int soundio_dummy_init(SoundIo *soundio) {
device->description = strdup("Dummy input device"); device->description = strdup("Dummy input device");
if (!device->name || !device->description) { if (!device->name || !device->description) {
soundio_device_unref(device); soundio_device_unref(device);
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; 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; int err;
if ((err = set_all_device_formats(device))) { if ((err = set_all_device_formats(device))) {
soundio_device_unref(device); soundio_device_unref(device);
destroy_dummy(soundio); destroy_dummy(si);
return err; return err;
} }
device->default_latency = 0.01; device->default_latency = 0.01;
@ -372,33 +376,33 @@ int soundio_dummy_init(SoundIo *soundio) {
device->sample_rate_current = 48000; device->sample_rate_current = 48000;
device->purpose = SoundIoDevicePurposeInput; 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); soundio_device_unref(device);
destroy_dummy(soundio); destroy_dummy(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
} }
soundio->destroy = destroy_dummy; si->destroy = destroy_dummy;
soundio->flush_events = flush_events; si->flush_events = flush_events;
soundio->wait_events = wait_events; si->wait_events = wait_events;
soundio->wakeup = wakeup; si->wakeup = wakeup;
soundio->out_stream_init = out_stream_init_dummy; si->outstream_init = outstream_init_dummy;
soundio->out_stream_destroy = out_stream_destroy_dummy; si->outstream_destroy = outstream_destroy_dummy;
soundio->out_stream_start = out_stream_start_dummy; si->outstream_start = outstream_start_dummy;
soundio->out_stream_free_count = out_stream_free_count_dummy; si->outstream_free_count = outstream_free_count_dummy;
soundio->out_stream_begin_write = out_stream_begin_write_dummy; si->outstream_begin_write = outstream_begin_write_dummy;
soundio->out_stream_write = out_stream_write_dummy; si->outstream_write = outstream_write_dummy;
soundio->out_stream_clear_buffer = out_stream_clear_buffer_dummy; si->outstream_clear_buffer = outstream_clear_buffer_dummy;
soundio->in_stream_init = in_stream_init_dummy; si->instream_init = instream_init_dummy;
soundio->in_stream_destroy = in_stream_destroy_dummy; si->instream_destroy = instream_destroy_dummy;
soundio->in_stream_start = in_stream_start_dummy; si->instream_start = instream_start_dummy;
soundio->in_stream_peek = in_stream_peek_dummy; si->instream_peek = instream_peek_dummy;
soundio->in_stream_drop = in_stream_drop_dummy; si->instream_drop = instream_drop_dummy;
soundio->in_stream_clear_buffer = in_stream_clear_buffer_dummy; si->instream_clear_buffer = instream_clear_buffer_dummy;
return 0; return 0;
} }

View file

@ -8,6 +8,6 @@
#ifndef SOUNDIO_DUMMY_HPP #ifndef SOUNDIO_DUMMY_HPP
#define SOUNDIO_DUMMY_HPP #define SOUNDIO_DUMMY_HPP
int soundio_dummy_init(struct SoundIo *soundio); int soundio_dummy_init(struct SoundIoPrivate *si);
#endif #endif

View file

@ -55,26 +55,26 @@ struct SoundIoPulseAudio {
static void subscribe_callback(pa_context *context, static void subscribe_callback(pa_context *context,
pa_subscription_event_type_t event_bits, uint32_t index, void *userdata) pa_subscription_event_type_t event_bits, uint32_t index, void *userdata)
{ {
SoundIo *soundio = (SoundIo *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
sipa->device_scan_queued = true; sipa->device_scan_queued = true;
pa_threaded_mainloop_signal(sipa->main_loop, 0); pa_threaded_mainloop_signal(sipa->main_loop, 0);
} }
static void subscribe_to_events(SoundIo *soundio) { static void subscribe_to_events(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_subscription_mask_t events = (pa_subscription_mask_t)( pa_subscription_mask_t events = (pa_subscription_mask_t)(
PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER); PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER);
pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context, pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context,
events, nullptr, soundio); events, nullptr, si);
if (!subscribe_op) if (!subscribe_op)
soundio_panic("pa_context_subscribe failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context))); soundio_panic("pa_context_subscribe failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
pa_operation_unref(subscribe_op); pa_operation_unref(subscribe_op);
} }
static void context_state_callback(pa_context *context, void *userdata) { static void context_state_callback(pa_context *context, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
switch (pa_context_get_state(context)) { switch (pa_context_get_state(context)) {
case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet. 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; return;
case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations. case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations.
sipa->device_scan_queued = true; sipa->device_scan_queued = true;
subscribe_to_events(soundio); subscribe_to_events(si);
sipa->ready_flag = true; sipa->ready_flag = true;
pa_threaded_mainloop_signal(sipa->main_loop, 0); pa_threaded_mainloop_signal(sipa->main_loop, 0);
return; return;
@ -107,8 +107,8 @@ static void context_state_callback(pa_context *context, void *userdata) {
} }
} }
static void destroy_pa(SoundIo *soundio) { static void destroy_pa(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
if (!sipa) if (!sipa)
return; return;
@ -131,7 +131,7 @@ static void destroy_pa(SoundIo *soundio) {
free(sipa->default_source_name); free(sipa->default_source_name);
destroy(sipa); destroy(sipa);
soundio->backend_data = nullptr; si->backend_data = nullptr;
} }
static double usec_to_sec(pa_usec_t usec) { 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; return sample_spec.rate;
} }
/* TODO
static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) { static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) {
switch (pos) { switch (pos) {
case PA_CHANNEL_POSITION_MONO: return SoundIoChannelIdFrontCenter; 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) { static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
for (;;) { for (;;) {
switch (pa_operation_get_state(op)) { switch (pa_operation_get_state(op)) {
case PA_OPERATION_RUNNING: case PA_OPERATION_RUNNING:
@ -226,8 +228,9 @@ static int perform_operation(SoundIo *soundio, pa_operation *op) {
} }
} }
static void finish_device_query(SoundIo *soundio) { static void finish_device_query(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
if (!sipa->have_sink_list || if (!sipa->have_sink_list ||
!sipa->have_source_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) { static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *info, int eol, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
if (eol) { if (eol) {
sipa->have_sink_list = true; sipa->have_sink_list = true;
finish_device_query(soundio); finish_device_query(si);
} else { } else {
SoundIoDevice *device = create<SoundIoDevice>(); SoundIoDevice *device = create<SoundIoDevice>();
if (!device) 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); device->description = strdup(info->description);
if (!device->name || !device->description) if (!device->name || !device->description)
soundio_panic("out of memory"); 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 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->current_format = format_from_pulseaudio(info->sample_spec);
device->default_latency = usec_to_sec(info->configured_latency); device->default_latency = usec_to_sec(info->configured_latency);
device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); 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) { static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
if (eol) { if (eol) {
sipa->have_source_list = true; sipa->have_source_list = true;
finish_device_query(soundio); finish_device_query(si);
} else { } else {
SoundIoDevice *device = create<SoundIoDevice>(); SoundIoDevice *device = create<SoundIoDevice>();
if (!device) if (!device)
@ -309,8 +315,9 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
device->description = strdup(info->description); device->description = strdup(info->description);
if (!device->name || !device->description) if (!device->name || !device->description)
soundio_panic("out of memory"); 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 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->current_format = format_from_pulseaudio(info->sample_spec);
device->default_latency = usec_to_sec(info->configured_latency); device->default_latency = usec_to_sec(info->configured_latency);
device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); 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) { static void server_info_callback(pa_context *pulse_context, const pa_server_info *info, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
assert(soundio); assert(si);
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
free(sipa->default_sink_name); free(sipa->default_sink_name);
free(sipa->default_source_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"); soundio_panic("out of memory");
sipa->have_default_sink = true; sipa->have_default_sink = true;
finish_device_query(soundio); finish_device_query(si);
pa_threaded_mainloop_signal(sipa->main_loop, 0); pa_threaded_mainloop_signal(sipa->main_loop, 0);
} }
static void scan_devices(SoundIo *soundio) { static void scan_devices(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
sipa->have_sink_list = false; sipa->have_sink_list = false;
sipa->have_default_sink = false; sipa->have_default_sink = false;
@ -356,17 +363,17 @@ static void scan_devices(SoundIo *soundio) {
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context, 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, 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, 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"); soundio_panic("list sinks failed");
if (perform_operation(soundio, list_source_op)) if (perform_operation(si, list_source_op))
soundio_panic("list sources failed"); 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"); soundio_panic("get server info failed");
pa_threaded_mainloop_signal(sipa->main_loop, 0); 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); pa_threaded_mainloop_unlock(sipa->main_loop);
} }
static void block_until_have_devices(SoundIo *soundio) { static void block_until_have_devices(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
if (sipa->have_devices_flag) if (sipa->have_devices_flag)
return; return;
pa_threaded_mainloop_lock(sipa->main_loop); 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); pa_threaded_mainloop_unlock(sipa->main_loop);
} }
static void block_until_ready(SoundIo *soundio) { static void block_until_ready(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
if (sipa->ready_flag) if (sipa->ready_flag)
return; return;
pa_threaded_mainloop_lock(sipa->main_loop); 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); pa_threaded_mainloop_unlock(sipa->main_loop);
} }
static void flush_events(SoundIo *soundio) { static void flush_events(SoundIoPrivate *si) {
block_until_ready(soundio); 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) { if (sipa->device_scan_queued) {
sipa->device_scan_queued = false; sipa->device_scan_queued = false;
scan_devices(soundio); scan_devices(si);
} }
SoundIoDevicesInfo *old_devices_info = nullptr; SoundIoDevicesInfo *old_devices_info = nullptr;
@ -412,8 +420,8 @@ static void flush_events(SoundIo *soundio) {
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
if (sipa->ready_devices_info) { if (sipa->ready_devices_info) {
old_devices_info = soundio->safe_devices_info; old_devices_info = si->safe_devices_info;
soundio->safe_devices_info = sipa->ready_devices_info; si->safe_devices_info = sipa->ready_devices_info;
sipa->ready_devices_info = nullptr; sipa->ready_devices_info = nullptr;
change = true; change = true;
} }
@ -425,17 +433,17 @@ static void flush_events(SoundIo *soundio) {
soundio_destroy_devices_info(old_devices_info); soundio_destroy_devices_info(old_devices_info);
block_until_have_devices(soundio); block_until_have_devices(si);
} }
static void wait_events(SoundIo *soundio) { static void wait_events(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
flush_events(soundio); flush_events(si);
pa_threaded_mainloop_wait(sipa->main_loop); pa_threaded_mainloop_wait(sipa->main_loop);
} }
static void wakeup(SoundIo *soundio) { static void wakeup(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_threaded_mainloop_signal(sipa->main_loop, 0); 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 SoundIoChannelIdTopBackCenter: return PA_CHANNEL_POSITION_TOP_REAR_CENTER;
case SoundIoChannelIdTopBackRight: return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; case SoundIoChannelIdTopBackRight: return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
case SoundIoChannelIdCount:
case SoundIoChannelIdInvalid: case SoundIoChannelIdInvalid:
case SoundIoChannelIdBackLeftCenter: case SoundIoChannelIdBackLeftCenter:
case SoundIoChannelIdBackRightCenter: 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) { static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoOutStream *out_stream = (SoundIoOutStream*) userdata; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate*) userdata;
SoundIo *soundio = out_stream->device->soundio; SoundIoOutStream *outstream = &os->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIo *soundio = outstream->device->soundio;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
switch (pa_stream_get_state(stream)) { switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING: 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) { static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) {
SoundIoOutStream *out_stream = (SoundIoOutStream*)userdata; SoundIoOutStream *outstream = (SoundIoOutStream*)userdata;
out_stream->underrun_callback(out_stream); outstream->underrun_callback(outstream);
} }
static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) { static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoOutStream *out_stream = (SoundIoOutStream*)(userdata); SoundIoOutStream *outstream = (SoundIoOutStream*)(userdata);
int frame_count = ((int)nbytes) / out_stream->bytes_per_frame; int frame_count = ((int)nbytes) / outstream->bytes_per_frame;
out_stream->write_callback(out_stream, frame_count); outstream->write_callback(outstream, frame_count);
} }
static void out_stream_destroy_pa(SoundIo *soundio, static void outstream_destroy_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
{
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
if (!ospa) if (!ospa)
return; return;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
if (stream) { if (stream) {
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -580,21 +587,19 @@ static void out_stream_destroy_pa(SoundIo *soundio,
} }
destroy(ospa); destroy(ospa);
out_stream->backend_data = nullptr; os->backend_data = nullptr;
} }
static int out_stream_init_pa(SoundIo *soundio, static int outstream_init_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStream *outstream = &os->pub;
{
SoundIoOutStreamPulseAudio *ospa = create<SoundIoOutStreamPulseAudio>(); SoundIoOutStreamPulseAudio *ospa = create<SoundIoOutStreamPulseAudio>();
if (!ospa) { if (!ospa) {
out_stream_destroy_pa(soundio, out_stream); outstream_destroy_pa(si, os);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
out_stream->backend_data = ospa; os->backend_data = ospa;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
SoundIoDevice *device = out_stream->device;
ospa->stream_ready = false; ospa->stream_ready = false;
assert(sipa->pulse_context); assert(sipa->pulse_context);
@ -602,24 +607,26 @@ static int out_stream_init_pa(SoundIo *soundio,
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
sample_spec.format = to_pulseaudio_format(out_stream->format); sample_spec.format = to_pulseaudio_format(outstream->format);
sample_spec.rate = out_stream->sample_rate; sample_spec.rate = outstream->sample_rate;
sample_spec.channels = device->channel_layout.channel_count;
pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout);
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); ospa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map);
if (!ospa->stream) { if (!ospa->stream) {
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
out_stream_destroy_pa(soundio, out_stream); outstream_destroy_pa(si, os);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
pa_stream_set_state_callback(ospa->stream, playback_stream_state_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, out_stream); pa_stream_set_write_callback(ospa->stream, playback_stream_write_callback, os);
pa_stream_set_underflow_callback(ospa->stream, playback_stream_underflow_callback, out_stream); 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 bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate;
int buffer_length = out_stream->bytes_per_frame * int buffer_length = outstream->bytes_per_frame *
ceil(out_stream->latency * bytes_per_second / (double)out_stream->bytes_per_frame); ceil(outstream->latency * bytes_per_second / (double)outstream->bytes_per_frame);
ospa->buffer_attr.maxlength = buffer_length; ospa->buffer_attr.maxlength = buffer_length;
ospa->buffer_attr.tlength = buffer_length; ospa->buffer_attr.tlength = buffer_length;
@ -632,17 +639,16 @@ static int out_stream_init_pa(SoundIo *soundio,
return 0; return 0;
} }
static int out_stream_start_pa(SoundIo *soundio, static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStream *outstream = &os->pub;
{ SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
int err = pa_stream_connect_playback(ospa->stream, 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); PA_STREAM_ADJUST_LATENCY, nullptr, nullptr);
if (err) { if (err) {
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
@ -652,50 +658,51 @@ static int out_stream_start_pa(SoundIo *soundio,
while (!ospa->stream_ready) while (!ospa->stream_ready)
pa_threaded_mainloop_wait(sipa->main_loop); 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); pa_threaded_mainloop_unlock(sipa->main_loop);
return 0; return 0;
} }
static int out_stream_free_count_pa(SoundIo *soundio, static int outstream_free_count_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *out_stream) SoundIoOutStream *outstream = &os->pub;
{ SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; return pa_stream_writable_size(ospa->stream) / outstream->bytes_per_frame;
return pa_stream_writable_size(ospa->stream) / out_stream->bytes_per_frame;
} }
static void out_stream_begin_write_pa(SoundIo *soundio, static void outstream_begin_write_pa(SoundIoPrivate *si,
SoundIoOutStream *out_stream, char **data, int *frame_count) SoundIoOutStreamPrivate *os, char **data, int *frame_count)
{ {
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; SoundIoOutStream *outstream = &os->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_stream *stream = ospa->stream; 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)) 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))); 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, static void outstream_write_pa(SoundIoPrivate *si,
SoundIoOutStream *out_stream, char *data, int frame_count) SoundIoOutStreamPrivate *os, char *data, int frame_count)
{ {
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; SoundIoOutStream *outstream = &os->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_stream *stream = ospa->stream; 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)) 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))); soundio_panic("pa_stream_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
} }
static void out_stream_clear_buffer_pa(SoundIo *soundio, static void outstream_clear_buffer_pa(SoundIoPrivate *si,
SoundIoOutStream *out_stream) SoundIoOutStreamPrivate *os)
{ {
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
pa_operation *op = pa_stream_flush(stream, NULL, NULL); pa_operation *op = pa_stream_flush(stream, NULL, NULL);
@ -706,8 +713,8 @@ static void out_stream_clear_buffer_pa(SoundIo *soundio,
} }
static void recording_stream_state_callback(pa_stream *stream, void *userdata) { static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoInStream *in_stream = (SoundIoInStream*)userdata; SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
switch (pa_stream_get_state(stream)) { switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING: 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) { static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoInStream *in_stream = (SoundIoInStream*)userdata; SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
in_stream->read_callback(in_stream); SoundIoInStream *instream = &is->pub;
instream->read_callback(instream);
} }
static void in_stream_destroy_pa(SoundIo *soundio, static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *instream) {
SoundIoInStream *in_stream) SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)instream->backend_data;
{
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
if (!ispa) if (!ispa)
return; return;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
if (stream) { if (stream) {
pa_threaded_mainloop_lock(sipa->main_loop); 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, static int instream_init_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *in_stream) SoundIoInStream *instream = &is->pub;
{
SoundIoInStreamPulseAudio *ispa = create<SoundIoInStreamPulseAudio>(); SoundIoInStreamPulseAudio *ispa = create<SoundIoInStreamPulseAudio>();
if (!ispa) { if (!ispa) {
in_stream_destroy_pa(soundio, in_stream); instream_destroy_pa(si, is);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
in_stream->backend_data = ispa; is->backend_data = ispa;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
SoundIoDevice *device = in_stream->device;
ispa->stream_ready = false; ispa->stream_ready = false;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
sample_spec.format = to_pulseaudio_format(in_stream->format); sample_spec.format = to_pulseaudio_format(instream->format);
sample_spec.rate = in_stream->sample_rate; sample_spec.rate = instream->sample_rate;
sample_spec.channels = device->channel_layout.channel_count; 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); 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); pa_threaded_mainloop_unlock(sipa->main_loop);
in_stream_destroy_pa(soundio, in_stream); instream_destroy_pa(si, is);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
pa_stream_set_state_callback(stream, recording_stream_state_callback, in_stream); pa_stream_set_state_callback(stream, recording_stream_state_callback, is);
pa_stream_set_read_callback(stream, recording_stream_read_callback, in_stream); 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 bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
int buffer_length = in_stream->bytes_per_frame * int buffer_length = instream->bytes_per_frame *
ceil(in_stream->latency * bytes_per_second / (double)in_stream->bytes_per_frame); ceil(instream->latency * bytes_per_second / (double)instream->bytes_per_frame);
ispa->buffer_attr.maxlength = UINT32_MAX; ispa->buffer_attr.maxlength = UINT32_MAX;
ispa->buffer_attr.tlength = UINT32_MAX; ispa->buffer_attr.tlength = UINT32_MAX;
@ -801,15 +806,14 @@ static int in_stream_init_pa(SoundIo *soundio,
return 0; return 0;
} }
static int in_stream_start_pa(SoundIo *soundio, static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *in_stream) SoundIoInStream *instream = &is->pub;
{ SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
int err = pa_stream_connect_record(ispa->stream, int err = pa_stream_connect_record(ispa->stream,
in_stream->device->name, instream->device->name,
&ispa->buffer_attr, PA_STREAM_ADJUST_LATENCY); &ispa->buffer_attr, PA_STREAM_ADJUST_LATENCY);
if (err) { if (err) {
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
@ -820,40 +824,37 @@ static int in_stream_start_pa(SoundIo *soundio,
return 0; return 0;
} }
static void in_stream_peek_pa(SoundIo *soundio, static void instream_peek_pa(SoundIoPrivate *si,
SoundIoInStream *in_stream, const char **data, int *frame_count) 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; pa_stream *stream = ispa->stream;
if (ispa->stream_ready) { if (ispa->stream_ready) {
size_t nbytes; size_t nbytes;
if (pa_stream_peek(stream, (const void **)data, &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)))); 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 { } else {
*data = nullptr; *data = nullptr;
*frame_count = 0; *frame_count = 0;
} }
} }
static void in_stream_drop_pa(SoundIo *soundio, static void instream_drop_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *in_stream) SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
{
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
if (pa_stream_drop(stream)) if (pa_stream_drop(stream))
soundio_panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(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, static void instream_clear_buffer_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *in_stream) SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
{
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
if (!ispa->stream_ready) if (!ispa->stream_ready)
return; return;
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data; SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
pa_threaded_mainloop_lock(sipa->main_loop); 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); pa_threaded_mainloop_unlock(sipa->main_loop);
} }
int soundio_pulseaudio_init(SoundIo *soundio) { int soundio_pulseaudio_init(SoundIoPrivate *si) {
assert(!soundio->backend_data); assert(!si->backend_data);
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>(); SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
if (!sipa) { if (!sipa) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
soundio->backend_data = sipa; si->backend_data = sipa;
sipa->connection_refused = false; sipa->connection_refused = false;
sipa->device_scan_queued = false; sipa->device_scan_queued = false;
@ -889,7 +890,7 @@ int soundio_pulseaudio_init(SoundIo *soundio) {
sipa->main_loop = pa_threaded_mainloop_new(); sipa->main_loop = pa_threaded_mainloop_new();
if (!sipa->main_loop) { if (!sipa->main_loop) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
@ -897,7 +898,7 @@ int soundio_pulseaudio_init(SoundIo *soundio) {
sipa->props = pa_proplist_new(); sipa->props = pa_proplist_new();
if (!sipa->props) { if (!sipa->props) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorNoMem; 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); sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, "SoundIo", sipa->props);
if (!sipa->pulse_context) { if (!sipa->pulse_context) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, soundio); pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, si);
pa_context_set_state_callback(sipa->pulse_context, context_state_callback, soundio); 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); int err = pa_context_connect(sipa->pulse_context, NULL, (pa_context_flags_t)0, NULL);
if (err) { if (err) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorInitAudioBackend; return SoundIoErrorInitAudioBackend;
} }
if (sipa->connection_refused) { if (sipa->connection_refused) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorInitAudioBackend; return SoundIoErrorInitAudioBackend;
} }
if (pa_threaded_mainloop_start(sipa->main_loop)) { if (pa_threaded_mainloop_start(sipa->main_loop)) {
destroy_pa(soundio); destroy_pa(si);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
soundio->destroy = destroy_pa; si->destroy = destroy_pa;
soundio->flush_events = flush_events; si->flush_events = flush_events;
soundio->wait_events = wait_events; si->wait_events = wait_events;
soundio->wakeup = wakeup; si->wakeup = wakeup;
soundio->out_stream_init = out_stream_init_pa; si->outstream_init = outstream_init_pa;
soundio->out_stream_destroy = out_stream_destroy_pa; si->outstream_destroy = outstream_destroy_pa;
soundio->out_stream_start = out_stream_start_pa; si->outstream_start = outstream_start_pa;
soundio->out_stream_free_count = out_stream_free_count_pa; si->outstream_free_count = outstream_free_count_pa;
soundio->out_stream_begin_write = out_stream_begin_write_pa; si->outstream_begin_write = outstream_begin_write_pa;
soundio->out_stream_write = out_stream_write_pa; si->outstream_write = outstream_write_pa;
soundio->out_stream_clear_buffer = out_stream_clear_buffer_pa; si->outstream_clear_buffer = outstream_clear_buffer_pa;
soundio->in_stream_init = in_stream_init_pa; si->instream_init = instream_init_pa;
soundio->in_stream_destroy = in_stream_destroy_pa; si->instream_destroy = instream_destroy_pa;
soundio->in_stream_start = in_stream_start_pa; si->instream_start = instream_start_pa;
soundio->in_stream_peek = in_stream_peek_pa; si->instream_peek = instream_peek_pa;
soundio->in_stream_drop = in_stream_drop_pa; si->instream_drop = instream_drop_pa;
soundio->in_stream_clear_buffer = in_stream_clear_buffer_pa; si->instream_clear_buffer = instream_clear_buffer_pa;
return 0; return 0;
} }

View file

@ -8,6 +8,6 @@
#ifndef SOUNDIO_PULSEAUDIO_HPP #ifndef SOUNDIO_PULSEAUDIO_HPP
#define SOUNDIO_PULSEAUDIO_HPP #define SOUNDIO_PULSEAUDIO_HPP
int soundio_pulseaudio_init(struct SoundIo *soundio); int soundio_pulseaudio_init(struct SoundIoPrivate *si);
#endif #endif

View file

@ -22,6 +22,16 @@
#include <string.h> #include <string.h>
#include <assert.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) { const char *soundio_strerror(int error) {
switch ((enum SoundIoError)error) { switch ((enum SoundIoError)error) {
case SoundIoErrorNone: return "(no 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 SoundIoErrorInitAudioBackend: return "unable to initialize audio backend";
case SoundIoErrorSystemResources: return "system resource not available"; case SoundIoErrorSystemResources: return "system resource not available";
case SoundIoErrorOpeningDevice: return "unable to open device"; 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); 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) { void soundio_destroy(struct SoundIo *soundio) {
if (!soundio) SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si)
return; return;
soundio_disconnect(soundio); soundio_disconnect(soundio);
destroy(soundio); destroy(si);
} }
static void default_on_devices_change(struct SoundIo *) { } 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) { struct SoundIo * soundio_create(void) {
soundio_os_init(); soundio_os_init();
struct SoundIo *soundio = create<SoundIo>(); struct SoundIoPrivate *si = create<SoundIoPrivate>();
if (!soundio) { if (!si)
soundio_destroy(soundio);
return NULL; return NULL;
} SoundIo *soundio = &si->pub;
soundio->on_devices_change = default_on_devices_change; soundio->on_devices_change = default_on_devices_change;
soundio->on_events_signal = default_on_events_signal; soundio->on_events_signal = default_on_events_signal;
return soundio; return soundio;
} }
int soundio_connect(struct SoundIo *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; int err;
switch (backend) {
case SoundIoBackendPulseAudio:
#ifdef SOUNDIO_HAVE_PULSEAUDIO #ifdef SOUNDIO_HAVE_PULSEAUDIO
soundio->current_backend = SoundIoBackendPulseAudio; si->current_backend = SoundIoBackendPulseAudio;
err = soundio_pulseaudio_init(soundio); if ((err = soundio_pulseaudio_init(si))) {
if (!err)
return 0;
if (err != SoundIoErrorInitAudioBackend) {
soundio_disconnect(soundio); soundio_disconnect(soundio);
return err; return err;
} }
return 0;
#else
return SoundIoErrorBackendUnavailable;
#endif #endif
case SoundIoBackendAlsa:
#ifdef SOUNDIO_HAVE_ALSA #ifdef SOUNDIO_HAVE_ALSA
soundio->current_backend = SoundIoBackendAlsa; si->current_backend = SoundIoBackendAlsa;
err = soundio_alsa_init(soundio); if ((err = soundio_alsa_init(si))) {
if (!err)
return 0;
if (err != SoundIoErrorInitAudioBackend) {
soundio_disconnect(soundio); soundio_disconnect(soundio);
return err; return err;
} }
return 0;
#else
return SoundIoErrorBackendUnavailable;
#endif #endif
case SoundIoBackendDummy:
soundio->current_backend = SoundIoBackendDummy; si->current_backend = SoundIoBackendDummy;
err = soundio_dummy_init(soundio); err = soundio_dummy_init(si);
if (err) { if (err)
soundio_disconnect(soundio); soundio_disconnect(soundio);
return err; return err;
case SoundIoBackendNone:
return SoundIoErrorInvalid;
} }
return SoundIoErrorInvalid;
return 0;
} }
void soundio_disconnect(struct SoundIo *soundio) { void soundio_disconnect(struct SoundIo *soundio) {
if (soundio->destroy) SoundIoPrivate *si = (SoundIoPrivate *)soundio;
soundio->destroy(soundio);
assert(!soundio->backend_data);
soundio->current_backend = SoundIoBackendNone; if (si->destroy)
si->destroy(si);
assert(!si->backend_data);
soundio_destroy_devices_info(soundio->safe_devices_info); si->current_backend = SoundIoBackendNone;
soundio->safe_devices_info = nullptr;
soundio->destroy = nullptr; soundio_destroy_devices_info(si->safe_devices_info);
soundio->flush_events = nullptr; si->safe_devices_info = nullptr;
soundio->wait_events = nullptr;
soundio->wakeup = nullptr;
soundio->out_stream_init = nullptr; si->destroy = nullptr;
soundio->out_stream_destroy = nullptr; si->flush_events = nullptr;
soundio->out_stream_start = nullptr; si->wait_events = nullptr;
soundio->out_stream_free_count = nullptr; si->wakeup = nullptr;
soundio->out_stream_begin_write = nullptr;
soundio->out_stream_write = nullptr;
soundio->out_stream_clear_buffer = nullptr;
soundio->in_stream_init = nullptr; si->outstream_init = nullptr;
soundio->in_stream_destroy = nullptr; si->outstream_destroy = nullptr;
soundio->in_stream_start = nullptr; si->outstream_start = nullptr;
soundio->in_stream_peek = nullptr; si->outstream_free_count = nullptr;
soundio->in_stream_drop = nullptr; si->outstream_begin_write = nullptr;
soundio->in_stream_clear_buffer = 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) { void soundio_flush_events(struct SoundIo *soundio) {
assert(soundio->flush_events); SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (soundio->flush_events) assert(si->flush_events);
soundio->flush_events(soundio); if (si->flush_events)
si->flush_events(si);
} }
int soundio_get_input_device_count(struct SoundIo *soundio) { int soundio_get_input_device_count(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
soundio_flush_events(soundio); soundio_flush_events(soundio);
assert(soundio->safe_devices_info); assert(si->safe_devices_info);
return soundio->safe_devices_info->input_devices.length; return si->safe_devices_info->input_devices.length;
} }
int soundio_get_output_device_count(struct SoundIo *soundio) { int soundio_get_output_device_count(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
soundio_flush_events(soundio); soundio_flush_events(soundio);
assert(soundio->safe_devices_info); assert(si->safe_devices_info);
return soundio->safe_devices_info->output_devices.length; return si->safe_devices_info->output_devices.length;
} }
int soundio_get_default_input_device_index(struct SoundIo *soundio) { int soundio_get_default_input_device_index(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
soundio_flush_events(soundio); soundio_flush_events(soundio);
assert(soundio->safe_devices_info); assert(si->safe_devices_info);
return soundio->safe_devices_info->default_input_index; return si->safe_devices_info->default_input_index;
} }
int soundio_get_default_output_device_index(struct SoundIo *soundio) { int soundio_get_default_output_device_index(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
soundio_flush_events(soundio); soundio_flush_events(soundio);
assert(soundio->safe_devices_info); assert(si->safe_devices_info);
return soundio->safe_devices_info->default_output_index; return si->safe_devices_info->default_output_index;
} }
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int 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 >= 0);
assert(index < soundio->safe_devices_info->input_devices.length); assert(index < si->safe_devices_info->input_devices.length);
SoundIoDevice *device = soundio->safe_devices_info->input_devices.at(index); SoundIoDevice *device = si->safe_devices_info->input_devices.at(index);
soundio_device_ref(device); soundio_device_ref(device);
return device; return device;
} }
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) { 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 >= 0);
assert(index < soundio->safe_devices_info->output_devices.length); assert(index < si->safe_devices_info->output_devices.length);
SoundIoDevice *device = soundio->safe_devices_info->output_devices.at(index); SoundIoDevice *device = si->safe_devices_info->output_devices.at(index);
soundio_device_ref(device); soundio_device_ref(device);
return device; return device;
} }
@ -250,10 +293,6 @@ enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *dev
return device->purpose; 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) { void soundio_device_unref(struct SoundIoDevice *device) {
if (!device) if (!device)
return; return;
@ -274,151 +313,140 @@ void soundio_device_ref(struct SoundIoDevice *device) {
} }
void soundio_wait_events(struct SoundIo *soundio) { 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) { 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; 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) { while (requested_frame_count > 0) {
int frame_count = requested_frame_count; int frame_count = requested_frame_count;
soundio_out_stream_begin_write(out_stream, &buffer, &frame_count); soundio_outstream_begin_write(outstream, &buffer, &frame_count);
memset(buffer, 0, frame_count * out_stream->bytes_per_frame); memset(buffer, 0, frame_count * outstream->bytes_per_frame);
soundio_out_stream_write(out_stream, buffer, frame_count); soundio_outstream_write(outstream, buffer, frame_count);
requested_frame_count -= frame_count; requested_frame_count -= frame_count;
} }
} }
int soundio_out_stream_free_count(struct SoundIoOutStream *out_stream) { int soundio_outstream_free_count(struct SoundIoOutStream *outstream) {
SoundIo *soundio = out_stream->device->soundio; SoundIo *soundio = outstream->device->soundio;
return soundio->out_stream_free_count(soundio, out_stream); 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) char **data, int *frame_count)
{ {
SoundIo *soundio = out_stream->device->soundio; SoundIo *soundio = outstream->device->soundio;
soundio->out_stream_begin_write(soundio, out_stream, data, frame_count); 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) char *data, int frame_count)
{ {
SoundIo *soundio = out_stream->device->soundio; SoundIo *soundio = outstream->device->soundio;
soundio->out_stream_write(soundio, out_stream, data, frame_count); SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
si->outstream_write(si, os, data, frame_count);
} }
int soundio_out_stream_create(struct SoundIoDevice *device, struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
enum SoundIoFormat format, int sample_rate, SoundIoOutStreamPrivate *os = create<SoundIoOutStreamPrivate>();
double latency, void *userdata, if (!os)
void (*write_callback)(struct SoundIoOutStream *, int frame_count), return nullptr;
void (*underrun_callback)(struct SoundIoOutStream *), SoundIoOutStream *outstream = &os->pub;
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;
}
outstream->device = device;
soundio_device_ref(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; // TODO set defaults
int err = soundio->out_stream_init(soundio, out_stream);
if (err) {
soundio_out_stream_destroy(out_stream);
return err;
}
*out_out_stream = out_stream; return outstream;
return 0;
} }
void soundio_out_stream_destroy(SoundIoOutStream *out_stream) { int soundio_outstream_open(struct SoundIoOutStream *outstream) {
if (!out_stream) 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; 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) if (si->outstream_destroy)
soundio->out_stream_destroy(soundio, out_stream); si->outstream_destroy(si, os);
soundio_device_unref(out_stream->device); soundio_device_unref(outstream->device);
destroy(out_stream); destroy(os);
} }
int soundio_out_stream_start(struct SoundIoOutStream *out_stream) { int soundio_outstream_start(struct SoundIoOutStream *outstream) {
SoundIo *soundio = out_stream->device->soundio; SoundIo *soundio = outstream->device->soundio;
return soundio->out_stream_start(soundio, out_stream); SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_start(si, os);
} }
int soundio_in_stream_create(struct SoundIoDevice *device, struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
enum SoundIoFormat format, int sample_rate, SoundIoInStreamPrivate *is = create<SoundIoInStreamPrivate>();
double latency, void *userdata, if (!is)
void (*read_callback)(struct SoundIoInStream *), return nullptr;
struct SoundIoInStream **out_in_stream) SoundIoInStream *instream = &is->pub;
{
*out_in_stream = nullptr;
SoundIoInStream *sid = create<SoundIoInStream>();
if (!sid) {
soundio_in_stream_destroy(sid);
return SoundIoErrorNoMem;
}
instream->device = device;
soundio_device_ref(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; // TODO set defaults
int err = soundio->in_stream_init(soundio, sid);
if (err) {
soundio_in_stream_destroy(sid);
return err;
}
*out_in_stream = sid; return instream;
return 0;
} }
int soundio_in_stream_start(struct SoundIoInStream *in_stream) { int soundio_instream_open(struct SoundIoInStream *instream) {
SoundIo *soundio = in_stream->device->soundio; instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
return soundio->in_stream_start(soundio, in_stream); 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) { int soundio_instream_start(struct SoundIoInStream *instream) {
if (!in_stream) 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; 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) if (si->instream_destroy)
soundio->in_stream_destroy(soundio, in_stream); si->instream_destroy(si, is);
soundio_device_unref(in_stream->device); soundio_device_unref(instream->device);
destroy(in_stream); destroy(is);
} }
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) { void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
@ -432,3 +460,78 @@ void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
destroy(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 #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif /* __cplusplus */ #endif
struct SoundIo; struct SoundIo;
struct SoundIoDevicesInfo; struct SoundIoDevicesInfo;
@ -25,6 +25,8 @@ enum SoundIoError {
SoundIoErrorInitAudioBackend, SoundIoErrorInitAudioBackend,
SoundIoErrorSystemResources, SoundIoErrorSystemResources,
SoundIoErrorOpeningDevice, SoundIoErrorOpeningDevice,
SoundIoErrorInvalid,
SoundIoErrorBackendUnavailable,
}; };
enum SoundIoChannelId { enum SoundIoChannelId {
@ -64,15 +66,6 @@ enum SoundIoChannelId {
SoundIoChannelIdBottomCenter, SoundIoChannelIdBottomCenter,
SoundIoChannelIdBottomLeftCenter, SoundIoChannelIdBottomLeftCenter,
SoundIoChannelIdBottomRightCenter, SoundIoChannelIdBottomRightCenter,
SoundIoChannelIdCount,
};
#define SOUNDIO_MAX_CHANNELS 32
struct SoundIoChannelLayout {
const char *name;
int channel_count;
enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS];
}; };
enum SoundIoChannelLayoutId { enum SoundIoChannelLayoutId {
@ -160,7 +153,17 @@ enum SoundIoFormat {
#error unknown byte order #error unknown byte order
#endif #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 { struct SoundIoDevice {
// Read-only. Set automatically.
struct SoundIo *soundio; struct SoundIo *soundio;
// `name` uniquely identifies this device. `description` is user-friendly // `name` uniquely identifies this device. `description` is user-friendly
@ -168,9 +171,14 @@ struct SoundIoDevice {
char *name; char *name;
char *description; char *description;
// If this information is missing due to a `probe_error`, the number of // Channel layouts are handled similarly to sample format; see those docs.
// channels will be zero. // If this information is missing due to a `probe_error`, `layouts`
struct SoundIoChannelLayout channel_layout; // 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 // 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 // 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, // `format_count`. If this information is missing due to a probe error,
// `formats` will be `NULL`. If `current_format` is unavailable, it will be // `formats` will be `NULL`. If `current_format` is unavailable, it will be
// set to `SoundIoFormatInvalid`. // set to `SoundIoFormatInvalid`.
// Devices are guaranteed to have at least 1 format available.
enum SoundIoFormat *formats; enum SoundIoFormat *formats;
int format_count; int format_count;
enum SoundIoFormat current_format; enum SoundIoFormat current_format;
@ -194,6 +203,7 @@ struct SoundIoDevice {
// Sample rate is handled very similar to sample format; see those docs. // 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 // If sample rate information is missing due to a probe error, the field
// will be set to zero. // will be set to zero.
// Devices are guaranteed to have at least 1 sample rate available.
int sample_rate_min; int sample_rate_min;
int sample_rate_max; int sample_rate_max;
int sample_rate_current; int sample_rate_current;
@ -222,66 +232,42 @@ struct SoundIoDevice {
int probe_error; int probe_error;
}; };
// The size of this struct is not part of the API or ABI.
struct SoundIoOutStream { struct SoundIoOutStream {
void *backend_data;
struct SoundIoDevice *device; struct SoundIoDevice *device;
enum SoundIoFormat format; enum SoundIoFormat format;
int sample_rate; int sample_rate;
struct SoundIoChannelLayout layout;
double latency; double latency;
int bytes_per_frame;
void *userdata; void *userdata;
void (*underrun_callback)(struct SoundIoOutStream *); void (*underrun_callback)(struct SoundIoOutStream *);
void (*write_callback)(struct SoundIoOutStream *, int frame_count); 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 { struct SoundIoInStream {
void *backend_data;
struct SoundIoDevice *device; struct SoundIoDevice *device;
enum SoundIoFormat format; enum SoundIoFormat format;
int sample_rate; int sample_rate;
struct SoundIoChannelLayout layout;
double latency; double latency;
int bytes_per_frame;
void *userdata; void *userdata;
void (*read_callback)(struct SoundIoInStream *); 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 { struct SoundIo {
enum SoundIoBackend current_backend;
// safe to read without a mutex from a single thread
struct SoundIoDevicesInfo *safe_devices_info;
void *userdata; void *userdata;
void (*on_devices_change)(struct SoundIo *); void (*on_devices_change)(struct SoundIo *);
void (*on_events_signal)(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 // Main Context
@ -291,12 +277,23 @@ struct SoundIo {
struct SoundIo * soundio_create(void); struct SoundIo * soundio_create(void);
void soundio_destroy(struct SoundIo *soundio); 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); 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); void soundio_disconnect(struct SoundIo *soundio);
const char *soundio_strerror(int error); const char *soundio_strerror(int error);
const char *soundio_backend_name(enum SoundIoBackend backend); 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 // 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. // might be called. This is the only time those functions will be called.
void soundio_flush_events(struct SoundIo *soundio); void soundio_flush_events(struct SoundIo *soundio);
@ -312,7 +309,10 @@ void soundio_wakeup(struct SoundIo *soundio);
// Channel Layouts // 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 struct SoundIoChannelLayout *b);
const char *soundio_get_channel_name(enum SoundIoChannelId id); 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); int soundio_channel_layout_builtin_count(void);
const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index); 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); void soundio_debug_print_channel_layout(const struct SoundIoChannelLayout *layout);
int soundio_channel_layout_find_channel( int soundio_channel_layout_find_channel(
const struct SoundIoChannelLayout *layout, enum SoundIoChannelId 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 // returns whether it found a match
bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout); 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 // Sample Formats
@ -375,51 +385,52 @@ bool soundio_device_equal(
const struct SoundIoDevice *b); const struct SoundIoDevice *b);
enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device); 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, // Output Streams
enum SoundIoFormat format, int sample_rate, // allocates memory and sets defaults. Next you should fill out the struct fields
double latency, void *userdata, // and then call soundio_outstream_open
void (*write_callback)(struct SoundIoOutStream *, int frame_count), struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
void (*underrun_callback)(struct SoundIoOutStream *),
struct SoundIoOutStream **out_out_stream);
void soundio_out_stream_destroy(struct SoundIoOutStream *out_stream);
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 // number of frames available to write
int soundio_out_stream_free_count(struct SoundIoOutStream *out_stream); int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
void soundio_out_stream_begin_write(struct SoundIoOutStream *out_stream, void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
char **data, int *frame_count); 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); 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, int soundio_instream_open(struct SoundIoInStream *instream);
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_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); const char **data, int *out_frame_count);
// this will drop all of the frames from when you called soundio_in_stream_peek // this will drop all of the frames from when you called soundio_instream_peek
void soundio_in_stream_drop(struct SoundIoInStream *in_stream); 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 // Ring Buffer
@ -448,6 +459,6 @@ void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif
#endif #endif

View file

@ -19,6 +19,51 @@ struct SoundIoDevicesInfo {
int default_input_index; 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); void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);
#endif #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 write_callback(struct SoundIoOutStream *device, int frame_count) { }
static void underrun_callback(struct SoundIoOutStream *device) { } 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(); struct SoundIo *soundio = soundio_create();
assert(soundio); assert(soundio);
ok_or_panic(soundio_connect(soundio)); ok_or_panic(soundio_connect(soundio));
@ -34,10 +34,17 @@ static void test_create_out_stream(void) {
assert(default_out_device_index >= 0); assert(default_out_device_index >= 0);
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index); struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
assert(device); assert(device);
struct SoundIoOutStream *out_stream; struct SoundIoOutStream *outstream = soundio_outstream_create(device);
soundio_out_stream_create(device, SoundIoFormatFloat32NE, 48000, 0.1, NULL, outstream->format = SoundIoFormatFloat32NE;
write_callback, underrun_callback, &out_stream); outstream->sample_rate = 48000;
soundio_out_stream_destroy(out_stream); 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_device_unref(device);
soundio_destroy(soundio); soundio_destroy(soundio);
} }
@ -161,7 +168,7 @@ struct Test {
static struct Test tests[] = { static struct Test tests[] = {
{"os_get_time", test_os_get_time}, {"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 basic", test_ring_buffer_basic},
{"ring buffer threaded", test_ring_buffer_threaded}, {"ring buffer threaded", test_ring_buffer_threaded},
{NULL, NULL}, {NULL, NULL},