ALSA backend determines what formats a device supports

This commit is contained in:
Andrew Kelley 2015-07-10 02:21:47 -07:00
parent 8887a528ef
commit 3b8d896c8e
9 changed files with 361 additions and 182 deletions

View file

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

View file

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

View file

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

View file

@ -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<SoundIoFormat>(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));
}
}
}

View file

@ -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<SoundIoFormat>(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<SoundIoDummy>();
@ -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)) {

View file

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

View file

@ -22,7 +22,7 @@
#include <string.h>
#include <assert.h>
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;

View file

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

View file

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