mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
control: remap - add possibility to remap multiple source channels
For UCM (ASoC), there is a requirement to remap two stereo controls to one stereo control (amplifiers). Link: https://github.com/alsa-project/alsa-ucm-conf/pull/525 Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
e9e3c01ff7
commit
6855cb838d
1 changed files with 97 additions and 18 deletions
|
|
@ -2,11 +2,11 @@
|
||||||
* \file control/control_remap.c
|
* \file control/control_remap.c
|
||||||
* \brief CTL Remap Plugin Interface
|
* \brief CTL Remap Plugin Interface
|
||||||
* \author Jaroslav Kysela <perex@perex.cz>
|
* \author Jaroslav Kysela <perex@perex.cz>
|
||||||
* \date 2021
|
* \date 2021-2025
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Control - Remap Controls
|
* Control - Remap Controls
|
||||||
* Copyright (c) 2021 by Jaroslav Kysela <perex@perex.cz>
|
* Copyright (c) 2021-2025 by Jaroslav Kysela <perex@perex.cz>
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or modify
|
* This library is free software; you can redistribute it and/or modify
|
||||||
|
|
@ -76,6 +76,7 @@ typedef struct {
|
||||||
snd_ctl_elem_id_t id_child;
|
snd_ctl_elem_id_t id_child;
|
||||||
size_t channel_map_items;
|
size_t channel_map_items;
|
||||||
size_t channel_map_alloc;
|
size_t channel_map_alloc;
|
||||||
|
unsigned int src_channels;
|
||||||
long *channel_map;
|
long *channel_map;
|
||||||
} *controls;
|
} *controls;
|
||||||
unsigned int event_mask;
|
unsigned int event_mask;
|
||||||
|
|
@ -481,7 +482,7 @@ static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *cont
|
||||||
snd_ctl_map_t *map;
|
snd_ctl_map_t *map;
|
||||||
struct snd_ctl_map_ctl *mctl;
|
struct snd_ctl_map_ctl *mctl;
|
||||||
snd_ctl_elem_value_t control2;
|
snd_ctl_elem_value_t control2;
|
||||||
size_t item, index;
|
size_t item, index, src_index;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
map = remap_find_map_id(priv, &control->id);
|
map = remap_find_map_id(priv, &control->id);
|
||||||
|
|
@ -501,19 +502,36 @@ static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *cont
|
||||||
if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
|
if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
|
||||||
map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
|
map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
|
||||||
for (index = 0; index < mctl->channel_map_items; index++) {
|
for (index = 0; index < mctl->channel_map_items; index++) {
|
||||||
long src = mctl->channel_map[index];
|
long base_idx = mctl->src_channels * index;
|
||||||
|
long src = mctl->channel_map[base_idx];
|
||||||
if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value))
|
if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value))
|
||||||
control->value.integer.value[index] = control2.value.integer.value[src];
|
control->value.integer.value[index] = control2.value.integer.value[src];
|
||||||
|
for (src_index = 1; src_index < mctl->src_channels; src_index++) {
|
||||||
|
src = mctl->channel_map[base_idx + src_index];
|
||||||
|
if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value))
|
||||||
|
if (control2.value.integer.value[src] < control->value.integer.value[index])
|
||||||
|
control->value.integer.value[index] = control2.value.integer.value[src];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
|
} else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
|
||||||
for (index = 0; index < mctl->channel_map_items; index++) {
|
for (index = 0; index < mctl->channel_map_items; index++) {
|
||||||
long src = mctl->channel_map[index];
|
long base_idx = mctl->src_channels * index;
|
||||||
|
long src = mctl->channel_map[base_idx];
|
||||||
if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value))
|
if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value))
|
||||||
control->value.integer64.value[index] = control2.value.integer64.value[src];
|
control->value.integer64.value[index] = control2.value.integer64.value[src];
|
||||||
|
for (src_index = 1; src_index < mctl->src_channels; src_index++) {
|
||||||
|
src = mctl->channel_map[base_idx + src_index];
|
||||||
|
if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value))
|
||||||
|
if (control2.value.integer64.value[src] < control->value.integer64.value[index])
|
||||||
|
control->value.integer64.value[index] = control2.value.integer64.value[src];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) {
|
} else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) {
|
||||||
|
/* does it make sense to merge bytes? */
|
||||||
|
if (mctl->src_channels > 1)
|
||||||
|
return -EINVAL;
|
||||||
for (index = 0; index < mctl->channel_map_items; index++) {
|
for (index = 0; index < mctl->channel_map_items; index++) {
|
||||||
long src = mctl->channel_map[index];
|
long src = mctl->channel_map[mctl->src_channels];
|
||||||
if ((unsigned long)src < ARRAY_SIZE(control->value.bytes.data))
|
if ((unsigned long)src < ARRAY_SIZE(control->value.bytes.data))
|
||||||
control->value.bytes.data[index] = control2.value.bytes.data[src];
|
control->value.bytes.data[index] = control2.value.bytes.data[src];
|
||||||
}
|
}
|
||||||
|
|
@ -544,7 +562,7 @@ static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *con
|
||||||
snd_ctl_map_t *map;
|
snd_ctl_map_t *map;
|
||||||
struct snd_ctl_map_ctl *mctl;
|
struct snd_ctl_map_ctl *mctl;
|
||||||
snd_ctl_elem_value_t control2;
|
snd_ctl_elem_value_t control2;
|
||||||
size_t item, index;
|
size_t item, index, src_index;
|
||||||
int err, changes;
|
int err, changes;
|
||||||
|
|
||||||
map = remap_find_map_id(priv, &control->id);
|
map = remap_find_map_id(priv, &control->id);
|
||||||
|
|
@ -564,21 +582,40 @@ static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *con
|
||||||
if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
|
if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
|
||||||
map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
|
map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
|
||||||
for (index = 0; index < mctl->channel_map_items; index++) {
|
for (index = 0; index < mctl->channel_map_items; index++) {
|
||||||
long dst = mctl->channel_map[index];
|
long base_idx = mctl->src_channels * index;
|
||||||
|
long dst = mctl->channel_map[base_idx];
|
||||||
if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) {
|
if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) {
|
||||||
changes |= control2.value.integer.value[dst] != control->value.integer.value[index];
|
changes |= control2.value.integer.value[dst] != control->value.integer.value[index];
|
||||||
control2.value.integer.value[dst] = control->value.integer.value[index];
|
control2.value.integer.value[dst] = control->value.integer.value[index];
|
||||||
}
|
}
|
||||||
|
for (src_index = 1; src_index < mctl->src_channels; src_index++) {
|
||||||
|
dst = mctl->channel_map[base_idx + src_index];
|
||||||
|
if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) {
|
||||||
|
changes |= control2.value.integer.value[dst] != control->value.integer.value[index];
|
||||||
|
control2.value.integer.value[dst] = control->value.integer.value[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
|
} else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
|
||||||
for (index = 0; index < mctl->channel_map_items; index++) {
|
for (index = 0; index < mctl->channel_map_items; index++) {
|
||||||
long dst = mctl->channel_map[index];
|
long base_idx = mctl->src_channels * index;
|
||||||
|
long dst = mctl->channel_map[base_idx];
|
||||||
if ((unsigned long)dst < ARRAY_SIZE(control->value.integer64.value)) {
|
if ((unsigned long)dst < ARRAY_SIZE(control->value.integer64.value)) {
|
||||||
changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index];
|
changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index];
|
||||||
control2.value.integer64.value[dst] = control->value.integer64.value[index];
|
control2.value.integer64.value[dst] = control->value.integer64.value[index];
|
||||||
}
|
}
|
||||||
|
for (src_index = 1; src_index < mctl->src_channels; src_index++) {
|
||||||
|
dst = mctl->channel_map[base_idx + src_index];
|
||||||
|
if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) {
|
||||||
|
changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index];
|
||||||
|
control2.value.integer64.value[dst] = control->value.integer64.value[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) {
|
} else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) {
|
||||||
|
/* does it make sense to merge bytes? */
|
||||||
|
if (mctl->src_channels > 1)
|
||||||
|
return -EINVAL;
|
||||||
for (index = 0; index < mctl->channel_map_items; index++) {
|
for (index = 0; index < mctl->channel_map_items; index++) {
|
||||||
long dst = mctl->channel_map[index];
|
long dst = mctl->channel_map[index];
|
||||||
if ((unsigned long)dst < ARRAY_SIZE(control->value.bytes.data)) {
|
if ((unsigned long)dst < ARRAY_SIZE(control->value.bytes.data)) {
|
||||||
|
|
@ -1017,43 +1054,79 @@ static int add_ctl_to_map(snd_ctl_map_t *map, struct snd_ctl_map_ctl **_mctl, sn
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_chn_to_map(struct snd_ctl_map_ctl *mctl, long idx, long val)
|
static int add_chn_to_map(struct snd_ctl_map_ctl *mctl, long idx, long src_idx, long val)
|
||||||
{
|
{
|
||||||
size_t off;
|
size_t off;
|
||||||
long *map;
|
long *map;
|
||||||
|
|
||||||
|
if (src_idx >= mctl->src_channels) {
|
||||||
|
SNDERR("Wrong channel mapping (extra source channel?)");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
if (mctl->channel_map_alloc <= (size_t)idx) {
|
if (mctl->channel_map_alloc <= (size_t)idx) {
|
||||||
map = realloc(mctl->channel_map, (idx + 4) * sizeof(*map));
|
map = realloc(mctl->channel_map, mctl->src_channels * (idx + 4) * sizeof(*map));
|
||||||
if (map == NULL)
|
if (map == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
mctl->channel_map = map;
|
mctl->channel_map = map;
|
||||||
off = mctl->channel_map_alloc;
|
off = mctl->channel_map_alloc;
|
||||||
mctl->channel_map_alloc = idx + 4;
|
mctl->channel_map_alloc = idx + 4;
|
||||||
for ( ; off < mctl->channel_map_alloc; off++)
|
for ( ; off < mctl->src_channels * mctl->channel_map_alloc; off++)
|
||||||
map[off] = -1;
|
map[off] = -1;
|
||||||
}
|
}
|
||||||
if ((size_t)idx >= mctl->channel_map_items)
|
if ((size_t)idx >= mctl->channel_map_items)
|
||||||
mctl->channel_map_items = idx + 1;
|
mctl->channel_map_items = idx + 1;
|
||||||
mctl->channel_map[idx] = val;
|
mctl->channel_map[mctl->src_channels * idx + src_idx] = val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_chn_to_map_array(struct snd_ctl_map_ctl *mctl, const char *dst_id, snd_config_t *conf)
|
||||||
|
{
|
||||||
|
snd_config_iterator_t i, next;
|
||||||
|
long src_idx = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
snd_config_for_each(i, next, conf) {
|
||||||
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
|
long idx = -1, chn = -1;
|
||||||
|
if (safe_strtol(dst_id, &idx) || snd_config_get_integer(n, &chn)) {
|
||||||
|
SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
err = add_chn_to_map(mctl, idx, src_idx, chn);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
src_idx++;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_map_vindex(struct snd_ctl_map_ctl *mctl, snd_config_t *conf)
|
static int parse_map_vindex(struct snd_ctl_map_ctl *mctl, snd_config_t *conf)
|
||||||
{
|
{
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
snd_config_t *n;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
n = snd_config_iterator_entry(i);
|
||||||
|
err = snd_config_is_array(n);
|
||||||
|
if (err > 0 && (unsigned int)err > mctl->src_channels)
|
||||||
|
mctl->src_channels = err; /* count of array items */
|
||||||
|
}
|
||||||
|
snd_config_for_each(i, next, conf) {
|
||||||
|
n = snd_config_iterator_entry(i);
|
||||||
long idx = -1, chn = -1;
|
long idx = -1, chn = -1;
|
||||||
const char *id;
|
const char *id;
|
||||||
if (snd_config_get_id(n, &id) < 0)
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
continue;
|
continue;
|
||||||
if (safe_strtol(id, &idx) || snd_config_get_integer(n, &chn)) {
|
if (snd_config_is_array(n) > 0) {
|
||||||
SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn);
|
err = add_chn_to_map_array(mctl, id, n);
|
||||||
return -EINVAL;
|
} else {
|
||||||
|
if (safe_strtol(id, &idx) || snd_config_get_integer(n, &chn)) {
|
||||||
|
SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
err = add_chn_to_map(mctl, idx, 0, chn);
|
||||||
}
|
}
|
||||||
err = add_chn_to_map(mctl, idx, chn);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -1066,6 +1139,7 @@ static int parse_map_config(struct snd_ctl_map_ctl *mctl, snd_config_t *conf)
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
mctl->src_channels = 1;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id;
|
const char *id;
|
||||||
|
|
@ -1264,6 +1338,11 @@ ctl.name {
|
||||||
vindex.0 1 # stereo to mono (second channel)
|
vindex.0 1 # stereo to mono (second channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# join two stereo to one stereo (minimum value is returned for read operation)
|
||||||
|
CREATE_ID4_STR {
|
||||||
|
SRC_ID5_STR.vindex.0 [ 0 1 ] # source channels 0+1 to merged channel 0
|
||||||
|
SRC_ID6_STR.vindex.1 [ 0 1 ] # source channels 0+1 to merged channel 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\endcode
|
\endcode
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue