diff --git a/README.md b/README.md index 0953864..0865e90 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/example/list_devices.c b/example/list_devices.c index 40192be..b189176 100644 --- a/example/list_devices.c +++ b/example/list_devices.c @@ -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) { diff --git a/example/microphone.c b/example/microphone.c index f5d64f5..580fecd 100644 --- a/example/microphone.c +++ b/example/microphone.c @@ -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"); diff --git a/example/sine.c b/example/sine.c index 3b12ab6..251c603 100644 --- a/example/sine.c +++ b/example/sine.c @@ -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"); diff --git a/src/alsa.cpp b/src/alsa.cpp index d9bdbf8..0689722 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -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(); + 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(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(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(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) { diff --git a/src/soundio.cpp b/src/soundio.cpp index cea6e34..200ab3b 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -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); diff --git a/src/soundio.h b/src/soundio.h index 5264127..c5c233f 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -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); diff --git a/test/unit_tests.cpp b/test/unit_tests.cpp index 44eafd7..63017d3 100644 --- a/test/unit_tests.cpp +++ b/test/unit_tests.cpp @@ -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);