From 6d830bf0f08c7f92418c2d8b0e73c0415dca03c8 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 27 Nov 2019 11:34:49 +0100 Subject: [PATCH] alsa-ucm: add support for master volume Signed-off-by: Jaroslav Kysela --- src/modules/alsa/alsa-mixer.c | 20 +++++----- src/modules/alsa/alsa-mixer.h | 1 + src/modules/alsa/alsa-ucm.c | 86 +++++++++++++++++++++++++++++++++---------- src/modules/alsa/alsa-ucm.h | 19 ++++++++++ 4 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index f57aabe5d885..ed06f42d86da 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -1923,7 +1923,7 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m) return 0; } -static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool prefixed) { +pa_alsa_element * pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed) { pa_alsa_element *e; char *name; int index; @@ -2025,7 +2025,7 @@ static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) { return p->last_option; } - pa_assert_se(e = element_get(p, en, false)); + pa_assert_se(e = pa_alsa_element_get(p, en, false)); PA_LLIST_FOREACH(o, e->options) if (pa_streq(o->alsa_name, on)) @@ -2054,7 +2054,7 @@ static int element_parse_switch(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } @@ -2085,7 +2085,7 @@ static int element_parse_volume(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } @@ -2121,7 +2121,7 @@ static int element_parse_enumeration(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } @@ -2213,7 +2213,7 @@ static int element_parse_required(pa_config_parser_state *state) { p = state->userdata; - e = element_get(p, state->section, true); + e = pa_alsa_element_get(p, state->section, true); o = option_get(p, state->section); j = jack_get(p, state->section); if (!e && !o && !j) { @@ -2279,7 +2279,7 @@ static int element_parse_direction(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } @@ -2305,7 +2305,7 @@ static int element_parse_direction_try_other(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } @@ -2328,7 +2328,7 @@ static int element_parse_volume_limit(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } @@ -2386,7 +2386,7 @@ static int element_parse_override_map(pa_config_parser_state *state) { p = state->userdata; - if (!(e = element_get(p, state->section, true))) { + if (!(e = pa_alsa_element_get(p, state->section, true))) { pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section); return -1; } diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 709f270fbfa5..0b634f2f2be1 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -244,6 +244,7 @@ void pa_alsa_element_dump(pa_alsa_element *e); pa_alsa_path *pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction); pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction); +pa_alsa_element *pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed); int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB); void pa_alsa_path_dump(pa_alsa_path *p); int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v); diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 65ec6941e8de..46d016541ddf 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -200,6 +200,14 @@ static void ucm_add_devices_to_idxset( } } +static void ucm_volume_free(pa_alsa_ucm_volume *vol) { + pa_assert(vol); + pa_xfree(vol->mixer_elem); + pa_xfree(vol->master_elem); + pa_xfree(vol->master_type); + pa_xfree(vol); +} + /* Get the volume identifier */ static char *ucm_get_mixer_id( pa_alsa_ucm_device *device, @@ -244,6 +252,32 @@ static char *ucm_get_mixer_id( return value2; } +/* Get the volume identifier */ +static pa_alsa_ucm_volume *ucm_get_mixer_volume( + pa_alsa_ucm_device *device, + const char *mprop, + const char *cprop, + const char *cid, + const char *masterid, + const char *mastertype) +{ + pa_alsa_ucm_volume *vol; + char *mixer_elem; + + mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid); + if (mixer_elem == NULL) + return NULL; + vol = pa_xnew0(pa_alsa_ucm_volume, 1); + if (vol == NULL) { + pa_xfree(mixer_elem); + return NULL; + } + vol->mixer_elem = mixer_elem; + vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, masterid)); + vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, mastertype)); + return vol; +} + /* Create a property list for this ucm device */ static int ucm_get_device_property( pa_alsa_ucm_device *device, @@ -258,6 +292,7 @@ static int ucm_get_device_property( int err; uint32_t ui; int n_confdev, n_suppdev; + pa_alsa_ucm_volume *vol; for (i = 0; item[i].id; i++) { id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name); @@ -340,12 +375,14 @@ static int ucm_get_device_property( pa_log_debug("UCM playback priority %s for device %s error", value, device_name); } - value = ucm_get_mixer_id(device, - PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM, - PA_ALSA_PROP_UCM_PLAYBACK_VOLUME, - "PlaybackVolume"); - if (value) - pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value); + vol = ucm_get_mixer_volume(device, + PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM, + PA_ALSA_PROP_UCM_PLAYBACK_VOLUME, + "PlaybackVolume", + PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM, + PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE); + if (vol) + pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol); } if (device->capture_channels) { /* source device */ @@ -368,12 +405,14 @@ static int ucm_get_device_property( pa_log_debug("UCM capture priority %s for device %s error", value, device_name); } - value = ucm_get_mixer_id(device, - PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM, - PA_ALSA_PROP_UCM_CAPTURE_VOLUME, - "CaptureVolume"); - if (value) - pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value); + vol = ucm_get_mixer_volume(device, + PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM, + PA_ALSA_PROP_UCM_CAPTURE_VOLUME, + "CaptureVolume", + PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM, + PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE); + if (vol) + pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol); } if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) { @@ -478,9 +517,9 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { d->available = PA_AVAILABLE_UNKNOWN; d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, - pa_xfree); + (pa_free_cb_t) ucm_volume_free); d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, - pa_xfree); + (pa_free_cb_t) ucm_volume_free); PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d); } @@ -819,9 +858,10 @@ static void ucm_add_port_combination( char *name, *desc; const char *dev_name; const char *direction; - const char *profile, *volume_element; + const char *profile; pa_alsa_ucm_device *sorted[num], *dev; pa_alsa_ucm_port_data *data; + pa_alsa_ucm_volume *vol; void *state; for (i = 0; i < num; i++) @@ -892,21 +932,27 @@ static void ucm_add_port_combination( * ports. */ data = PA_DEVICE_PORT_DATA(port); - PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { - pa_alsa_path *path = pa_alsa_path_synthesize(volume_element, + PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { + pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); if (!path) - pa_log_warn("Failed to set up volume control: %s", volume_element); + pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); else { + if (vol->master_elem) { + pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); + e->switch_use = PA_ALSA_SWITCH_MUTE; + e->volume_use = PA_ALSA_VOLUME_MERGE; + } + pa_hashmap_put(data->paths, pa_xstrdup(profile), path); /* Add path also to already created empty path set */ dev = sorted[0]; if (is_sink) - pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(volume_element), path); + pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); else - pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(volume_element), path); + pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); } } } diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index d8507a83615c..a6863abc05a6 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -60,6 +60,12 @@ typedef void snd_use_case_mgr_t; /** For devices: Playback mixer master type */ #define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type" +/** For devices: Playback mixer master identifier */ +#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ID "alsa.ucm.playback.master.id" + +/** For devices: Playback mixer master type */ +#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type" + /** For devices: Playback priority */ #define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY "alsa.ucm.playback.priority" @@ -87,6 +93,12 @@ typedef void snd_use_case_mgr_t; /** For devices: Capture mixer identifier */ #define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type" +/** For devices: Capture mixer identifier */ +#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ID "alsa.ucm.capture.master.id" + +/** For devices: Capture mixer identifier */ +#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type" + /** For devices: Capture priority */ #define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY "alsa.ucm.capture.priority" @@ -114,6 +126,7 @@ typedef struct pa_alsa_ucm_device pa_alsa_ucm_device; typedef struct pa_alsa_ucm_config pa_alsa_ucm_config; typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context; typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data; +typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume; int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index); pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map); @@ -246,4 +259,10 @@ struct pa_alsa_ucm_port_data { pa_alsa_path *path; }; +struct pa_alsa_ucm_volume { + char *mixer_elem; /* mixer element identifier */ + char *master_elem; /* master mixer element identifier */ + char *master_type; +}; + #endif -- 2.16.4