mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	alsa-ucm: add support for master volume
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
		
							parent
							
								
									dc9dc70fcc
								
							
						
					
					
						commit
						6d830bf0f0
					
				
					 4 changed files with 96 additions and 30 deletions
				
			
		| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
        vol = ucm_get_mixer_volume(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);
 | 
			
		||||
                                   "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,
 | 
			
		||||
        vol = ucm_get_mixer_volume(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);
 | 
			
		||||
                                   "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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue