beginning of ALSA support - list_devices works

This commit is contained in:
Andrew Kelley 2015-07-07 02:55:32 -07:00
parent d6fcef468b
commit fe61322b23
13 changed files with 720 additions and 78 deletions

View file

@ -79,6 +79,14 @@ if(SOUNDIO_HAVE_PULSEAUDIO)
"${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp"
)
endif()
if(SOUNDIO_HAVE_ALSA)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${CMAKE_SOURCE_DIR}/src/alsa.cpp"
)
set(TEST_SOURCES ${TEST_SOURCES}
"${CMAKE_SOURCE_DIR}/src/alsa.cpp"
)
endif()
# GTFO, -lstdc++ !!

View file

@ -115,21 +115,28 @@ view `coverage/index.html` in a browser.
## Roadmap
0. implement ALSA (Linux) backend, get examples working
0. pipe record to playback example working with dummy linux, osx, windows
0. pipe record to playback example working with pulseaudio linux
0. implement CoreAudio (OSX) backend, get examples working
0. implement DirectSound (Windows) backend, get examples working
0. implement ALSA (Linux) backend, get examples working
0. implement JACK backend, get examples working
0. Avoid calling `panic` in PulseAudio.
0. implement ASIO (Windows) backend, get examples working
0. clean up API and improve documentation
- make sure every function which can return an error documents which errors
it can return
- consider doing the public/private struct thing and make `backend_data` a
union instead of a `void *`
0. use a documentation generator and host the docs somewhere
0. -fvisibility=hidden and then explicitly export stuff
0. Integrate into libgroove and test with Groove Basin
0. Consider testing on FreeBSD
0. look at microphone example and determine if fewer memcpys can be done
with the audio data
- pulseaudio has peek() drop() which sucks, but what if libsoundio lets you
specify how much to peek() and if you don't peek all of it, save the
unused to a buffer for you.
## Planned Uses for libsoundio

View file

@ -52,7 +52,7 @@ int main(int argc, char **argv) {
if (default_out_device_index < 0)
panic("no output device found");
int default_in_device_index = soundio_get_default_output_device_index(soundio);
int default_in_device_index = soundio_get_default_input_device_index(soundio);
if (default_in_device_index < 0)
panic("no output device found");
@ -64,12 +64,8 @@ int main(int argc, char **argv) {
if (!in_device)
panic("could not get input device: out of memory");
fprintf(stderr, "Input device: %s: %s\n",
soundio_device_name(in_device),
soundio_device_description(in_device));
fprintf(stderr, "Output device: %s: %s\n",
soundio_device_name(out_device),
soundio_device_description(out_device));
fprintf(stderr, "Input device: %s\n", soundio_device_description(in_device));
fprintf(stderr, "Output device: %s\n", soundio_device_description(out_device));
const struct SoundIoChannelLayout *in_layout = soundio_device_channel_layout(in_device);
const struct SoundIoChannelLayout *out_layout = soundio_device_channel_layout(out_device);

511
src/alsa.cpp Normal file
View file

@ -0,0 +1,511 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "alsa.hpp"
#include "soundio.hpp"
#include "os.hpp"
#include "atomics.hpp"
#include <alsa/asoundlib.h>
#include <sys/inotify.h>
static snd_pcm_stream_t stream_types[] = {SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE};
struct SoundIoAlsa {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
struct SoundIoOsThread *thread;
atomic_flag abort_flag;
int notify_fd;
int notify_wd;
atomic_bool have_devices_flag;
int notify_pipe_fd[2];
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
};
static void wakeup_device_poll(SoundIoAlsa *sia) {
ssize_t amt = write(sia->notify_pipe_fd[1], "a", 1);
if (amt == -1) {
assert(errno != EBADF);
assert(errno != EIO);
assert(errno != ENOSPC);
assert(errno != EPERM);
assert(errno != EPIPE);
}
}
static void destroy_alsa(SoundIo *soundio) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
if (!sia)
return;
if (sia->thread) {
sia->abort_flag.clear();
wakeup_device_poll(sia);
soundio_os_thread_destroy(sia->thread);
}
if (sia->cond)
soundio_os_cond_destroy(sia->cond);
if (sia->mutex)
soundio_os_mutex_destroy(sia->mutex);
soundio_destroy_devices_info(sia->ready_devices_info);
close(sia->notify_pipe_fd[0]);
close(sia->notify_pipe_fd[1]);
close(sia->notify_fd);
destroy(sia);
soundio->backend_data = nullptr;
}
static int refresh_devices(SoundIo *soundio) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
int card_index = -1;
if (snd_card_next(&card_index) < 0)
return SoundIoErrorSystemResources;
snd_ctl_card_info_t *card_info;
snd_ctl_card_info_alloca(&card_info);
snd_pcm_info_t *pcm_info;
snd_pcm_info_alloca(&pcm_info);
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
if (!devices_info)
return SoundIoErrorNoMem;
while (card_index >= 0) {
int err;
snd_ctl_t *handle;
char name[32];
sprintf(name, "hw:%d", card_index);
if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
if (err == -ENOENT) {
break;
} else {
destroy(devices_info);
return SoundIoErrorOpeningDevice;
}
}
if ((err = snd_ctl_card_info(handle, card_info)) < 0) {
snd_ctl_close(handle);
destroy(devices_info);
return SoundIoErrorSystemResources;
}
const char *card_name = snd_ctl_card_info_get_name(card_info);
int device_index = -1;
for (;;) {
if (snd_ctl_pcm_next_device(handle, &device_index) < 0) {
snd_ctl_close(handle);
destroy(devices_info);
return SoundIoErrorSystemResources;
}
if (device_index < 0)
break;
snd_pcm_info_set_device(pcm_info, device_index);
snd_pcm_info_set_subdevice(pcm_info, 0);
for (int stream_type_i = 0; stream_type_i < array_length(stream_types); stream_type_i += 1) {
snd_pcm_stream_t stream = stream_types[stream_type_i];
snd_pcm_info_set_stream(pcm_info, stream);
if ((err = snd_ctl_pcm_info(handle, pcm_info)) < 0) {
if (err == -ENOENT) {
continue;
} else {
snd_ctl_close(handle);
destroy(devices_info);
return SoundIoErrorSystemResources;
}
}
const char *device_name = snd_pcm_info_get_name(pcm_info);
SoundIoDevice *device = create<SoundIoDevice>();
if (!device) {
snd_ctl_close(handle);
destroy(devices_info);
return SoundIoErrorNoMem;
}
device->ref_count = 1;
device->soundio = soundio;
device->name = soundio_alloc_sprintf(nullptr, "hw:%d,%d,%d", card_index, device_index, 0);
device->description = soundio_alloc_sprintf(nullptr, "%s %s", card_name, device_name);
if (!device->name || !device->description) {
soundio_device_unref(device);
snd_ctl_close(handle);
destroy(devices_info);
return SoundIoErrorNoMem;
}
// TODO: device->channel_layout
// TODO: device->default_sample_format
// TODO: device->default_latency
// TODO: device->default_sample_rate
SoundIoList<SoundIoDevice *> *device_list;
if (stream == SND_PCM_STREAM_PLAYBACK) {
device->purpose = SoundIoDevicePurposeOutput;
device_list = &devices_info->output_devices;
} else {
assert(stream == SND_PCM_STREAM_CAPTURE);
device->purpose = SoundIoDevicePurposeInput;
device_list = &devices_info->input_devices;
}
if (device_list->append(device)) {
soundio_device_unref(device);
destroy(devices_info);
return SoundIoErrorNoMem;
}
}
}
snd_ctl_close(handle);
if (snd_card_next(&card_index) < 0) {
destroy(devices_info);
return SoundIoErrorSystemResources;
}
}
soundio_os_mutex_lock(sia->mutex);
soundio_destroy_devices_info(sia->ready_devices_info);
sia->ready_devices_info = devices_info;
sia->have_devices_flag.store(true);
soundio_os_cond_signal(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex);
return 0;
}
static void device_thread_run(void *arg) {
SoundIo *soundio = (SoundIo *)arg;
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
// Some systems cannot read integer variables if they are not
// properly aligned. On other systems, incorrect alignment may
// decrease performance. Hence, the buffer used for reading from
// the inotify file descriptor should have the same alignment as
// struct inotify_event.
char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
const struct inotify_event *event;
struct pollfd fds[2];
fds[0].fd = sia->notify_fd;
fds[0].events = POLLIN;
fds[1].fd = sia->notify_pipe_fd[0];
fds[1].events = POLLIN;
int err;
for (;;) {
int poll_num = poll(fds, 2, -1);
if (!sia->abort_flag.test_and_set())
break;
if (poll_num == -1) {
if (errno == EINTR)
continue;
assert(errno != EFAULT);
assert(errno != EFAULT);
assert(errno != EINVAL);
assert(errno == ENOMEM);
soundio_panic("kernel ran out of polling memory");
}
if (poll_num <= 0)
continue;
bool got_rescan_event = false;
if (fds[0].revents & POLLIN) {
for (;;) {
ssize_t len = read(sia->notify_fd, buf, sizeof(buf));
if (len == -1) {
assert(errno != EBADF);
assert(errno != EFAULT);
assert(errno != EINVAL);
assert(errno != EIO);
assert(errno != EISDIR);
}
// catches EINTR and EAGAIN
if (len <= 0)
break;
// loop over all events in the buffer
for (char *ptr = buf; ptr < buf + len; ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *) ptr;
if (!((event->mask & IN_CREATE) || (event->mask & IN_DELETE)))
continue;
if (event->mask & IN_ISDIR)
continue;
if (!event->len || event->len < 8)
continue;
if (event->name[0] != 'p' ||
event->name[1] != 'c' ||
event->name[2] != 'm')
{
continue;
}
got_rescan_event = true;
break;
}
}
}
if (fds[1].revents & POLLIN) {
got_rescan_event = true;
for (;;) {
ssize_t len = read(sia->notify_pipe_fd[0], buf, sizeof(buf));
if (len == -1) {
assert(errno != EBADF);
assert(errno != EFAULT);
assert(errno != EINVAL);
assert(errno != EIO);
assert(errno != EISDIR);
}
if (len <= 0)
break;
}
}
if (got_rescan_event) {
if ((err = refresh_devices(soundio)))
soundio_panic("error refreshing devices: %s", soundio_error_string(err));
}
}
}
static void block_until_have_devices(SoundIoAlsa *sia) {
if (sia->have_devices_flag.load())
return;
soundio_os_mutex_lock(sia->mutex);
while (!sia->have_devices_flag.load())
soundio_os_cond_wait(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex);
}
static void flush_events(SoundIo *soundio) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
block_until_have_devices(sia);
bool change = false;
SoundIoDevicesInfo *old_devices_info = nullptr;
soundio_os_mutex_lock(sia->mutex);
if (sia->ready_devices_info) {
old_devices_info = soundio->safe_devices_info;
soundio->safe_devices_info = sia->ready_devices_info;
sia->ready_devices_info = nullptr;
change = true;
}
soundio_os_mutex_unlock(sia->mutex);
if (change)
soundio->on_devices_change(soundio);
soundio_destroy_devices_info(old_devices_info);
}
static void wait_events(SoundIo *soundio) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
flush_events(soundio);
soundio_os_mutex_lock(sia->mutex);
soundio_os_cond_wait(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex);
}
static void wakeup(SoundIo *soundio) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
soundio_os_mutex_lock(sia->mutex);
soundio_os_cond_signal(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex);
}
static void output_device_destroy_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
soundio_panic("TODO");
}
static int output_device_init_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
soundio_panic("TODO");
}
static int output_device_start_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
soundio_panic("TODO");
}
static int output_device_free_count_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
soundio_panic("TODO");
}
static void output_device_begin_write_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device, char **data, int *frame_count)
{
soundio_panic("TODO");
}
static void output_device_write_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device, char *data, int frame_count)
{
soundio_panic("TODO");
}
static void output_device_clear_buffer_alsa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
soundio_panic("TODO");
}
static int input_device_init_alsa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
}
static void input_device_destroy_alsa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
}
static int input_device_start_alsa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
}
static void input_device_peek_alsa(SoundIo *soundio,
SoundIoInputDevice *input_device, const char **data, int *frame_count)
{
soundio_panic("TODO");
}
static void input_device_drop_alsa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
}
static void input_device_clear_buffer_alsa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
}
int soundio_alsa_init(SoundIo *soundio) {
int err;
assert(!soundio->backend_data);
SoundIoAlsa *sia = create<SoundIoAlsa>();
if (!sia) {
destroy_alsa(soundio);
return SoundIoErrorNoMem;
}
soundio->backend_data = sia;
sia->notify_fd = -1;
sia->notify_wd = -1;
sia->have_devices_flag.store(false);
sia->abort_flag.test_and_set();
sia->mutex = soundio_os_mutex_create();
if (!sia->mutex) {
destroy_alsa(soundio);
return SoundIoErrorNoMem;
}
sia->cond = soundio_os_cond_create();
if (!sia->cond) {
destroy_alsa(soundio);
return SoundIoErrorNoMem;
}
// set up inotify to watch /dev/snd for devices added or removed
sia->notify_fd = inotify_init1(IN_NONBLOCK);
if (sia->notify_fd == -1) {
err = errno;
assert(err != EINVAL);
destroy_alsa(soundio);
if (err == EMFILE || err == ENFILE) {
return SoundIoErrorSystemResources;
} else {
assert(err == ENOMEM);
return SoundIoErrorNoMem;
}
}
sia->notify_wd = inotify_add_watch(sia->notify_fd, "/dev/snd", IN_CREATE | IN_DELETE);
if (sia->notify_wd == -1) {
err = errno;
assert(err != EACCES);
assert(err != EBADF);
assert(err != EFAULT);
assert(err != EINVAL);
assert(err != ENAMETOOLONG);
assert(err != ENOENT);
destroy_alsa(soundio);
if (err == ENOSPC) {
return SoundIoErrorSystemResources;
} else {
assert(err == ENOMEM);
return SoundIoErrorNoMem;
}
}
if (pipe2(sia->notify_pipe_fd, O_NONBLOCK)) {
assert(errno != EFAULT);
assert(errno != EINVAL);
assert(errno == EMFILE || errno == ENFILE);
return SoundIoErrorSystemResources;
}
wakeup_device_poll(sia);
if ((err = soundio_os_thread_create(device_thread_run, soundio, false, &sia->thread))) {
destroy_alsa(soundio);
return err;
}
soundio->destroy = destroy_alsa;
soundio->flush_events = flush_events;
soundio->wait_events = wait_events;
soundio->wakeup = wakeup;
soundio->output_device_init = output_device_init_alsa;
soundio->output_device_destroy = output_device_destroy_alsa;
soundio->output_device_start = output_device_start_alsa;
soundio->output_device_free_count = output_device_free_count_alsa;
soundio->output_device_begin_write = output_device_begin_write_alsa;
soundio->output_device_write = output_device_write_alsa;
soundio->output_device_clear_buffer = output_device_clear_buffer_alsa;
soundio->input_device_init = input_device_init_alsa;
soundio->input_device_destroy = input_device_destroy_alsa;
soundio->input_device_start = input_device_start_alsa;
soundio->input_device_peek = input_device_peek_alsa;
soundio->input_device_drop = input_device_drop_alsa;
soundio->input_device_clear_buffer = input_device_clear_buffer_alsa;
return 0;
}

14
src/alsa.hpp Normal file
View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_ALSA_HPP
#define SOUNDIO_ALSA_HPP
int soundio_alsa_init(struct SoundIo *soundio);
#endif

View file

@ -108,8 +108,6 @@ static void wakeup(SoundIo *soundio) {
soundio_os_cond_signal(sid->cond, nullptr);
}
static void refresh_devices(SoundIo *soundio) { }
static void output_device_destroy_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
@ -216,39 +214,37 @@ static void output_device_clear_buffer_dummy(SoundIo *soundio,
static int input_device_init_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
return 0;
soundio_panic("TODO");
}
static void input_device_destroy_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
soundio_panic("TODO");
}
static int input_device_start_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
return 0;
soundio_panic("TODO");
}
static void input_device_peek_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device, const char **data, int *frame_count)
{
// TODO
soundio_panic("TODO");
}
static void input_device_drop_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
soundio_panic("TODO");
}
static void input_device_clear_buffer_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
soundio_panic("TODO");
}
int soundio_dummy_init(SoundIo *soundio) {
@ -326,8 +322,7 @@ int soundio_dummy_init(SoundIo *soundio) {
device->name = strdup("dummy-in");
device->description = strdup("Dummy input device");
if (!device->name || !device->description) {
free(device->name);
free(device->description);
soundio_device_unref(device);
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
@ -349,7 +344,6 @@ int soundio_dummy_init(SoundIo *soundio) {
soundio->flush_events = flush_events;
soundio->wait_events = wait_events;
soundio->wakeup = wakeup;
soundio->refresh_devices = refresh_devices;
soundio->output_device_init = output_device_init_dummy;
soundio->output_device_destroy = output_device_destroy_dummy;

View file

@ -1,3 +1,10 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "pulseaudio.hpp"
#include "soundio.hpp"
#include "atomics.hpp"
@ -100,32 +107,6 @@ static void context_state_callback(pa_context *context, void *userdata) {
}
}
static void destroy_current_devices_info(SoundIo *soundio) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
if (sipa->current_devices_info) {
for (int i = 0; i < sipa->current_devices_info->input_devices.length; i += 1)
soundio_device_unref(sipa->current_devices_info->input_devices.at(i));
for (int i = 0; i < sipa->current_devices_info->output_devices.length; i += 1)
soundio_device_unref(sipa->current_devices_info->output_devices.at(i));
destroy(sipa->current_devices_info);
sipa->current_devices_info = nullptr;
}
}
static void destroy_ready_devices_info(SoundIo *soundio) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
if (sipa->ready_devices_info) {
for (int i = 0; i < sipa->ready_devices_info->input_devices.length; i += 1)
soundio_device_unref(sipa->ready_devices_info->input_devices.at(i));
for (int i = 0; i < sipa->ready_devices_info->output_devices.length; i += 1)
soundio_device_unref(sipa->ready_devices_info->output_devices.at(i));
destroy(sipa->ready_devices_info);
sipa->ready_devices_info = nullptr;
}
}
static void destroy_pa(SoundIo *soundio) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
if (!sipa)
@ -134,8 +115,8 @@ static void destroy_pa(SoundIo *soundio) {
if (sipa->main_loop)
pa_threaded_mainloop_stop(sipa->main_loop);
destroy_current_devices_info(soundio);
destroy_ready_devices_info(soundio);
soundio_destroy_devices_info(sipa->current_devices_info);
soundio_destroy_devices_info(sipa->ready_devices_info);
pa_context_disconnect(sipa->pulse_context);
pa_context_unref(sipa->pulse_context);
@ -259,7 +240,7 @@ static void finish_device_query(SoundIo *soundio) {
}
}
destroy_ready_devices_info(soundio);
soundio_destroy_devices_info(sipa->ready_devices_info);
sipa->ready_devices_info = sipa->current_devices_info;
sipa->current_devices_info = NULL;
sipa->have_devices_flag = true;
@ -351,7 +332,7 @@ static void scan_devices(SoundIo *soundio) {
sipa->have_default_sink = false;
sipa->have_source_list = false;
destroy_current_devices_info(soundio);
soundio_destroy_devices_info(sipa->current_devices_info);
sipa->current_devices_info = create<SoundIoDevicesInfo>();
if (!sipa->current_devices_info)
soundio_panic("out of memory");
@ -426,13 +407,7 @@ static void flush_events(SoundIo *soundio) {
if (change)
soundio->on_devices_change(soundio);
if (old_devices_info) {
for (int i = 0; i < old_devices_info->input_devices.length; i += 1)
soundio_device_unref(old_devices_info->input_devices.at(i));
for (int i = 0; i < old_devices_info->output_devices.length; i += 1)
soundio_device_unref(old_devices_info->output_devices.at(i));
destroy(old_devices_info);
}
soundio_destroy_devices_info(old_devices_info);
block_until_have_devices(soundio);
}
@ -876,12 +851,6 @@ static void input_device_clear_buffer_pa(SoundIo *soundio,
pa_threaded_mainloop_unlock(sipa->main_loop);
}
static void refresh_devices(SoundIo *soundio) {
block_until_ready(soundio);
soundio_flush_events(soundio);
block_until_have_devices(soundio);
}
int soundio_pulseaudio_init(SoundIo *soundio) {
assert(!soundio->backend_data);
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
@ -942,7 +911,6 @@ int soundio_pulseaudio_init(SoundIo *soundio) {
soundio->destroy = destroy_pa;
soundio->flush_events = flush_events;
soundio->refresh_devices = refresh_devices;
soundio->wait_events = wait_events;
soundio->wakeup = wakeup;

View file

@ -15,6 +15,10 @@
#include "pulseaudio.hpp"
#endif
#ifdef SOUNDIO_HAVE_ALSA
#include "alsa.hpp"
#endif
#include <string.h>
#include <assert.h>
@ -61,6 +65,7 @@ const char *soundio_backend_name(enum SoundIoBackend backend) {
switch (backend) {
case SoundIoBackendNone: return "(none)";
case SoundIoBackendPulseAudio: return "PulseAudio";
case SoundIoBackendAlsa: return "ALSA";
case SoundIoBackendDummy: return "Dummy";
}
soundio_panic("invalid backend enum value: %d", (int)backend);
@ -104,6 +109,17 @@ int soundio_connect(struct SoundIo *soundio) {
}
#endif
#ifdef SOUNDIO_HAVE_ALSA
soundio->current_backend = SoundIoBackendAlsa;
err = soundio_alsa_init(soundio);
if (!err)
return 0;
if (err != SoundIoErrorInitAudioBackend) {
soundio_disconnect(soundio);
return err;
}
#endif
soundio->current_backend = SoundIoBackendDummy;
err = soundio_dummy_init(soundio);
if (err) {
@ -121,20 +137,13 @@ void soundio_disconnect(struct SoundIo *soundio) {
soundio->current_backend = SoundIoBackendNone;
if (soundio->safe_devices_info) {
for (int i = 0; i < soundio->safe_devices_info->input_devices.length; i += 1)
soundio_device_unref(soundio->safe_devices_info->input_devices.at(i));
for (int i = 0; i < soundio->safe_devices_info->output_devices.length; i += 1)
soundio_device_unref(soundio->safe_devices_info->output_devices.at(i));
destroy(soundio->safe_devices_info);
soundio->safe_devices_info = nullptr;
}
soundio_destroy_devices_info(soundio->safe_devices_info);
soundio->safe_devices_info = nullptr;
soundio->destroy = nullptr;
soundio->flush_events = nullptr;
soundio->wait_events = nullptr;
soundio->wakeup = nullptr;
soundio->refresh_devices = nullptr;
soundio->output_device_init = nullptr;
soundio->output_device_destroy = nullptr;
@ -200,9 +209,6 @@ struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int ind
return device;
}
void soundio_device_ref(struct SoundIoDevice *device);
void soundio_device_unref(struct SoundIoDevice *device);
// the name is the identifier for the device. UTF-8 encoded
const char *soundio_device_name(const struct SoundIoDevice *device) {
return device->name;
@ -387,3 +393,15 @@ void soundio_input_device_destroy(struct SoundIoInputDevice *input_device) {
soundio_device_unref(input_device->device);
destroy(input_device);
}
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
if (!devices_info)
return;
for (int i = 0; i < devices_info->input_devices.length; i += 1)
soundio_device_unref(devices_info->input_devices.at(i));
for (int i = 0; i < devices_info->output_devices.length; i += 1)
soundio_device_unref(devices_info->output_devices.at(i));
destroy(devices_info);
}

View file

@ -90,6 +90,7 @@ enum SoundIoChannelLayoutId {
enum SoundIoBackend {
SoundIoBackendNone,
SoundIoBackendPulseAudio,
SoundIoBackendAlsa,
SoundIoBackendDummy,
};
@ -160,7 +161,6 @@ struct SoundIo {
void (*flush_events)(struct SoundIo *);
void (*wait_events)(struct SoundIo *);
void (*wakeup)(struct SoundIo *);
void (*refresh_devices)(struct SoundIo *);
int (*output_device_init)(struct SoundIo *, struct SoundIoOutputDevice *);
void (*output_device_destroy)(struct SoundIo *, struct SoundIoOutputDevice *);

View file

@ -19,4 +19,6 @@ struct SoundIoDevicesInfo {
int default_input_index;
};
void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);
#endif

View file

@ -19,3 +19,27 @@ void soundio_panic(const char *format, ...) {
va_end(ap);
abort();
}
char *soundio_alloc_sprintf(int *len, const char *format, ...) {
va_list ap, ap2;
va_start(ap, format);
va_copy(ap2, ap);
int len1 = vsnprintf(nullptr, 0, format, ap);
assert(len1 >= 0);
size_t required_size = len1 + 1;
char *mem = allocate<char>(required_size);
if (!mem)
return nullptr;
int len2 = vsnprintf(mem, required_size, format, ap2);
assert(len2 == len1);
va_end(ap2);
va_end(ap);
if (len)
*len = len1;
return mem;
}

View file

@ -84,6 +84,10 @@ void soundio_panic(const char *format, ...)
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)));
char *soundio_alloc_sprintf(int *len, const char *format, ...)
__attribute__ ((format (printf, 2, 3)));
template <typename T, long n>
constexpr long array_length(const T (&)[n]) {
return n;

96
test/valgrind.supp Normal file
View file

@ -0,0 +1,96 @@
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_hook_load
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_hook_load
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_update_r
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_update_r
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_load
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:parse_array_defs
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
fun:snd1_dlobj_cache_get
}