From ebe22c34fd42a73f82d106a86234b41823b05279 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Fri, 5 Jul 2019 18:37:17 +0300 Subject: [PATCH] mixer: implement listing controls of multiple streams Currently, the stream names have to be duplicated in the configuration file. This is going to change in a future version. --- modules/module-mixer.c | 95 +++++++++++++++++++++++++++++++----------- src/wireplumber.conf | 9 +++- 2 files changed, 78 insertions(+), 26 deletions(-) diff --git a/modules/module-mixer.c b/modules/module-mixer.c index b9e99586..5a5153f2 100644 --- a/modules/module-mixer.c +++ b/modules/module-mixer.c @@ -11,10 +11,10 @@ G_DECLARE_FINAL_TYPE (WpMixerEndpoint, mixer_endpoint, WP, MIXER_ENDPOINT, WpEndpoint) -static const char * streams[] = { - "Master" +enum { + PROP_0, + PROP_STREAMS, }; -#define N_STREAMS (sizeof (streams) / sizeof (const char *)) enum { CONTROL_VOLUME = 0, @@ -28,7 +28,7 @@ enum { struct group { WpMixerEndpoint *mixer; - const gchar *name; + gchar *name; guint32 mixer_stream_id; GWeakRef backend; @@ -38,7 +38,8 @@ struct group struct _WpMixerEndpoint { WpEndpoint parent; - struct group groups[N_STREAMS]; + GVariant *streams; + GArray *groups; }; G_DEFINE_TYPE (WpMixerEndpoint, mixer_endpoint, WP_TYPE_ENDPOINT) @@ -104,14 +105,15 @@ policy_changed (WpPolicyManager *mgr, WpMixerEndpoint * self) int i; g_autoptr (WpCore) core = wp_endpoint_get_core (WP_ENDPOINT (self)); - for (i = 0; i < N_STREAMS; i++) { - group_find_backend (&self->groups[i], core); + for (i = 0; i < self->groups->len; i++) { + group_find_backend (&g_array_index (self->groups, struct group, i), core); } } static void mixer_endpoint_init (WpMixerEndpoint * self) { + self->groups = g_array_new (FALSE, TRUE, sizeof (struct group)); } static void @@ -120,18 +122,27 @@ mixer_endpoint_constructed (GObject * object) WpMixerEndpoint *self = WP_MIXER_ENDPOINT (object); g_autoptr (WpCore) core = NULL; g_autoptr (WpPolicyManager) policymgr = NULL; + struct group empty_group = {0}; GVariantDict d; + GVariantIter iter; gint i; + gchar *stream; core = wp_endpoint_get_core (WP_ENDPOINT (self)); policymgr = wp_policy_manager_get_instance (core); g_signal_connect_object (policymgr, "policy-changed", (GCallback) policy_changed, self, 0); - for (i = 0; i < N_STREAMS; i++) { + g_variant_iter_init (&iter, self->streams); + for (i = 0; g_variant_iter_next (&iter, "s", &stream); i++) { + struct group *group; + + g_array_append_val (self->groups, empty_group); + group = &g_array_index (self->groups, struct group, i); + g_variant_dict_init (&d, NULL); g_variant_dict_insert (&d, "id", "u", i); - g_variant_dict_insert (&d, "name", "s", streams[i]); + g_variant_dict_insert (&d, "name", "s", stream); wp_endpoint_register_stream (WP_ENDPOINT (self), g_variant_dict_end (&d)); g_variant_dict_init (&d, NULL); @@ -151,12 +162,12 @@ mixer_endpoint_constructed (GObject * object) g_variant_dict_insert (&d, "default-value", "b", FALSE); wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d)); - self->groups[i].mixer = self; - self->groups[i].name = streams[i]; - self->groups[i].mixer_stream_id = i; - g_weak_ref_init (&self->groups[i].backend, NULL); + group->mixer = self; + group->name = stream; + group->mixer_stream_id = i; + g_weak_ref_init (&group->backend, NULL); - group_find_backend (&self->groups[i], core); + group_find_backend (group, core); } G_OBJECT_CLASS (mixer_endpoint_parent_class)->constructed (object); @@ -168,16 +179,37 @@ mixer_endpoint_finalize (GObject * object) WpMixerEndpoint *self = WP_MIXER_ENDPOINT (object); gint i; - for (i = 0; i < N_STREAMS; i++) { - g_autoptr (WpEndpoint) backend = g_weak_ref_get (&self->groups[i].backend); + for (i = 0; i < self->groups->len; i++) { + struct group *group = &g_array_index (self->groups, struct group, i); + g_autoptr (WpEndpoint) backend = g_weak_ref_get (&group->backend); if (backend) - g_signal_handlers_disconnect_by_data (backend, &self->groups[i]); - g_weak_ref_clear (&self->groups[i].backend); + g_signal_handlers_disconnect_by_data (backend, group); + g_weak_ref_clear (&group->backend); + g_free (group->name); } + g_clear_pointer (&self->groups, g_array_unref); + g_clear_pointer (&self->streams, g_variant_unref); + G_OBJECT_CLASS (mixer_endpoint_parent_class)->finalize (object); } +static void +mixer_endpoint_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + WpMixerEndpoint *self = WP_MIXER_ENDPOINT (object); + + switch (property_id) { + case PROP_STREAMS: + self->streams = g_value_dup_variant (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + static GVariant * mixer_endpoint_get_control_value (WpEndpoint * ep, guint32 control_id) { @@ -189,12 +221,12 @@ mixer_endpoint_get_control_value (WpEndpoint * ep, guint32 control_id) stream_id = control_id / N_CONTROLS; control_id = control_id % N_CONTROLS; - if (stream_id >= N_STREAMS) { + if (stream_id >= self->groups->len) { g_warning ("Mixer:%p Invalid stream id %u", self, stream_id); return NULL; } - group = &self->groups[stream_id]; + group = &g_array_index (self->groups, struct group, stream_id); backend = g_weak_ref_get (&group->backend); /* if there is no backend, return the default value */ @@ -228,12 +260,12 @@ mixer_endpoint_set_control_value (WpEndpoint * ep, guint32 control_id, stream_id = control_id / N_CONTROLS; control_id = control_id % N_CONTROLS; - if (stream_id >= N_STREAMS) { + if (stream_id >= self->groups->len) { g_warning ("Mixer:%p Invalid stream id %u", self, stream_id); return FALSE; } - group = &self->groups[stream_id]; + group = &g_array_index (self->groups, struct group, stream_id); backend = g_weak_ref_get (&group->backend); if (!backend) { @@ -251,20 +283,29 @@ mixer_endpoint_class_init (WpMixerEndpointClass * klass) GObjectClass *object_class = (GObjectClass *) klass; WpEndpointClass *endpoint_class = (WpEndpointClass *) klass; + object_class->set_property = mixer_endpoint_set_property; object_class->constructed = mixer_endpoint_constructed; object_class->finalize = mixer_endpoint_finalize; endpoint_class->get_control_value = mixer_endpoint_get_control_value; endpoint_class->set_control_value = mixer_endpoint_set_control_value; + + g_object_class_install_property (object_class, PROP_STREAMS, + g_param_spec_variant ("streams", "streams", + "The stream names for the streams to create", + G_VARIANT_TYPE ("as"), NULL, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void -remote_connected (WpRemote *remote, WpRemoteState state, WpCore *core) +remote_connected (WpRemote *remote, WpRemoteState state, GVariant *streams) { + g_autoptr (WpCore) core = wp_remote_get_core (remote); g_autoptr (WpEndpoint) ep = g_object_new (mixer_endpoint_get_type (), "core", core, "name", "Mixer", "media-class", "Mixer/Audio", + "streams", streams, NULL); wp_endpoint_register (ep); } @@ -273,10 +314,14 @@ void wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) { WpRemote *remote; + GVariant *streams; remote = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE); g_return_if_fail (remote != NULL); - g_signal_connect (remote, "state-changed::connected", - (GCallback) remote_connected, core); + streams = g_variant_lookup_value (args, "streams", G_VARIANT_TYPE ("as")); + + g_signal_connect_data (remote, "state-changed::connected", + (GCallback) remote_connected, streams, (GClosureNotify) g_variant_unref, + 0); } diff --git a/src/wireplumber.conf b/src/wireplumber.conf index 370a4dff..999e0e06 100644 --- a/src/wireplumber.conf +++ b/src/wireplumber.conf @@ -9,10 +9,17 @@ load-module C libwireplumber-module-client-permissions load-module C libwireplumber-module-pw-audio-softdsp-endpoint # Endpoint that provides high-level volume controls for the AGL mixer -load-module C libwireplumber-module-mixer +# The streams specified here are the ones that will appear in the mixer. +# They must match the stream names in the alsa-udev module, +# except for "Master", which is treated specially. +load-module C libwireplumber-module-mixer { + "streams": <["Master", "Multimedia", "Navigation", "Communication", "Emergency"]> +} # Monitors the ALSA devices that are discovered via udev # and creates softdsp-endopints for each one of them +# The streams specified here are the ones that will be available for linking +# clients. Currently, they are matched against the client's role string. load-module C libwireplumber-module-pw-alsa-udev { "streams": <["Multimedia", "Navigation", "Communication", "Emergency"]> } -- GitLab