mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -04:00
control: remap - add sync feature
For UCM, it may be required to sync multiple controls. The logic is really simple - last write to any control in the group wins. Link: https://github.com/alsa-project/alsa-ucm-conf/pull/410 Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
42b8f1299f
commit
d5f19bcabc
1 changed files with 255 additions and 8 deletions
|
|
@ -87,6 +87,11 @@ typedef struct {
|
||||||
unsigned int event_mask;
|
unsigned int event_mask;
|
||||||
} snd_ctl_map_event_t;
|
} snd_ctl_map_event_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t control_items;
|
||||||
|
snd_ctl_elem_id_t *control_ids;
|
||||||
|
} snd_ctl_sync_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
snd_ctl_t *child;
|
snd_ctl_t *child;
|
||||||
int numid_remap_active;
|
int numid_remap_active;
|
||||||
|
|
@ -105,6 +110,10 @@ typedef struct {
|
||||||
size_t map_alloc;
|
size_t map_alloc;
|
||||||
snd_ctl_map_t *map;
|
snd_ctl_map_t *map;
|
||||||
|
|
||||||
|
size_t sync_items;
|
||||||
|
size_t sync_alloc;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
|
||||||
size_t event_items;
|
size_t event_items;
|
||||||
size_t event_queue_head;
|
size_t event_queue_head;
|
||||||
size_t event_queue_tail;
|
size_t event_queue_tail;
|
||||||
|
|
@ -183,6 +192,23 @@ static snd_ctl_numid_t *remap_find_numid_child(snd_ctl_remap_t *priv, unsigned i
|
||||||
return remap_numid_child_new(priv, numid_child);
|
return remap_numid_child_new(priv, numid_child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void remap_forget_numid_child(snd_ctl_remap_t *priv, unsigned int numid_child)
|
||||||
|
{
|
||||||
|
snd_ctl_numid_t *numid;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
if (!priv->numid_remap_active)
|
||||||
|
return;
|
||||||
|
numid = priv->numid;
|
||||||
|
for (index = 0; index < priv->numid_items; index++) {
|
||||||
|
if (numid[index].numid_child != numid_child)
|
||||||
|
continue;
|
||||||
|
memcpy(&priv->numid[index], &priv->numid[index + 1],
|
||||||
|
(priv->numid_items - 1 - index) * sizeof(*numid));
|
||||||
|
priv->numid_items++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static snd_ctl_remap_id_t *remap_find_id_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
|
static snd_ctl_remap_id_t *remap_find_id_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
|
||||||
{
|
{
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
@ -302,11 +328,63 @@ static int remap_id_to_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static snd_ctl_sync_t *remap_find_sync_numid(snd_ctl_remap_t *priv, unsigned int numid)
|
||||||
|
{
|
||||||
|
size_t count, index2;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
|
||||||
|
if (numid == 0)
|
||||||
|
return NULL;
|
||||||
|
sync = priv->sync;
|
||||||
|
for (count = priv->sync_items; count > 0; count--, sync++)
|
||||||
|
for (index2 = 0; index2 < sync->control_items; index2++)
|
||||||
|
if (numid == sync->control_ids[index2].numid)
|
||||||
|
return sync;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static snd_ctl_sync_t *remap_find_sync_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
|
||||||
|
{
|
||||||
|
size_t count, index2;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
|
||||||
|
if (id->numid > 0)
|
||||||
|
return remap_find_sync_numid(priv, id->numid);
|
||||||
|
sync = priv->sync;
|
||||||
|
for (count = priv->sync_items; count > 0; count--, sync++)
|
||||||
|
for (index2 = 0; index2 < sync->control_items; index2++)
|
||||||
|
if (snd_ctl_elem_id_compare_set(id, &sync->control_ids[index2]) == 0)
|
||||||
|
return sync;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remap_update_sync_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
|
||||||
|
{
|
||||||
|
size_t count, index2;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
|
||||||
|
if (id->numid == 0)
|
||||||
|
return;
|
||||||
|
sync = priv->sync;
|
||||||
|
for (count = priv->sync_items; count > 0; count--, sync++)
|
||||||
|
for (index2 = 0; index2 < sync->control_items; index2++) {
|
||||||
|
if (snd_ctl_elem_id_compare_set(id, &sync->control_ids[index2]) == 0) {
|
||||||
|
sync->control_ids[index2].numid = id->numid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void remap_free(snd_ctl_remap_t *priv)
|
static void remap_free(snd_ctl_remap_t *priv)
|
||||||
{
|
{
|
||||||
size_t idx1, idx2;
|
size_t idx1, idx2;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
snd_ctl_map_t *map;
|
snd_ctl_map_t *map;
|
||||||
|
|
||||||
|
for (idx1 = 0; idx1 < priv->sync_items; idx1++) {
|
||||||
|
sync = &priv->sync[idx1];
|
||||||
|
free(sync->control_ids);
|
||||||
|
}
|
||||||
for (idx1 = 0; idx1 < priv->map_items; idx1++) {
|
for (idx1 = 0; idx1 < priv->map_items; idx1++) {
|
||||||
map = &priv->map[idx1];
|
map = &priv->map[idx1];
|
||||||
for (idx2 = 0; idx2 < map->controls_items; idx2++)
|
for (idx2 = 0; idx2 < map->controls_items; idx2++)
|
||||||
|
|
@ -314,6 +392,7 @@ static void remap_free(snd_ctl_remap_t *priv)
|
||||||
free(map->controls);
|
free(map->controls);
|
||||||
}
|
}
|
||||||
free(priv->event_queue);
|
free(priv->event_queue);
|
||||||
|
free(priv->sync);
|
||||||
free(priv->map);
|
free(priv->map);
|
||||||
free(priv->remap);
|
free(priv->remap);
|
||||||
free(priv->numid);
|
free(priv->numid);
|
||||||
|
|
@ -484,6 +563,8 @@ static int snd_ctl_remap_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = snd_ctl_elem_info(priv->child, info);
|
err = snd_ctl_elem_info(priv->child, info);
|
||||||
|
if (err >= 0 && priv->sync_items > 0)
|
||||||
|
remap_update_sync_id(priv, &info->id);
|
||||||
return remap_id_to_app(priv, &info->id, rid, err);
|
return remap_id_to_app(priv, &info->id, rid, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,6 +725,29 @@ static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *con
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int remap_sync_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control)
|
||||||
|
{
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
snd_ctl_elem_value_t control2;
|
||||||
|
size_t item;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
sync = remap_find_sync_id(priv, &control->id);
|
||||||
|
if (sync == NULL)
|
||||||
|
return -EREMAPNOTFOUND;
|
||||||
|
debug_id(&control->id, "%s\n", __func__);
|
||||||
|
control2 = *control;
|
||||||
|
for (item = 0; item < sync->control_items; item++) {
|
||||||
|
control2.id = sync->control_ids[item];
|
||||||
|
debug_id(&control2.id, "%s sync[%zd]\n", __func__, item);
|
||||||
|
/* TODO: it's a blind write - no checks if the values are in range for all controls */
|
||||||
|
err = snd_ctl_elem_write(priv->child, &control2);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return remap_id_to_app(priv, &control->id, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control)
|
static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control)
|
||||||
{
|
{
|
||||||
snd_ctl_remap_t *priv = ctl->private_data;
|
snd_ctl_remap_t *priv = ctl->private_data;
|
||||||
|
|
@ -652,6 +756,9 @@ static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *contro
|
||||||
|
|
||||||
debug_id(&control->id, "%s\n", __func__);
|
debug_id(&control->id, "%s\n", __func__);
|
||||||
err = remap_map_elem_write(priv, control);
|
err = remap_map_elem_write(priv, control);
|
||||||
|
if (err != -EREMAPNOTFOUND)
|
||||||
|
return err;
|
||||||
|
err = remap_sync_elem_write(priv, control);
|
||||||
if (err != -EREMAPNOTFOUND)
|
if (err != -EREMAPNOTFOUND)
|
||||||
return err;
|
return err;
|
||||||
err = remap_id_to_child(priv, &control->id, &rid);
|
err = remap_id_to_child(priv, &control->id, &rid);
|
||||||
|
|
@ -897,12 +1004,55 @@ static void remap_event_for_all_map_controls(snd_ctl_remap_t *priv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void remap_event_for_all_sync_controls(snd_ctl_remap_t *priv,
|
||||||
|
snd_ctl_elem_id_t *id,
|
||||||
|
unsigned int event_mask)
|
||||||
|
{
|
||||||
|
size_t count, index;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
snd_ctl_numid_t *numid;
|
||||||
|
snd_ctl_elem_id_t *sid;
|
||||||
|
int changed = 0;
|
||||||
|
|
||||||
|
if (event_mask == SNDRV_CTL_EVENT_MASK_REMOVE)
|
||||||
|
return;
|
||||||
|
sync = priv->sync;
|
||||||
|
for (count = priv->sync_items; count > 0; count--, sync++) {
|
||||||
|
changed = 0;
|
||||||
|
for (index = 0; index < sync->control_items; index++) {
|
||||||
|
sid = &sync->control_ids[index];
|
||||||
|
if (sid->numid == 0) {
|
||||||
|
if (snd_ctl_elem_id_compare_set(id, sid))
|
||||||
|
continue;
|
||||||
|
sid->numid = id->numid;
|
||||||
|
}
|
||||||
|
if (id->numid != sid->numid)
|
||||||
|
continue;
|
||||||
|
debug_id(sid, "%s found (all)\n", __func__);
|
||||||
|
changed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!changed)
|
||||||
|
continue;
|
||||||
|
for (index = 0; index < sync->control_items; index++) {
|
||||||
|
sid = &sync->control_ids[index];
|
||||||
|
/* skip double updates */
|
||||||
|
if (sid->numid == id->numid)
|
||||||
|
continue;
|
||||||
|
numid = remap_find_numid_child(priv, sid->numid);
|
||||||
|
if (numid)
|
||||||
|
event_add(priv, sid, numid->numid_app, event_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
|
static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
|
||||||
{
|
{
|
||||||
snd_ctl_remap_t *priv = ctl->private_data;
|
snd_ctl_remap_t *priv = ctl->private_data;
|
||||||
snd_ctl_remap_id_t *rid;
|
snd_ctl_remap_id_t *rid;
|
||||||
snd_ctl_numid_t *numid;
|
snd_ctl_numid_t *numid;
|
||||||
snd_ctl_map_event_t *map_event;
|
snd_ctl_map_event_t *map_event;
|
||||||
|
unsigned int numid_child;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (priv->event_queue_head != priv->event_queue_tail) {
|
if (priv->event_queue_head != priv->event_queue_tail) {
|
||||||
|
|
@ -923,11 +1073,13 @@ static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
|
||||||
(event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO |
|
(event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO |
|
||||||
SNDRV_CTL_EVENT_MASK_ADD | SNDRV_CTL_EVENT_MASK_TLV)) != 0) {
|
SNDRV_CTL_EVENT_MASK_ADD | SNDRV_CTL_EVENT_MASK_TLV)) != 0) {
|
||||||
debug_id(&event->data.elem.id, "%s event mask 0x%x\n", __func__, event->data.elem.mask);
|
debug_id(&event->data.elem.id, "%s event mask 0x%x\n", __func__, event->data.elem.mask);
|
||||||
|
numid_child = event->data.elem.id.numid;
|
||||||
remap_event_for_all_map_controls(priv, &event->data.elem.id, event->data.elem.mask);
|
remap_event_for_all_map_controls(priv, &event->data.elem.id, event->data.elem.mask);
|
||||||
|
remap_event_for_all_sync_controls(priv, &event->data.elem.id, event->data.elem.mask);
|
||||||
rid = remap_find_id_child(priv, &event->data.elem.id);
|
rid = remap_find_id_child(priv, &event->data.elem.id);
|
||||||
if (rid) {
|
if (rid) {
|
||||||
if (rid->id_child.numid == 0) {
|
if (rid->id_child.numid == 0) {
|
||||||
numid = remap_find_numid_child(priv, event->data.elem.id.numid);
|
numid = remap_find_numid_child(priv, numid_child);
|
||||||
if (numid == NULL)
|
if (numid == NULL)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
rid->id_child.numid = numid->numid_child;
|
rid->id_child.numid = numid->numid_child;
|
||||||
|
|
@ -935,11 +1087,13 @@ static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
|
||||||
}
|
}
|
||||||
event->data.elem.id = rid->id_app;
|
event->data.elem.id = rid->id_app;
|
||||||
} else {
|
} else {
|
||||||
numid = remap_find_numid_child(priv, event->data.elem.id.numid);
|
numid = remap_find_numid_child(priv, numid_child);
|
||||||
if (numid == NULL)
|
if (numid == NULL)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
event->data.elem.id.numid = numid->numid_app;
|
event->data.elem.id.numid = numid->numid_app;
|
||||||
}
|
}
|
||||||
|
if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE)
|
||||||
|
remap_forget_numid_child(priv, numid_child);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -1240,12 +1394,84 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static snd_ctl_sync_t *alloc_sync(snd_ctl_remap_t *priv)
|
||||||
|
{
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
|
||||||
|
if (priv->sync_alloc == priv->sync_items) {
|
||||||
|
sync = realloc(priv->sync, (priv->sync_alloc + 16) * sizeof(*sync));
|
||||||
|
if (sync == NULL)
|
||||||
|
return NULL;
|
||||||
|
memset(sync + priv->sync_alloc, 0, sizeof(*sync) * 16);
|
||||||
|
priv->sync_alloc += 16;
|
||||||
|
priv->sync = sync;
|
||||||
|
}
|
||||||
|
return &priv->sync[priv->sync_items++];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_sync1(snd_ctl_remap_t *priv, unsigned int count, snd_config_t *conf)
|
||||||
|
{
|
||||||
|
snd_config_iterator_t i, next;
|
||||||
|
snd_ctl_elem_id_t *eid;
|
||||||
|
snd_ctl_sync_t *sync;
|
||||||
|
const char *str;
|
||||||
|
int err, index = 0;
|
||||||
|
|
||||||
|
sync = alloc_sync(priv);
|
||||||
|
if (sync == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
sync->control_ids = calloc(count, sizeof(sync->control_ids[0]));
|
||||||
|
if (sync->control_ids == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
snd_config_for_each(i, next, conf) {
|
||||||
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
|
if (snd_config_get_string(n, &str) < 0) {
|
||||||
|
SNDERR("strings are expected in sync array");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
eid = &sync->control_ids[index];
|
||||||
|
snd_ctl_elem_id_clear(eid);
|
||||||
|
err = snd_ctl_ascii_elem_id_parse(eid, str);
|
||||||
|
if (err < 0) {
|
||||||
|
SNDERR("unable to parse control id '%s'!", str);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
sync->control_items++;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_sync(snd_ctl_remap_t *priv, snd_config_t *conf)
|
||||||
|
{
|
||||||
|
snd_config_iterator_t i, next;
|
||||||
|
int count, err;
|
||||||
|
|
||||||
|
if (conf == NULL)
|
||||||
|
return 0;
|
||||||
|
snd_config_for_each(i, next, conf) {
|
||||||
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
|
count = snd_config_is_array(n);
|
||||||
|
if (count <= 0) {
|
||||||
|
SNDERR("Array is expected for sync!");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
err = parse_sync1(priv, count, n);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Creates a new remap & map control handle
|
* \brief Creates a new remap/map/sync control handle
|
||||||
* \param handlep Returns created control handle
|
* \param handlep Returns created control handle
|
||||||
* \param name Name of control device
|
* \param name Name of control device
|
||||||
* \param remap Remap configuration
|
* \param remap Remap configuration
|
||||||
* \param map Map configuration
|
* \param map Map configuration
|
||||||
|
* \param sync Sync configuration
|
||||||
* \param child child configuration root
|
* \param child child configuration root
|
||||||
* \param mode Control handle mode
|
* \param mode Control handle mode
|
||||||
* \retval zero on success otherwise a negative error code
|
* \retval zero on success otherwise a negative error code
|
||||||
|
|
@ -1254,14 +1480,15 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf)
|
||||||
* changed in future.
|
* changed in future.
|
||||||
*/
|
*/
|
||||||
int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *remap,
|
int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *remap,
|
||||||
snd_config_t *map, snd_ctl_t *child, int mode)
|
snd_config_t *map, snd_config_t *sync, snd_ctl_t *child, int mode)
|
||||||
{
|
{
|
||||||
snd_ctl_remap_t *priv;
|
snd_ctl_remap_t *priv;
|
||||||
snd_ctl_t *ctl;
|
snd_ctl_t *ctl;
|
||||||
|
size_t index;
|
||||||
int result, err;
|
int result, err;
|
||||||
|
|
||||||
/* no-op, remove the plugin */
|
/* no-op, remove the plugin */
|
||||||
if (!remap && !map)
|
if (!remap && !map && !sync)
|
||||||
goto _noop;
|
goto _noop;
|
||||||
|
|
||||||
priv = calloc(1, sizeof(*priv));
|
priv = calloc(1, sizeof(*priv));
|
||||||
|
|
@ -1280,8 +1507,14 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema
|
||||||
goto _err;
|
goto _err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = parse_sync(priv, sync);
|
||||||
|
if (err < 0) {
|
||||||
|
result = err;
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
|
||||||
/* no-op check, remove the plugin */
|
/* no-op check, remove the plugin */
|
||||||
if (priv->map_items == 0 && priv->remap_items == 0) {
|
if (priv->map_items == 0 && priv->remap_items == 0 && priv->sync_items == 0) {
|
||||||
remap_free(priv);
|
remap_free(priv);
|
||||||
_noop:
|
_noop:
|
||||||
free(child->name);
|
free(child->name);
|
||||||
|
|
@ -1293,13 +1526,15 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->event_items = priv->map_items;
|
priv->event_items = priv->map_items;
|
||||||
|
for (index = 0; index < priv->sync_items; index++)
|
||||||
|
priv->event_items += priv->sync[index].control_items;
|
||||||
priv->event_queue = calloc(priv->event_items, sizeof(priv->event_queue[0]));
|
priv->event_queue = calloc(priv->event_items, sizeof(priv->event_queue[0]));
|
||||||
if (priv->event_queue == NULL) {
|
if (priv->event_queue == NULL) {
|
||||||
result = -ENOMEM;
|
result = -ENOMEM;
|
||||||
goto _err;
|
goto _err;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->numid_remap_active = priv->map_items > 0;
|
priv->numid_remap_active = priv->map_items > 0 || priv->sync_items;
|
||||||
|
|
||||||
priv->child = child;
|
priv->child = child;
|
||||||
err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode);
|
err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode);
|
||||||
|
|
@ -1371,6 +1606,13 @@ ctl.name {
|
||||||
SRC_ID6_STR.vindex.1 [ 0 1 ] # source channels 0+1 to merged channel 1
|
SRC_ID6_STR.vindex.1 [ 0 1 ] # source channels 0+1 to merged channel 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sync {
|
||||||
|
# synchronize multiple controls without any translations
|
||||||
|
sample_group_1 [
|
||||||
|
SYNC_ID1_STR
|
||||||
|
SYNC_ID2_STR
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
|
|
@ -1401,6 +1643,7 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd
|
||||||
snd_config_t *child = NULL;
|
snd_config_t *child = NULL;
|
||||||
snd_config_t *remap = NULL;
|
snd_config_t *remap = NULL;
|
||||||
snd_config_t *map = NULL;
|
snd_config_t *map = NULL;
|
||||||
|
snd_config_t *sync = NULL;
|
||||||
snd_ctl_t *cctl;
|
snd_ctl_t *cctl;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
@ -1419,6 +1662,10 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd
|
||||||
map = n;
|
map = n;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (strcmp(id, "sync") == 0) {
|
||||||
|
sync = n;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (strcmp(id, "child") == 0) {
|
if (strcmp(id, "child") == 0) {
|
||||||
child = n;
|
child = n;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1433,7 +1680,7 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd
|
||||||
err = _snd_ctl_open_child(&cctl, root, child, mode, conf);
|
err = _snd_ctl_open_child(&cctl, root, child, mode, conf);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = snd_ctl_remap_open(handlep, name, remap, map, cctl, mode);
|
err = snd_ctl_remap_open(handlep, name, remap, map, sync, cctl, mode);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
snd_ctl_close(cctl);
|
snd_ctl_close(cctl);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue