summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-09-12 17:13:22 +0200
committerTakashi Iwai <tiwai@suse.de>2012-09-13 08:24:59 +0200
commitec7acfc408856cfe71a122e3b5387463d7b3d919 (patch)
treefb4065f591e09821c763db999398209524ec8b4d
parent5ba11d69dafbd4502469fd957b4580169779664c (diff)
downloadalsa-lib-ec7acfc408856cfe71a122e3b5387463d7b3d919.tar.gz
PCM: Use compounds for overriding / enhancing chmaps
Instead of a single channel map, multiple channel maps can be provided in a form of compound (array) to hw and null plugins. In null get_chmap, the channel map corresponding to the current channels is copied from the given channel maps. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--src/conf/cards/Audigy.conf6
-rw-r--r--src/conf/cards/Audigy2.conf8
-rw-r--r--src/conf/cards/EMU10K1.conf6
-rw-r--r--src/pcm/pcm.c109
-rw-r--r--src/pcm/pcm_hw.c25
-rw-r--r--src/pcm/pcm_local.h10
-rw-r--r--src/pcm/pcm_null.c25
7 files changed, 144 insertions, 45 deletions
diff --git a/src/conf/cards/Audigy.conf b/src/conf/cards/Audigy.conf
index e008b678..4fe38732 100644
--- a/src/conf/cards/Audigy.conf
+++ b/src/conf/cards/Audigy.conf
@@ -13,7 +13,7 @@ Audigy.pcm.front.0 {
slave.pcm {
type hw
card $CARD
- chmap "FL,FR"
+ chmap [ "UNKNOWN" "FL,FR" ]
}
hooks.0 {
type ctl_elems
@@ -66,7 +66,7 @@ Audigy.pcm.rear.0 {
slave.pcm {
type hw
card $CARD
- chmap "RL,RR"
+ chmap [ "UNKNOWN" "RL,RR" ]
}
hooks.0 {
type ctl_elems
@@ -102,7 +102,7 @@ Audigy.pcm.center_lfe.0 {
slave.pcm {
type hw
card $CARD
- chmap "FC,LFE"
+ chmap [ "UNKNOWN" "FC,LFE" ]
}
hooks.0 {
type ctl_elems
diff --git a/src/conf/cards/Audigy2.conf b/src/conf/cards/Audigy2.conf
index e34fadcf..db45776e 100644
--- a/src/conf/cards/Audigy2.conf
+++ b/src/conf/cards/Audigy2.conf
@@ -13,7 +13,7 @@ Audigy2.pcm.front.0 {
slave.pcm {
type hw
card $CARD
- chmap "FL,FR"
+ chmap [ "UNKNOWN" "FL,FR" ]
}
hooks.0 {
type ctl_elems
@@ -66,7 +66,7 @@ Audigy2.pcm.rear.0 {
slave.pcm {
type hw
card $CARD
- chmap "RL,RR"
+ chmap [ "UNKNOWN" "RL,RR" ]
}
hooks.0 {
type ctl_elems
@@ -102,7 +102,7 @@ Audigy2.pcm.center_lfe.0 {
slave.pcm {
type hw
card $CARD
- chmap "FC,LFE"
+ chmap [ "UNKNOWN" "FC,LFE" ]
}
hooks.0 {
type ctl_elems
@@ -154,7 +154,7 @@ Audigy2.pcm.side.0 {
slave.pcm {
type hw
card $CARD
- chmap "SL,SR"
+ chmap [ "UNKNOWN" "SL,SR" ]
}
hooks.0 {
type ctl_elems
diff --git a/src/conf/cards/EMU10K1.conf b/src/conf/cards/EMU10K1.conf
index 02a732fb..d51f6dc0 100644
--- a/src/conf/cards/EMU10K1.conf
+++ b/src/conf/cards/EMU10K1.conf
@@ -15,7 +15,7 @@ EMU10K1.pcm.front.0 {
slave.pcm {
type hw
card $CARD
- chmap "FL,FR"
+ chmap [ "UNKNOWN" "FL,FR" ]
}
hooks.0 {
type ctl_elems
@@ -74,7 +74,7 @@ EMU10K1.pcm.rear.0 {
slave.pcm {
type hw
card $CARD
- chmap "RL,RR"
+ chmap [ "UNKNOWN" "RL,RR" ]
}
hooks.0 {
type ctl_elems
@@ -113,7 +113,7 @@ EMU10K1.pcm.center_lfe.0 {
slave.pcm {
type hw
card $CARD
- chmap "FC,LFE"
+ chmap [ "UNKNOWN" "FC,LFE" ]
}
hooks.0 {
type ctl_elems
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index b102c625..19a9abe4 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -7531,25 +7531,37 @@ snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str)
return map;
}
+/* copy a single channel map with the fixed type to chmap_query pointer */
+static int _copy_to_fixed_query_map(snd_pcm_chmap_query_t **dst,
+ const snd_pcm_chmap_t *src)
+{
+ *dst = malloc((src->channels + 2) * sizeof(int));
+ if (!*dst)
+ return -ENOMEM;
+ (*dst)->type = SND_CHMAP_TYPE_FIXED;
+ memcpy(&(*dst)->map, src, (src->channels + 1) * sizeof(int));
+ return 0;
+}
+
+#ifndef DOC_HIDDEN
+/* make a chmap_query array from a single channel map */
snd_pcm_chmap_query_t **
-_snd_pcm_make_single_query_chmaps(snd_pcm_chmap_t *src)
+_snd_pcm_make_single_query_chmaps(const snd_pcm_chmap_t *src)
{
snd_pcm_chmap_query_t **maps;
maps = calloc(2, sizeof(*maps));
if (!maps)
return NULL;
- *maps = malloc((src->channels + 2) * sizeof(int));
- if (!*maps) {
+ if (_copy_to_fixed_query_map(maps, src)) {
free(maps);
return NULL;
}
- (*maps)->type = SND_CHMAP_TYPE_FIXED;
- memcpy(&(*maps)->map, src, (src->channels + 1) * sizeof(int));
return maps;
}
-snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src)
+/* make a copy of chmap */
+snd_pcm_chmap_t *_snd_pcm_copy_chmap(const snd_pcm_chmap_t *src)
{
snd_pcm_chmap_t *map;
@@ -7560,6 +7572,91 @@ snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src)
return map;
}
+/* make a copy of channel maps */
+snd_pcm_chmap_query_t **
+_snd_pcm_copy_chmap_query(snd_pcm_chmap_query_t * const *src)
+{
+ snd_pcm_chmap_query_t * const *p;
+ snd_pcm_chmap_query_t **maps;
+ int i, nums;
+
+ for (nums = 0, p = src; *p; p++)
+ nums++;
+
+ maps = calloc(nums + 1, sizeof(*maps));
+ if (!maps)
+ return NULL;
+ for (i = 0; i < nums; i++) {
+ maps[i] = malloc((src[i]->map.channels + 2) * sizeof(int));
+ if (!maps[i]) {
+ snd_pcm_free_chmaps(maps);
+ return NULL;
+ }
+ memcpy(maps[i], src[i], (src[i]->map.channels + 2) * sizeof(int));
+ }
+ return maps;
+}
+
+/* select the channel map with the current PCM channels and make a copy */
+snd_pcm_chmap_t *
+_snd_pcm_choose_fixed_chmap(snd_pcm_t *pcm, snd_pcm_chmap_query_t * const *maps)
+{
+ snd_pcm_chmap_query_t * const *p;
+
+ for (p = maps; *p; p++) {
+ if ((*p)->map.channels == pcm->channels)
+ return _snd_pcm_copy_chmap(&(*p)->map);
+ }
+ return NULL;
+}
+
+/* make chmap_query array from the config tree;
+ * conf must be a compound (array)
+ */
+snd_pcm_chmap_query_t **
+_snd_pcm_parse_config_chmaps(snd_config_t *conf)
+{
+ snd_pcm_chmap_t *chmap;
+ snd_pcm_chmap_query_t **maps;
+ snd_config_iterator_t i, next;
+ const char *str;
+ int nums, err;
+
+ if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
+ return NULL;
+
+ nums = 0;
+ snd_config_for_each(i, next, conf) {
+ nums++;
+ }
+
+ maps = calloc(nums + 1, sizeof(*maps));
+ if (!maps)
+ return NULL;
+
+ nums = 0;
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ err = snd_config_get_string(n, &str);
+ if (err < 0)
+ goto error;
+ chmap = snd_pcm_chmap_parse_string(str);
+ if (!chmap)
+ goto error;
+ if (_copy_to_fixed_query_map(maps + nums, chmap)) {
+ free(chmap);
+ goto error;
+ }
+ nums++;
+ }
+ return maps;
+
+ error:
+ snd_pcm_free_chmaps(maps);
+ return NULL;
+}
+#endif /* DOC_HIDDEN */
+
/*
* basic helpers
*/
diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
index 514f8ca4..649bf9df 100644
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -107,7 +107,7 @@ typedef struct {
int channels;
/* for chmap */
unsigned int chmap_caps;
- snd_pcm_chmap_t *chmap_override;
+ snd_pcm_chmap_query_t **chmap_override;
} snd_pcm_hw_t;
#define SNDRV_FILE_PCM_STREAM_PLAYBACK ALSA_DEVICE_DIRECTORY "pcmC%iD%ip"
@@ -1146,7 +1146,7 @@ static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
snd_pcm_chmap_query_t **map;
if (hw->chmap_override)
- return _snd_pcm_make_single_query_chmaps(hw->chmap_override);
+ return _snd_pcm_copy_chmap_query(hw->chmap_override);
if (!chmap_caps(hw, CHMAP_CTL_QUERY))
return NULL;
@@ -1171,7 +1171,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
int ret;
if (hw->chmap_override)
- return _snd_pcm_copy_chmap(hw->chmap_override);
+ return _snd_pcm_choose_fixed_chmap(pcm, hw->chmap_override);
if (!chmap_caps(hw, CHMAP_CTL_GET))
return NULL;
@@ -1603,7 +1603,7 @@ pcm.name {
[format STR] # Restrict only to the given format
[channels INT] # Restrict only to the given channels
[rate INT] # Restrict only to the given rate
- [chmap MAP] # Override channel map
+ [chmap MAP] # Override channel maps; MAP is a string array
}
\endcode
@@ -1640,7 +1640,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
snd_config_t *n;
int nonblock = 1; /* non-block per default */
- snd_pcm_chmap_t *chmap = NULL;
+ snd_pcm_chmap_query_t **chmap = NULL;
snd_pcm_hw_t *hw;
/* look for defaults.pcm.nonblock definition */
@@ -1732,13 +1732,8 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
continue;
}
if (strcmp(id, "chmap") == 0) {
- err = snd_config_get_string(n, &str);
- if (err < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- free(chmap);
- chmap = snd_pcm_chmap_parse_string(str);
+ snd_pcm_free_chmaps(chmap);
+ chmap = _snd_pcm_parse_config_chmaps(n);
if (!chmap) {
SNDERR("Invalid channel map for %s", id);
return -EINVAL;
@@ -1746,17 +1741,21 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
continue;
}
SNDERR("Unknown field %s", id);
+ snd_pcm_free_chmaps(chmap);
return -EINVAL;
}
if (card < 0) {
SNDERR("card is not defined");
+ snd_pcm_free_chmaps(chmap);
return -EINVAL;
}
err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,
mode | (nonblock ? SND_PCM_NONBLOCK : 0),
0, sync_ptr_ioctl);
- if (err < 0)
+ if (err < 0) {
+ snd_pcm_free_chmaps(chmap);
return err;
+ }
if (nonblock && ! (mode & SND_PCM_NONBLOCK)) {
/* revert to blocking mode for read/write access */
snd_pcm_hw_nonblock(*pcmp, 0);
diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
index 726bd6d0..e7798fd0 100644
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -975,6 +975,12 @@ static inline void gettimestamp(snd_htimestamp_t *tstamp, int monotonic)
}
snd_pcm_chmap_query_t **
-_snd_pcm_make_single_query_chmaps(snd_pcm_chmap_t *src);
-snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src);
+_snd_pcm_make_single_query_chmaps(const snd_pcm_chmap_t *src);
+snd_pcm_chmap_t *_snd_pcm_copy_chmap(const snd_pcm_chmap_t *src);
+snd_pcm_chmap_query_t **
+_snd_pcm_copy_chmap_query(snd_pcm_chmap_query_t * const *src);
+snd_pcm_chmap_query_t **
+_snd_pcm_parse_config_chmaps(snd_config_t *conf);
+snd_pcm_chmap_t *
+_snd_pcm_choose_fixed_chmap(snd_pcm_t *pcm, snd_pcm_chmap_query_t * const *maps);
diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c
index 223df24a..a154a5cd 100644
--- a/src/pcm/pcm_null.c
+++ b/src/pcm/pcm_null.c
@@ -44,7 +44,7 @@ typedef struct {
snd_pcm_uframes_t appl_ptr;
snd_pcm_uframes_t hw_ptr;
int poll_fd;
- snd_pcm_chmap_t *chmap;
+ snd_pcm_chmap_query_t **chmap;
} snd_pcm_null_t;
#endif
@@ -274,7 +274,7 @@ static snd_pcm_chmap_query_t **snd_pcm_null_query_chmaps(snd_pcm_t *pcm)
snd_pcm_null_t *null = pcm->private_data;
if (null->chmap)
- return _snd_pcm_make_single_query_chmaps(null->chmap);
+ return _snd_pcm_copy_chmap_query(null->chmap);
return NULL;
}
@@ -283,7 +283,7 @@ static snd_pcm_chmap_t *snd_pcm_null_get_chmap(snd_pcm_t *pcm)
snd_pcm_null_t *null = pcm->private_data;
if (null->chmap)
- return _snd_pcm_copy_chmap(null->chmap);
+ return _snd_pcm_choose_fixed_chmap(pcm, null->chmap);
return NULL;
}
@@ -407,7 +407,7 @@ and /dev/full (capture, must be readable).
\code
pcm.name {
type null # Null PCM
- [chmap MAP]
+ [chmap MAP] # Provide channel maps; MAP is a string array
}
\endcode
@@ -439,7 +439,7 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
{
snd_config_iterator_t i, next;
snd_pcm_null_t *null;
- snd_pcm_chmap_t *chmap = NULL;
+ snd_pcm_chmap_query_t **chmap = NULL;
int err;
snd_config_for_each(i, next, conf) {
@@ -450,14 +450,8 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "chmap") == 0) {
- const char *str;
- err = snd_config_get_string(n, &str);
- if (err < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- free(chmap);
- chmap = snd_pcm_chmap_parse_string(str);
+ snd_pcm_free_chmaps(chmap);
+ chmap = _snd_pcm_parse_config_chmaps(n);
if (!chmap) {
SNDERR("Invalid channel map for %s", id);
return -EINVAL;
@@ -465,11 +459,14 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
continue;
}
SNDERR("Unknown field %s", id);
+ snd_pcm_free_chmaps(chmap);
return -EINVAL;
}
err = snd_pcm_null_open(pcmp, name, stream, mode);
- if (err < 0)
+ if (err < 0) {
+ snd_pcm_free_chmaps(chmap);
return err;
+ }
null = (*pcmp)->private_data;
null->chmap = chmap;