snd_config_t *father;
};
+int snd_config_top(snd_config_t **config);
-int snd_config_load(snd_config_t **config, FILE *fp);
+int snd_config_load(snd_config_t *config, FILE *fp);
int snd_config_save(snd_config_t *config, FILE *fp);
int snd_config_search(snd_config_t *config, char *key, snd_config_t **result);
+int snd_config_searchv(snd_config_t *config,
+ snd_config_t **result, ...);
int snd_config_add(snd_config_t *config, snd_config_t *leaf);
int snd_config_delete(snd_config_t *config);
#define snd_config_entry(iterator) list_entry(iterator, snd_config_t, list)
+static inline snd_config_type_t snd_config_type(snd_config_t *config)
+{
+ return config->type;
+}
+
+static inline char *snd_config_id(snd_config_t *config)
+{
+ return config->id;
+}
+
+snd_config_t *snd_config;
+int snd_config_update();
#endif
+int snd_pcm_open(snd_pcm_t **handle, char *name,
+ int stream, int mode);
+
int snd_pcm_hw_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode);
int snd_pcm_hw_open(snd_pcm_t **handle, int card, int device, int stream, int mode);
libasound_la_LIBADD = control/libcontrol.la mixer/libmixer.la pcm/libpcm.la \
rawmidi/librawmidi.la timer/libtimer.la \
hwdep/libhwdep.la seq/libseq.la instr/libinstr.la \
- compat/libcompat.la
+ compat/libcompat.la conf/libconf.la
libasound_la_LDFLAGS = -version-info $(COMPATNUM)
compat/libcompat.la:
$(MAKE) -C compat libcompat.la
+conf/libconf.la:
+ $(MAKE) -C conf libconf.la
+
INCLUDES=-I$(top_srcdir)/include
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
+#include <sys/stat.h>
#include "asoundlib.h"
#include "list.h"
+#define SYS_ASOUNDRC "/etc/asound.conf"
+#define USR_ASOUNDRC ".asoundrc"
+
typedef struct {
FILE *fp;
unsigned int line, column;
}
if (idx >= alloc) {
size_t old_alloc = alloc;
- alloc *= 2;
+ alloc += bufsize;
if (old_alloc == bufsize) {
buf = malloc(alloc);
memcpy(buf, _buf, old_alloc);
}
if (idx >= alloc) {
size_t old_alloc = alloc;
- alloc *= 2;
+ alloc += bufsize;
if (old_alloc == bufsize) {
buf = malloc(alloc);
memcpy(buf, _buf, old_alloc);
return 0;
}
+int snd_config_top(snd_config_t **config)
+{
+ assert(config);
+ return _snd_config_make(config, 0, SND_CONFIG_TYPE_COMPOUND);
+}
-int snd_config_load(snd_config_t **config, FILE *fp)
+int snd_config_load(snd_config_t *config, FILE *fp)
{
int err;
input_t input;
- snd_config_t *c;
assert(config && fp);
- err = _snd_config_make(&c, 0, SND_CONFIG_TYPE_COMPOUND);
- if (err < 0)
- return err;
input.fp = fp;
input.line = 1;
input.column = 0;
input.unget = 0;
- err = parse_defs(c, &input);
+ err = parse_defs(config, &input);
if (err < 0) {
- snd_config_delete(c);
+ snd_config_delete(config);
return err;
}
if (get_char(&input) != EOF) {
- snd_config_delete(c);
+ snd_config_delete(config);
return -1;
}
- *config = c;
return 0;
}
int snd_config_integer_set(snd_config_t *config, long value)
{
- assert(config->type == SND_CONFIG_TYPE_INTEGER);
+ assert(config);
+ if (config->type != SND_CONFIG_TYPE_INTEGER)
+ return -EINVAL;
config->u.integer = value;
return 0;
}
int snd_config_real_set(snd_config_t *config, double value)
{
- assert(config->type == SND_CONFIG_TYPE_REAL);
+ assert(config);
+ if (config->type != SND_CONFIG_TYPE_REAL)
+ return -EINVAL;
config->u.real = value;
return 0;
}
int snd_config_string_set(snd_config_t *config, char *value)
{
assert(config);
- assert(config->type == SND_CONFIG_TYPE_INTEGER);
+ if (config->type != SND_CONFIG_TYPE_STRING)
+ return -EINVAL;
if (config->u.string)
free(config->u.string);
config->u.string = strdup(value);
int snd_config_integer_get(snd_config_t *config, long *ptr)
{
assert(config && ptr);
- assert(config->type == SND_CONFIG_TYPE_INTEGER);
+ if (config->type != SND_CONFIG_TYPE_INTEGER)
+ return -EINVAL;
*ptr = config->u.integer;
return 0;
}
int snd_config_real_get(snd_config_t *config, double *ptr)
{
assert(config && ptr);
- assert(config->type == SND_CONFIG_TYPE_REAL);
+ if (config->type != SND_CONFIG_TYPE_REAL)
+ return -EINVAL;
*ptr = config->u.real;
return 0;
}
int snd_config_string_get(snd_config_t *config, char **ptr)
{
assert(config && ptr);
- assert(config->type == SND_CONFIG_TYPE_INTEGER);
+ if (config->type != SND_CONFIG_TYPE_STRING)
+ return -EINVAL;
*ptr = config->u.string;
return 0;
}
return _snd_config_search(config, key, -1, result);
}
}
+
+int snd_config_searchv(snd_config_t *config,
+ snd_config_t **result, ...)
+{
+ va_list arg;
+ const size_t bufsize = 256;
+ char _buf[bufsize];
+ char *buf = _buf;
+ size_t alloc = bufsize;
+ size_t idx = 0;
+ size_t dot = 0;
+ assert(config && result);
+ va_start(arg, result);
+ while (1) {
+ char *k = va_arg(arg, char *);
+ size_t len;
+ if (!k)
+ break;
+ len = strlen(k);
+ if (idx + len + dot>= alloc) {
+ size_t old_alloc = alloc;
+ alloc = idx + len + dot;
+ alloc += bufsize - alloc % bufsize;
+ if (old_alloc == bufsize) {
+ buf = malloc(alloc);
+ memcpy(buf, _buf, old_alloc);
+ } else
+ buf = realloc(buf, alloc);
+ }
+ if (dot)
+ buf[idx] = '.';
+ memcpy(buf + idx + dot, k, len);
+ idx += len + dot;
+ if (dot == 0)
+ dot = 1;
+ }
+ buf[idx] = '\0';
+ return snd_config_search(config, buf, result);
+}
+
+snd_config_t *snd_config = 0;
+static dev_t sys_asoundrc_device;
+static ino_t sys_asoundrc_inode;
+static time_t sys_asoundrc_mtime;
+static dev_t usr_asoundrc_device;
+static ino_t usr_asoundrc_inode;
+static time_t usr_asoundrc_mtime;
+int snd_config_update()
+{
+ int err;
+ char *usr_asoundrc = NULL;
+ char *home = getenv("HOME");
+ struct stat st;
+ int reload;
+ FILE *fp;
+ if (home) {
+ size_t len = strlen(home);
+ size_t len1 = strlen(USR_ASOUNDRC);
+ usr_asoundrc = alloca(len + len1 + 2);
+ memcpy(usr_asoundrc, home, len);
+ usr_asoundrc[len] = '/';
+ memcpy(usr_asoundrc + len + 1, USR_ASOUNDRC, len1);
+ usr_asoundrc[len + 1 + len1] = '\0';
+ }
+ reload = (snd_config == NULL);
+ if (!reload &&
+ stat(usr_asoundrc, &st) == 0 &&
+ (st.st_dev != usr_asoundrc_device ||
+ st.st_ino != usr_asoundrc_inode ||
+ st.st_mtime != usr_asoundrc_mtime))
+ reload = 1;
+ if (!reload &&
+ stat(SYS_ASOUNDRC, &st) == 0 &&
+ (st.st_dev != sys_asoundrc_device ||
+ st.st_ino != sys_asoundrc_inode ||
+ st.st_mtime != sys_asoundrc_mtime))
+ reload = 1;
+ if (!reload)
+ return 0;
+ if (snd_config == NULL) {
+ err = snd_config_top(&snd_config);
+ if (err < 0)
+ return err;
+ }
+ fp = fopen(SYS_ASOUNDRC, "r");
+ if (fp) {
+ err = snd_config_load(snd_config, fp);
+ if (err < 0) {
+ snd_config = NULL;
+ fclose(fp);
+ return err;
+ }
+ fclose(fp);
+ }
+ fp = fopen(usr_asoundrc, "r");
+ if (fp) {
+ err = snd_config_load(snd_config, fp);
+ if (err < 0) {
+ snd_config = NULL;
+ fclose(fp);
+ return err;
+ }
+ fclose(fp);
+ }
+ return 0;
+}
#include <sys/poll.h>
#include <sys/uio.h>
#include "pcm_local.h"
+#include "list.h"
snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle)
{
return samples * handle->bits_per_sample / 8;
}
+static int _snd_pcm_open_hw(snd_pcm_t **handlep, snd_config_t *conf,
+ int stream, int mode)
+{
+ snd_config_iterator_t i;
+ long card = -1, device = -1, subdevice = -1;
+ char *str;
+ int err;
+ snd_config_foreach(i, conf) {
+ snd_config_t *n = snd_config_entry(i);
+ if (strcmp(n->id, "comment") == 0)
+ continue;
+ if (strcmp(n->id, "type") == 0)
+ continue;
+ if (strcmp(n->id, "stream") == 0)
+ continue;
+ if (strcmp(n->id, "card") == 0) {
+ err = snd_config_integer_get(n, &card);
+ if (err < 0) {
+ err = snd_config_string_get(n, &str);
+ if (err < 0)
+ return -EINVAL;
+ card = snd_card_name(str);
+ if (card < 0)
+ return card;
+ }
+ continue;
+ }
+ if (strcmp(n->id, "device") == 0) {
+ err = snd_config_integer_get(n, &device);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ if (strcmp(n->id, "subdevice") == 0) {
+ err = snd_config_integer_get(n, &subdevice);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ return -EINVAL;
+ }
+ if (card < 0 || device < 0)
+ return -EINVAL;
+ return snd_pcm_hw_open_subdevice(handlep, card, device, subdevice, stream, mode);
+}
+
+static int _snd_pcm_open_plug(snd_pcm_t **handlep, snd_config_t *conf,
+ int stream, int mode)
+{
+ snd_config_iterator_t i;
+ long card = -1, device = -1, subdevice = -1;
+ char *str;
+ int err;
+ snd_config_foreach(i, conf) {
+ snd_config_t *n = snd_config_entry(i);
+ if (strcmp(n->id, "comment") == 0)
+ continue;
+ if (strcmp(n->id, "type") == 0)
+ continue;
+ if (strcmp(n->id, "stream") == 0)
+ continue;
+ if (strcmp(n->id, "card") == 0) {
+ err = snd_config_integer_get(n, &card);
+ if (err < 0) {
+ err = snd_config_string_get(n, &str);
+ if (err < 0)
+ return -EINVAL;
+ card = snd_card_name(str);
+ if (card < 0)
+ return card;
+ }
+ continue;
+ }
+ if (strcmp(n->id, "device") == 0) {
+ err = snd_config_integer_get(n, &device);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ if (strcmp(n->id, "subdevice") == 0) {
+ err = snd_config_integer_get(n, &subdevice);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ return -EINVAL;
+ }
+ if (card < 0 || device < 0)
+ return -EINVAL;
+ return snd_pcm_plug_open_subdevice(handlep, card, device, subdevice, stream, mode);
+}
+
+static int _snd_pcm_open_multi(snd_pcm_t **handle, snd_config_t *conf,
+ int stream, int mode)
+{
+ snd_config_iterator_t i;
+ char *str;
+ int err;
+ snd_config_foreach(i, conf) {
+ snd_config_t *n = snd_config_entry(i);
+ if (strcmp(n->id, "comment") == 0)
+ continue;
+ if (strcmp(n->id, "type") == 0)
+ continue;
+ if (strcmp(n->id, "stream") == 0)
+ continue;
+ if (strcmp(n->id, "slave") == 0) {
+ if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND)
+ return -EINVAL;
+ /* Not yet implemented */
+ continue;
+ }
+ if (strcmp(n->id, "binding") == 0) {
+ if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND)
+ return -EINVAL;
+ /* Not yet implemented */
+ continue;
+ }
+ return -EINVAL;
+ }
+ return -ENOSYS;
+}
+
+int snd_pcm_open(snd_pcm_t **handlep, char *name,
+ int stream, int mode)
+{
+ char *str;
+ int err;
+ snd_config_t *pcm_conf, *conf;
+ assert(handlep && name);
+ err = snd_config_update();
+ if (err < 0)
+ return err;
+ err = snd_config_searchv(snd_config, &pcm_conf, "pcm", name, 0);
+ if (err < 0)
+ return err;
+ if (snd_config_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND)
+ return -EINVAL;
+ err = snd_config_search(pcm_conf, "stream", &conf);
+ if (err >= 0) {
+ err = snd_config_string_get(conf, &str);
+ if (err < 0)
+ return err;
+ if (strcmp(str, "playback") == 0) {
+ if (stream != SND_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+ } else if (strcmp(str, "capture") == 0) {
+ if (stream != SND_PCM_STREAM_CAPTURE)
+ return -EINVAL;
+ } else
+ return -EINVAL;
+ }
+ err = snd_config_search(pcm_conf, "type", &conf);
+ if (err < 0)
+ return err;
+ err = snd_config_string_get(conf, &str);
+ if (err < 0)
+ return err;
+ if (strcmp(str, "hw") == 0)
+ return _snd_pcm_open_hw(handlep, pcm_conf, stream, mode);
+ else if (strcmp(str, "plug") == 0)
+ return _snd_pcm_open_plug(handlep, pcm_conf, stream, mode);
+ else if (strcmp(str, "multi") == 0)
+ return _snd_pcm_open_multi(handlep, pcm_conf, stream, mode);
+ else
+ return -EINVAL;
+}
snd_pcm_t *handle;
snd_pcm_hw_t *hw;
+ assert(handlep);
*handlep = 0;
if ((ret = snd_ctl_open(&ctl, card)) < 0)
char client_map[32] = { 0 };
char slave_map[32][32] = { { 0 } };
+ assert(handlep);
assert(slaves_count > 0 && slaves_handle && slaves_channels_count);
assert(binds_count > 0 && binds_slave && binds_client_channel && binds_slave_channel);
{
snd_pcm_t *handle;
snd_pcm_plug_t *plug;
+ assert(handlep && slave);
handle = calloc(1, sizeof(snd_pcm_t));
if (!handle)
return -ENOMEM;