mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-11-03 09:01:52 -05:00 
			
		
		
		
	PCM: Add chmap options to hw and null plugins
Add a config definition "chmap" to override (or enhance) the channel maps. So far, only a single channel map can be provided, and the channel count consistency isn't strictly tested at all. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
		
							parent
							
								
									81135aac49
								
							
						
					
					
						commit
						e1975d20f5
					
				
					 4 changed files with 111 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -7531,6 +7531,35 @@ snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str)
 | 
			
		|||
	return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
snd_pcm_chmap_query_t **
 | 
			
		||||
_snd_pcm_make_single_query_chmaps(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) {
 | 
			
		||||
		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)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_chmap_t *map;
 | 
			
		||||
 | 
			
		||||
	map = malloc((src->channels + 1) * sizeof(int));
 | 
			
		||||
	if (!map)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	memcpy(map, src, (src->channels + 1) * sizeof(int));
 | 
			
		||||
	return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * basic helpers
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,6 +107,7 @@ typedef struct {
 | 
			
		|||
	int channels;
 | 
			
		||||
	/* for chmap */
 | 
			
		||||
	unsigned int chmap_caps;
 | 
			
		||||
	snd_pcm_chmap_t *chmap_override;
 | 
			
		||||
} snd_pcm_hw_t;
 | 
			
		||||
 | 
			
		||||
#define SNDRV_FILE_PCM_STREAM_PLAYBACK		ALSA_DEVICE_DIRECTORY "pcmC%iD%ip"
 | 
			
		||||
| 
						 | 
				
			
			@ -1144,6 +1145,9 @@ static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
 | 
			
		|||
	snd_pcm_hw_t *hw = pcm->private_data;
 | 
			
		||||
	snd_pcm_chmap_query_t **map;
 | 
			
		||||
 | 
			
		||||
	if (hw->chmap_override)
 | 
			
		||||
		return _snd_pcm_make_single_query_chmaps(hw->chmap_override);
 | 
			
		||||
 | 
			
		||||
	if (!chmap_caps(hw, CHMAP_CTL_QUERY))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1166,6 +1170,9 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
 | 
			
		|||
	unsigned int i;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (hw->chmap_override)
 | 
			
		||||
		return _snd_pcm_copy_chmap(hw->chmap_override);
 | 
			
		||||
 | 
			
		||||
	if (!chmap_caps(hw, CHMAP_CTL_GET))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1220,6 +1227,9 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
 | 
			
		|||
	unsigned int i;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (hw->chmap_override)
 | 
			
		||||
		return -ENXIO;
 | 
			
		||||
 | 
			
		||||
	if (!chmap_caps(hw, CHMAP_CTL_SET))
 | 
			
		||||
		return -ENXIO;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1593,6 +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
 | 
			
		||||
}
 | 
			
		||||
\endcode
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1629,6 +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_hw_t *hw;
 | 
			
		||||
 | 
			
		||||
	/* look for defaults.pcm.nonblock definition */
 | 
			
		||||
| 
						 | 
				
			
			@ -1719,6 +1731,20 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
 | 
			
		|||
			channels = val;
 | 
			
		||||
			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);
 | 
			
		||||
			if (!chmap) {
 | 
			
		||||
				SNDERR("Invalid channel map for %s", id);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		SNDERR("Unknown field %s", id);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1750,6 +1776,8 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
 | 
			
		|||
		hw->channels = channels;
 | 
			
		||||
	if (rate > 0)
 | 
			
		||||
		hw->rate = rate;
 | 
			
		||||
	if (chmap)
 | 
			
		||||
		hw->chmap_override = chmap;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -973,3 +973,8 @@ static inline void gettimestamp(snd_htimestamp_t *tstamp, int monotonic)
 | 
			
		|||
	}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +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_null_t;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -268,6 +269,24 @@ static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_pa
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void snd_pcm_null_dump(snd_pcm_t *pcm, snd_output_t *out)
 | 
			
		||||
{
 | 
			
		||||
	snd_output_printf(out, "Null PCM\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -290,6 +309,9 @@ static const snd_pcm_ops_t snd_pcm_null_ops = {
 | 
			
		|||
	.async = snd_pcm_null_async,
 | 
			
		||||
	.mmap = snd_pcm_generic_mmap,
 | 
			
		||||
	.munmap = snd_pcm_generic_munmap,
 | 
			
		||||
	.query_chmaps = snd_pcm_null_query_chmaps,
 | 
			
		||||
	.get_chmap = snd_pcm_null_get_chmap,
 | 
			
		||||
	.set_chmap = NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const snd_pcm_fast_ops_t snd_pcm_null_fast_ops = {
 | 
			
		||||
| 
						 | 
				
			
			@ -385,6 +407,7 @@ and /dev/full (capture, must be readable).
 | 
			
		|||
\code
 | 
			
		||||
pcm.name {
 | 
			
		||||
        type null               # Null PCM
 | 
			
		||||
	[chmap MAP]
 | 
			
		||||
}
 | 
			
		||||
\endcode
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -415,6 +438,10 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
 | 
			
		|||
		       snd_pcm_stream_t stream, int mode)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	snd_pcm_null_t *null;
 | 
			
		||||
	snd_pcm_chmap_t *chmap = NULL;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	snd_config_for_each(i, next, conf) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		const char *id;
 | 
			
		||||
| 
						 | 
				
			
			@ -422,10 +449,31 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
 | 
			
		|||
			continue;
 | 
			
		||||
		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);
 | 
			
		||||
			if (!chmap) {
 | 
			
		||||
				SNDERR("Invalid channel map for %s", id);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		SNDERR("Unknown field %s", id);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	return snd_pcm_null_open(pcmp, name, stream, mode);
 | 
			
		||||
	err = snd_pcm_null_open(pcmp, name, stream, mode);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	null = (*pcmp)->private_data;
 | 
			
		||||
	null->chmap = chmap;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#ifndef DOC_HIDDEN
 | 
			
		||||
SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue