diff --git a/example/list_devices.c b/example/list_devices.c index 5d9b88d..0864481 100644 --- a/example/list_devices.c +++ b/example/list_devices.c @@ -43,10 +43,15 @@ static void print_device(struct SoundIoDevice *device, bool is_default) { } raw_str = device->is_raw ? "(raw) " : ""; const char *description = soundio_device_description(device); - int sample_rate = soundio_device_sample_rate(device); + int sample_rate = device->sample_rate_max; fprintf(stderr, "%s%s device: ", raw_str, purpose_str); - print_channel_layout(soundio_device_channel_layout(device)); - fprintf(stderr, " %d Hz %s%s\n", sample_rate, description, default_str); + if (device->probe_error) { + fprintf(stderr, "[%s] %s%s\n", soundio_strerror(device->probe_error), + description, default_str); + } else { + print_channel_layout(soundio_device_channel_layout(device)); + fprintf(stderr, " %d Hz %s%s\n", sample_rate, description, default_str); + } } static int list_devices(struct SoundIo *soundio) { @@ -97,7 +102,7 @@ int main(int argc, char **argv) { int err; if ((err = soundio_connect(soundio))) { - fprintf(stderr, "%s\n", soundio_error_string(err)); + fprintf(stderr, "%s\n", soundio_strerror(err)); return err; } diff --git a/example/microphone.c b/example/microphone.c index 6c09a3f..ba3bda4 100644 --- a/example/microphone.c +++ b/example/microphone.c @@ -46,7 +46,7 @@ int main(int argc, char **argv) { int err; if ((err = soundio_connect(soundio))) - panic("error connecting: %s", soundio_error_string(err)); + panic("error connecting: %s", soundio_strerror(err)); int default_out_device_index = soundio_get_default_output_device_index(soundio); if (default_out_device_index < 0) @@ -73,24 +73,21 @@ int main(int argc, char **argv) { if (!soundio_channel_layout_equal(in_layout, out_layout)) panic("channel layouts not compatible"); - if (soundio_device_sample_rate(in_device) != soundio_device_sample_rate(out_device)) - panic("sample rates not compatible"); - double latency = 0.1; struct SoundIoInputDevice *input_device; - soundio_input_device_create(in_device, SoundIoSampleFormatFloat32NE, 48000, latency, NULL, + soundio_input_device_create(in_device, SoundIoFormatFloat32NE, 48000, latency, NULL, read_callback, &input_device); struct SoundIoOutputDevice *output_device; - soundio_output_device_create(out_device, SoundIoSampleFormatFloat32NE, 48000, latency, NULL, + soundio_output_device_create(out_device, SoundIoFormatFloat32NE, 48000, latency, NULL, write_callback, underrun_callback, &output_device); if ((err = soundio_input_device_start(input_device))) - panic("unable to start input device: %s", soundio_error_string(err)); + panic("unable to start input device: %s", soundio_strerror(err)); if ((err = soundio_output_device_start(output_device))) - panic("unable to start output device: %s", soundio_error_string(err)); + panic("unable to start output device: %s", soundio_strerror(err)); for (;;) soundio_wait_events(soundio); diff --git a/example/sine.c b/example/sine.c index 586a3d0..b8b170f 100644 --- a/example/sine.c +++ b/example/sine.c @@ -75,7 +75,7 @@ int main(int argc, char **argv) { int err; if ((err = soundio_connect(soundio))) - panic("error connecting: %s", soundio_error_string(err)); + panic("error connecting: %s", soundio_strerror(err)); int default_out_device_index = soundio_get_default_output_device_index(soundio); if (default_out_device_index < 0) @@ -90,11 +90,11 @@ int main(int argc, char **argv) { soundio_device_description(device)); struct SoundIoOutputDevice *output_device; - soundio_output_device_create(device, SoundIoSampleFormatFloat32NE, 48000, + soundio_output_device_create(device, SoundIoFormatFloat32NE, 48000, 0.1, NULL, write_callback, underrun_callback, &output_device); if ((err = soundio_output_device_start(output_device))) - panic("unable to start device: %s", soundio_error_string(err)); + panic("unable to start device: %s", soundio_strerror(err)); for (;;) soundio_wait_events(soundio); diff --git a/src/alsa.cpp b/src/alsa.cpp index 6e411d0..8224ffa 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -15,8 +15,6 @@ static snd_pcm_stream_t stream_types[] = {SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE}; -static const int MAX_SAMPLE_RATE = 48000; - struct SoundIoAlsa { SoundIoOsMutex *mutex; SoundIoOsCond *cond; @@ -158,6 +156,40 @@ static void handle_channel_maps(SoundIoDevice *device, snd_pcm_chmap_query_t **m snd_pcm_free_chmaps(maps); } +static snd_pcm_format_t to_alsa_fmt(SoundIoFormat fmt) { + switch (fmt) { + case SoundIoFormatS8: return SND_PCM_FORMAT_S8; + case SoundIoFormatU8: return SND_PCM_FORMAT_U8; + case SoundIoFormatS16LE: return SND_PCM_FORMAT_S16_LE; + case SoundIoFormatS16BE: return SND_PCM_FORMAT_S16_BE; + case SoundIoFormatU16LE: return SND_PCM_FORMAT_U16_LE; + case SoundIoFormatU16BE: return SND_PCM_FORMAT_U16_BE; + case SoundIoFormatS24LE: return SND_PCM_FORMAT_S24_LE; + case SoundIoFormatS24BE: return SND_PCM_FORMAT_S24_BE; + case SoundIoFormatU24LE: return SND_PCM_FORMAT_U24_LE; + case SoundIoFormatU24BE: return SND_PCM_FORMAT_U24_BE; + case SoundIoFormatS32LE: return SND_PCM_FORMAT_S32_LE; + case SoundIoFormatS32BE: return SND_PCM_FORMAT_S32_BE; + case SoundIoFormatU32LE: return SND_PCM_FORMAT_U32_LE; + case SoundIoFormatU32BE: return SND_PCM_FORMAT_U32_BE; + case SoundIoFormatFloat32LE: return SND_PCM_FORMAT_FLOAT_LE; + case SoundIoFormatFloat32BE: return SND_PCM_FORMAT_FLOAT_BE; + case SoundIoFormatFloat64LE: return SND_PCM_FORMAT_FLOAT64_LE; + case SoundIoFormatFloat64BE: return SND_PCM_FORMAT_FLOAT64_BE; + + case SoundIoFormatInvalid: + return SND_PCM_FORMAT_UNKNOWN; + } + return SND_PCM_FORMAT_UNKNOWN; +} + +static void test_fmt_mask(SoundIoDevice *device, const snd_pcm_format_mask_t *fmt_mask, SoundIoFormat fmt) { + if (snd_pcm_format_mask_test(fmt_mask, to_alsa_fmt(fmt))) { + device->formats[device->format_count] = fmt; + device->format_count += 1; + } +} + static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { int err; snd_pcm_t *handle; @@ -176,23 +208,27 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { } if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) { + handle_channel_maps(device, maps); snd_pcm_close(handle); return SoundIoErrorOpeningDevice; } // disable hardware resampling because we're trying to probe if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, 0)) < 0) { + handle_channel_maps(device, maps); snd_pcm_close(handle); return SoundIoErrorOpeningDevice; } if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + handle_channel_maps(device, maps); snd_pcm_close(handle); return SoundIoErrorOpeningDevice; } unsigned int channel_count; if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0) { + handle_channel_maps(device, maps); snd_pcm_close(handle); return SoundIoErrorOpeningDevice; } @@ -203,6 +239,7 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { int max_dir; if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &max_sample_rate, &max_dir)) < 0) { + handle_channel_maps(device, maps); snd_pcm_close(handle); return SoundIoErrorOpeningDevice; } @@ -210,12 +247,68 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { max_sample_rate -= 1; if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &min_sample_rate, &min_dir)) < 0) { + handle_channel_maps(device, maps); snd_pcm_close(handle); return SoundIoErrorOpeningDevice; } if (min_dir > 0) min_sample_rate += 1; + snd_pcm_format_mask_t *fmt_mask; + snd_pcm_format_mask_alloca(&fmt_mask); + snd_pcm_format_mask_none(fmt_mask); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S8); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U8); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S16_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S16_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U16_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U16_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S24_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S24_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U24_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U24_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S32_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S32_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U32_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U32_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT_BE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_LE); + snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_BE); + + if ((err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask)) < 0) { + handle_channel_maps(device, maps); + snd_pcm_close(handle); + return SoundIoErrorOpeningDevice; + } + + snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask); + device->formats = allocate(18); + if (!device->formats) { + handle_channel_maps(device, maps); + snd_pcm_close(handle); + return SoundIoErrorNoMem; + } + + device->format_count = 0; + test_fmt_mask(device, fmt_mask, SoundIoFormatS8); + test_fmt_mask(device, fmt_mask, SoundIoFormatU8); + test_fmt_mask(device, fmt_mask, SoundIoFormatS16LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS16BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU16LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU16BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS24LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS24BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU24LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU24BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS32LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatS32BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU32LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatU32BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE); + test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64BE); snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle); @@ -229,16 +322,14 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { device->sample_rate_min = min_sample_rate; - device->sample_rate_min = max_sample_rate; - device->sample_rate_default = - (min_sample_rate <= MAX_SAMPLE_RATE && - MAX_SAMPLE_RATE <= max_sample_rate) ? MAX_SAMPLE_RATE : 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? + device->sample_rate_current = 0; snd_pcm_close(handle); return 0; - // TODO: device->default_sample_format // TODO: device->default_latency } @@ -352,7 +443,7 @@ static int refresh_devices(SoundIo *soundio) { devices_info->default_input_index = device_list->length; } - probe_device(device, nullptr); + device->probe_error = probe_device(device, nullptr); if (device_list->append(device)) { soundio_device_unref(device); @@ -461,7 +552,7 @@ static int refresh_devices(SoundIo *soundio) { } snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps_from_hw(card_index, device_index, -1, stream); - probe_device(device, maps); + device->probe_error = probe_device(device, maps); if (device_list->append(device)) { soundio_device_unref(device); @@ -575,7 +666,7 @@ static void device_thread_run(void *arg) { } if (got_rescan_event) { if ((err = refresh_devices(soundio))) - soundio_panic("error refreshing devices: %s", soundio_error_string(err)); + soundio_panic("error refreshing devices: %s", soundio_strerror(err)); } } } diff --git a/src/dummy.cpp b/src/dummy.cpp index d1ed371..3685f2e 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -245,6 +245,34 @@ static void input_device_clear_buffer_dummy(SoundIo *soundio, soundio_panic("TODO"); } +static int set_all_device_formats(SoundIoDevice *device) { + device->format_count = 18; + device->formats = allocate(device->format_count); + if (!device->formats) + return SoundIoErrorNoMem; + + device->formats[0] = SoundIoFormatS8; + device->formats[1] = SoundIoFormatU8; + device->formats[2] = SoundIoFormatS16LE; + device->formats[3] = SoundIoFormatS16BE; + device->formats[4] = SoundIoFormatU16LE; + device->formats[5] = SoundIoFormatU16BE; + device->formats[6] = SoundIoFormatS24LE; + device->formats[7] = SoundIoFormatS24BE; + device->formats[8] = SoundIoFormatU24LE; + device->formats[9] = SoundIoFormatU24BE; + device->formats[10] = SoundIoFormatS32LE; + device->formats[11] = SoundIoFormatS32BE; + device->formats[12] = SoundIoFormatU32LE; + device->formats[13] = SoundIoFormatU32BE; + device->formats[14] = SoundIoFormatFloat32LE; + device->formats[15] = SoundIoFormatFloat32BE; + device->formats[16] = SoundIoFormatFloat64LE; + device->formats[17] = SoundIoFormatFloat64BE; + + return 0; +} + int soundio_dummy_init(SoundIo *soundio) { assert(!soundio->backend_data); SoundIoDummy *sid = create(); @@ -287,17 +315,24 @@ int soundio_dummy_init(SoundIo *soundio) { device->ref_count = 1; device->soundio = soundio; device->name = strdup("dummy-out"); - device->description = strdup("Dummy output device"); + device->description = strdup("Dummy Output Device"); if (!device->name || !device->description) { - free(device->name); - free(device->description); + soundio_device_unref(device); destroy_dummy(soundio); return SoundIoErrorNoMem; } device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); - device->default_sample_format = SoundIoSampleFormatFloat32NE; + int err; + if ((err = set_all_device_formats(device))) { + soundio_device_unref(device); + destroy_dummy(soundio); + return err; + } + device->default_latency = 0.01; - device->sample_rate_default = 48000; + device->sample_rate_min = 2; + device->sample_rate_max = 5644800; + device->sample_rate_current = 48000; device->purpose = SoundIoDevicePurposeOutput; if (soundio->safe_devices_info->output_devices.append(device)) { @@ -325,9 +360,16 @@ int soundio_dummy_init(SoundIo *soundio) { return SoundIoErrorNoMem; } device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); - device->default_sample_format = SoundIoSampleFormatFloat32NE; + int err; + if ((err = set_all_device_formats(device))) { + soundio_device_unref(device); + destroy_dummy(soundio); + return err; + } device->default_latency = 0.01; - device->sample_rate_default = 48000; + device->sample_rate_min = 2; + device->sample_rate_max = 5644800; + device->sample_rate_current = 48000; device->purpose = SoundIoDevicePurposeInput; if (soundio->safe_devices_info->input_devices.append(device)) { diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index 032991f..f73310f 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -139,17 +139,17 @@ static double usec_to_sec(pa_usec_t usec) { } -static SoundIoSampleFormat sample_format_from_pulseaudio(pa_sample_spec sample_spec) { +static SoundIoFormat format_from_pulseaudio(pa_sample_spec sample_spec) { switch (sample_spec.format) { - case PA_SAMPLE_U8: return SoundIoSampleFormatU8; - case PA_SAMPLE_S16LE: return SoundIoSampleFormatS16LE; - case PA_SAMPLE_S16BE: return SoundIoSampleFormatS16BE; - case PA_SAMPLE_FLOAT32LE: return SoundIoSampleFormatFloat32LE; - case PA_SAMPLE_FLOAT32BE: return SoundIoSampleFormatFloat32BE; - case PA_SAMPLE_S32LE: return SoundIoSampleFormatS32LE; - case PA_SAMPLE_S32BE: return SoundIoSampleFormatS32BE; - case PA_SAMPLE_S24_32LE: return SoundIoSampleFormatS24LE; - case PA_SAMPLE_S24_32BE: return SoundIoSampleFormatS24BE; + case PA_SAMPLE_U8: return SoundIoFormatU8; + case PA_SAMPLE_S16LE: return SoundIoFormatS16LE; + case PA_SAMPLE_S16BE: return SoundIoFormatS16BE; + case PA_SAMPLE_FLOAT32LE: return SoundIoFormatFloat32LE; + case PA_SAMPLE_FLOAT32BE: return SoundIoFormatFloat32BE; + case PA_SAMPLE_S32LE: return SoundIoFormatS32LE; + case PA_SAMPLE_S32BE: return SoundIoFormatS32BE; + case PA_SAMPLE_S24_32LE: return SoundIoFormatS24LE; + case PA_SAMPLE_S24_32BE: return SoundIoFormatS24BE; case PA_SAMPLE_MAX: case PA_SAMPLE_INVALID: @@ -157,9 +157,9 @@ static SoundIoSampleFormat sample_format_from_pulseaudio(pa_sample_spec sample_s case PA_SAMPLE_ULAW: case PA_SAMPLE_S24LE: case PA_SAMPLE_S24BE: - return SoundIoSampleFormatInvalid; + return SoundIoFormatInvalid; } - return SoundIoSampleFormatInvalid; + return SoundIoFormatInvalid; } static int sample_rate_from_pulseaudio(pa_sample_spec sample_spec) { @@ -280,9 +280,10 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in if (!device->name || !device->description) soundio_panic("out of memory"); set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout); - device->default_sample_format = sample_format_from_pulseaudio(info->sample_spec); + // TODO determine the list of supported formats and the min and max sample rate + device->current_format = format_from_pulseaudio(info->sample_spec); device->default_latency = usec_to_sec(info->configured_latency); - device->sample_rate_default = sample_rate_from_pulseaudio(info->sample_spec); + device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); device->purpose = SoundIoDevicePurposeOutput; if (sipa->current_devices_info->output_devices.append(device)) @@ -309,9 +310,10 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info if (!device->name || !device->description) soundio_panic("out of memory"); set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout); - device->default_sample_format = sample_format_from_pulseaudio(info->sample_spec); + // TODO determine the list of supported formats and the min and max sample rate + device->current_format = format_from_pulseaudio(info->sample_spec); device->default_latency = usec_to_sec(info->configured_latency); - device->sample_rate_default = sample_rate_from_pulseaudio(info->sample_spec); + device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); device->purpose = SoundIoDevicePurposeInput; if (sipa->current_devices_info->input_devices.append(device)) @@ -437,28 +439,28 @@ static void wakeup(SoundIo *soundio) { pa_threaded_mainloop_signal(sipa->main_loop, 0); } -static pa_sample_format_t to_pulseaudio_sample_format(SoundIoSampleFormat sample_format) { - switch (sample_format) { - case SoundIoSampleFormatU8: return PA_SAMPLE_U8; - case SoundIoSampleFormatS16LE: return PA_SAMPLE_S16LE; - case SoundIoSampleFormatS16BE: return PA_SAMPLE_S16BE; - case SoundIoSampleFormatS24LE: return PA_SAMPLE_S24_32LE; - case SoundIoSampleFormatS24BE: return PA_SAMPLE_S24_32BE; - case SoundIoSampleFormatS32LE: return PA_SAMPLE_S32LE; - case SoundIoSampleFormatS32BE: return PA_SAMPLE_S32BE; - case SoundIoSampleFormatFloat32LE: return PA_SAMPLE_FLOAT32LE; - case SoundIoSampleFormatFloat32BE: return PA_SAMPLE_FLOAT32BE; +static pa_sample_format_t to_pulseaudio_format(SoundIoFormat format) { + switch (format) { + case SoundIoFormatU8: return PA_SAMPLE_U8; + case SoundIoFormatS16LE: return PA_SAMPLE_S16LE; + case SoundIoFormatS16BE: return PA_SAMPLE_S16BE; + case SoundIoFormatS24LE: return PA_SAMPLE_S24_32LE; + case SoundIoFormatS24BE: return PA_SAMPLE_S24_32BE; + case SoundIoFormatS32LE: return PA_SAMPLE_S32LE; + case SoundIoFormatS32BE: return PA_SAMPLE_S32BE; + case SoundIoFormatFloat32LE: return PA_SAMPLE_FLOAT32LE; + case SoundIoFormatFloat32BE: return PA_SAMPLE_FLOAT32BE; - case SoundIoSampleFormatInvalid: - case SoundIoSampleFormatS8: - case SoundIoSampleFormatU16LE: - case SoundIoSampleFormatU16BE: - case SoundIoSampleFormatU24LE: - case SoundIoSampleFormatU24BE: - case SoundIoSampleFormatU32LE: - case SoundIoSampleFormatU32BE: - case SoundIoSampleFormatFloat64LE: - case SoundIoSampleFormatFloat64BE: + case SoundIoFormatInvalid: + case SoundIoFormatS8: + case SoundIoFormatU16LE: + case SoundIoFormatU16BE: + case SoundIoFormatU24LE: + case SoundIoFormatU24BE: + case SoundIoFormatU32LE: + case SoundIoFormatU32BE: + case SoundIoFormatFloat64LE: + case SoundIoFormatFloat64BE: return PA_SAMPLE_INVALID; } return PA_SAMPLE_INVALID; @@ -600,7 +602,7 @@ static int output_device_init_pa(SoundIo *soundio, pa_threaded_mainloop_lock(sipa->main_loop); pa_sample_spec sample_spec; - sample_spec.format = to_pulseaudio_sample_format(output_device->sample_format); + sample_spec.format = to_pulseaudio_format(output_device->format); sample_spec.rate = output_device->sample_rate; sample_spec.channels = device->channel_layout.channel_count; pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout); @@ -766,7 +768,7 @@ static int input_device_init_pa(SoundIo *soundio, pa_threaded_mainloop_lock(sipa->main_loop); pa_sample_spec sample_spec; - sample_spec.format = to_pulseaudio_sample_format(input_device->sample_format); + sample_spec.format = to_pulseaudio_format(input_device->format); sample_spec.rate = input_device->sample_rate; sample_spec.channels = device->channel_layout.channel_count; diff --git a/src/soundio.cpp b/src/soundio.cpp index 0b58840..ce2c1be 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -22,7 +22,7 @@ #include #include -const char *soundio_error_string(int error) { +const char *soundio_strerror(int error) { switch ((enum SoundIoError)error) { case SoundIoErrorNone: return "(no error)"; case SoundIoErrorNoMem: return "out of memory"; @@ -33,52 +33,56 @@ const char *soundio_error_string(int error) { soundio_panic("invalid error enum value: %d", error); } -int soundio_get_bytes_per_sample(enum SoundIoSampleFormat sample_format) { - switch (sample_format) { - case SoundIoSampleFormatInvalid: soundio_panic("invalid sample format"); - case SoundIoSampleFormatU8: return 1; - case SoundIoSampleFormatS8: return 1; - case SoundIoSampleFormatS16LE: return 2; - case SoundIoSampleFormatS16BE: return 2; - case SoundIoSampleFormatU16LE: return 2; - case SoundIoSampleFormatU16BE: return 2; - case SoundIoSampleFormatS24LE: return 4; - case SoundIoSampleFormatS24BE: return 4; - case SoundIoSampleFormatU24LE: return 4; - case SoundIoSampleFormatU24BE: return 4; - case SoundIoSampleFormatS32LE: return 4; - case SoundIoSampleFormatS32BE: return 4; - case SoundIoSampleFormatU32LE: return 4; - case SoundIoSampleFormatU32BE: return 4; - case SoundIoSampleFormatFloat32LE: return 4; - case SoundIoSampleFormatFloat32BE: return 4; - case SoundIoSampleFormatFloat64LE: return 8; - case SoundIoSampleFormatFloat64BE: return 8; +int soundio_get_bytes_per_sample(enum SoundIoFormat format) { + switch (format) { + case SoundIoFormatU8: return 1; + case SoundIoFormatS8: return 1; + case SoundIoFormatS16LE: return 2; + case SoundIoFormatS16BE: return 2; + case SoundIoFormatU16LE: return 2; + case SoundIoFormatU16BE: return 2; + case SoundIoFormatS24LE: return 4; + case SoundIoFormatS24BE: return 4; + case SoundIoFormatU24LE: return 4; + case SoundIoFormatU24BE: return 4; + case SoundIoFormatS32LE: return 4; + case SoundIoFormatS32BE: return 4; + case SoundIoFormatU32LE: return 4; + case SoundIoFormatU32BE: return 4; + case SoundIoFormatFloat32LE: return 4; + case SoundIoFormatFloat32BE: return 4; + case SoundIoFormatFloat64LE: return 8; + case SoundIoFormatFloat64BE: return 8; + + case SoundIoFormatInvalid: + soundio_panic("invalid sample format"); } soundio_panic("invalid sample format"); } -const char * soundio_sample_format_string(enum SoundIoSampleFormat sample_format) { - switch (sample_format) { - case SoundIoSampleFormatInvalid: return "(invalid sample format)"; - case SoundIoSampleFormatU8: return "signed 8-bit"; - case SoundIoSampleFormatS8: return "unsigned 8-bit"; - case SoundIoSampleFormatS16LE: return "signed 16-bit LE"; - case SoundIoSampleFormatS16BE: return "signed 16-bit BE"; - case SoundIoSampleFormatU16LE: return "unsigned 16-bit LE"; - case SoundIoSampleFormatU16BE: return "unsigned 16-bit LE"; - case SoundIoSampleFormatS24LE: return "signed 24-bit LE"; - case SoundIoSampleFormatS24BE: return "signed 24-bit BE"; - case SoundIoSampleFormatU24LE: return "unsigned 24-bit LE"; - case SoundIoSampleFormatU24BE: return "unsigned 24-bit BE"; - case SoundIoSampleFormatS32LE: return "signed 32-bit LE"; - case SoundIoSampleFormatS32BE: return "signed 32-bit BE"; - case SoundIoSampleFormatU32LE: return "unsigned 32-bit LE"; - case SoundIoSampleFormatU32BE: return "unsigned 32-bit BE"; - case SoundIoSampleFormatFloat32LE: return "float 32-bit LE"; - case SoundIoSampleFormatFloat32BE: return "float 32-bit BE"; - case SoundIoSampleFormatFloat64LE: return "float 64-bit LE"; - case SoundIoSampleFormatFloat64BE: return "float 64-bit BE"; +const char * soundio_format_string(enum SoundIoFormat format) { + switch (format) { + case SoundIoFormatU8: return "signed 8-bit"; + case SoundIoFormatS8: return "unsigned 8-bit"; + case SoundIoFormatS16LE: return "signed 16-bit LE"; + case SoundIoFormatS16BE: return "signed 16-bit BE"; + case SoundIoFormatU16LE: return "unsigned 16-bit LE"; + case SoundIoFormatU16BE: return "unsigned 16-bit LE"; + case SoundIoFormatS24LE: return "signed 24-bit LE"; + case SoundIoFormatS24BE: return "signed 24-bit BE"; + case SoundIoFormatU24LE: return "unsigned 24-bit LE"; + case SoundIoFormatU24BE: return "unsigned 24-bit BE"; + case SoundIoFormatS32LE: return "signed 32-bit LE"; + case SoundIoFormatS32BE: return "signed 32-bit BE"; + case SoundIoFormatU32LE: return "unsigned 32-bit LE"; + case SoundIoFormatU32BE: return "unsigned 32-bit BE"; + case SoundIoFormatFloat32LE: return "float 32-bit LE"; + case SoundIoFormatFloat32BE: return "float 32-bit BE"; + case SoundIoFormatFloat64LE: return "float 64-bit LE"; + case SoundIoFormatFloat64BE: return "float 64-bit BE"; + + case SoundIoFormatInvalid: + return "(invalid sample format)"; } return "(invalid sample format)"; } @@ -250,10 +254,6 @@ const struct SoundIoChannelLayout *soundio_device_channel_layout(const struct So return &device->channel_layout; } -int soundio_device_sample_rate(const struct SoundIoDevice *device) { - return device->sample_rate_default; -} - void soundio_device_unref(struct SoundIoDevice *device) { if (!device) return; @@ -264,6 +264,7 @@ void soundio_device_unref(struct SoundIoDevice *device) { if (device->ref_count == 0) { free(device->name); free(device->description); + deallocate(device->formats, device->format_count); destroy(device); } } @@ -313,7 +314,7 @@ void soundio_output_device_write(struct SoundIoOutputDevice *output_device, int soundio_output_device_create(struct SoundIoDevice *device, - enum SoundIoSampleFormat sample_format, int sample_rate, + enum SoundIoFormat format, int sample_rate, double latency, void *userdata, void (*write_callback)(struct SoundIoOutputDevice *, int frame_count), void (*underrun_callback)(struct SoundIoOutputDevice *), @@ -332,10 +333,10 @@ int soundio_output_device_create(struct SoundIoDevice *device, output_device->userdata = userdata; output_device->write_callback = write_callback; output_device->underrun_callback = underrun_callback; - output_device->sample_format = sample_format; + output_device->format = format; output_device->sample_rate = sample_rate; output_device->latency = latency; - output_device->bytes_per_frame = soundio_get_bytes_per_frame(sample_format, + output_device->bytes_per_frame = soundio_get_bytes_per_frame(format, device->channel_layout.channel_count); SoundIo *soundio = device->soundio; @@ -368,7 +369,7 @@ int soundio_output_device_start(struct SoundIoOutputDevice *output_device) { } int soundio_input_device_create(struct SoundIoDevice *device, - enum SoundIoSampleFormat sample_format, int sample_rate, + enum SoundIoFormat format, int sample_rate, double latency, void *userdata, void (*read_callback)(struct SoundIoInputDevice *), struct SoundIoInputDevice **out_input_device) @@ -385,10 +386,10 @@ int soundio_input_device_create(struct SoundIoDevice *device, sid->device = device; sid->userdata = userdata; sid->read_callback = read_callback; - sid->sample_format = sample_format; + sid->format = format; sid->latency = latency; sid->sample_rate = sample_rate; - sid->bytes_per_frame = soundio_get_bytes_per_frame(sample_format, + sid->bytes_per_frame = soundio_get_bytes_per_frame(format, device->channel_layout.channel_count); SoundIo *soundio = device->soundio; diff --git a/src/soundio.h b/src/soundio.h index 7cfb880..2f35d36 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -140,74 +140,115 @@ enum SoundIoDevicePurpose { SoundIoDevicePurposeOutput, }; -enum SoundIoSampleFormat { - SoundIoSampleFormatInvalid, - SoundIoSampleFormatU8, // Signed 8 bit - SoundIoSampleFormatS8, // Unsigned 8 bit - SoundIoSampleFormatS16LE, // Signed 16 bit Little Endian - SoundIoSampleFormatS16BE, // Signed 16 bit Big Endian - SoundIoSampleFormatU16LE, // Unsigned 16 bit Little Endian - SoundIoSampleFormatU16BE, // Unsigned 16 bit Little Endian - SoundIoSampleFormatS24LE, // Signed 24 bit Little Endian using low three bytes in 32-bit word - SoundIoSampleFormatS24BE, // Signed 24 bit Big Endian using low three bytes in 32-bit word - SoundIoSampleFormatU24LE, // Unsigned 24 bit Little Endian using low three bytes in 32-bit word - SoundIoSampleFormatU24BE, // Unsigned 24 bit Big Endian using low three bytes in 32-bit word - SoundIoSampleFormatS32LE, // Signed 32 bit Little Endian - SoundIoSampleFormatS32BE, // Signed 32 bit Big Endian - SoundIoSampleFormatU32LE, // Unsigned 32 bit Little Endian - SoundIoSampleFormatU32BE, // Unsigned 32 bit Big Endian - SoundIoSampleFormatFloat32LE, // Float 32 bit Little Endian, Range -1.0 to 1.0 - SoundIoSampleFormatFloat32BE, // Float 32 bit Big Endian, Range -1.0 to 1.0 - SoundIoSampleFormatFloat64LE, // Float 64 bit Little Endian, Range -1.0 to 1.0 - SoundIoSampleFormatFloat64BE, // Float 64 bit Big Endian, Range -1.0 to 1.0 +enum SoundIoFormat { + SoundIoFormatInvalid, + SoundIoFormatS8, // Signed 8 bit + SoundIoFormatU8, // Unsigned 8 bit + SoundIoFormatS16LE, // Signed 16 bit Little Endian + SoundIoFormatS16BE, // Signed 16 bit Big Endian + SoundIoFormatU16LE, // Unsigned 16 bit Little Endian + SoundIoFormatU16BE, // Unsigned 16 bit Little Endian + SoundIoFormatS24LE, // Signed 24 bit Little Endian using low three bytes in 32-bit word + SoundIoFormatS24BE, // Signed 24 bit Big Endian using low three bytes in 32-bit word + SoundIoFormatU24LE, // Unsigned 24 bit Little Endian using low three bytes in 32-bit word + SoundIoFormatU24BE, // Unsigned 24 bit Big Endian using low three bytes in 32-bit word + SoundIoFormatS32LE, // Signed 32 bit Little Endian + SoundIoFormatS32BE, // Signed 32 bit Big Endian + SoundIoFormatU32LE, // Unsigned 32 bit Little Endian + SoundIoFormatU32BE, // Unsigned 32 bit Big Endian + SoundIoFormatFloat32LE, // Float 32 bit Little Endian, Range -1.0 to 1.0 + SoundIoFormatFloat32BE, // Float 32 bit Big Endian, Range -1.0 to 1.0 + SoundIoFormatFloat64LE, // Float 64 bit Little Endian, Range -1.0 to 1.0 + SoundIoFormatFloat64BE, // Float 64 bit Big Endian, Range -1.0 to 1.0 }; #if defined(SOUNDIO_OS_BIG_ENDIAN) -#define SoundIoSampleFormatS16NE SoundIoSampleFormatS16BE -#define SoundIoSampleFormatU16NE SoundIoSampleFormatU16BE -#define SoundIoSampleFormatS24NE SoundIoSampleFormatS24BE -#define SoundIoSampleFormatU24NE SoundIoSampleFormatU24BE -#define SoundIoSampleFormatS32NE SoundIoSampleFormatS32BE -#define SoundIoSampleFormatU32NE SoundIoSampleFormatU32BE -#define SoundIoSampleFormatFloat32NE SoundIoSampleFormatFloat32BE -#define SoundIoSampleFormatFloat64NE SoundIoSampleFormatFloat64BE +#define SoundIoFormatS16NE SoundIoFormatS16BE +#define SoundIoFormatU16NE SoundIoFormatU16BE +#define SoundIoFormatS24NE SoundIoFormatS24BE +#define SoundIoFormatU24NE SoundIoFormatU24BE +#define SoundIoFormatS32NE SoundIoFormatS32BE +#define SoundIoFormatU32NE SoundIoFormatU32BE +#define SoundIoFormatFloat32NE SoundIoFormatFloat32BE +#define SoundIoFormatFloat64NE SoundIoFormatFloat64BE #elif defined(SOUNDIO_OS_LITTLE_ENDIAN) -#define SoundIoSampleFormatS16NE SoundIoSampleFormatS16LE -#define SoundIoSampleFormatU16NE SoundIoSampleFormatU16LE -#define SoundIoSampleFormatS24NE SoundIoSampleFormatS24LE -#define SoundIoSampleFormatU24NE SoundIoSampleFormatU24LE -#define SoundIoSampleFormatS32NE SoundIoSampleFormatS32LE -#define SoundIoSampleFormatU32NE SoundIoSampleFormatU32LE -#define SoundIoSampleFormatFloat32NE SoundIoSampleFormatFloat32LE -#define SoundIoSampleFormatFloat64NE SoundIoSampleFormatFloat64LE +#define SoundIoFormatS16NE SoundIoFormatS16LE +#define SoundIoFormatU16NE SoundIoFormatU16LE +#define SoundIoFormatS24NE SoundIoFormatS24LE +#define SoundIoFormatU24NE SoundIoFormatU24LE +#define SoundIoFormatS32NE SoundIoFormatS32LE +#define SoundIoFormatU32NE SoundIoFormatU32LE +#define SoundIoFormatFloat32NE SoundIoFormatFloat32LE +#define SoundIoFormatFloat64NE SoundIoFormatFloat64LE #endif struct SoundIoDevice { struct SoundIo *soundio; + + // `name` uniquely identifies this device. `description` is user-friendly + // text to describe the device. char *name; char *description; + + // If this information is missing due to a `probe_error`, the number of + // channels will be zero. struct SoundIoChannelLayout channel_layout; - // these values might not actually matter. audio hardware has a set of - // {sample format, sample rate} that they support. you can't know - // whether something worked until you try it. - // these values can be unknown - enum SoundIoSampleFormat default_sample_format; + // 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 + // `is_raw`). If it is a raw device, `current_format` is meaningless; + // the device has no current format until you open it. On the other hand, + // if it is a virtual device, `current_format` describes the destination + // sample format that your audio will be converted to. Or, if you're the + // lucky first application to open the device, you might cause the + // `current_format` to change to your format. Generally, you want to + // ignore `current_format` and use whatever format is most convenient + // for you which is supported by the device, because when you are the only + // application left, the mixer might decide to switch `current_format` to + // yours. You can learn the supported formats via `formats` and + // `format_count`. If this information is missing due to a probe error, + // `formats` will be `NULL`. If `current_format` is unavailable, it will be + // set to `SoundIoFormatInvalid`. + enum SoundIoFormat *formats; + int format_count; + enum SoundIoFormat current_format; + // Sample rate is handled very similar to sample format; see those docs. + // If sample rate information is missing due to a probe error, the field + // will be set to zero. int sample_rate_min; int sample_rate_max; - int sample_rate_default; + int sample_rate_current; double default_latency; + + // Tells whether this device is an input device or an output device. enum SoundIoDevicePurpose purpose; - int ref_count; + + // raw means that you are directly opening the hardware device and not + // going through a proxy such as dmix or PulseAudio. When you open a raw + // device, other applications on the computer are not able to + // simultaneously access the device. Raw devices do not perform automatic + // resampling and thus tend to have fewer formats available. bool is_raw; + + // Devices are reference counted. See `soundio_device_ref` and + // `soundio_device_unref`. + int ref_count; + + // This is set to a SoundIoError representing the result of the device + // probe. Ideally this will be SoundIoErrorNone in which case all the + // fields of the device will be populated. If there is an error code here + // then information about formats, sample rates, and channel layouts might + // be missing. + int probe_error; }; +// TODO rename this to SoundIoOutStream struct SoundIoOutputDevice { void *backend_data; struct SoundIoDevice *device; - enum SoundIoSampleFormat sample_format; + enum SoundIoFormat format; int sample_rate; double latency; int bytes_per_frame; @@ -217,10 +258,11 @@ struct SoundIoOutputDevice { void (*write_callback)(struct SoundIoOutputDevice *, int frame_count); }; +// TODO rename this to SoundIoInStream struct SoundIoInputDevice { void *backend_data; struct SoundIoDevice *device; - enum SoundIoSampleFormat sample_format; + enum SoundIoFormat format; int sample_rate; double latency; int bytes_per_frame; @@ -276,7 +318,7 @@ void soundio_destroy(struct SoundIo *soundio); int soundio_connect(struct SoundIo *soundio); void soundio_disconnect(struct SoundIo *soundio); -const char *soundio_error_string(int error); +const char *soundio_strerror(int error); const char *soundio_backend_name(enum SoundIoBackend backend); // when you call this, the on_devices_change and on_events_signal callbacks @@ -315,19 +357,19 @@ bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout); // Sample Formats -int soundio_get_bytes_per_sample(enum SoundIoSampleFormat sample_format); +int soundio_get_bytes_per_sample(enum SoundIoFormat format); -static inline int soundio_get_bytes_per_frame(enum SoundIoSampleFormat sample_format, int channel_count) { - return soundio_get_bytes_per_sample(sample_format) * channel_count; +static inline int soundio_get_bytes_per_frame(enum SoundIoFormat format, int channel_count) { + return soundio_get_bytes_per_sample(format) * channel_count; } -static inline int soundio_get_bytes_per_second(enum SoundIoSampleFormat sample_format, +static inline int soundio_get_bytes_per_second(enum SoundIoFormat format, int channel_count, int sample_rate) { - return soundio_get_bytes_per_frame(sample_format, channel_count) * sample_rate; + return soundio_get_bytes_per_frame(format, channel_count) * sample_rate; } -const char * soundio_sample_format_string(enum SoundIoSampleFormat sample_format); +const char * soundio_format_string(enum SoundIoFormat format); @@ -359,7 +401,6 @@ const char *soundio_device_name(const struct SoundIoDevice *device); const char *soundio_device_description(const struct SoundIoDevice *device); const struct SoundIoChannelLayout *soundio_device_channel_layout(const struct SoundIoDevice *device); -int soundio_device_sample_rate(const struct SoundIoDevice *device); bool soundio_device_equal( const struct SoundIoDevice *a, @@ -371,7 +412,7 @@ enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *dev // Output Devices int soundio_output_device_create(struct SoundIoDevice *device, - enum SoundIoSampleFormat sample_format, int sample_rate, + enum SoundIoFormat format, int sample_rate, double latency, void *userdata, void (*write_callback)(struct SoundIoOutputDevice *, int frame_count), void (*underrun_callback)(struct SoundIoOutputDevice *), @@ -397,7 +438,7 @@ void soundio_output_device_clear_buffer(struct SoundIoOutputDevice *output_devic // Input Devices int soundio_input_device_create(struct SoundIoDevice *device, - enum SoundIoSampleFormat sample_format, int sample_rate, + enum SoundIoFormat format, int sample_rate, double latency, void *userdata, void (*read_callback)(struct SoundIoInputDevice *), struct SoundIoInputDevice **out_input_device); diff --git a/test/unit_tests.cpp b/test/unit_tests.cpp index 962d721..43ba57f 100644 --- a/test/unit_tests.cpp +++ b/test/unit_tests.cpp @@ -11,7 +11,7 @@ static inline void ok_or_panic(int err) { if (err) - soundio_panic("%s", soundio_error_string(err)); + soundio_panic("%s", soundio_strerror(err)); } static void test_os_get_time(void) { @@ -37,7 +37,7 @@ static void test_create_output_device(void) { soundio_device_name(device); soundio_device_description(device); struct SoundIoOutputDevice *output_device; - soundio_output_device_create(device, SoundIoSampleFormatFloat32NE, 48000, 0.1, NULL, + soundio_output_device_create(device, SoundIoFormatFloat32NE, 48000, 0.1, NULL, write_callback, underrun_callback, &output_device); soundio_output_device_destroy(output_device); soundio_device_unref(device);