File 0020-PCM-Use-compounds-for-overriding-enhancing-chmaps.patch of Package alsa

From ec7acfc408856cfe71a122e3b5387463d7b3d919 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Wed, 12 Sep 2012 17:13:22 +0200
Subject: [PATCH 20/30] 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>
---
 src/conf/cards/Audigy.conf  |    6 +-
 src/conf/cards/Audigy2.conf |    8 +--
 src/conf/cards/EMU10K1.conf |    6 +-
 src/pcm/pcm.c               |  109 +++++++++++++++++++++++++++++++++++++++++---
 src/pcm/pcm_hw.c            |   25 ++++------
 src/pcm/pcm_local.h         |   10 +++-
 src/pcm/pcm_null.c          |   25 ++++------
 7 files changed, 144 insertions(+), 45 deletions(-)

--- 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
--- 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
--- 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
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -7531,25 +7531,37 @@ snd_pcm_chmap_t *snd_pcm_chmap_parse_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
 	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
  */
--- 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_h
 	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_c
 	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, c
 	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, c
 			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, c
 			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);
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -975,6 +975,12 @@ static inline void gettimestamp(snd_htim
 }
 
 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);
 
--- 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_n
 	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
 	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,
 {
 	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,
 		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,
 			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;
openSUSE Build Service is sponsored by