microphone example working with ALSA

This commit is contained in:
Andrew Kelley 2015-07-23 20:55:36 -07:00
parent 511fcafc3b
commit 34039b4858
8 changed files with 453 additions and 39 deletions

View file

@ -28,6 +28,7 @@ exposed.
* Supports channel layouts (also known as channel maps), important for
surround sound applications.
* Ability to monitor devices and get an event when available devices change.
* Detects which input device is default and which output device is default.
* Ability to connect to multiple backends at once. For example you could have
an ALSA device open and a JACK device open at the same time.
* Meticulously checks all return codes and memory allocations and uses
@ -234,6 +235,7 @@ view `coverage/index.html` in a browser.
0. pipe record to playback example working with ALSA linux
0. expose prebuf
0. why does pulseaudio microphone use up all the CPU?
0. merge in/out stream structures and functions?
0. implement JACK backend, get examples working
0. implement CoreAudio (OSX) backend, get examples working
0. implement WASAPI (Windows) backend, get examples working

View file

@ -77,11 +77,11 @@ static void print_device(struct SoundIoDevice *device, bool is_default) {
}
static int list_devices(struct SoundIo *soundio) {
int output_count = soundio_get_output_device_count(soundio);
int input_count = soundio_get_input_device_count(soundio);
int output_count = soundio_output_device_count(soundio);
int input_count = soundio_input_device_count(soundio);
int default_output = soundio_get_default_output_device_index(soundio);
int default_input = soundio_get_default_input_device_index(soundio);
int default_output = soundio_default_output_device_index(soundio);
int default_input = soundio_default_input_device_index(soundio);
fprintf(stderr, "--------Input Devices--------\n\n");
for (int i = 0; i < input_count; i += 1) {

View file

@ -143,13 +143,15 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio]\n", exe);
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio] [--in-device name] [--out-device name]\n", exe);
return 1;
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
char *in_device_name = NULL;
char *out_device_name = NULL;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (strcmp("--dummy", arg) == 0) {
@ -158,6 +160,18 @@ int main(int argc, char **argv) {
backend = SoundIoBackendAlsa;
} else if (strcmp("--pulseaudio", arg) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("--in-device", arg) == 0) {
if (++i >= argc) {
return usage(exe);
} else {
in_device_name = argv[i];
}
} else if (strcmp("--out-device", arg) == 0) {
if (++i >= argc) {
return usage(exe);
} else {
out_device_name = argv[i];
}
} else {
return usage(exe);
}
@ -169,19 +183,53 @@ int main(int argc, char **argv) {
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
int default_out_device_index = soundio_get_default_output_device_index(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
panic("no output device found");
int default_in_device_index = soundio_get_default_input_device_index(soundio);
int default_in_device_index = soundio_default_input_device_index(soundio);
if (default_in_device_index < 0)
panic("no output device found");
struct SoundIoDevice *out_device = soundio_get_output_device(soundio, default_out_device_index);
int in_device_index = default_in_device_index;
if (in_device_name) {
bool found = false;
for (int i = 0; i < soundio_input_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
if (strcmp(device->name, in_device_name) == 0) {
in_device_index = i;
found = true;
soundio_device_unref(device);
break;
}
soundio_device_unref(device);
}
if (!found)
panic("invalid input device name: %s", in_device_name);
}
int out_device_index = default_out_device_index;
if (out_device_name) {
bool found = false;
for (int i = 0; i < soundio_output_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
if (strcmp(device->name, out_device_name) == 0) {
out_device_index = i;
found = true;
soundio_device_unref(device);
break;
}
soundio_device_unref(device);
}
if (!found)
panic("invalid output device name: %s", out_device_name);
}
struct SoundIoDevice *out_device = soundio_get_output_device(soundio, out_device_index);
if (!out_device)
panic("could not get output device: out of memory");
struct SoundIoDevice *in_device = soundio_get_input_device(soundio, default_in_device_index);
struct SoundIoDevice *in_device = soundio_get_input_device(soundio, in_device_index);
if (!in_device)
panic("could not get input device: out of memory");

View file

@ -101,7 +101,7 @@ int main(int argc, char **argv) {
if (err)
panic("error connecting: %s", soundio_strerror(err));
int default_out_device_index = soundio_get_default_output_device_index(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
panic("no output device found");

View file

@ -58,6 +58,19 @@ struct SoundIoOutStreamAlsa {
struct SoundIoInStreamAlsa {
snd_pcm_t *handle;
snd_pcm_chmap_t *chmap;
int chmap_size;
snd_pcm_uframes_t offset;
snd_pcm_access_t access;
int sample_buffer_size;
char *sample_buffer;
int poll_fd_count;
struct pollfd *poll_fds;
SoundIoOsThread *thread;
atomic_flag thread_exit_flag;
int period_size;
int read_frame_count;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
static void wakeup_device_poll(SoundIoAlsa *sia) {
@ -601,13 +614,13 @@ static int refresh_devices(SoundIoPrivate *si) {
if (stream == SND_PCM_STREAM_PLAYBACK) {
device->purpose = SoundIoDevicePurposeOutput;
device_list = &devices_info->output_devices;
if (str_has_prefix(name, "default:"))
if (devices_info->default_output_index < 0 && str_has_prefix(name, "default:"))
devices_info->default_output_index = device_list->length;
} else {
assert(stream == SND_PCM_STREAM_CAPTURE);
device->purpose = SoundIoDevicePurposeInput;
device_list = &devices_info->input_devices;
if (str_has_prefix(name, "default:"))
if (devices_info->default_input_index < 0 && str_has_prefix(name, "default:"))
devices_info->default_input_index = device_list->length;
}
@ -933,6 +946,21 @@ static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
return err;
}
static int instream_xrun_recovery(SoundIoInStreamPrivate *is, int err) {
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *)is->backend_data;
if (err == -EPIPE) {
err = snd_pcm_prepare(isa->handle);
} else if (err == -ESTRPIPE) {
while ((err = snd_pcm_resume(isa->handle)) == -EAGAIN) {
// wait until suspend flag is released
poll(nullptr, 0, 1);
}
if (err < 0)
err = snd_pcm_prepare(isa->handle);
}
return err;
}
static int wait_for_poll(SoundIoOutStreamAlsa *osa) {
int err;
unsigned short revents;
@ -951,6 +979,24 @@ static int wait_for_poll(SoundIoOutStreamAlsa *osa) {
}
}
static int instream_wait_for_poll(SoundIoInStreamAlsa *isa) {
int err;
unsigned short revents;
for (;;) {
if ((err = poll(isa->poll_fds, isa->poll_fd_count, -1)) < 0)
return err;
if ((err = snd_pcm_poll_descriptors_revents(isa->handle,
isa->poll_fds, isa->poll_fd_count, &revents)) < 0)
{
return err;
}
if (revents & POLLERR)
return -EIO;
if (revents & POLLIN)
return 0;
}
}
void outstream_thread_run(void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) arg;
SoundIoOutStream *outstream = &os->pub;
@ -959,14 +1005,6 @@ void outstream_thread_run(void *arg) {
int err;
for (;;) {
if ((err = wait_for_poll(osa)) < 0) {
if (!osa->thread_exit_flag.test_and_set())
return;
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
if (!osa->thread_exit_flag.test_and_set())
return;
snd_pcm_state_t state = snd_pcm_state(osa->handle);
switch (state) {
case SND_PCM_STATE_OPEN:
@ -985,6 +1023,15 @@ void outstream_thread_run(void *arg) {
continue;
case SND_PCM_STATE_RUNNING:
{
if ((err = wait_for_poll(osa)) < 0) {
if (!osa->thread_exit_flag.test_and_set())
return;
outstream->error_callback(outstream, SoundIoErrorStreaming);
return;
}
if (!osa->thread_exit_flag.test_and_set())
return;
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
if (avail < 0) {
if ((err = xrun_recovery(os, avail)) < 0) {
@ -1019,6 +1066,75 @@ void outstream_thread_run(void *arg) {
}
}
static void instream_thread_run(void *arg) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *) arg;
SoundIoInStream *instream = &is->pub;
SoundIoInStreamAlsa *osa = (SoundIoInStreamAlsa *) is->backend_data;
int err;
for (;;) {
snd_pcm_state_t state = snd_pcm_state(osa->handle);
switch (state) {
case SND_PCM_STATE_OPEN:
soundio_panic("TODO open");
case SND_PCM_STATE_SETUP:
if ((err = snd_pcm_prepare(osa->handle)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming);
return;
}
continue;
case SND_PCM_STATE_PREPARED:
if ((err = snd_pcm_start(osa->handle)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming);
return;
}
continue;
case SND_PCM_STATE_RUNNING:
{
if ((err = instream_wait_for_poll(osa)) < 0) {
if (!osa->thread_exit_flag.test_and_set())
return;
instream->error_callback(instream, SoundIoErrorStreaming);
return;
}
if (!osa->thread_exit_flag.test_and_set())
return;
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
if (avail < 0) {
if ((err = instream_xrun_recovery(is, avail)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming);
return;
}
continue;
}
instream->read_callback(instream, avail);
continue;
}
case SND_PCM_STATE_XRUN:
if ((err = instream_xrun_recovery(is, -EPIPE)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming);
return;
}
continue;
case SND_PCM_STATE_DRAINING:
soundio_panic("TODO draining");
case SND_PCM_STATE_PAUSED:
soundio_panic("TODO paused");
case SND_PCM_STATE_SUSPENDED:
if ((err = instream_xrun_recovery(is, -ESTRPIPE)) < 0) {
instream->error_callback(instream, SoundIoErrorStreaming);
return;
}
continue;
case SND_PCM_STATE_DISCONNECTED:
soundio_panic("TODO disconnected");
}
}
}
static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *outstream = &os->pub;
SoundIoDevice *device = outstream->device;
@ -1293,27 +1409,271 @@ static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStre
return 0;
}
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
// TODO default buffer_duration and period_duration
soundio_panic("TODO");
static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
if (!isa)
return;
deallocate(isa->chmap, isa->chmap_size);
// TODO
destroy(isa);
is->backend_data = nullptr;
}
static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *instream = &is->pub;
SoundIoDevice *device = instream->device;
if (instream->buffer_duration == 0.0)
instream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
if (instream->period_duration == 0.0) {
instream->period_duration = clamp(device->period_duration_min,
instream->buffer_duration / 8.0, device->period_duration_max);
}
SoundIoInStreamAlsa *isa = create<SoundIoInStreamAlsa>();
if (!isa) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
is->backend_data = isa;
int ch_count = instream->layout.channel_count;
isa->chmap_size = sizeof(int) + sizeof(int) * ch_count;
isa->chmap = (snd_pcm_chmap_t *)allocate<char>(isa->chmap_size);
if (!isa->chmap) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
int err;
snd_pcm_hw_params_t *hwparams;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_stream_t stream = purpose_to_stream(instream->device->purpose);
if ((err = snd_pcm_open(&isa->handle, instream->device->name, stream, 0)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = snd_pcm_hw_params_any(isa->handle, hwparams)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
int want_resample = !instream->device->is_raw;
if ((err = snd_pcm_hw_params_set_rate_resample(isa->handle, hwparams, want_resample)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = set_access(isa->handle, hwparams, &isa->access))) {
instream_destroy_alsa(si, is);
return err;
}
if ((err = snd_pcm_hw_params_set_channels(isa->handle, hwparams, ch_count)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = snd_pcm_hw_params_set_rate(isa->handle, hwparams, instream->sample_rate, 0)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
snd_pcm_format_t format = to_alsa_fmt(instream->format);
int phys_bits_per_sample = snd_pcm_format_physical_width(format);
if (phys_bits_per_sample % 8 != 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorIncompatibleDevice;
}
int phys_bytes_per_sample = phys_bits_per_sample / 8;
if ((err = snd_pcm_hw_params_set_format(isa->handle, hwparams, format)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
snd_pcm_uframes_t period_frames = ceil(instream->period_duration * (double)instream->sample_rate);
if ((err = snd_pcm_hw_params_set_period_size_near(isa->handle, hwparams, &period_frames, nullptr)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
instream->period_duration = ((double)period_frames) / (double)instream->sample_rate;
snd_pcm_uframes_t buffer_size_frames = ceil(instream->buffer_duration * (double)instream->sample_rate);
if ((err = snd_pcm_hw_params_set_buffer_size_near(isa->handle, hwparams, &buffer_size_frames)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
instream->buffer_duration = ((double)buffer_size_frames) / (double)instream->sample_rate;
snd_pcm_uframes_t period_size;
if ((snd_pcm_hw_params_get_period_size(hwparams, &period_size, nullptr)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
isa->period_size = period_size;
// write the hardware parameters to device
if ((err = snd_pcm_hw_params(isa->handle, hwparams)) < 0) {
instream_destroy_alsa(si, is);
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
}
// set channel map
isa->chmap->channels = ch_count;
for (int i = 0; i < ch_count; i += 1) {
isa->chmap->pos[i] = to_alsa_chmap_pos(instream->layout.channels[i]);
}
if ((err = snd_pcm_set_chmap(isa->handle, isa->chmap)) < 0)
instream->layout_error = SoundIoErrorIncompatibleDevice;
// get current swparams
snd_pcm_sw_params_t *swparams;
snd_pcm_sw_params_alloca(&swparams);
if ((err = snd_pcm_sw_params_current(isa->handle, swparams)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
// write the software parameters to device
if ((err = snd_pcm_sw_params(isa->handle, swparams)) < 0) {
instream_destroy_alsa(si, is);
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
}
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED || isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
isa->sample_buffer_size = ch_count * isa->period_size * phys_bytes_per_sample;
isa->sample_buffer = allocate_nonzero<char>(isa->sample_buffer_size);
if (!isa->sample_buffer) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
}
isa->poll_fd_count = snd_pcm_poll_descriptors_count(isa->handle);
if (isa->poll_fd_count <= 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
isa->poll_fds = allocate<struct pollfd>(isa->poll_fd_count);
if (!isa->poll_fds) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
if ((err = snd_pcm_poll_descriptors(isa->handle, isa->poll_fds, isa->poll_fd_count)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
return 0;
}
static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
assert(!isa->thread);
isa->thread_exit_flag.test_and_set();
int err;
if ((err = soundio_os_thread_create(instream_thread_run, is, true, &isa->thread))) {
instream_destroy_alsa(si, is);
return err;
}
return 0;
}
static int instream_begin_read_alsa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
{
soundio_panic("TODO");
*out_areas = nullptr;
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
SoundIoInStream *instream = &is->pub;
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
isa->areas[ch].ptr = isa->sample_buffer + ch * instream->bytes_per_sample;
isa->areas[ch].step = instream->bytes_per_frame;
}
isa->read_frame_count = min(*frame_count, isa->period_size);
*frame_count = isa->read_frame_count;
snd_pcm_sframes_t commitres = snd_pcm_readi(isa->handle, isa->sample_buffer, isa->read_frame_count);
if (commitres < 0 || commitres != *frame_count) {
int err = (commitres >= 0) ? -EPIPE : commitres;
if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming;
}
} else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
char *ptrs[SOUNDIO_MAX_CHANNELS];
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
isa->areas[ch].ptr = isa->sample_buffer + ch * instream->bytes_per_sample * isa->period_size;
isa->areas[ch].step = instream->bytes_per_sample;
ptrs[ch] = isa->areas[ch].ptr;
}
isa->read_frame_count = min(*frame_count, isa->period_size);
*frame_count = isa->read_frame_count;
snd_pcm_sframes_t commitres = snd_pcm_readn(isa->handle, (void**)ptrs, isa->read_frame_count);
if (commitres < 0 || commitres != *frame_count) {
int err = (commitres >= 0) ? -EPIPE : commitres;
if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming;
}
} else {
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t frames = *frame_count;
int err;
if ((err = snd_pcm_mmap_begin(isa->handle, &areas, &isa->offset, &frames)) < 0) {
if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming;
}
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
if ((areas[ch].first % 8 != 0) || (areas[ch].step % 8 != 0))
return SoundIoErrorIncompatibleDevice;
isa->areas[ch].step = areas[ch].step / 8;
isa->areas[ch].ptr = ((char *)areas[ch].addr) + (areas[ch].first / 8) +
(isa->areas[ch].step * isa->offset);
}
isa->read_frame_count = frames;
*frame_count = isa->read_frame_count;
}
*out_areas = isa->areas;
return 0;
}
static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
return 0;
} else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
return 0;
} else {
snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(isa->handle, isa->offset, isa->read_frame_count);
if (commitres < 0 || commitres != isa->read_frame_count) {
int err = (commitres >= 0) ? -EPIPE : commitres;
if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming;
}
}
return 0;
}
static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {

View file

@ -231,7 +231,7 @@ void soundio_flush_events(struct SoundIo *soundio) {
si->flush_events(si);
}
int soundio_get_input_device_count(struct SoundIo *soundio) {
int soundio_input_device_count(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info)
soundio_flush_events(soundio);
@ -239,7 +239,7 @@ int soundio_get_input_device_count(struct SoundIo *soundio) {
return si->safe_devices_info->input_devices.length;
}
int soundio_get_output_device_count(struct SoundIo *soundio) {
int soundio_output_device_count(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info)
soundio_flush_events(soundio);
@ -247,7 +247,7 @@ int soundio_get_output_device_count(struct SoundIo *soundio) {
return si->safe_devices_info->output_devices.length;
}
int soundio_get_default_input_device_index(struct SoundIo *soundio) {
int soundio_default_input_device_index(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info)
soundio_flush_events(soundio);
@ -255,7 +255,7 @@ int soundio_get_default_input_device_index(struct SoundIo *soundio) {
return si->safe_devices_info->default_input_index;
}
int soundio_get_default_output_device_index(struct SoundIo *soundio) {
int soundio_default_output_device_index(struct SoundIo *soundio) {
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info)
soundio_flush_events(soundio);

View file

@ -407,6 +407,10 @@ struct SoundIoInStream {
// computed automatically when you call soundio_instream_open
int bytes_per_frame;
int bytes_per_sample;
// If setting the channel layout fails for some reason, this field is set
// to an error code. Possible error codes are: SoundIoErrorIncompatibleDevice
int layout_error;
};
// Main Context
@ -503,23 +507,23 @@ const char * soundio_format_string(enum SoundIoFormat format);
// Devices
int soundio_get_input_device_count(struct SoundIo *soundio);
int soundio_get_output_device_count(struct SoundIo *soundio);
int soundio_input_device_count(struct SoundIo *soundio);
int soundio_output_device_count(struct SoundIo *soundio);
// Always returns a device. Call soundio_device_unref when done.
// `index` must be 0 <= index < soundio_get_input_device_count
// `index` must be 0 <= index < soundio_input_device_count
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index);
// Always returns a device. Call soundio_device_unref when done.
// `index` must be 0 <= index < soundio_get_output_device_count
// `index` must be 0 <= index < soundio_output_device_count
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index);
// returns the index of the default input device
// returns -1 if there are no devices.
int soundio_get_default_input_device_index(struct SoundIo *soundio);
int soundio_default_input_device_index(struct SoundIo *soundio);
// returns the index of the default output device
// returns -1 if there are no devices.
int soundio_get_default_output_device_index(struct SoundIo *soundio);
int soundio_default_output_device_index(struct SoundIo *soundio);
void soundio_device_ref(struct SoundIoDevice *device);
void soundio_device_unref(struct SoundIoDevice *device);

View file

@ -30,7 +30,7 @@ static void test_create_outstream(void) {
struct SoundIo *soundio = soundio_create();
assert(soundio);
ok_or_panic(soundio_connect(soundio));
int default_out_device_index = soundio_get_default_output_device_index(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
assert(default_out_device_index >= 0);
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
assert(device);