beginning of ALSA support - list_devices works
This commit is contained in:
parent
d6fcef468b
commit
fe61322b23
13 changed files with 720 additions and 78 deletions
|
@ -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++ !!
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
511
src/alsa.cpp
Normal 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
14
src/alsa.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -19,4 +19,6 @@ struct SoundIoDevicesInfo {
|
|||
int default_input_index;
|
||||
};
|
||||
|
||||
void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);
|
||||
|
||||
#endif
|
||||
|
|
24
src/util.cpp
24
src/util.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
96
test/valgrind.supp
Normal 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
|
||||
}
|
Loading…
Reference in a new issue