microphone example working with ALSA

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

View file

@ -28,6 +28,7 @@ exposed.
* Supports channel layouts (also known as channel maps), important for * Supports channel layouts (also known as channel maps), important for
surround sound applications. surround sound applications.
* Ability to monitor devices and get an event when available devices change. * 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 * 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. an ALSA device open and a JACK device open at the same time.
* Meticulously checks all return codes and memory allocations and uses * 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. pipe record to playback example working with ALSA linux
0. expose prebuf 0. expose prebuf
0. why does pulseaudio microphone use up all the CPU? 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 JACK backend, get examples working
0. implement CoreAudio (OSX) backend, get examples working 0. implement CoreAudio (OSX) backend, get examples working
0. implement WASAPI (Windows) backend, get examples working 0. implement WASAPI (Windows) backend, get examples working

View file

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

View file

@ -143,13 +143,15 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
} }
static int usage(char *exe) { 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; return 1;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
char *exe = argv[0]; char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone; enum SoundIoBackend backend = SoundIoBackendNone;
char *in_device_name = NULL;
char *out_device_name = NULL;
for (int i = 1; i < argc; i += 1) { for (int i = 1; i < argc; i += 1) {
char *arg = argv[i]; char *arg = argv[i];
if (strcmp("--dummy", arg) == 0) { if (strcmp("--dummy", arg) == 0) {
@ -158,6 +160,18 @@ int main(int argc, char **argv) {
backend = SoundIoBackendAlsa; backend = SoundIoBackendAlsa;
} else if (strcmp("--pulseaudio", arg) == 0) { } else if (strcmp("--pulseaudio", arg) == 0) {
backend = SoundIoBackendPulseAudio; 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 { } else {
return usage(exe); return usage(exe);
} }
@ -169,19 +183,53 @@ int main(int argc, char **argv) {
int err = (backend == SoundIoBackendNone) ? int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend); 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) if (default_out_device_index < 0)
panic("no output device found"); 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) if (default_in_device_index < 0)
panic("no output device found"); 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) if (!out_device)
panic("could not get output device: out of memory"); 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) if (!in_device)
panic("could not get input device: out of memory"); panic("could not get input device: out of memory");

View file

@ -101,7 +101,7 @@ int main(int argc, char **argv) {
if (err) if (err)
panic("error connecting: %s", soundio_strerror(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) if (default_out_device_index < 0)
panic("no output device found"); panic("no output device found");

View file

@ -58,6 +58,19 @@ struct SoundIoOutStreamAlsa {
struct SoundIoInStreamAlsa { struct SoundIoInStreamAlsa {
snd_pcm_t *handle; 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) { static void wakeup_device_poll(SoundIoAlsa *sia) {
@ -601,13 +614,13 @@ static int refresh_devices(SoundIoPrivate *si) {
if (stream == SND_PCM_STREAM_PLAYBACK) { if (stream == SND_PCM_STREAM_PLAYBACK) {
device->purpose = SoundIoDevicePurposeOutput; device->purpose = SoundIoDevicePurposeOutput;
device_list = &devices_info->output_devices; 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; devices_info->default_output_index = device_list->length;
} else { } else {
assert(stream == SND_PCM_STREAM_CAPTURE); assert(stream == SND_PCM_STREAM_CAPTURE);
device->purpose = SoundIoDevicePurposeInput; device->purpose = SoundIoDevicePurposeInput;
device_list = &devices_info->input_devices; 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; devices_info->default_input_index = device_list->length;
} }
@ -933,6 +946,21 @@ static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
return 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) { static int wait_for_poll(SoundIoOutStreamAlsa *osa) {
int err; int err;
unsigned short revents; 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) { void outstream_thread_run(void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) arg; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) arg;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
@ -959,14 +1005,6 @@ void outstream_thread_run(void *arg) {
int err; int err;
for (;;) { 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); snd_pcm_state_t state = snd_pcm_state(osa->handle);
switch (state) { switch (state) {
case SND_PCM_STATE_OPEN: case SND_PCM_STATE_OPEN:
@ -985,6 +1023,15 @@ void outstream_thread_run(void *arg) {
continue; continue;
case SND_PCM_STATE_RUNNING: 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); snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
if (avail < 0) { if (avail < 0) {
if ((err = xrun_recovery(os, 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) { static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoDevice *device = outstream->device; SoundIoDevice *device = outstream->device;
@ -1293,27 +1409,271 @@ static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStre
return 0; return 0;
} }
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
// TODO default buffer_duration and period_duration SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
soundio_panic("TODO"); 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) { static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO"); SoundIoInStream *instream = &is->pub;
SoundIoDevice *device = instream->device;
if (instream->buffer_duration == 0.0)
instream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
if (instream->period_duration == 0.0) {
instream->period_duration = clamp(device->period_duration_min,
instream->buffer_duration / 8.0, device->period_duration_max);
}
SoundIoInStreamAlsa *isa = create<SoundIoInStreamAlsa>();
if (!isa) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
is->backend_data = isa;
int ch_count = instream->layout.channel_count;
isa->chmap_size = sizeof(int) + sizeof(int) * ch_count;
isa->chmap = (snd_pcm_chmap_t *)allocate<char>(isa->chmap_size);
if (!isa->chmap) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
int err;
snd_pcm_hw_params_t *hwparams;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_stream_t stream = purpose_to_stream(instream->device->purpose);
if ((err = snd_pcm_open(&isa->handle, instream->device->name, stream, 0)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = snd_pcm_hw_params_any(isa->handle, hwparams)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
int want_resample = !instream->device->is_raw;
if ((err = snd_pcm_hw_params_set_rate_resample(isa->handle, hwparams, want_resample)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = set_access(isa->handle, hwparams, &isa->access))) {
instream_destroy_alsa(si, is);
return err;
}
if ((err = snd_pcm_hw_params_set_channels(isa->handle, hwparams, ch_count)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = snd_pcm_hw_params_set_rate(isa->handle, hwparams, instream->sample_rate, 0)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
snd_pcm_format_t format = to_alsa_fmt(instream->format);
int phys_bits_per_sample = snd_pcm_format_physical_width(format);
if (phys_bits_per_sample % 8 != 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorIncompatibleDevice;
}
int phys_bytes_per_sample = phys_bits_per_sample / 8;
if ((err = snd_pcm_hw_params_set_format(isa->handle, hwparams, format)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
snd_pcm_uframes_t period_frames = ceil(instream->period_duration * (double)instream->sample_rate);
if ((err = snd_pcm_hw_params_set_period_size_near(isa->handle, hwparams, &period_frames, nullptr)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
instream->period_duration = ((double)period_frames) / (double)instream->sample_rate;
snd_pcm_uframes_t buffer_size_frames = ceil(instream->buffer_duration * (double)instream->sample_rate);
if ((err = snd_pcm_hw_params_set_buffer_size_near(isa->handle, hwparams, &buffer_size_frames)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
instream->buffer_duration = ((double)buffer_size_frames) / (double)instream->sample_rate;
snd_pcm_uframes_t period_size;
if ((snd_pcm_hw_params_get_period_size(hwparams, &period_size, nullptr)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
isa->period_size = period_size;
// write the hardware parameters to device
if ((err = snd_pcm_hw_params(isa->handle, hwparams)) < 0) {
instream_destroy_alsa(si, is);
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
}
// set channel map
isa->chmap->channels = ch_count;
for (int i = 0; i < ch_count; i += 1) {
isa->chmap->pos[i] = to_alsa_chmap_pos(instream->layout.channels[i]);
}
if ((err = snd_pcm_set_chmap(isa->handle, isa->chmap)) < 0)
instream->layout_error = SoundIoErrorIncompatibleDevice;
// get current swparams
snd_pcm_sw_params_t *swparams;
snd_pcm_sw_params_alloca(&swparams);
if ((err = snd_pcm_sw_params_current(isa->handle, swparams)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
// write the software parameters to device
if ((err = snd_pcm_sw_params(isa->handle, swparams)) < 0) {
instream_destroy_alsa(si, is);
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
}
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED || isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
isa->sample_buffer_size = ch_count * isa->period_size * phys_bytes_per_sample;
isa->sample_buffer = allocate_nonzero<char>(isa->sample_buffer_size);
if (!isa->sample_buffer) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
}
isa->poll_fd_count = snd_pcm_poll_descriptors_count(isa->handle);
if (isa->poll_fd_count <= 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
isa->poll_fds = allocate<struct pollfd>(isa->poll_fd_count);
if (!isa->poll_fds) {
instream_destroy_alsa(si, is);
return SoundIoErrorNoMem;
}
if ((err = snd_pcm_poll_descriptors(isa->handle, isa->poll_fds, isa->poll_fd_count)) < 0) {
instream_destroy_alsa(si, is);
return SoundIoErrorOpeningDevice;
}
return 0;
} }
static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { 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, static int instream_begin_read_alsa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count) 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) { 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) { static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {

View file

@ -231,7 +231,7 @@ void soundio_flush_events(struct SoundIo *soundio) {
si->flush_events(si); 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; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info) if (!si->safe_devices_info)
soundio_flush_events(soundio); 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; 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; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info) if (!si->safe_devices_info)
soundio_flush_events(soundio); 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; 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; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info) if (!si->safe_devices_info)
soundio_flush_events(soundio); 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; 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; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (!si->safe_devices_info) if (!si->safe_devices_info)
soundio_flush_events(soundio); soundio_flush_events(soundio);

View file

@ -407,6 +407,10 @@ struct SoundIoInStream {
// computed automatically when you call soundio_instream_open // computed automatically when you call soundio_instream_open
int bytes_per_frame; int bytes_per_frame;
int bytes_per_sample; 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 // Main Context
@ -503,23 +507,23 @@ const char * soundio_format_string(enum SoundIoFormat format);
// Devices // Devices
int soundio_get_input_device_count(struct SoundIo *soundio); int soundio_input_device_count(struct SoundIo *soundio);
int soundio_get_output_device_count(struct SoundIo *soundio); int soundio_output_device_count(struct SoundIo *soundio);
// Always returns a device. Call soundio_device_unref when done. // 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); struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index);
// Always returns a device. Call soundio_device_unref when done. // 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); struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index);
// returns the index of the default input device // returns the index of the default input device
// returns -1 if there are no devices. // 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 the index of the default output device
// returns -1 if there are no devices. // 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_ref(struct SoundIoDevice *device);
void soundio_device_unref(struct SoundIoDevice *device); void soundio_device_unref(struct SoundIoDevice *device);

View file

@ -30,7 +30,7 @@ static void test_create_outstream(void) {
struct SoundIo *soundio = soundio_create(); struct SoundIo *soundio = soundio_create();
assert(soundio); assert(soundio);
ok_or_panic(soundio_connect(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); assert(default_out_device_index >= 0);
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index); struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
assert(device); assert(device);