mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 18:45:29 +00:00
microphone example working with ALSA
This commit is contained in:
parent
511fcafc3b
commit
34039b4858
|
@ -28,6 +28,7 @@ exposed.
|
|||
* Supports channel layouts (also known as channel maps), important for
|
||||
surround sound applications.
|
||||
* Ability to monitor devices and get an event when available devices change.
|
||||
* Detects which input device is default and which output device is default.
|
||||
* Ability to connect to multiple backends at once. For example you could have
|
||||
an ALSA device open and a JACK device open at the same time.
|
||||
* Meticulously checks all return codes and memory allocations and uses
|
||||
|
@ -234,6 +235,7 @@ view `coverage/index.html` in a browser.
|
|||
0. pipe record to playback example working with ALSA linux
|
||||
0. expose prebuf
|
||||
0. why does pulseaudio microphone use up all the CPU?
|
||||
0. merge in/out stream structures and functions?
|
||||
0. implement JACK backend, get examples working
|
||||
0. implement CoreAudio (OSX) backend, get examples working
|
||||
0. implement WASAPI (Windows) backend, get examples working
|
||||
|
|
|
@ -77,11 +77,11 @@ static void print_device(struct SoundIoDevice *device, bool is_default) {
|
|||
}
|
||||
|
||||
static int list_devices(struct SoundIo *soundio) {
|
||||
int output_count = soundio_get_output_device_count(soundio);
|
||||
int input_count = soundio_get_input_device_count(soundio);
|
||||
int output_count = soundio_output_device_count(soundio);
|
||||
int input_count = soundio_input_device_count(soundio);
|
||||
|
||||
int default_output = soundio_get_default_output_device_index(soundio);
|
||||
int default_input = soundio_get_default_input_device_index(soundio);
|
||||
int default_output = soundio_default_output_device_index(soundio);
|
||||
int default_input = soundio_default_input_device_index(soundio);
|
||||
|
||||
fprintf(stderr, "--------Input Devices--------\n\n");
|
||||
for (int i = 0; i < input_count; i += 1) {
|
||||
|
|
|
@ -143,13 +143,15 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
}
|
||||
|
||||
static int usage(char *exe) {
|
||||
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio]\n", exe);
|
||||
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio] [--in-device name] [--out-device name]\n", exe);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *exe = argv[0];
|
||||
enum SoundIoBackend backend = SoundIoBackendNone;
|
||||
char *in_device_name = NULL;
|
||||
char *out_device_name = NULL;
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
char *arg = argv[i];
|
||||
if (strcmp("--dummy", arg) == 0) {
|
||||
|
@ -158,6 +160,18 @@ int main(int argc, char **argv) {
|
|||
backend = SoundIoBackendAlsa;
|
||||
} else if (strcmp("--pulseaudio", arg) == 0) {
|
||||
backend = SoundIoBackendPulseAudio;
|
||||
} else if (strcmp("--in-device", arg) == 0) {
|
||||
if (++i >= argc) {
|
||||
return usage(exe);
|
||||
} else {
|
||||
in_device_name = argv[i];
|
||||
}
|
||||
} else if (strcmp("--out-device", arg) == 0) {
|
||||
if (++i >= argc) {
|
||||
return usage(exe);
|
||||
} else {
|
||||
out_device_name = argv[i];
|
||||
}
|
||||
} else {
|
||||
return usage(exe);
|
||||
}
|
||||
|
@ -169,19 +183,53 @@ int main(int argc, char **argv) {
|
|||
int err = (backend == SoundIoBackendNone) ?
|
||||
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
|
||||
|
||||
int default_out_device_index = soundio_get_default_output_device_index(soundio);
|
||||
int default_out_device_index = soundio_default_output_device_index(soundio);
|
||||
if (default_out_device_index < 0)
|
||||
panic("no output device found");
|
||||
|
||||
int default_in_device_index = soundio_get_default_input_device_index(soundio);
|
||||
int default_in_device_index = soundio_default_input_device_index(soundio);
|
||||
if (default_in_device_index < 0)
|
||||
panic("no output device found");
|
||||
|
||||
struct SoundIoDevice *out_device = soundio_get_output_device(soundio, default_out_device_index);
|
||||
int in_device_index = default_in_device_index;
|
||||
if (in_device_name) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < soundio_input_device_count(soundio); i += 1) {
|
||||
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
|
||||
if (strcmp(device->name, in_device_name) == 0) {
|
||||
in_device_index = i;
|
||||
found = true;
|
||||
soundio_device_unref(device);
|
||||
break;
|
||||
}
|
||||
soundio_device_unref(device);
|
||||
}
|
||||
if (!found)
|
||||
panic("invalid input device name: %s", in_device_name);
|
||||
}
|
||||
|
||||
int out_device_index = default_out_device_index;
|
||||
if (out_device_name) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < soundio_output_device_count(soundio); i += 1) {
|
||||
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
|
||||
if (strcmp(device->name, out_device_name) == 0) {
|
||||
out_device_index = i;
|
||||
found = true;
|
||||
soundio_device_unref(device);
|
||||
break;
|
||||
}
|
||||
soundio_device_unref(device);
|
||||
}
|
||||
if (!found)
|
||||
panic("invalid output device name: %s", out_device_name);
|
||||
}
|
||||
|
||||
struct SoundIoDevice *out_device = soundio_get_output_device(soundio, out_device_index);
|
||||
if (!out_device)
|
||||
panic("could not get output device: out of memory");
|
||||
|
||||
struct SoundIoDevice *in_device = soundio_get_input_device(soundio, default_in_device_index);
|
||||
struct SoundIoDevice *in_device = soundio_get_input_device(soundio, in_device_index);
|
||||
if (!in_device)
|
||||
panic("could not get input device: out of memory");
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ int main(int argc, char **argv) {
|
|||
if (err)
|
||||
panic("error connecting: %s", soundio_strerror(err));
|
||||
|
||||
int default_out_device_index = soundio_get_default_output_device_index(soundio);
|
||||
int default_out_device_index = soundio_default_output_device_index(soundio);
|
||||
if (default_out_device_index < 0)
|
||||
panic("no output device found");
|
||||
|
||||
|
|
396
src/alsa.cpp
396
src/alsa.cpp
|
@ -58,6 +58,19 @@ struct SoundIoOutStreamAlsa {
|
|||
|
||||
struct SoundIoInStreamAlsa {
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_chmap_t *chmap;
|
||||
int chmap_size;
|
||||
snd_pcm_uframes_t offset;
|
||||
snd_pcm_access_t access;
|
||||
int sample_buffer_size;
|
||||
char *sample_buffer;
|
||||
int poll_fd_count;
|
||||
struct pollfd *poll_fds;
|
||||
SoundIoOsThread *thread;
|
||||
atomic_flag thread_exit_flag;
|
||||
int period_size;
|
||||
int read_frame_count;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static void wakeup_device_poll(SoundIoAlsa *sia) {
|
||||
|
@ -601,13 +614,13 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
if (stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
device->purpose = SoundIoDevicePurposeOutput;
|
||||
device_list = &devices_info->output_devices;
|
||||
if (str_has_prefix(name, "default:"))
|
||||
if (devices_info->default_output_index < 0 && str_has_prefix(name, "default:"))
|
||||
devices_info->default_output_index = device_list->length;
|
||||
} else {
|
||||
assert(stream == SND_PCM_STREAM_CAPTURE);
|
||||
device->purpose = SoundIoDevicePurposeInput;
|
||||
device_list = &devices_info->input_devices;
|
||||
if (str_has_prefix(name, "default:"))
|
||||
if (devices_info->default_input_index < 0 && str_has_prefix(name, "default:"))
|
||||
devices_info->default_input_index = device_list->length;
|
||||
}
|
||||
|
||||
|
@ -933,6 +946,21 @@ static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
|
|||
return err;
|
||||
}
|
||||
|
||||
static int instream_xrun_recovery(SoundIoInStreamPrivate *is, int err) {
|
||||
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *)is->backend_data;
|
||||
if (err == -EPIPE) {
|
||||
err = snd_pcm_prepare(isa->handle);
|
||||
} else if (err == -ESTRPIPE) {
|
||||
while ((err = snd_pcm_resume(isa->handle)) == -EAGAIN) {
|
||||
// wait until suspend flag is released
|
||||
poll(nullptr, 0, 1);
|
||||
}
|
||||
if (err < 0)
|
||||
err = snd_pcm_prepare(isa->handle);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int wait_for_poll(SoundIoOutStreamAlsa *osa) {
|
||||
int err;
|
||||
unsigned short revents;
|
||||
|
@ -951,6 +979,24 @@ static int wait_for_poll(SoundIoOutStreamAlsa *osa) {
|
|||
}
|
||||
}
|
||||
|
||||
static int instream_wait_for_poll(SoundIoInStreamAlsa *isa) {
|
||||
int err;
|
||||
unsigned short revents;
|
||||
for (;;) {
|
||||
if ((err = poll(isa->poll_fds, isa->poll_fd_count, -1)) < 0)
|
||||
return err;
|
||||
if ((err = snd_pcm_poll_descriptors_revents(isa->handle,
|
||||
isa->poll_fds, isa->poll_fd_count, &revents)) < 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
if (revents & POLLERR)
|
||||
return -EIO;
|
||||
if (revents & POLLIN)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void outstream_thread_run(void *arg) {
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) arg;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
|
@ -959,14 +1005,6 @@ void outstream_thread_run(void *arg) {
|
|||
int err;
|
||||
|
||||
for (;;) {
|
||||
if ((err = wait_for_poll(osa)) < 0) {
|
||||
if (!osa->thread_exit_flag.test_and_set())
|
||||
return;
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
if (!osa->thread_exit_flag.test_and_set())
|
||||
return;
|
||||
snd_pcm_state_t state = snd_pcm_state(osa->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_OPEN:
|
||||
|
@ -985,6 +1023,15 @@ void outstream_thread_run(void *arg) {
|
|||
continue;
|
||||
case SND_PCM_STATE_RUNNING:
|
||||
{
|
||||
if ((err = wait_for_poll(osa)) < 0) {
|
||||
if (!osa->thread_exit_flag.test_and_set())
|
||||
return;
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
if (!osa->thread_exit_flag.test_and_set())
|
||||
return;
|
||||
|
||||
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
|
||||
if (avail < 0) {
|
||||
if ((err = xrun_recovery(os, avail)) < 0) {
|
||||
|
@ -1019,6 +1066,75 @@ void outstream_thread_run(void *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
static void instream_thread_run(void *arg) {
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *) arg;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamAlsa *osa = (SoundIoInStreamAlsa *) is->backend_data;
|
||||
|
||||
int err;
|
||||
|
||||
for (;;) {
|
||||
snd_pcm_state_t state = snd_pcm_state(osa->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_OPEN:
|
||||
soundio_panic("TODO open");
|
||||
case SND_PCM_STATE_SETUP:
|
||||
if ((err = snd_pcm_prepare(osa->handle)) < 0) {
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
if ((err = snd_pcm_start(osa->handle)) < 0) {
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
case SND_PCM_STATE_RUNNING:
|
||||
{
|
||||
if ((err = instream_wait_for_poll(osa)) < 0) {
|
||||
if (!osa->thread_exit_flag.test_and_set())
|
||||
return;
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
if (!osa->thread_exit_flag.test_and_set())
|
||||
return;
|
||||
|
||||
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
|
||||
if (avail < 0) {
|
||||
if ((err = instream_xrun_recovery(is, avail)) < 0) {
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
instream->read_callback(instream, avail);
|
||||
continue;
|
||||
}
|
||||
case SND_PCM_STATE_XRUN:
|
||||
if ((err = instream_xrun_recovery(is, -EPIPE)) < 0) {
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
case SND_PCM_STATE_DRAINING:
|
||||
soundio_panic("TODO draining");
|
||||
case SND_PCM_STATE_PAUSED:
|
||||
soundio_panic("TODO paused");
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
if ((err = instream_xrun_recovery(is, -ESTRPIPE)) < 0) {
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
case SND_PCM_STATE_DISCONNECTED:
|
||||
soundio_panic("TODO disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoDevice *device = outstream->device;
|
||||
|
@ -1293,27 +1409,271 @@ static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStre
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
// TODO default buffer_duration and period_duration
|
||||
soundio_panic("TODO");
|
||||
static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
|
||||
if (!isa)
|
||||
return;
|
||||
|
||||
deallocate(isa->chmap, isa->chmap_size);
|
||||
// TODO
|
||||
|
||||
destroy(isa);
|
||||
is->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoDevice *device = instream->device;
|
||||
|
||||
if (instream->buffer_duration == 0.0)
|
||||
instream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
||||
if (instream->period_duration == 0.0) {
|
||||
instream->period_duration = clamp(device->period_duration_min,
|
||||
instream->buffer_duration / 8.0, device->period_duration_max);
|
||||
}
|
||||
|
||||
SoundIoInStreamAlsa *isa = create<SoundIoInStreamAlsa>();
|
||||
if (!isa) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
is->backend_data = isa;
|
||||
|
||||
int ch_count = instream->layout.channel_count;
|
||||
|
||||
isa->chmap_size = sizeof(int) + sizeof(int) * ch_count;
|
||||
isa->chmap = (snd_pcm_chmap_t *)allocate<char>(isa->chmap_size);
|
||||
if (!isa->chmap) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
int err;
|
||||
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
|
||||
snd_pcm_stream_t stream = purpose_to_stream(instream->device->purpose);
|
||||
|
||||
if ((err = snd_pcm_open(&isa->handle, instream->device->name, stream, 0)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_any(isa->handle, hwparams)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
int want_resample = !instream->device->is_raw;
|
||||
if ((err = snd_pcm_hw_params_set_rate_resample(isa->handle, hwparams, want_resample)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = set_access(isa->handle, hwparams, &isa->access))) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_channels(isa->handle, hwparams, ch_count)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_rate(isa->handle, hwparams, instream->sample_rate, 0)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
snd_pcm_format_t format = to_alsa_fmt(instream->format);
|
||||
int phys_bits_per_sample = snd_pcm_format_physical_width(format);
|
||||
if (phys_bits_per_sample % 8 != 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorIncompatibleDevice;
|
||||
}
|
||||
int phys_bytes_per_sample = phys_bits_per_sample / 8;
|
||||
if ((err = snd_pcm_hw_params_set_format(isa->handle, hwparams, format)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
snd_pcm_uframes_t period_frames = ceil(instream->period_duration * (double)instream->sample_rate);
|
||||
if ((err = snd_pcm_hw_params_set_period_size_near(isa->handle, hwparams, &period_frames, nullptr)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
instream->period_duration = ((double)period_frames) / (double)instream->sample_rate;
|
||||
|
||||
|
||||
snd_pcm_uframes_t buffer_size_frames = ceil(instream->buffer_duration * (double)instream->sample_rate);
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_buffer_size_near(isa->handle, hwparams, &buffer_size_frames)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
instream->buffer_duration = ((double)buffer_size_frames) / (double)instream->sample_rate;
|
||||
|
||||
snd_pcm_uframes_t period_size;
|
||||
if ((snd_pcm_hw_params_get_period_size(hwparams, &period_size, nullptr)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
isa->period_size = period_size;
|
||||
|
||||
// write the hardware parameters to device
|
||||
if ((err = snd_pcm_hw_params(isa->handle, hwparams)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
// set channel map
|
||||
isa->chmap->channels = ch_count;
|
||||
for (int i = 0; i < ch_count; i += 1) {
|
||||
isa->chmap->pos[i] = to_alsa_chmap_pos(instream->layout.channels[i]);
|
||||
}
|
||||
if ((err = snd_pcm_set_chmap(isa->handle, isa->chmap)) < 0)
|
||||
instream->layout_error = SoundIoErrorIncompatibleDevice;
|
||||
|
||||
// get current swparams
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
if ((err = snd_pcm_sw_params_current(isa->handle, swparams)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
// write the software parameters to device
|
||||
if ((err = snd_pcm_sw_params(isa->handle, swparams)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED || isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||
isa->sample_buffer_size = ch_count * isa->period_size * phys_bytes_per_sample;
|
||||
isa->sample_buffer = allocate_nonzero<char>(isa->sample_buffer_size);
|
||||
if (!isa->sample_buffer) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
}
|
||||
|
||||
isa->poll_fd_count = snd_pcm_poll_descriptors_count(isa->handle);
|
||||
if (isa->poll_fd_count <= 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
isa->poll_fds = allocate<struct pollfd>(isa->poll_fd_count);
|
||||
if (!isa->poll_fds) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_poll_descriptors(isa->handle, isa->poll_fds, isa->poll_fd_count)) < 0) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
|
||||
|
||||
assert(!isa->thread);
|
||||
|
||||
isa->thread_exit_flag.test_and_set();
|
||||
int err;
|
||||
if ((err = soundio_os_thread_create(instream_thread_run, is, true, &isa->thread))) {
|
||||
instream_destroy_alsa(si, is);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instream_begin_read_alsa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
*out_areas = nullptr;
|
||||
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
|
||||
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||
isa->areas[ch].ptr = isa->sample_buffer + ch * instream->bytes_per_sample;
|
||||
isa->areas[ch].step = instream->bytes_per_frame;
|
||||
}
|
||||
|
||||
isa->read_frame_count = min(*frame_count, isa->period_size);
|
||||
*frame_count = isa->read_frame_count;
|
||||
snd_pcm_sframes_t commitres = snd_pcm_readi(isa->handle, isa->sample_buffer, isa->read_frame_count);
|
||||
if (commitres < 0 || commitres != *frame_count) {
|
||||
int err = (commitres >= 0) ? -EPIPE : commitres;
|
||||
if ((err = instream_xrun_recovery(is, err)) < 0)
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
} else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||
char *ptrs[SOUNDIO_MAX_CHANNELS];
|
||||
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||
isa->areas[ch].ptr = isa->sample_buffer + ch * instream->bytes_per_sample * isa->period_size;
|
||||
isa->areas[ch].step = instream->bytes_per_sample;
|
||||
ptrs[ch] = isa->areas[ch].ptr;
|
||||
}
|
||||
|
||||
isa->read_frame_count = min(*frame_count, isa->period_size);
|
||||
*frame_count = isa->read_frame_count;
|
||||
snd_pcm_sframes_t commitres = snd_pcm_readn(isa->handle, (void**)ptrs, isa->read_frame_count);
|
||||
if (commitres < 0 || commitres != *frame_count) {
|
||||
int err = (commitres >= 0) ? -EPIPE : commitres;
|
||||
if ((err = instream_xrun_recovery(is, err)) < 0)
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
} else {
|
||||
const snd_pcm_channel_area_t *areas;
|
||||
snd_pcm_uframes_t frames = *frame_count;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_mmap_begin(isa->handle, &areas, &isa->offset, &frames)) < 0) {
|
||||
if ((err = instream_xrun_recovery(is, err)) < 0)
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
|
||||
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||
if ((areas[ch].first % 8 != 0) || (areas[ch].step % 8 != 0))
|
||||
return SoundIoErrorIncompatibleDevice;
|
||||
isa->areas[ch].step = areas[ch].step / 8;
|
||||
isa->areas[ch].ptr = ((char *)areas[ch].addr) + (areas[ch].first / 8) +
|
||||
(isa->areas[ch].step * isa->offset);
|
||||
}
|
||||
|
||||
isa->read_frame_count = frames;
|
||||
*frame_count = isa->read_frame_count;
|
||||
}
|
||||
|
||||
*out_areas = isa->areas;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
|
||||
|
||||
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
return 0;
|
||||
} else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||
return 0;
|
||||
} else {
|
||||
snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(isa->handle, isa->offset, isa->read_frame_count);
|
||||
if (commitres < 0 || commitres != isa->read_frame_count) {
|
||||
int err = (commitres >= 0) ? -EPIPE : commitres;
|
||||
if ((err = instream_xrun_recovery(is, err)) < 0)
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
||||
|
|
|
@ -231,7 +231,7 @@ void soundio_flush_events(struct SoundIo *soundio) {
|
|||
si->flush_events(si);
|
||||
}
|
||||
|
||||
int soundio_get_input_device_count(struct SoundIo *soundio) {
|
||||
int soundio_input_device_count(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
soundio_flush_events(soundio);
|
||||
|
@ -239,7 +239,7 @@ int soundio_get_input_device_count(struct SoundIo *soundio) {
|
|||
return si->safe_devices_info->input_devices.length;
|
||||
}
|
||||
|
||||
int soundio_get_output_device_count(struct SoundIo *soundio) {
|
||||
int soundio_output_device_count(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
soundio_flush_events(soundio);
|
||||
|
@ -247,7 +247,7 @@ int soundio_get_output_device_count(struct SoundIo *soundio) {
|
|||
return si->safe_devices_info->output_devices.length;
|
||||
}
|
||||
|
||||
int soundio_get_default_input_device_index(struct SoundIo *soundio) {
|
||||
int soundio_default_input_device_index(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
soundio_flush_events(soundio);
|
||||
|
@ -255,7 +255,7 @@ int soundio_get_default_input_device_index(struct SoundIo *soundio) {
|
|||
return si->safe_devices_info->default_input_index;
|
||||
}
|
||||
|
||||
int soundio_get_default_output_device_index(struct SoundIo *soundio) {
|
||||
int soundio_default_output_device_index(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
soundio_flush_events(soundio);
|
||||
|
|
|
@ -407,6 +407,10 @@ struct SoundIoInStream {
|
|||
// computed automatically when you call soundio_instream_open
|
||||
int bytes_per_frame;
|
||||
int bytes_per_sample;
|
||||
|
||||
// If setting the channel layout fails for some reason, this field is set
|
||||
// to an error code. Possible error codes are: SoundIoErrorIncompatibleDevice
|
||||
int layout_error;
|
||||
};
|
||||
|
||||
// Main Context
|
||||
|
@ -503,23 +507,23 @@ const char * soundio_format_string(enum SoundIoFormat format);
|
|||
|
||||
// Devices
|
||||
|
||||
int soundio_get_input_device_count(struct SoundIo *soundio);
|
||||
int soundio_get_output_device_count(struct SoundIo *soundio);
|
||||
int soundio_input_device_count(struct SoundIo *soundio);
|
||||
int soundio_output_device_count(struct SoundIo *soundio);
|
||||
|
||||
// Always returns a device. Call soundio_device_unref when done.
|
||||
// `index` must be 0 <= index < soundio_get_input_device_count
|
||||
// `index` must be 0 <= index < soundio_input_device_count
|
||||
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index);
|
||||
// Always returns a device. Call soundio_device_unref when done.
|
||||
// `index` must be 0 <= index < soundio_get_output_device_count
|
||||
// `index` must be 0 <= index < soundio_output_device_count
|
||||
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index);
|
||||
|
||||
// returns the index of the default input device
|
||||
// returns -1 if there are no devices.
|
||||
int soundio_get_default_input_device_index(struct SoundIo *soundio);
|
||||
int soundio_default_input_device_index(struct SoundIo *soundio);
|
||||
|
||||
// returns the index of the default output device
|
||||
// returns -1 if there are no devices.
|
||||
int soundio_get_default_output_device_index(struct SoundIo *soundio);
|
||||
int soundio_default_output_device_index(struct SoundIo *soundio);
|
||||
|
||||
void soundio_device_ref(struct SoundIoDevice *device);
|
||||
void soundio_device_unref(struct SoundIoDevice *device);
|
||||
|
|
|
@ -30,7 +30,7 @@ static void test_create_outstream(void) {
|
|||
struct SoundIo *soundio = soundio_create();
|
||||
assert(soundio);
|
||||
ok_or_panic(soundio_connect(soundio));
|
||||
int default_out_device_index = soundio_get_default_output_device_index(soundio);
|
||||
int default_out_device_index = soundio_default_output_device_index(soundio);
|
||||
assert(default_out_device_index >= 0);
|
||||
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
|
||||
assert(device);
|
||||
|
|
Loading…
Reference in a new issue