From c2f0d65723db52e8141dd0692d2dcf0831f13a84 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Tue, 11 Jun 2019 11:38:56 +0300 Subject: [PATCH] softdsp-endpoint: add master volume & mute controls --- modules/module-pw-audio-softdsp-endpoint.c | 166 ++++++++++++++++++++- 1 file changed, 159 insertions(+), 7 deletions(-) diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c index 5d2d8231..e1fdd9eb 100644 --- a/modules/module-pw-audio-softdsp-endpoint.c +++ b/modules/module-pw-audio-softdsp-endpoint.c @@ -16,6 +16,8 @@ #include <wp/wp.h> #include <pipewire/pipewire.h> #include <spa/param/audio/format-utils.h> +#include <spa/pod/builder.h> +#include <spa/param/props.h> #include "module-pipewire/port.h" @@ -29,7 +31,7 @@ struct _WpPwAudioSoftdspEndpoint { struct pw_core_proxy *core_proxy; /* Node proxy and listener */ - struct pw_proxy *node_proxy; + struct pw_node_proxy *node_proxy; struct spa_hook listener; struct spa_hook proxy_listener; @@ -41,11 +43,13 @@ struct _WpPwAudioSoftdspEndpoint { enum pw_direction direction; /* DSP proxy and listener */ - struct pw_proxy *dsp_proxy; + struct pw_node_proxy *dsp_proxy; struct spa_hook dsp_listener; /* DSP info */ struct pw_node_info *dsp_info; + gfloat master_volume; + gboolean master_mute; /* Link proxy and listener */ struct pw_proxy *link_proxy; @@ -60,6 +64,11 @@ enum { PROP_NODE_PROXY, }; +enum { + CONTROL_VOLUME = 0, + CONTROL_MUTE, +}; + G_DECLARE_FINAL_TYPE (WpPwAudioSoftdspEndpoint, endpoint, WP_PW, AUDIO_SOFTDSP_ENDPOINT, WpEndpoint) @@ -134,9 +143,56 @@ dsp_node_event_info (void *data, const struct pw_node_info *info) } } +static void +dsp_node_event_param (void *object, int seq, uint32_t id, + uint32_t index, uint32_t next, const struct spa_pod *param) +{ + WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); + + switch (id) { + case SPA_PARAM_Props: + { + struct spa_pod_prop *prop; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + float volume = self->master_volume; + bool mute = self->master_mute; + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_volume: + spa_pod_get_float(&prop->value, &volume); + break; + case SPA_PROP_mute: + spa_pod_get_bool(&prop->value, &mute); + break; + default: + break; + } + } + + g_debug ("WpEndpoint:%p param event, vol:(%lf -> %f) mute:(%d -> %d)", + self, self->master_volume, volume, self->master_mute, mute); + + if (self->master_volume != volume) { + self->master_volume = volume; + wp_endpoint_notify_control_value (WP_ENDPOINT (self), CONTROL_VOLUME); + } + if (self->master_mute != mute) { + self->master_mute = mute; + wp_endpoint_notify_control_value (WP_ENDPOINT (self), CONTROL_MUTE); + } + + break; + } + default: + break; + } +} + static const struct pw_node_proxy_events dsp_node_events = { PW_VERSION_NODE_PROXY_EVENTS, .info = dsp_node_event_info, + .param = dsp_node_event_param, }; static void @@ -147,6 +203,7 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self) uint8_t buf[1024]; struct spa_pod_builder pod_builder = { 0, }; struct spa_pod *param; + uint32_t ids[1] = { SPA_PARAM_Props }; /* Return if the node has been already emitted */ if (self->dsp_proxy) @@ -171,8 +228,9 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self) /* Set the DSP proxy and listener */ self->dsp_proxy = pw_core_proxy_create_object(self->core_proxy, "audio-dsp", PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, &props->dict, 0); - pw_proxy_add_proxy_listener(self->dsp_proxy, &self->dsp_listener, + pw_node_proxy_add_listener(self->dsp_proxy, &self->dsp_listener, &dsp_node_events, self); + pw_node_proxy_subscribe_params (self->dsp_proxy, ids, SPA_N_ELEMENTS (ids)); /* Set DSP proxy params */ spa_pod_builder_init(&pod_builder, buf, sizeof(buf)); @@ -251,6 +309,7 @@ endpoint_constructed (GObject * object) { WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); const gchar *media_class = wp_endpoint_get_media_class (WP_ENDPOINT (self)); + GVariantDict d; /* Set the direction */ if (g_str_has_suffix (media_class, "Source")) { @@ -262,10 +321,32 @@ endpoint_constructed (GObject * object) } /* Set the node and proxy listeners */ - pw_proxy_add_listener (self->node_proxy, &self->listener, &node_proxy_events, - self); - pw_node_proxy_add_listener((struct pw_node_proxy *) self->node_proxy, - &self->proxy_listener, &node_events, self); + pw_proxy_add_listener ((struct pw_proxy *) self->node_proxy, &self->listener, + &node_proxy_events, self); + pw_node_proxy_add_listener(self->node_proxy, &self->proxy_listener, + &node_events, self); + + g_variant_dict_init (&d, NULL); + g_variant_dict_insert (&d, "id", "u", 0); + g_variant_dict_insert (&d, "name", "s", "default"); + wp_endpoint_register_stream (WP_ENDPOINT (self), g_variant_dict_end (&d)); + + self->master_volume = 1.0; + g_variant_dict_init (&d, NULL); + g_variant_dict_insert (&d, "id", "u", CONTROL_VOLUME); + g_variant_dict_insert (&d, "name", "s", "volume"); + g_variant_dict_insert (&d, "type", "s", "d"); + g_variant_dict_insert (&d, "range", "(dd)", 0.0, 1.0); + g_variant_dict_insert (&d, "default-value", "d", self->master_volume); + wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d)); + + self->master_mute = FALSE; + g_variant_dict_init (&d, NULL); + g_variant_dict_insert (&d, "id", "u", CONTROL_MUTE); + g_variant_dict_insert (&d, "name", "s", "mute"); + g_variant_dict_insert (&d, "type", "s", "b"); + g_variant_dict_insert (&d, "default-value", "b", self->master_mute); + wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d)); G_OBJECT_CLASS (endpoint_parent_class)->constructed (object); } @@ -368,6 +449,75 @@ endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id, return TRUE; } +static GVariant * +endpoint_get_control_value (WpEndpoint * ep, guint32 control_id) +{ + WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep); + + switch (control_id) { + case CONTROL_VOLUME: + return g_variant_new_double (self->master_volume); + case CONTROL_MUTE: + return g_variant_new_boolean (self->master_mute); + default: + g_warning ("Unknown control id %u", control_id); + return NULL; + } +} + +static gboolean +endpoint_set_control_value (WpEndpoint * ep, guint32 control_id, + GVariant * value) +{ + WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep); + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + float volume; + bool mute; + + if (!self->dsp_proxy) { + g_debug("WpEndpoint:%p too early to set control, dsp is not created yet", + self); + return FALSE; + } + + switch (control_id) { + case CONTROL_VOLUME: + volume = g_variant_get_double (value); + + g_debug("WpEndpoint:%p set volume control (%u) value, vol:%f", self, + control_id, volume); + + pw_node_proxy_set_param (self->dsp_proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_add_object (&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, SPA_POD_Float(volume), + NULL)); + break; + + case CONTROL_MUTE: + mute = g_variant_get_boolean (value); + + g_debug("WpEndpoint:%p set mute control (%u) value, mute:%d", self, + control_id, mute); + + pw_node_proxy_set_param (self->dsp_proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_add_object (&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, SPA_POD_Bool(mute), + NULL)); + break; + + default: + g_warning ("Unknown control id %u", control_id); + return FALSE; + } + + return TRUE; +} + static void endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass) { @@ -380,6 +530,8 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass) object_class->get_property = endpoint_get_property; endpoint_class->prepare_link = endpoint_prepare_link; + endpoint_class->get_control_value = endpoint_get_control_value; + endpoint_class->set_control_value = endpoint_set_control_value; g_object_class_install_property (object_class, PROP_NODE_PROXY, g_param_spec_pointer ("node-proxy", "node-proxy", -- GitLab