From e030445e4967a5572ceca3fd25fe98089e5e90f2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 7 Jul 2020 13:12:57 +0200 Subject: [PATCH] acp: add volume base and step Add a READONLY property flag to makr properties READONLY Set the base_volume and volume_step in the acp device Send the base volume and step as REAONLY properties. Use these in pulse layer. --- pipewire-pulseaudio/src/context.c | 10 +++++++++- pipewire-pulseaudio/src/internal.h | 2 ++ pipewire-pulseaudio/src/introspect.c | 8 ++++---- spa/include/spa/param/props.h | 9 ++++++--- spa/include/spa/pod/pod.h | 3 ++- spa/plugins/alsa/acp/acp.c | 6 +++++- spa/plugins/alsa/acp/acp.h | 3 +++ spa/plugins/alsa/alsa-acp-device.c | 10 +++++++++- 8 files changed, 40 insertions(+), 11 deletions(-) diff --git a/pipewire-pulseaudio/src/context.c b/pipewire-pulseaudio/src/context.c index 9f2d285a1..bf8b630a7 100644 --- a/pipewire-pulseaudio/src/context.c +++ b/pipewire-pulseaudio/src/context.c @@ -384,6 +384,12 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi prop->flags & SPA_POD_PROP_FLAG_HARDWARE); break; } + case SPA_PROP_volumeBase: + spa_pod_get_float(&prop->value, &g->node_info.base_volume); + break; + case SPA_PROP_volumeStep: + spa_pod_get_float(&prop->value, &g->node_info.volume_step); + break; default: break; } @@ -1037,8 +1043,10 @@ static int set_mask(pa_context *c, struct global *g) g->node_info.device_id = SPA_ID_INVALID; ginfo = &node_info; - g->node_info.volume = 1.0; + g->node_info.volume = 1.0f; g->node_info.mute = false; + g->node_info.base_volume = 1.0f; + g->node_info.volume_step = 1.0f / (PA_VOLUME_NORM+1); } else if (strcmp(g->type, PW_TYPE_INTERFACE_Port) == 0) { if (g->props == NULL) return 0; diff --git a/pipewire-pulseaudio/src/internal.h b/pipewire-pulseaudio/src/internal.h index 0d05b8db5..7565ea9c0 100644 --- a/pipewire-pulseaudio/src/internal.h +++ b/pipewire-pulseaudio/src/internal.h @@ -283,6 +283,8 @@ struct global { float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; uint32_t device_id; /* id of device (card) */ uint32_t profile_device_id; /* id in profile */ + float base_volume; + float volume_step; } node_info; struct { uint32_t node_id; diff --git a/pipewire-pulseaudio/src/introspect.c b/pipewire-pulseaudio/src/introspect.c index 18b1cf37a..0c9fe714b 100644 --- a/pipewire-pulseaudio/src/introspect.c +++ b/pipewire-pulseaudio/src/introspect.c @@ -149,9 +149,9 @@ static void sink_callback(pa_context *c, struct global *g, struct sink_data *d) i.flags |= PA_SINK_HW_MUTE_CTRL; i.proplist = pa_proplist_new_dict(info->props); i.configured_latency = 0; - i.base_volume = PA_VOLUME_NORM; + i.base_volume = g->node_info.base_volume * PA_VOLUME_NORM; + i.n_volume_steps = g->node_info.volume_step * (PA_VOLUME_NORM+1); i.state = node_state_to_sink(info->state); - i.n_volume_steps = PA_VOLUME_NORM+1; i.card = g->node_info.device_id; i.n_ports = 0; i.ports = NULL; @@ -872,9 +872,9 @@ static void source_callback(pa_context *c, struct global *g, struct source_data i.flags = flags; i.proplist = pa_proplist_new_dict(info->props); i.configured_latency = 0; - i.base_volume = PA_VOLUME_NORM; + i.base_volume = g->node_info.base_volume * PA_VOLUME_NORM; + i.n_volume_steps = g->node_info.volume_step * (PA_VOLUME_NORM+1); i.state = node_state_to_source(info->state); - i.n_volume_steps = PA_VOLUME_NORM+1; i.card = g->node_info.device_id; i.n_ports = 0; i.ports = NULL; diff --git a/spa/include/spa/param/props.h b/spa/include/spa/param/props.h index 0399cd7e1..b8dee2cca 100644 --- a/spa/include/spa/param/props.h +++ b/spa/include/spa/param/props.h @@ -69,12 +69,15 @@ enum spa_prop { SPA_PROP_START_Audio = 0x10000, /**< audio related properties */ SPA_PROP_waveType, SPA_PROP_frequency, - SPA_PROP_volume, - SPA_PROP_mute, + SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */ + SPA_PROP_mute, /**< mute (Bool) */ SPA_PROP_patternType, SPA_PROP_ditherType, SPA_PROP_truncate, - SPA_PROP_channelVolumes, + SPA_PROP_channelVolumes, /**< a volume array, one volume per + * channel (Array of Float) */ + SPA_PROP_volumeBase, /**< a volume base (Float) */ + SPA_PROP_volumeStep, /**< a volume step (Float) */ SPA_PROP_START_Video = 0x20000, /**< video related properties */ SPA_PROP_brightness, diff --git a/spa/include/spa/pod/pod.h b/spa/include/spa/pod/pod.h index f7b350e13..3111d7690 100644 --- a/spa/include/spa/pod/pod.h +++ b/spa/include/spa/pod/pod.h @@ -194,7 +194,8 @@ struct spa_pod_fd { struct spa_pod_prop { uint32_t key; /**< key of property, list of valid keys depends on the * object type */ -#define SPA_POD_PROP_FLAG_HARDWARE (1u<<0) /**< property for some sort of hardware parameter */ +#define SPA_POD_PROP_FLAG_READONLY (1u<<0) /**< is read-only */ +#define SPA_POD_PROP_FLAG_HARDWARE (1u<<1) /**< some sort of hardware parameter */ uint32_t flags; /**< flags for property */ struct spa_pod value; /* value follows */ diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c index 59aae6b76..1c0835889 100644 --- a/spa/plugins/alsa/acp/acp.c +++ b/spa/plugins/alsa/acp/acp.c @@ -55,7 +55,7 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t dev->device.format.format_mask = m->sample_spec.format; dev->device.format.rate_mask = m->sample_spec.rate; dev->device.format.channels = m->channel_map.channels; - pa_cvolume_set(&dev->real_volume, m->channel_map.channels, PA_VOLUME_NORM); + pa_cvolume_reset(&dev->real_volume, m->channel_map.channels); for (i = 0; i < m->channel_map.channels; i++) dev->device.format.map[i]= m->channel_map.map[i]; dev->direction = direction; @@ -813,6 +813,8 @@ static void mixer_volume_init(pa_alsa_device *dev) { dev->read_volume = NULL; dev->set_volume = NULL; pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + dev->base_volume = PA_VOLUME_NORM; + dev->n_volume_steps = PA_VOLUME_NORM+1; dev->device.flags &= ~ACP_DEVICE_HW_VOLUME; } else { dev->read_volume = read_volume; @@ -846,6 +848,8 @@ static void mixer_volume_init(pa_alsa_device *dev) { pa_log_info("Using hardware volume control. Hardware dB scale %s.", dev->mixer_path->has_dB ? "supported" : "not supported"); } + dev->device.base_volume = (float)dev->base_volume / PA_VOLUME_NORM; + dev->device.volume_step = 1.0f / dev->n_volume_steps; if (!dev->mixer_path || !dev->mixer_path->has_mute) { dev->read_mute = NULL; diff --git a/spa/plugins/alsa/acp/acp.h b/spa/plugins/alsa/acp/acp.h index 161d5ca23..067d9e0fc 100644 --- a/spa/plugins/alsa/acp/acp.h +++ b/spa/plugins/alsa/acp/acp.h @@ -169,6 +169,9 @@ struct acp_device { char **device_strings; struct acp_format format; + float base_volume; + float volume_step; + struct acp_port **ports; uint32_t n_ports; }; diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index 58a436c20..0715d3676 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -354,15 +354,23 @@ static struct spa_pod *build_route(struct spa_pod_builder *b, uint32_t id, spa_pod_builder_prop(b, SPA_PARAM_ROUTE_props, 0); spa_pod_builder_push_object(b, &f[1], SPA_TYPE_OBJECT_Props, id); + spa_pod_builder_prop(b, SPA_PROP_mute, SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_HW_MUTE) ? SPA_POD_PROP_FLAG_HARDWARE : 0); spa_pod_builder_bool(b, mute); + spa_pod_builder_prop(b, SPA_PROP_channelVolumes, SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_HW_VOLUME) ? SPA_POD_PROP_FLAG_HARDWARE : 0); spa_pod_builder_array(b, sizeof(float), SPA_TYPE_Float, channels, volumes); + + spa_pod_builder_prop(b, SPA_PROP_volumeBase, SPA_POD_PROP_FLAG_READONLY); + spa_pod_builder_float(b, dev->base_volume); + spa_pod_builder_prop(b, SPA_PROP_volumeStep, SPA_POD_PROP_FLAG_READONLY); + spa_pod_builder_float(b, dev->volume_step); + spa_pod_builder_pop(b, &f[1]); } return spa_pod_builder_pop(b, &f[0]); @@ -653,7 +661,7 @@ static void on_volume_changed(void *data, struct acp_device *dev) static void on_mute_changed(void *data, struct acp_device *dev) { struct impl *this = data; - spa_log_debug(this->log, "device %s mute changed", dev->name); + spa_log_info(this->log, "device %s mute changed", dev->name); this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; this->params[3].flags ^= SPA_PARAM_INFO_SERIAL; emit_info(this, false);