pulse-server: track and set monitor volume on monitor sources

This commit is contained in:
Wim Taymans 2021-04-29 15:25:04 +02:00
parent 664df03bbb
commit 620c863b6d
3 changed files with 71 additions and 39 deletions

View file

@ -253,11 +253,11 @@ struct device_info {
}
static void collect_device_info(struct pw_manager_object *device,
struct pw_manager_object *card, struct device_info *dev_info)
struct pw_manager_object *card, struct device_info *dev_info, bool monitor)
{
struct pw_manager_param *p;
if (card) {
if (card && !monitor) {
spa_list_for_each(p, &card->param_list, link) {
uint32_t id, device;
struct spa_pod *props;
@ -275,7 +275,7 @@ static void collect_device_info(struct pw_manager_object *device,
continue;
dev_info->active_port = id;
if (props) {
volume_parse_param(props, &dev_info->volume_info);
volume_parse_param(props, &dev_info->volume_info, monitor);
dev_info->have_volume = true;
}
}
@ -297,7 +297,7 @@ static void collect_device_info(struct pw_manager_object *device,
case SPA_PARAM_Props:
if (!dev_info->have_volume) {
volume_parse_param(p->param, &dev_info->volume_info);
volume_parse_param(p->param, &dev_info->volume_info, monitor);
dev_info->have_volume = true;
}
break;

View file

@ -641,7 +641,7 @@ static int send_object_event(struct client *client, struct pw_manager_object *o,
}
static struct pw_manager_object *find_device(struct client *client,
uint32_t id, const char *name, bool sink);
uint32_t id, const char *name, bool sink, bool *is_monitor);
static int64_t get_node_latency_offset(struct pw_manager_object *o)
{
@ -704,7 +704,7 @@ static void send_default_change_subscribe_event(struct client *client, bool sink
bool changed = false;
if (sink) {
def = find_device(client, SPA_ID_INVALID, NULL, true);
def = find_device(client, SPA_ID_INVALID, NULL, true, NULL);
if (client->prev_default_sink != def) {
client->prev_default_sink = def;
changed = true;
@ -712,7 +712,7 @@ static void send_default_change_subscribe_event(struct client *client, bool sink
}
if (source) {
def = find_device(client, SPA_ID_INVALID, NULL, false);
def = find_device(client, SPA_ID_INVALID, NULL, false, NULL);
if (client->prev_default_source != def) {
client->prev_default_source = def;
changed = true;
@ -2588,10 +2588,11 @@ static const char *get_default(struct client *client, bool sink)
}
static struct pw_manager_object *find_device(struct client *client,
uint32_t id, const char *name, bool sink)
uint32_t id, const char *name, bool sink, bool *is_monitor)
{
struct selector sel;
const char *def;
bool monitor = false;
if (id == 0)
id = SPA_ID_INVALID;
@ -2599,18 +2600,22 @@ static struct pw_manager_object *find_device(struct client *client,
if (name != NULL && !sink) {
if (pw_endswith(name, ".monitor")) {
name = strndupa(name, strlen(name)-8);
sink = true;
monitor = true;
} else if (strcmp(name, DEFAULT_MONITOR) == 0) {
name = NULL;
sink = true;
monitor = true;
}
}
if (id != SPA_ID_INVALID && !sink) {
if (id & MONITOR_FLAG) {
sink = true;
monitor = true;
id &= ~MONITOR_FLAG;
}
}
if (monitor)
sink = true;
if (is_monitor)
*is_monitor = monitor;
spa_zero(sel);
sel.id = id;
@ -2741,7 +2746,7 @@ static int do_play_sample(struct client *client, uint32_t command, uint32_t tag,
if (sink_index != SPA_ID_INVALID && sink_name != NULL)
goto error_inval;
o = find_device(client, sink_index, sink_name, PW_DIRECTION_OUTPUT);
o = find_device(client, sink_index, sink_name, PW_DIRECTION_OUTPUT, NULL);
if (o == NULL)
goto error_noent;
@ -2907,29 +2912,38 @@ static int do_flush_trigger_prebuf_stream(struct client *client, uint32_t comman
}
static int set_node_volume_mute(struct pw_manager_object *o,
struct volume *vol, bool *mute)
struct volume *vol, bool *mute, bool is_monitor)
{
char buf[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
struct spa_pod_frame f[1];
struct spa_pod *param;
uint32_t volprop, muteprop;
if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
return -EACCES;
if (o->proxy == NULL)
return -ENOENT;
if (is_monitor) {
volprop = SPA_PROP_monitorVolumes;
muteprop = SPA_PROP_monitorMute;
} else {
volprop = SPA_PROP_channelVolumes;
muteprop = SPA_PROP_mute;
}
spa_pod_builder_push_object(&b, &f[0],
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
if (vol)
spa_pod_builder_add(&b,
SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float),
volprop, SPA_POD_Array(sizeof(float),
SPA_TYPE_Float,
vol->channels,
vol->values), 0);
if (mute)
spa_pod_builder_add(&b,
SPA_PROP_mute, SPA_POD_Bool(*mute), 0);
muteprop, SPA_POD_Bool(*mute), 0);
param = spa_pod_builder_pop(&b, &f[0]);
pw_node_set_param((struct pw_node*)o->proxy,
@ -3047,7 +3061,7 @@ static int do_set_stream_volume(struct client *client, uint32_t command, uint32_
if (o == NULL)
return -ENOENT;
if ((res = set_node_volume_mute(o, &volume, NULL)) < 0)
if ((res = set_node_volume_mute(o, &volume, NULL, false)) < 0)
return res;
}
done:
@ -3098,7 +3112,7 @@ static int do_set_stream_mute(struct client *client, uint32_t command, uint32_t
if (o == NULL)
return -ENOENT;
if ((res = set_node_volume_mute(o, NULL, &mute)) < 0)
if ((res = set_node_volume_mute(o, NULL, &mute, false)) < 0)
return res;
}
done:
@ -3117,6 +3131,7 @@ static int do_set_volume(struct client *client, uint32_t command, uint32_t tag,
int res;
struct device_info dev_info;
enum pw_direction direction;
bool is_monitor;
if ((res = message_get(m,
TAG_U32, &id,
@ -3137,7 +3152,7 @@ static int do_set_volume(struct client *client, uint32_t command, uint32_t tag,
else
direction = PW_DIRECTION_INPUT;
o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT);
o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT, &is_monitor);
if (o == NULL || (info = o->info) == NULL || info->props == NULL)
return -ENOENT;
@ -3151,17 +3166,17 @@ static int do_set_volume(struct client *client, uint32_t command, uint32_t tag,
struct selector sel = { .id = card_id, .type = pw_manager_object_is_card, };
card = select_object(manager, &sel);
}
collect_device_info(o, card, &dev_info);
collect_device_info(o, card, &dev_info, is_monitor);
if (dev_info.have_volume &&
volume_compare(&dev_info.volume_info.volume, &volume) == 0)
goto done;
if (card != NULL && dev_info.active_port != SPA_ID_INVALID)
if (card != NULL && !is_monitor && dev_info.active_port != SPA_ID_INVALID)
res = set_card_volume_mute_delay(card, dev_info.active_port,
dev_info.device, &volume, NULL, NULL);
else
res = set_node_volume_mute(o, &volume, NULL);
res = set_node_volume_mute(o, &volume, NULL, is_monitor);
if (res < 0)
return res;
@ -3182,6 +3197,7 @@ static int do_set_mute(struct client *client, uint32_t command, uint32_t tag, st
int res;
struct device_info dev_info;
enum pw_direction direction;
bool is_monitor;
if ((res = message_get(m,
TAG_U32, &id,
@ -3202,7 +3218,7 @@ static int do_set_mute(struct client *client, uint32_t command, uint32_t tag, st
else
direction = PW_DIRECTION_INPUT;
o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT);
o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT, &is_monitor);
if (o == NULL || (info = o->info) == NULL || info->props == NULL)
return -ENOENT;
@ -3216,17 +3232,17 @@ static int do_set_mute(struct client *client, uint32_t command, uint32_t tag, st
struct selector sel = { .id = card_id, .type = pw_manager_object_is_card, };
card = select_object(manager, &sel);
}
collect_device_info(o, card, &dev_info);
collect_device_info(o, card, &dev_info, is_monitor);
if (dev_info.have_volume &&
dev_info.volume_info.mute == mute)
goto done;
if (card != NULL && dev_info.active_port != SPA_ID_INVALID)
if (card != NULL && !is_monitor && dev_info.active_port != SPA_ID_INVALID)
res = set_card_volume_mute_delay(card, dev_info.active_port,
dev_info.device, NULL, &mute, NULL);
else
res = set_node_volume_mute(o, NULL, &mute);
res = set_node_volume_mute(o, NULL, &mute, is_monitor);
if (res < 0)
return res;
@ -3265,7 +3281,7 @@ static int do_set_port(struct client *client, uint32_t command, uint32_t tag, st
else
direction = PW_DIRECTION_INPUT;
o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT);
o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT, NULL);
if (o == NULL || (info = o->info) == NULL || info->props == NULL)
return -ENOENT;
@ -3595,11 +3611,9 @@ static int do_lookup(struct client *client, uint32_t command, uint32_t tag, stru
pw_log_info(NAME" %p: [%s] LOOKUP tag:%u name:'%s'", impl, client->name, tag, name);
if ((o = find_device(client, SPA_ID_INVALID, name, is_sink)) == NULL)
if ((o = find_device(client, SPA_ID_INVALID, name, is_sink, &is_monitor)) == NULL)
return -ENOENT;
is_monitor = !is_sink && pw_manager_object_is_monitor(o);
reply = reply_new(client, tag);
message_put(reply,
TAG_U32, is_monitor ? o->id | MONITOR_FLAG : o->id,
@ -3931,7 +3945,7 @@ static int fill_sink_info(struct client *client, struct message *m,
if (card)
collect_card_info(card, &card_info);
collect_device_info(o, card, &dev_info);
collect_device_info(o, card, &dev_info, false);
if (!sample_spec_valid(&dev_info.ss) ||
!channel_map_valid(&dev_info.map) ||
@ -4075,7 +4089,7 @@ static int fill_source_info(struct client *client, struct message *m,
if (card)
collect_card_info(card, &card_info);
collect_device_info(o, card, &dev_info);
collect_device_info(o, card, &dev_info, is_monitor);
if (!sample_spec_valid(&dev_info.ss) ||
!channel_map_valid(&dev_info.map) ||
@ -4198,7 +4212,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
(str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
client_id = (uint32_t)atoi(str);
collect_device_info(o, NULL, &dev_info);
collect_device_info(o, NULL, &dev_info, false);
if (!sample_spec_valid(&dev_info.ss) ||
!channel_map_valid(&dev_info.map) ||
@ -4269,7 +4283,7 @@ static int fill_source_output_info(struct client *client, struct message *m,
(str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
client_id = (uint32_t)atoi(str);
collect_device_info(o, NULL, &dev_info);
collect_device_info(o, NULL, &dev_info, false);
if (!sample_spec_valid(&dev_info.ss) ||
!channel_map_valid(&dev_info.map) ||
@ -4819,7 +4833,7 @@ static int do_set_default(struct client *client, uint32_t command, uint32_t tag,
pw_log_info(NAME" %p: [%s] %s tag:%u name:%s", impl, client->name,
commands[command].name, tag, name);
if (name != NULL && (o = find_device(client, SPA_ID_INVALID, name, sink)) == NULL)
if (name != NULL && (o = find_device(client, SPA_ID_INVALID, name, sink, NULL)) == NULL)
return -ENOENT;
if (name != NULL) {
@ -4863,7 +4877,7 @@ static int do_suspend(struct client *client, uint32_t command, uint32_t tag, str
pw_log_info(NAME" %p: [%s] %s tag:%u id:%u name:%s", impl, client->name,
commands[command].name, tag, id, name);
if ((o = find_device(client, id, name, sink)) == NULL)
if ((o = find_device(client, id, name, sink, NULL)) == NULL)
return -ENOENT;
if (o->proxy == NULL)
@ -4909,7 +4923,7 @@ static int do_move_stream(struct client *client, uint32_t command, uint32_t tag,
if (o == NULL)
return -ENOENT;
if ((dev = find_device(client, id_device, name_device, sink)) == NULL)
if ((dev = find_device(client, id_device, name_device, sink, NULL)) == NULL)
return -ENOENT;
if ((res = pw_manager_set_metadata(manager, client->metadata_default,
@ -4918,7 +4932,7 @@ static int do_move_stream(struct client *client, uint32_t command, uint32_t tag,
SPA_TYPE_INFO_BASE"Id", "%d", dev->id)) < 0)
return res;
dev_default = find_device(client, SPA_ID_INVALID, NULL, sink);
dev_default = find_device(client, SPA_ID_INVALID, NULL, sink, NULL);
if (dev == dev_default) {
/*
* When moving streams to a node that is equal to the default,

View file

@ -46,7 +46,7 @@ static inline int volume_compare(struct volume *vol, struct volume *other)
}
for (i = 0; i < vol->channels; i++) {
if (vol->values[i] != other->values[i]) {
pw_log_info("val %f<>%f", vol->values[i], other->values[i]);
pw_log_info("%d: val %f<>%f", i, vol->values[i], other->values[i]);
return -1;
}
}
@ -74,7 +74,7 @@ struct volume_info {
}
static int volume_parse_param(const struct spa_pod *param, struct volume_info *info)
static int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bool monitor)
{
struct spa_pod_object *obj = (struct spa_pod_object *) param;
struct spa_pod_prop *prop;
@ -89,17 +89,35 @@ static int volume_parse_param(const struct spa_pod *param, struct volume_info *i
break;
case SPA_PROP_mute:
if (monitor)
continue;
if (spa_pod_get_bool(&prop->value, &info->mute) < 0)
continue;
SPA_FLAG_UPDATE(info->flags, VOLUME_HW_MUTE,
prop->flags & SPA_POD_PROP_FLAG_HARDWARE);
break;
case SPA_PROP_channelVolumes:
if (monitor)
continue;
info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
info->volume.values, SPA_AUDIO_MAX_CHANNELS);
SPA_FLAG_UPDATE(info->flags, VOLUME_HW_VOLUME,
prop->flags & SPA_POD_PROP_FLAG_HARDWARE);
break;
case SPA_PROP_monitorMute:
if (!monitor)
continue;
if (spa_pod_get_bool(&prop->value, &info->mute) < 0)
continue;
SPA_FLAG_CLEAR(info->flags, VOLUME_HW_MUTE);
break;
case SPA_PROP_monitorVolumes:
if (!monitor)
continue;
info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
info->volume.values, SPA_AUDIO_MAX_CHANNELS);
SPA_FLAG_CLEAR(info->flags, VOLUME_HW_VOLUME);
break;
case SPA_PROP_volumeBase:
if (spa_pod_get_float(&prop->value, &info->base) < 0)
continue;