mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-02 09:01:46 -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;
|
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;
|
pa_alsa_element *e;
|
||||||
char *name;
|
char *name;
|
||||||
int index;
|
int index;
|
||||||
|
|
@ -2025,7 +2025,7 @@ static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
|
||||||
return p->last_option;
|
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)
|
PA_LLIST_FOREACH(o, e->options)
|
||||||
if (pa_streq(o->alsa_name, on))
|
if (pa_streq(o->alsa_name, on))
|
||||||
|
|
@ -2054,7 +2054,7 @@ static int element_parse_switch(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -2085,7 +2085,7 @@ static int element_parse_volume(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -2121,7 +2121,7 @@ static int element_parse_enumeration(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -2213,7 +2213,7 @@ static int element_parse_required(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
o = option_get(p, state->section);
|
||||||
j = jack_get(p, state->section);
|
j = jack_get(p, state->section);
|
||||||
if (!e && !o && !j) {
|
if (!e && !o && !j) {
|
||||||
|
|
@ -2279,7 +2279,7 @@ static int element_parse_direction(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -2305,7 +2305,7 @@ static int element_parse_direction_try_other(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -2328,7 +2328,7 @@ static int element_parse_volume_limit(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -2386,7 +2386,7 @@ static int element_parse_override_map(pa_config_parser_state *state) {
|
||||||
|
|
||||||
p = state->userdata;
|
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);
|
pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
|
||||||
return -1;
|
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_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_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);
|
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);
|
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);
|
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 */
|
/* Get the volume identifier */
|
||||||
static char *ucm_get_mixer_id(
|
static char *ucm_get_mixer_id(
|
||||||
pa_alsa_ucm_device *device,
|
pa_alsa_ucm_device *device,
|
||||||
|
|
@ -244,6 +252,32 @@ static char *ucm_get_mixer_id(
|
||||||
return value2;
|
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 */
|
/* Create a property list for this ucm device */
|
||||||
static int ucm_get_device_property(
|
static int ucm_get_device_property(
|
||||||
pa_alsa_ucm_device *device,
|
pa_alsa_ucm_device *device,
|
||||||
|
|
@ -258,6 +292,7 @@ static int ucm_get_device_property(
|
||||||
int err;
|
int err;
|
||||||
uint32_t ui;
|
uint32_t ui;
|
||||||
int n_confdev, n_suppdev;
|
int n_confdev, n_suppdev;
|
||||||
|
pa_alsa_ucm_volume *vol;
|
||||||
|
|
||||||
for (i = 0; item[i].id; i++) {
|
for (i = 0; item[i].id; i++) {
|
||||||
id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
|
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);
|
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_MIXER_ELEM,
|
||||||
PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
|
PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
|
||||||
"PlaybackVolume");
|
"PlaybackVolume",
|
||||||
if (value)
|
PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM,
|
||||||
pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value);
|
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 */
|
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);
|
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_MIXER_ELEM,
|
||||||
PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
|
PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
|
||||||
"CaptureVolume");
|
"CaptureVolume",
|
||||||
if (value)
|
PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM,
|
||||||
pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value);
|
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)) {
|
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->available = PA_AVAILABLE_UNKNOWN;
|
||||||
|
|
||||||
d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
|
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,
|
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);
|
PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
|
||||||
}
|
}
|
||||||
|
|
@ -819,9 +858,10 @@ static void ucm_add_port_combination(
|
||||||
char *name, *desc;
|
char *name, *desc;
|
||||||
const char *dev_name;
|
const char *dev_name;
|
||||||
const char *direction;
|
const char *direction;
|
||||||
const char *profile, *volume_element;
|
const char *profile;
|
||||||
pa_alsa_ucm_device *sorted[num], *dev;
|
pa_alsa_ucm_device *sorted[num], *dev;
|
||||||
pa_alsa_ucm_port_data *data;
|
pa_alsa_ucm_port_data *data;
|
||||||
|
pa_alsa_ucm_volume *vol;
|
||||||
void *state;
|
void *state;
|
||||||
|
|
||||||
for (i = 0; i < num; i++)
|
for (i = 0; i < num; i++)
|
||||||
|
|
@ -892,21 +932,27 @@ static void ucm_add_port_combination(
|
||||||
* ports. */
|
* ports. */
|
||||||
data = PA_DEVICE_PORT_DATA(port);
|
data = PA_DEVICE_PORT_DATA(port);
|
||||||
|
|
||||||
PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
|
PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
|
||||||
pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
|
pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
|
||||||
is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
|
is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
|
||||||
|
|
||||||
if (!path)
|
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 {
|
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);
|
pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
|
||||||
|
|
||||||
/* Add path also to already created empty path set */
|
/* Add path also to already created empty path set */
|
||||||
dev = sorted[0];
|
dev = sorted[0];
|
||||||
if (is_sink)
|
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
|
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 */
|
/** For devices: Playback mixer master type */
|
||||||
#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.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 */
|
/** For devices: Playback priority */
|
||||||
#define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY "alsa.ucm.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 */
|
/** For devices: Capture mixer identifier */
|
||||||
#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type"
|
#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 */
|
/** For devices: Capture priority */
|
||||||
#define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY "alsa.ucm.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_config pa_alsa_ucm_config;
|
||||||
typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
|
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_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);
|
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);
|
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;
|
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
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue