ALSA: expose pcm devices and introduce is_raw

This commit is contained in:
Andrew Kelley 2015-07-07 23:29:09 -07:00
parent fe61322b23
commit c39c1ab9f4
5 changed files with 151 additions and 7 deletions

View file

@ -75,7 +75,7 @@ make
sudo make install
```
### Building With MXE
### Building for Windows
You can build libsoundio with [mxe](http://mxe.cc/). Follow the
[requirements](http://mxe.cc/#requirements) section to install the
@ -115,6 +115,8 @@ view `coverage/index.html` in a browser.
## Roadmap
0. ALSA `list_devices` should list default, dmix, etc.
0. ALSA default devices are "default" and "default" respectively
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

View file

@ -33,6 +33,7 @@ static void print_channel_layout(const struct SoundIoChannelLayout *layout) {
static void print_device(struct SoundIoDevice *device, bool is_default) {
const char *purpose_str;
const char *default_str;
const char *raw_str;
if (soundio_device_purpose(device) == SoundIoDevicePurposeOutput) {
purpose_str = "playback";
default_str = is_default ? " (default)" : "";
@ -40,9 +41,10 @@ static void print_device(struct SoundIoDevice *device, bool is_default) {
purpose_str = "recording";
default_str = is_default ? " (default)" : "";
}
raw_str = device->is_raw ? "(raw) " : "";
const char *description = soundio_device_description(device);
int sample_rate = soundio_device_sample_rate(device);
fprintf(stderr, "%s device: ", purpose_str);
fprintf(stderr, "%s%s device: ", raw_str, purpose_str);
print_channel_layout(soundio_device_channel_layout(device));
fprintf(stderr, " %d Hz %s%s\n", sample_rate, description, default_str);
}

View file

@ -70,8 +70,145 @@ static void destroy_alsa(SoundIo *soundio) {
soundio->backend_data = nullptr;
}
static char * str_partition_on_char(char *str, char c) {
while (*str) {
if (*str == c) {
*str = 0;
return str + 1;
}
str += 1;
}
return nullptr;
}
static int refresh_devices(SoundIo *soundio) {
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
if (!devices_info)
return SoundIoErrorNoMem;
void **hints;
if (snd_device_name_hint(-1, "pcm", &hints) < 0) {
destroy(devices_info);
return SoundIoErrorNoMem;
}
for (void **hint_ptr = hints; *hint_ptr; hint_ptr += 1) {
char *name = snd_device_name_get_hint(*hint_ptr, "NAME");
// null - libsoundio has its own dummy backend. API clients should use
// that instead of alsa null device.
if (strcmp(name, "null") == 0 ||
// sysdefault is confusing - the name and description is identical
// to default, and my best guess for what it does is ignore ~/.asoundrc
// which is just an accident waiting to happen.
str_has_prefix(name, "sysdefault:") ||
// all these surround devices are clutter
str_has_prefix(name, "front:") ||
str_has_prefix(name, "surround21:") ||
str_has_prefix(name, "surround40:") ||
str_has_prefix(name, "surround41:") ||
str_has_prefix(name, "surround50:") ||
str_has_prefix(name, "surround51:") ||
str_has_prefix(name, "surround71:"))
{
free(name);
continue;
}
char *descr = snd_device_name_get_hint(*hint_ptr, "DESC");
char *descr1 = str_partition_on_char(descr, '\n');
char *io = snd_device_name_get_hint(*hint_ptr, "IOID");
bool is_playback;
bool is_capture;
if (io) {
if (strcmp(io, "Input") == 0) {
is_playback = false;
is_capture = true;
} else if (strcmp(io, "Output") == 0) {
is_playback = true;
is_capture = false;
} else {
soundio_panic("invalid io hint value");
}
free(io);
} else {
is_playback = true;
is_capture = true;
}
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];
if (stream == SND_PCM_STREAM_PLAYBACK && !is_playback) continue;
if (stream == SND_PCM_STREAM_CAPTURE && !is_capture) continue;
if (stream == SND_PCM_STREAM_CAPTURE && descr1 &&
(strstr(descr1, "Output") || strstr(descr1, "output")))
{
continue;
}
SoundIoDevice *device = create<SoundIoDevice>();
if (!device) {
free(name);
free(descr);
destroy(devices_info);
snd_device_name_free_hint(hints);
return SoundIoErrorNoMem;
}
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup(name);
device->description = descr1 ?
soundio_alloc_sprintf(nullptr, "%s: %s", descr, descr1) : strdup(descr);
device->is_raw = false;
if (!device->name || !device->description) {
soundio_device_unref(device);
free(name);
free(descr);
destroy(devices_info);
snd_device_name_free_hint(hints);
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;
if (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:"))
devices_info->default_input_index = device_list->length;
}
if (device_list->append(device)) {
soundio_device_unref(device);
free(name);
free(descr);
destroy(devices_info);
snd_device_name_free_hint(hints);
return SoundIoErrorNoMem;
}
}
free(name);
free(descr);
}
snd_device_name_free_hint(hints);
int card_index = -1;
if (snd_card_next(&card_index) < 0)
@ -83,10 +220,6 @@ static int refresh_devices(SoundIo *soundio) {
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;
@ -145,8 +278,9 @@ static int refresh_devices(SoundIo *soundio) {
}
device->ref_count = 1;
device->soundio = soundio;
device->name = soundio_alloc_sprintf(nullptr, "hw:%d,%d,%d", card_index, device_index, 0);
device->name = soundio_alloc_sprintf(nullptr, "hw:%d,%d", card_index, device_index);
device->description = soundio_alloc_sprintf(nullptr, "%s %s", card_name, device_name);
device->is_raw = true;
if (!device->name || !device->description) {
soundio_device_unref(device);

View file

@ -120,6 +120,7 @@ struct SoundIoDevice {
int default_sample_rate;
enum SoundIoDevicePurpose purpose;
int ref_count;
bool is_raw;
};
struct SoundIoOutputDevice {

View file

@ -9,6 +9,7 @@
#define SOUNDIO_UTIL_HPP
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <new>
@ -103,4 +104,8 @@ static inline T min(T a, T b) {
return (a <= b) ? a : b;
}
static inline bool str_has_prefix(const char *big_str, const char *prefix) {
return strncmp(big_str, prefix, strlen(prefix)) == 0;
}
#endif