diff options
-rw-r--r-- | include/Makefile.am | 4 | ||||
-rw-r--r-- | include/control.h | 16 | ||||
-rw-r--r-- | src/control/Makefile.am | 5 | ||||
-rw-r--r-- | src/control/cards.conf | 1 | ||||
-rw-r--r-- | src/control/cards_id.c | 154 | ||||
-rw-r--r-- | src/control/setup.c | 289 | ||||
-rw-r--r-- | src/pcm/pcm_surr.c | 579 | ||||
-rw-r--r-- | src/pcm/surround.conf | 33 | ||||
-rw-r--r-- | test/Makefile.am | 3 | ||||
-rw-r--r-- | test/cardid.c | 26 |
10 files changed, 775 insertions, 335 deletions
diff --git a/include/Makefile.am b/include/Makefile.am index c30331a2..72e0b1d6 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -4,11 +4,11 @@ sysinclude_HEADERS = asoundlib.h # This is the order they will be concatenated into asoundlib.h! # header_files=header.h version.h global.h input.h output.h error.h \ - pcm.h rawmidi.h timer.h \ + conf.h pcm.h rawmidi.h timer.h \ hwdep.h hwdep_m4.h control.h \ mixer.h \ seq.h seqmid.h seq_midi_event.h \ - conv.h instr.h conf.h footer.h + conv.h instr.h footer.h noinst_HEADERS=$(header_files) search.h list.h aserver.h local.h diff --git a/include/control.h b/include/control.h index 84e49a4c..4c6eb8e1 100644 --- a/include/control.h +++ b/include/control.h @@ -246,6 +246,16 @@ typedef enum _snd_ctl_type { /** CTL handle */ typedef struct _snd_ctl snd_ctl_t; +/** SCTL replace type */ +typedef struct { + const char *key; + const char *old_value; + const char *new_value; +} snd_sctl_replace_t; + +/** SCTL type */ +typedef struct _snd_sctl snd_sctl_t; + #ifdef __cplusplus extern "C" { #endif @@ -263,6 +273,12 @@ int snd_defaults_pcm_device(void); int snd_defaults_rawmidi_card(void); int snd_defaults_rawmidi_device(void); +int snd_card_type_string_to_enum(const char *strid, snd_card_type_t *enumid); +int snd_card_type_enum_to_string(snd_card_type_t enumid, char **strid); + +int snd_sctl_build(snd_ctl_t *ctl, snd_sctl_t **setup, snd_config_t *config, snd_sctl_replace_t *replace); +int snd_sctl_free(snd_ctl_t *ctl, snd_sctl_t *setup); + int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode); int snd_ctl_close(snd_ctl_t *ctl); int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock); diff --git a/src/control/Makefile.am b/src/control/Makefile.am index b1ad9c11..57d61769 100644 --- a/src/control/Makefile.am +++ b/src/control/Makefile.am @@ -1,7 +1,8 @@ EXTRA_LTLIBRARIES = libcontrol.la -libcontrol_la_SOURCES = cards.c hcontrol.c defaults.c \ - control.c control_hw.c control_shm.c +libcontrol_la_SOURCES = cards.c cards_id.c hcontrol.c defaults.c \ + control.c control_hw.c control_shm.c \ + setup.c noinst_HEADERS = control_local.h diff --git a/src/control/cards.conf b/src/control/cards.conf index 91287c98..6f044bd2 100644 --- a/src/control/cards.conf +++ b/src/control/cards.conf @@ -73,3 +73,4 @@ card.VIA8233 = 67; # VIA VT8233 card.PMAC_AWACS = 68; # PMac AWACS card.PMAC_BURGUNDY = 69; # PMac Burgundy card.PMAC_DACA = 70; # PMac DACA +card.ALI5451 = 71; # ALi PCI Audio M5451 diff --git a/src/control/cards_id.c b/src/control/cards_id.c new file mode 100644 index 00000000..6703cf89 --- /dev/null +++ b/src/control/cards_id.c @@ -0,0 +1,154 @@ +/* + * Control Interface - card ID conversions + * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz> + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include "control_local.h" + +#ifndef DATADIR +#define DATADIR "/usr/share" +#endif +#define ALSA_CARDS_FILE DATADIR "/alsa/cards.conf" + +static int build_config(snd_config_t **r_conf) +{ + int err; + snd_input_t *in; + snd_config_t *conf, *file; + const char *filename = ALSA_CARDS_FILE; + + assert(r_conf); + *r_conf = NULL; + if ((err = snd_config_update()) < 0) + return err; + if ((err = snd_config_search(snd_config, "cards_file", &file)) >= 0) { + if ((err = snd_config_get_string(file, &filename)) < 0) { + SNDERR("cards_file definition must be string"); + filename = ALSA_CARDS_FILE; + } + } + if ((err = snd_input_stdio_open(&in, filename, "r")) < 0) { + SNDERR("unable to open configuration file '%s'", filename); + return err; + } + if ((err = snd_config_top(&conf)) < 0) { + SNDERR("config_top"); + snd_input_close(in); + return err; + } + if ((err = snd_config_load(conf, in)) < 0) { + SNDERR("config load error"); + snd_config_delete(conf); + snd_input_close(in); + return err; + } + snd_input_close(in); + *r_conf = conf; + return 0; +} + +int snd_card_type_string_to_enum(const char *strid, snd_card_type_t *enumid) +{ + int err; + snd_config_t *conf = NULL, *card; + snd_config_iterator_t i, next; + + assert(enumid); + *enumid = SND_CARD_TYPE_GUS_CLASSIC; + if ((err = build_config(&conf)) < 0) + return err; + if ((err = snd_config_search(conf, "card", &card)) < 0) { + SNDERR("unable to find card definitions"); + snd_config_delete(conf); + return err; + } + if (snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + snd_config_delete(conf); + return err; + } + snd_config_for_each(i, next, card) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + unsigned long i; + if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { + SNDERR("entry '%s' is invalid", id); + continue; + } + if ((err = snd_config_get_integer(n, &i)) < 0) { + SNDERR("entry '%s' is invalid", id); + continue; + } + if (!strcmp(id, strid)) { + *enumid = i; + return 0; + } + } + snd_config_delete(conf); + return -ENOENT; +} + +int snd_card_type_enum_to_string(snd_card_type_t enumid, char **strid) +{ + int err; + snd_config_t *conf = NULL, *card; + snd_config_iterator_t i, next; + + assert(strid); + *strid = NULL; + if ((err = build_config(&conf)) < 0) + return err; + if ((err = snd_config_search(conf, "card", &card)) < 0) { + SNDERR("unable to find card definitions"); + snd_config_delete(conf); + return err; + } + if (snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + snd_config_delete(conf); + return err; + } + snd_config_for_each(i, next, card) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + unsigned long i; + if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { + SNDERR("entry '%s' is invalid", id); + continue; + } + if ((err = snd_config_get_integer(n, &i)) < 0) { + SNDERR("entry '%s' is invalid", id); + continue; + } + if ((unsigned long)enumid == i) { + *strid = id ? strdup(id) : NULL; + snd_config_delete(conf); + return 0; + } + } + snd_config_delete(conf); + return -ENOENT; +} diff --git a/src/control/setup.c b/src/control/setup.c new file mode 100644 index 00000000..67bf28bf --- /dev/null +++ b/src/control/setup.c @@ -0,0 +1,289 @@ +/** + * \file control/setup.c + * \author Jaroslav Kysela <perex@suse.cz> + * \date 2001 + * + * Routines to setup control primitives from configuration + */ +/* + * Control Interface - routines for setup from configuration + * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include "control_local.h" + +typedef struct snd_sctl_elem { + struct list_head list; + snd_ctl_elem_value_t *value; + int lock: 1, + preserve: 1; +} snd_sctl_elem_t; + +struct _snd_sctl { + struct list_head elements; +}; + +static const char *id_str(snd_ctl_elem_id_t *id) +{ + static char str[128]; + assert(id); + sprintf(str, "%i,%i,%i,%s,%i", + snd_enum_to_int(snd_ctl_elem_id_get_interface(id)), + snd_ctl_elem_id_get_device(id), + snd_ctl_elem_id_get_subdevice(id), + snd_ctl_elem_id_get_name(id), + snd_ctl_elem_id_get_index(id)); + return str; +} + +static int config_iface(const char *value) +{ + int idx; + + if (isdigit(*value)) + return atoi(value); + for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) + if (strcasecmp(snd_ctl_elem_iface_name(idx), value) == 0) + return snd_enum_to_int(idx); + SNDERR("iface '%s' error", value); + return -1; +} + +static void try_replace(snd_sctl_replace_t *replace, const char *id, const char **value) +{ + if (replace == NULL) + return; + while (replace->key) { + if (!strcmp(id, replace->key)) { + if (!strcmp(*value, replace->old_value)) { + *value = replace->new_value; + return; + } + } + replace++; + } +} + +static void add_value(snd_ctl_elem_info_t *info, snd_ctl_elem_value_t *evalue, int idx, const char *value) +{ + switch (snd_ctl_elem_info_get_type(info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + { + int val = 0; + if (idx < 0 || idx >= 128) { + SNDERR("index out of range (0-127): %i", idx); + return; + } + if (!strcasecmp(value, "true") || !strcasecmp(value, "on")) + val = 1; + else if (isdigit(*value)) + val = atoi(value); + snd_ctl_elem_value_set_boolean(evalue, idx, val); + } + break; + case SND_CTL_ELEM_TYPE_INTEGER: + { + if (idx < 0 || idx >= 128) { + SNDERR("index out of range (0-127): %i", idx); + return; + } + snd_ctl_elem_value_set_integer(evalue, idx, strtoul(value, NULL, 10)); + } + break; +#if 0 + case SND_CTL_ELEM_TYPE_ENUMERATED: + case SND_CTL_ELEM_TYPE_BYTES: + case SND_CTL_ELEM_TYPE_IEC958: + break; +#endif + default: + SNDERR("type %i is not supported", snd_ctl_elem_info_get_type(info)); + } +} + +/** + * \brief build and set control primitives from configuration + * \param setup container for control primitives + * \return 0 on success otherwise a negative error code + * + * Build and set control primitives from configuration. + */ +int snd_sctl_build(snd_ctl_t *handle, snd_sctl_t **r_setup, snd_config_t *config, snd_sctl_replace_t *replace) +{ + snd_sctl_t *setup; + snd_config_iterator_t i, next; + int err; + + assert(handle && r_setup && config); + *r_setup = NULL; + if ((setup = calloc(1, sizeof(*setup))) == NULL) + return -ENOMEM; + INIT_LIST_HEAD(&setup->elements); + snd_config_for_each(i, next, config) { + snd_config_t *n = snd_config_iterator_entry(i); + // const char *id = snd_config_get_id(n); + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) { + snd_config_iterator_t i, next; + snd_sctl_elem_t *elem; + snd_ctl_elem_id_t *eid; + snd_ctl_elem_info_t *einfo; + snd_ctl_elem_value_t *evalue; + snd_ctl_elem_id_alloca(&eid); + snd_ctl_elem_info_alloca(&einfo); + snd_ctl_elem_value_alloca(&evalue); + elem = calloc(1, sizeof(*elem)); + if (elem == NULL) { + SNDERR("malloc problem"); + continue; + } + snd_config_for_each(i, next, n) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + char svalue[16]; + const char *value; + unsigned long i; + if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) { + snd_config_get_integer(n, &i); + sprintf(svalue, "%li", i); + value = svalue; + } else if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) { + snd_config_get_string(n, &value); + } else if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) { + value = NULL; + } else { + SNDERR("unknown configuration entry type"); + continue; + } + try_replace(replace, id, &value); + if (!strcmp(id, "iface")) + snd_ctl_elem_id_set_interface(eid, config_iface(value)); + else if (!strcmp(id, "name")) + snd_ctl_elem_id_set_name(eid, value); + else if (!strcmp(id, "index")) + snd_ctl_elem_id_set_index(eid, atoi(value)); + else if (!strcmp(id, "lock")) + elem->lock = 1; + else if (!strcmp(id, "preserve")) + elem->preserve = 1; + else if (!strcmp(id, "value")) { + if (elem->value == NULL) { + if (snd_ctl_elem_value_malloc(&elem->value) < 0) { + SNDERR("malloc problem"); + continue; + } + snd_ctl_elem_info_set_id(einfo, eid); + if ((err = snd_ctl_elem_info(handle, einfo)) < 0) { + SNDERR("snd_ctl_elem_info %s : %s", id_str(eid), snd_strerror(err)); + continue; + } + snd_ctl_elem_value_set_id(elem->value, eid); + if ((err = snd_ctl_elem_read(handle, elem->value)) < 0) { + SNDERR("snd_ctl_elem_read %s : %s", id_str(eid), snd_strerror(err)); + continue; + } + snd_ctl_elem_value_copy(evalue, elem->value); + } + if (value == NULL) { + snd_config_iterator_t i, next; + snd_config_for_each(i, next, n) { + snd_config_t *n = snd_config_iterator_entry(i); + char svalue[16]; + const char *value; + int idx = atoi(snd_config_get_id(n)); + unsigned long i; + if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) { + snd_config_get_integer(n, &i); + sprintf(svalue, "%li", i); + value = svalue; + } else if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) { + snd_config_get_string(n, &value); + } else { + SNDERR("unknown configuration entry type"); + continue; + } + add_value(einfo, evalue, idx, value); + } + } else { + add_value(einfo, evalue, 0, value); + } + } + } + list_add_tail(&elem->list, &setup->elements); + if ((err = snd_ctl_elem_write(handle, evalue)) < 0) { + SNDERR("snd_ctl_elem_write %s : %s", id_str(eid), snd_strerror(err)); + snd_sctl_free(handle, setup); + return err; + } + if (elem->lock) { + if ((err = snd_ctl_elem_lock(handle, eid)) < 0) { + SNDERR("snd_ctl_elem_lock %s : %s", id_str(eid), snd_strerror(err)); + snd_sctl_free(handle, setup); + return err; + } + } + if (!elem->preserve) { + snd_ctl_elem_value_free(elem->value); + elem->value = NULL; + } + } else { + SNDERR("compound type expected here"); + } + } + *r_setup = setup; + return 0; +} + +/** + * \brief restore control primitives from configuration + * \param setup container for control primitives + * \return 0 on success otherwise a negative error code + * + * Restore control primitives from configuration + */ +int snd_sctl_free(snd_ctl_t *handle, snd_sctl_t *setup) +{ + struct list_head *pos; + snd_sctl_elem_t *elem; + int err; + + assert(setup); + __again: + list_for_each(pos, &setup->elements) { + elem = list_entry(pos, snd_sctl_elem_t, list); + if (elem->preserve && elem->value) { + if ((err = snd_ctl_elem_write(handle, elem->value)) < 0) + SNDERR("snd_ctl_elem_write %s : %s", id_str(&elem->value->id), snd_strerror(err)); + } + if (elem->value) { + snd_ctl_elem_value_free(elem->value); + elem->value = NULL; + } + list_del(&elem->list); + free(elem); + goto __again; + } + free(setup); + return 0; +} diff --git a/src/pcm/pcm_surr.c b/src/pcm/pcm_surr.c index 77743ebb..f16621c7 100644 --- a/src/pcm/pcm_surr.c +++ b/src/pcm/pcm_surr.c @@ -30,49 +30,34 @@ #include <byteswap.h> #include <limits.h> +#include <ctype.h> #include <sys/shm.h> #include "../control/control_local.h" #include "pcm_local.h" #include "pcm_plugin.h" -#define SURR_FLG_CAPTURE (1<<0) -#define SURR_FLG_NO_4CH (1<<1) -#define SURR_FLG_NO_6CH (1<<2) -#define SURR_FLG_NO_CTL_CLOSE (1<<3) -#define SURR_FLG_FD1 (1<<4) +#ifndef DATADIR +#define DATADIR "/usr/share" +#endif +#define ALSA_SURROUND_FILE DATADIR "/alsa/surround.conf" -typedef struct _snd_pcm_surround snd_pcm_surround_t; +#define SURR_CAP_CAPTURE (1<<0) +#define SURR_CAP_4CH (1<<1) +#define SURR_CAP_6CH (1<<2) -typedef struct { - snd_card_type_t type; - unsigned int flags; - int (*scount)(snd_ctl_t *ctl, snd_ctl_card_info_t *info, snd_pcm_surround_type_t type); - int (*sopen)(snd_pcm_surround_t *surr, - snd_ctl_card_info_t *info, - snd_pcm_surround_type_t type, - snd_pcm_stream_t stream, - int mode); - void (*sclose)(snd_pcm_surround_t *surr); -} surround_open_t; +typedef struct _snd_pcm_surround snd_pcm_surround_t; struct _snd_pcm_surround { int card; /* card number */ int device; /* device number */ unsigned int channels; /* count of channels (4 or 6) */ int pcms; /* count of PCM channels */ + int use_fd; /* use this FD for the direct access */ snd_pcm_t *pcm[3]; /* up to three PCM stereo streams */ int linked[3]; /* streams are linked */ snd_ctl_t *ctl; /* CTL handle */ - surround_open_t *po; - union { - struct { - snd_ctl_elem_value_t *sw4ch; - snd_ctl_elem_value_t *sw_pcm; - } ens1370; - struct { - snd_ctl_elem_value_t *rear_path; - } trid4nx; - } s; + unsigned int caps; /* capabilities */ + snd_sctl_t *store; /* control store container */ }; static int snd_pcm_surround_free(snd_pcm_surround_t *surr); @@ -484,8 +469,8 @@ static int snd_pcm_surround_free(snd_pcm_surround_t *surr) snd_pcm_close(surr->pcm[i]); surr->pcm[i] = NULL; } - if (surr->po && surr->po->sclose) - surr->po->sclose(surr); + if (surr->store) + snd_sctl_free(surr->ctl, surr->store); if (surr->ctl) snd_ctl_close(surr->ctl); free(surr); @@ -534,288 +519,258 @@ static int snd_pcm_surround_three_streams(snd_pcm_surround_t *surr, return 0; } -static int count_generic(snd_ctl_t *ctl ATTRIBUTE_UNUSED, - snd_ctl_card_info_t *info ATTRIBUTE_UNUSED, - snd_pcm_surround_type_t type ATTRIBUTE_UNUSED) -{ - return 1; -} - -static int count_si7018(snd_ctl_t *ctl ATTRIBUTE_UNUSED, - snd_ctl_card_info_t *info ATTRIBUTE_UNUSED, - snd_pcm_surround_type_t type ATTRIBUTE_UNUSED) -{ - /* this card supports multiopen also for DVD !!! */ - return 32 / 2 / 3; /* 32 stereo channels / 2 (one helper voice) / 3 (count of streams) */ -} - -static int open_si7018(snd_pcm_surround_t *surr, - snd_ctl_card_info_t *info, - snd_pcm_surround_type_t type, - snd_pcm_stream_t stream, int mode) -{ - int err; - - if ((err = snd_pcm_surround_three_streams(surr, type, - snd_ctl_card_info_get_card(info), - 0, -1, 0, -1, 0, -1, - stream, mode)) < 0) - return err; - return 0; -} - -static int open_fm801(snd_pcm_surround_t *surr, - snd_ctl_card_info_t *info, - snd_pcm_surround_type_t type, - snd_pcm_stream_t stream, int mode) -{ - int err; - - if ((err = snd_pcm_surround_one_stream(surr, type, - snd_ctl_card_info_get_card(info), - 0, 0, stream, mode)) < 0) - return err; - return 0; -} - -static int open_ens1370(snd_pcm_surround_t *surr, - snd_ctl_card_info_t *info, - snd_pcm_surround_type_t type, - snd_pcm_stream_t stream, int mode) +static int build_config(snd_config_t **r_conf) { int err; - snd_ctl_elem_id_t *id; - snd_ctl_elem_value_t *val, *value; - - if ((err = snd_pcm_surround_three_streams(surr, type, - snd_ctl_card_info_get_card(info), - 1, 0, 0, 0, -1, -1, - stream, mode)) < 0) - return err; - snd_ctl_elem_id_alloca(&id); - snd_ctl_elem_value_alloca(&val); - - /* OK, reroute PCM0 (rear) to Line-In Jack */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_CARD); - snd_ctl_elem_id_set_name(id, "PCM 0 Output also on Line-In Jack"); - snd_ctl_elem_id_set_index(id, 0); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) - return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); - return err; + snd_input_t *in; + snd_config_t *conf, *file; + const char *filename = ALSA_SURROUND_FILE; + + assert(r_conf); + *r_conf = NULL; + if ((err = snd_config_update()) < 0) + return err; + if ((err = snd_config_search(snd_config, "surround_file", &file)) >= 0) { + if ((err = snd_config_get_string(file, &filename)) < 0) { + SNDERR("cards_file definition must be string"); + filename = ALSA_SURROUND_FILE; + } } - surr->s.ens1370.sw4ch = value; - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_boolean(val, 0, 1); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) - return err; - - /* Turn off the PCM volume, the second PCM (front speakers) uses the second PCM control */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, "PCM Switch"); - snd_ctl_elem_id_set_index(id, 0); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) - return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); + if ((err = snd_input_stdio_open(&in, filename, "r")) < 0) { + SNDERR("unable to open configuration file '%s'", filename); return err; } - surr->s.ens1370.sw_pcm = value; - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_boolean(val, 0, 0); - snd_ctl_elem_value_set_boolean(val, 1, 0); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) + if ((err = snd_config_top(&conf)) < 0) { + SNDERR("config_top"); + snd_input_close(in); return err; - - return 0; -} - -static void close_ens1370(snd_pcm_surround_t *surr) -{ - if (surr->s.ens1370.sw4ch) { - snd_ctl_elem_write(surr->ctl, surr->s.ens1370.sw4ch); - free(surr->s.ens1370.sw4ch); - } - if (surr->s.ens1370.sw_pcm) { - snd_ctl_elem_write(surr->ctl, surr->s.ens1370.sw_pcm); - free(surr->s.ens1370.sw_pcm); } -} - -static int count_ymfpci(snd_ctl_t *ctl ATTRIBUTE_UNUSED, - snd_ctl_card_info_t *info ATTRIBUTE_UNUSED, - snd_pcm_surround_type_t type ATTRIBUTE_UNUSED) -{ - /* this card supports multiopen also for 4CH !!! */ - return 32 / 2 / 2; /* 32 voices / 2 (stereo) / 2 (count of streams) */ -} - -static int open_ymfpci(snd_pcm_surround_t *surr, - snd_ctl_card_info_t *info, - snd_pcm_surround_type_t type, - snd_pcm_stream_t stream, int mode) -{ - int err; - - if ((err = snd_pcm_surround_three_streams(surr, type, - snd_ctl_card_info_get_card(info), - 0, 0, 2, 0, -1, -1, - stream, mode)) < 0) + if ((err = snd_config_load(conf, in)) < 0) { + SNDERR("config load error"); + snd_config_delete(conf); + snd_input_close(in); return err; + } + snd_input_close(in); + *r_conf = conf; return 0; } -static int count_trid4nx(snd_ctl_t *ctl ATTRIBUTE_UNUSED, - snd_ctl_card_info_t *info ATTRIBUTE_UNUSED, - snd_pcm_surround_type_t type ATTRIBUTE_UNUSED) -{ - /* this card supports multiopen also for 4CH !!! */ - return 32 / 2 / 2; /* 32 voices / 2 (stereo) / 2 (count of streams) */ -} - -static int open_trid4nx(snd_pcm_surround_t *surr, - snd_ctl_card_info_t *info, - snd_pcm_surround_type_t type, - snd_pcm_stream_t stream, int mode) +int load_surround_config(snd_ctl_t *ctl, snd_pcm_surround_t *surr, + snd_pcm_surround_type_t stype, + snd_card_type_t ctype, + snd_pcm_stream_t stream, + int mode) { - int err, subdevice; - snd_pcm_info_t *pcm_info; - snd_ctl_elem_id_t *id; - snd_ctl_elem_value_t *val, *value; - - if ((err = snd_pcm_surround_three_streams(surr, type, - snd_ctl_card_info_get_card(info), - 0, -1, 0, -1, -1, -1, - stream, mode)) < 0) - return err; - - snd_pcm_info_alloca(&pcm_info); - if ((err = snd_pcm_info(surr->pcm[1], pcm_info)) < 0) - return err; - subdevice = snd_pcm_info_get_subdevice(pcm_info); - - snd_ctl_elem_id_alloca(&id); - snd_ctl_elem_value_alloca(&val); - - /* OK, Enable Rear Path */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, "Rear Path"); - snd_ctl_elem_id_set_index(id, subdevice); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) - return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); - return err; - } - surr->s.trid4nx.rear_path = value; - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_boolean(val, 0, 1); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) - return err; - - /* set Front Volume to mute */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, "PCM Front Playback Volume"); - snd_ctl_elem_id_set_index(id, subdevice); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) - return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); - return err; - } - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_integer(val, 0, 0); - snd_ctl_elem_value_set_integer(val, 1, 0); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) - return err; - - /* set Reverb (Rear) Volume */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, "PCM Reverb Playback Volume"); - snd_ctl_elem_id_set_index(id, subdevice); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) - return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); - return err; - } - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_integer(val, 0, 127); - snd_ctl_elem_value_set_integer(val, 1, 127); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) - return err; + int err, res = -EINVAL; + snd_config_t *conf = NULL, *surrconf; + snd_config_iterator_t i, next; - /* set Chorus Volume to mute */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, "PCM Chorus Playback Volume"); - snd_ctl_elem_id_set_index(id, subdevice); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) + if ((err = build_config(&conf)) < 0) return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); + if ((err = snd_config_search(conf, "surround_plugin", &surrconf)) < 0) { + SNDERR("unable to find card definitions"); + snd_config_delete(conf); return err; } - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_integer(val, 0, 0); - snd_ctl_elem_value_set_integer(val, 1, 0); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) - return err; - - /* set Pan Control to middle */ - snd_ctl_elem_id_set_interface(id, SNDRV_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, "PCM Pan Playback Control"); - snd_ctl_elem_id_set_index(id, subdevice); - if ((err = snd_ctl_elem_lock(surr->ctl, id)) < 0) - return err; - snd_ctl_elem_value_malloc(&value); - snd_ctl_elem_value_set_id(value, id); - if ((err = snd_ctl_elem_read(surr->ctl, value)) < 0) { - free(value); + if (snd_config_get_type(surrconf) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + snd_config_delete(conf); return err; } - snd_ctl_elem_value_copy(val, value); - snd_ctl_elem_value_set_integer(val, 0, 0); - snd_ctl_elem_value_set_integer(val, 1, 0); - if ((err = snd_ctl_elem_write(surr->ctl, val)) < 0) - return err; - - return 0; -} - -static void close_trid4nx(snd_pcm_surround_t *surr) -{ - if (surr->s.trid4nx.rear_path) { - snd_ctl_elem_write(surr->ctl, surr->s.trid4nx.rear_path); - free(surr->s.trid4nx.rear_path); + snd_config_for_each(i, next, surrconf) { + snd_config_t *n = snd_config_iterator_entry(i), *n1; + const char *id = snd_config_get_id(n); + snd_card_type_t mytype = (snd_card_type_t)-1; + int opened = 0; + if (isdigit(*id)) { + mytype = (snd_card_type_t)atoi(id); + } else { + if (snd_card_type_string_to_enum(id, &mytype) < 0) { + SNDERR("snd_card_type_string_to_enum %s", id); + continue; + } + } + if (mytype != ctype) + continue; + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + goto __error; + } + if (snd_config_search(n, "device", &n1) >= 0) { + unsigned long i; + if ((err = snd_config_get_integer(n1, &i)) < 0) { + SNDERR("Invalid type for field device"); + goto __error; + } + if (surr->device != (int)i) + continue; + } + if (snd_config_search(n, "channels_four", &n1) >= 0) { + const char *str; + if ((err = snd_config_get_string(n1, &str)) < 0) { + SNDERR("Invalid value for %s", id); + goto __error; + } else if (!strcasecmp(str, "true")) { + surr->caps |= SURR_CAP_4CH; + } else if (isdigit(*str) && atoi(str) != 0) + surr->caps |= SURR_CAP_4CH; + } + if (snd_config_search(n, "channels_six", &n1) >= 0) { + const char *str; + if ((err = snd_config_get_string(n1, &str)) < 0) { + SNDERR("Invalid value for %s", id); + goto __error; + } else if (!strcasecmp(str, "true")) { + surr->caps |= SURR_CAP_6CH; + } else if (isdigit(*str) && atoi(str) != 0) + surr->caps |= SURR_CAP_6CH; + } + if (snd_config_search(n, "use_fd", &n1) >= 0) { + unsigned long i; + if ((err = snd_config_get_integer(n1, &i)) < 0) { + SNDERR("Invalid type for %s", id); + goto __error; + } else if (i <= 2) + surr->use_fd = i; + else { + SNDERR("Invalid range for use_fd (0-2): %li", i); + goto __error; + } + } + if (snd_config_search(n, "open_single", &n1) >= 0) { + snd_config_iterator_t i, next; + int device = 0, subdevice = -1; + if (snd_config_get_type(n1) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + goto __error; + } + snd_config_for_each(i, next, n1) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + unsigned long i; + if (!strcmp(id, "device")) { + if ((err = snd_config_get_integer(n, &i)) < 0) { + SNDERR("Invalid type for %s", id); + goto __error; + } + device = i; + } else if (!strcmp(id, "subdevice")) { + if ((err = snd_config_get_integer(n, &i)) < 0) { + SNDERR("Invalid type for %s", id); + goto __error; + } + subdevice = i; + } else { + SNDERR("Invalid field %s", id); + goto __error; + } + } + if (stream == SND_PCM_STREAM_CAPTURE && !(surr->caps & SURR_CAP_CAPTURE)) { + err = -ENODEV; + goto __error; + } + switch (stype) { + case SND_PCM_SURROUND_40: + if (!(surr->caps & SURR_CAP_4CH)) { + err = -ENODEV; + goto __error; + } + break; + case SND_PCM_SURROUND_51: + if (!(surr->caps & SURR_CAP_6CH)) { + err = -ENODEV; + goto __error; + } + break; + } + if ((err = snd_pcm_surround_one_stream(surr, stype, surr->card, device, subdevice, stream, mode)) < 0) { + SNDERR("surround single stream open error %i,%i,%i: %s", surr->card, device, subdevice, snd_strerror(err)); + goto __error; + } + opened = 1; + } else if (snd_config_search(n, "open_multi", &n1) >= 0) { + snd_config_iterator_t i, next; + int device[3] = { 0, 0, 0 }, subdevice[3] = { -1, -1, -1 }; + if (snd_config_get_type(n1) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + goto __error; + } + snd_config_for_each(i, next, n1) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + snd_config_iterator_t i, next; + if (!strcmp(id, "device") || !strcmp(id, "subdevice")) { + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("compound type expected"); + goto __error; + } + snd_config_for_each(i, next, n) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + int idx = atoi(snd_config_get_id(n)); + unsigned long i; + if (idx < 0 || idx > 2) { + SNDERR("Invalid index %s", snd_config_get_id(n)); + goto __error; + } + if ((err = snd_config_get_integer(n, &i)) < 0) { + SNDERR("Invalid type for %s", id); + goto __error; + } + if (!strcmp(id, "device")) + device[idx] = i; + else + subdevice[idx] = i; + } + } else { + SNDERR("Invalid field %s", id); + goto __error; + } + } + if (stream == SND_PCM_STREAM_CAPTURE && !(surr->caps & SURR_CAP_CAPTURE)) { + err = -ENODEV; + goto __error; + } + switch (stype) { + case SND_PCM_SURROUND_40: + if (!(surr->caps & SURR_CAP_4CH)) { + err = -ENODEV; + goto __error; + } + break; + case SND_PCM_SURROUND_51: + if (!(surr->caps & SURR_CAP_6CH)) { + err = -ENODEV; + goto __error; + } + break; + } + if ((err = snd_pcm_surround_three_streams(surr, stype, surr->card, device[0], subdevice[0], device[1], subdevice[1], device[2], subdevice[2], stream, mode)) < 0) { + SNDERR("surround single stream open error %i,%i,%i,%i,%i,%i,%i: %s", surr->card, device[0], subdevice[0], device[1], subdevice[1], device[2], subdevice[2], snd_strerror(err)); + goto __error; + } + opened = 1; + } + if (opened == 0) { + err = -ENODEV; + goto __error; + } + if (snd_config_search(n, "open_control", &n1) >= 0) { + if ((err = snd_sctl_build(ctl, &surr->store, n1, NULL)) < 0) { + SNDERR("snd_sctl_build : %s\n", snd_strerror(err)); + goto __error; + } + } + return 0; } + res = -ENOENT; + SNDERR("configuration for card %i not found", (int)ctype); + __error: + snd_config_delete(conf); + return res; } - -#define SND_CARD_TYPE_NONE SNDRV_CARD_TYPE_SB_10 /* this card definitely doesn't support surround */ - -static surround_open_t open_table[] = { - { type: SND_CARD_TYPE_SI_7018, flags: 0, scount: count_si7018, sopen: open_si7018, sclose: NULL }, - { type: SND_CARD_TYPE_FM801, flags: 0, scount: count_generic, sopen: open_fm801, sclose: NULL }, - { type: SND_CARD_TYPE_ENS1370, flags: SURR_FLG_NO_6CH|SURR_FLG_NO_CTL_CLOSE|SURR_FLG_FD1, scount: count_generic, sopen: open_ens1370, sclose: close_ens1370 }, - { type: SND_CARD_TYPE_YMFPCI, flags: SURR_FLG_NO_6CH, scount: count_ymfpci, sopen: open_ymfpci, sclose: NULL }, - { type: SND_CARD_TYPE_TRID4DWAVENX, flags: SURR_FLG_NO_6CH|SURR_FLG_NO_CTL_CLOSE, scount: count_trid4nx, sopen: open_trid4nx, sclose: close_trid4nx }, - { type: SND_CARD_TYPE_INTEL8X0, flags: 0, scount: count_generic, sopen: open_fm801, sclose: NULL }, - { type: SND_CARD_TYPE_NONE, flags: 0, scount: NULL, sopen: NULL, sclose: NULL } -}; - int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev, snd_pcm_surround_type_t type, snd_pcm_stream_t stream, int mode) @@ -825,12 +780,9 @@ int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev, snd_ctl_t *ctl = NULL; snd_ctl_card_info_t *info; snd_card_type_t ctype; - surround_open_t *po; int err; assert(pcmp); - if (dev != 0) - return -EINVAL; /* not supported at the time */ surr = calloc(1, sizeof(snd_pcm_surround_t)); if (!surr) return -ENOMEM; @@ -850,41 +802,16 @@ int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev, return err; } surr->ctl = ctl; + surr->card = card; + surr->device = dev; + surr->caps = SURR_CAP_4CH; snd_ctl_card_info_alloca(&info); if ((err = snd_ctl_card_info(ctl, info)) < 0) goto __error; ctype = snd_ctl_card_info_get_type(info); - for (po = open_table; po->type != SND_CARD_TYPE_NONE; po++) { - if (po->type == ctype) { - surr->po = po; - if (stream == SND_PCM_STREAM_CAPTURE && !(po->flags & SURR_FLG_CAPTURE)) { - err = -ENODEV; - goto __error; - } - switch (type) { - case SND_PCM_SURROUND_40: - if (po->flags & SURR_FLG_NO_4CH) { - err = -ENODEV; - goto __error; - } - break; - case SND_PCM_SURROUND_51: - if (po->flags & SURR_FLG_NO_6CH) { - err = -ENODEV; - goto __error; - } - break; - } - if ((err = po->sopen(surr, info, type, stream, mode)) < 0) - goto __error; - break; - } - } - if (po->type == SND_CARD_TYPE_NONE) { - err = -ENODEV; + if ((err = load_surround_config(ctl, surr, type, ctype, stream, mode)) < 0) goto __error; - } - if (!(po->flags & SURR_FLG_NO_CTL_CLOSE)) { + if (surr->store == NULL) { snd_ctl_close(ctl); surr->ctl = NULL; } @@ -904,9 +831,9 @@ int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev, pcm->fast_ops = &snd_pcm_surround_fast_ops; pcm->fast_op_arg = pcm; pcm->private_data = surr; - pcm->poll_fd = surr->pcm[po->flags & SURR_FLG_FD1 ? 1 : 0]->poll_fd; - pcm->hw_ptr = surr->pcm[po->flags & SURR_FLG_FD1 ? 1 : 0]->hw_ptr; - pcm->appl_ptr = surr->pcm[po->flags & SURR_FLG_FD1 ? 1 : 0]->appl_ptr; + pcm->poll_fd = surr->pcm[surr->use_fd]->poll_fd; + pcm->hw_ptr = surr->pcm[surr->use_fd]->hw_ptr; + pcm->appl_ptr = surr->pcm[surr->use_fd]->appl_ptr; *pcmp = pcm; diff --git a/src/pcm/surround.conf b/src/pcm/surround.conf index 3a35fe4d..9295607e 100644 --- a/src/pcm/surround.conf +++ b/src/pcm/surround.conf @@ -3,7 +3,7 @@ # surround_plugin.SI_7018 { # test only - channels.six = true; + channels_six = true; open_multi { device.0 = 0; subdevice.0 = -1; @@ -12,10 +12,28 @@ surround_plugin.SI_7018 { # test only device.2 = 0; subdevice.2 = -1; } + open_control.0 { + iface = MIXER; + name = 'CD Playback Switch'; + index = 0; + lock = true; + preserve = true; + value.0 = 1; + value.1 = 1; + } + open_control.1 { + iface = MIXER; + name = 'CD Playback Volume'; + index = 0; + lock = true; + preserve = true; + value.0 = 16; + value.1 = 19; + } } surround_plugin.FM801 { - channels.six = true; + channels_six = true; open_single { device = 0; subdevice = 0; @@ -97,8 +115,15 @@ surround_plugin.TRID4DWAVENX { } surround_plugin.INTEL8X0 { - channels.four = true; - channels.six = true; + channels_six = true; + route { + channel.0 = 0; # FR = FR + channel.1 = 1; # FL = FL + channel.2 = 4; # SR = Center + channel.3 = 5; # SL = LFE + channel.4 = 2; # Center = SR + channel.5 = 3; # LFE = SL + } open_single { device = 0; subdevice = 0; diff --git a/test/Makefile.am b/test/Makefile.am index 126348d2..f36e4e8f 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,5 +1,5 @@ check_PROGRAMS=control mixer switches pause pcm pcmtest latency seq \ - playmidi1 timer loopback rawmidi midiloop + playmidi1 timer loopback rawmidi midiloop cardid control_LDADD=../src/libasound.la mixer_LDADD=../src/libasound.la @@ -14,6 +14,7 @@ timer_LDADD=../src/libasound.la loopback_LDADD=../src/libasound.la rawmidi_LDADD=../src/libasound.la midiloop_LDADD=../src/libasound.la +cardid_LDADD=../src/libasound.la INCLUDES=-I$(top_srcdir)/include # CFLAGS=-static -Wall -pipe -g diff --git a/test/cardid.c b/test/cardid.c new file mode 100644 index 00000000..94dadb99 --- /dev/null +++ b/test/cardid.c @@ -0,0 +1,26 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include "../include/asoundlib.h" + +int main(int argc, char *argv[]) +{ + char *str; + snd_card_type_t type; + int idx, err; + + for (idx = 1; idx < argc; idx++) { + if (isdigit(argv[idx][0])) { + type = (snd_card_type_t)atoi(argv[idx]); + err = snd_card_type_enum_to_string(type, &str); + printf("enum_to_string: input %i -> '%s', error %i\n", (int)type, str, err); + } else { + str = argv[idx]; + err = snd_card_type_string_to_enum(str, &type); + printf("string_to_enum: input '%s' -> %i, error %i\n", str, (int)type, err); + } + } + return EXIT_SUCCESS; +} |