From d93e96b666177a8b36249cebb3d094ad33ed9f17 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Mon, 27 Nov 2023 23:16:41 +0300 Subject: [PATCH] alsa-ucm: Split out helpers for device set name, description, priority Combination port logic calculates some useful properties for device groups that we could reuse while generating multiple profiles to support conflicting devices. Split them into their own functions. Link: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/commit/e8e2f4320c28dbf20b5601135ec11c867f6d4342 Signed-off-by: Alper Nebi Yasak --- spa/plugins/alsa/acp/alsa-ucm.c | 155 ++++++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 36 deletions(-) diff --git a/spa/plugins/alsa/acp/alsa-ucm.c b/spa/plugins/alsa/acp/alsa-ucm.c index 70f980184..9ea9f8694 100644 --- a/spa/plugins/alsa/acp/alsa-ucm.c +++ b/spa/plugins/alsa/acp/alsa-ucm.c @@ -1071,6 +1071,109 @@ static void ucm_add_port_props( pa_proplist_sets(port->proplist, "device.icon_name", icon); } +static char *devset_name(pa_idxset *devices, const char *sep) { + int i = 0; + int num = pa_idxset_size(devices); + pa_alsa_ucm_device *sorted[num], *dev; + char *dev_names = NULL; + char *tmp = NULL; + uint32_t idx; + + PA_IDXSET_FOREACH(dev, devices, idx) { + sorted[i] = dev; + i++; + } + + /* Sort by alphabetical order so as to have a deterministic naming scheme */ + qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); + + for (i = 0; i < num; i++) { + dev = sorted[i]; + const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + if (!dev_names) { + dev_names = pa_xstrdup(dev_name); + } else { + tmp = pa_sprintf_malloc("%s%s%s", dev_names, sep, dev_name); + pa_xfree(dev_names); + dev_names = tmp; + } + } + + return dev_names; +} + +static char *devset_description(pa_idxset *devices, const char *sep) { + int i = 0; + int num = pa_idxset_size(devices); + pa_alsa_ucm_device *sorted[num], *dev; + char *dev_descs = NULL; + char *tmp = NULL; + uint32_t idx; + + PA_IDXSET_FOREACH(dev, devices, idx) { + sorted[i] = dev; + i++; + } + + /* Sort by alphabetical order to match devset_name() */ + qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); + + for (i = 0; i < num; i++) { + dev = sorted[i]; + const char *dev_desc = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); + + if (!dev_descs) { + dev_descs = pa_xstrdup(dev_desc); + } else { + tmp = pa_sprintf_malloc("%s%s%s", dev_descs, sep, dev_desc); + pa_xfree(dev_descs); + dev_descs = tmp; + } + } + + return dev_descs; +} + +/* If invert is true, uses the formula 1/p = 1/p1 + 1/p2 + ... 1/pn. + * This way, the result will always be less than the individual components, + * yet higher components will lead to higher result. */ +static unsigned devset_playback_priority(pa_idxset *devices, bool invert) { + pa_alsa_ucm_device *dev; + uint32_t idx; + double priority = 0; + + PA_IDXSET_FOREACH(dev, devices, idx) { + if (dev->playback_priority > 0 && invert) + priority += 1.0 / dev->playback_priority; + else + priority += dev->playback_priority; + } + + if (priority > 0 && invert) + return 1.0 / priority; + + return (unsigned) priority; +} + +static unsigned devset_capture_priority(pa_idxset *devices, bool invert) { + pa_alsa_ucm_device *dev; + uint32_t idx; + double priority = 0; + + PA_IDXSET_FOREACH(dev, devices, idx) { + if (dev->capture_priority > 0 && invert) + priority += 1.0 / dev->capture_priority; + else + priority += dev->capture_priority; + } + + if (priority > 0 && invert) + return 1.0 / priority; + + return (unsigned) priority; +} + static void ucm_add_port_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, @@ -1085,9 +1188,7 @@ static void ucm_add_port_combination( int num = pa_idxset_size(devices); uint32_t idx; unsigned priority; - double prio2; - char *name, *desc; - const char *dev_name; + char *name, *desc, *tmp; const char *direction; const char *profile; pa_alsa_ucm_device *sorted[num], *dev; @@ -1107,36 +1208,26 @@ static void ucm_add_port_combination( * for combination ports */ qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); + name = devset_name(devices, "+"); + tmp = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, name); + pa_xfree(name); + name = tmp; + + desc = devset_description(devices, ", "); + if (pa_idxset_size(devices) > 1) { + tmp = pa_sprintf_malloc("Combination port for %s", desc); + pa_xfree(desc); + desc = tmp; + } + + /* Make combination ports always have lower priority */ + priority = is_sink ? devset_playback_priority(devices, true) : devset_capture_priority(devices, true); + dev = sorted[0]; - dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name); - desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION)) - : pa_sprintf_malloc("Combination port for %s", dev_name); - - priority = is_sink ? dev->playback_priority : dev->capture_priority; - prio2 = (priority == 0 ? 0 : 1.0/priority); jack = ucm_get_jack(context->ucm, dev); type = dev->type; for (i = 1; i < num; i++) { - char *tmp; - - dev = sorted[i]; - dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - tmp = pa_sprintf_malloc("%s+%s", name, dev_name); - pa_xfree(name); - name = tmp; - - tmp = pa_sprintf_malloc("%s,%s", desc, dev_name); - pa_xfree(desc); - desc = tmp; - - priority = is_sink ? dev->playback_priority : dev->capture_priority; - if (priority != 0 && prio2 > 0) - prio2 += 1.0/priority; - jack2 = ucm_get_jack(context->ucm, dev); if (jack2) { if (jack && jack != jack2) @@ -1152,14 +1243,6 @@ static void ucm_add_port_combination( } } - /* Make combination ports always have lower priority, and use the formula - 1/p = 1/p1 + 1/p2 + ... 1/pn. - This way, the result will always be less than the individual components, - yet higher components will lead to higher result. */ - - if (num > 1) - priority = prio2 > 0 ? 1.0/prio2 : 0; - port = pa_hashmap_get(ports, name); if (!port) { pa_device_port_new_data port_data;