audiomixer: port to wireplumber 0.4 55/26255/2 11.92.0 lamprey/11.91.0 lamprey/11.92.0 lamprey_11.91.0 lamprey_11.92.0
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Thu, 25 Feb 2021 19:55:58 +0000 (14:55 -0500)
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Tue, 20 Apr 2021 08:37:52 +0000 (11:37 +0300)
* use module-mixer-api and module-default-nodes-api
* use endpoints without endpoint streams (new design)
* improve thread safety a little bit

Bug-AGL: SPEC-3844
Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
Change-Id: Ia180d607698d7a8875c19cc48bd2b2c503772945

binding/audiomixer-binding.c
binding/audiomixer.c
binding/audiomixer.h
conf.d/cmake/config.cmake

index 80c4482..2c8981a 100644 (file)
@@ -149,12 +149,6 @@ list_controls_cb(afb_req_t request)
 
        audiomixer_lock(audiomixer);
 
-       if (audiomixer_ensure_connected(audiomixer, 3) < 0) {
-               afb_req_fail(request, "failed",
-                       "Could not connect to the PipeWire daemon");
-               goto unlock;
-       }
-
        if (audiomixer_ensure_controls(audiomixer, 3) < 0) {
                AFB_REQ_NOTICE(request, "No mixer controls were exposed "
                        "in PipeWire after 3 seconds");
@@ -175,7 +169,6 @@ list_controls_cb(afb_req_t request)
        }
        afb_req_success(request, ret_json, NULL);
 
-unlock:
        audiomixer_unlock(audiomixer);
 }
 
@@ -196,12 +189,6 @@ volume_cb(afb_req_t request)
                goto unlock;
        }
 
-       if (audiomixer_ensure_connected(audiomixer, 3) < 0) {
-               afb_req_fail(request, "failed",
-                       "Could not connect to the PipeWire daemon");
-               goto unlock;
-       }
-
        if (audiomixer_ensure_controls(audiomixer, 3) < 0) {
                AFB_REQ_NOTICE(request, "No mixer controls were exposed "
                        "in PipeWire after 3 seconds");
@@ -252,12 +239,6 @@ mute_cb(afb_req_t request)
                goto unlock;
        }
 
-       if (audiomixer_ensure_connected(audiomixer, 3) < 0) {
-               afb_req_fail(request, "failed",
-                       "Could not connect to the PipeWire daemon");
-               goto unlock;
-       }
-
        if (audiomixer_ensure_controls(audiomixer, 3) < 0) {
                AFB_REQ_NOTICE(request, "No mixer controls were exposed "
                        "in PipeWire after 3 seconds");
@@ -375,7 +356,7 @@ onevent(afb_api_t api, const char *event, struct json_object *object)
 
        audiomixer_lock(audiomixer);
 
-       ctl = audiomixer_find_control(audiomixer, "Master");
+       ctl = audiomixer_find_control(audiomixer, "Master Playback");
        if (!ctl)
                goto unlock;
 
index aceaefe..78dd347 100644 (file)
@@ -18,13 +18,12 @@ struct audiomixer
        GMutex lock;
        GCond cond;
 
-       WpRemoteState state;
        GPtrArray *mixer_controls;
-       GWeakRef session;
 
-       /* ref held in the thread function */
-       WpObjectManager *sessions_om;
-       WpObjectManager *endpoints_om;
+       gint initialized;
+       WpObjectManager *om;
+       WpPlugin *default_nodes_api;
+       WpPlugin *mixer_api;
 
        const struct audiomixer_events *events;
        void *events_data;
@@ -33,9 +32,7 @@ struct audiomixer
 struct mixer_control_impl
 {
        struct mixer_control pub;
-       gboolean is_master;
-       guint32 id;
-       WpEndpoint *endpoint;
+       guint32 node_id;
 };
 
 struct action
@@ -53,35 +50,67 @@ struct action
        };
 };
 
+static gboolean
+get_mixer_controls (struct audiomixer * self, guint32 node_id, gdouble * vol, gboolean * mute)
+{
+       g_autoptr (GVariant) v = NULL;
+       g_signal_emit_by_name (self->mixer_api, "get-volume", node_id, &v);
+       return v &&
+               g_variant_lookup (v, "volume", "d", &vol, NULL) &&
+               g_variant_lookup (v, "mute", "b", &mute, NULL);
+}
+
+static void
+add_control (struct audiomixer *self, const char *name, guint32 node_id)
+{
+       struct mixer_control_impl *mixctl = NULL;
+       gdouble volume = 1.0;
+       gboolean mute = FALSE;
+
+       /* get current values */
+       if (!get_mixer_controls (self, node_id, &volume, &mute)) {
+               g_warning ("failed to get object controls when populating controls");
+               return;
+       }
+
+       /* create the control */
+       mixctl = g_new0 (struct mixer_control_impl, 1);
+       snprintf (mixctl->pub.name, sizeof (mixctl->pub.name), "%s", name);
+       mixctl->pub.volume = volume;
+       mixctl->pub.mute = mute;
+       mixctl->node_id = node_id;
+       g_ptr_array_add (self->mixer_controls, mixctl);
+
+       g_debug ("added control %s", mixctl->pub.name);
+}
+
 static void
-control_changed (WpEndpoint * ep, guint32 control_id, struct audiomixer * self)
+volume_changed (struct audiomixer * self, guint32 node_id)
 {
-       struct mixer_control_impl *ctl;
+       g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
+       gdouble vol = 1.0;
+       gboolean mute = FALSE;
 
        for (guint i = 0; i < self->mixer_controls->len; i++) {
+               struct mixer_control_impl *ctl;
+               guint change_mask = 0;
+
                ctl = g_ptr_array_index (self->mixer_controls, i);
-               if (ctl->endpoint != ep)
+               if (ctl->node_id != node_id)
                        continue;
 
-               guint change_mask = 0;
+               if (!get_mixer_controls (self, node_id, &vol, &mute)) {
+                       g_warning ("failed to get object controls when volume changed");
+                       return;
+               }
 
-               switch (control_id) {
-               case WP_ENDPOINT_CONTROL_VOLUME: {
-                       float vol;
-                       wp_endpoint_get_control_float (ep, control_id, &vol);
+               if ((ctl->pub.volume - 0.01f) > vol || (ctl->pub.volume + 0.01f) < vol) {
                        ctl->pub.volume = vol;
-                       change_mask = MIXER_CONTROL_CHANGE_FLAG_VOLUME;
-                       break;
+                       change_mask |= MIXER_CONTROL_CHANGE_FLAG_VOLUME;
                }
-               case WP_ENDPOINT_CONTROL_MUTE: {
-                       gboolean mute;
-                       wp_endpoint_get_control_boolean (ep, control_id, &mute);
+               if (ctl->pub.mute != mute) {
                        ctl->pub.mute = mute;
-                       change_mask = MIXER_CONTROL_CHANGE_FLAG_MUTE;
-                       break;
-               }
-               default:
-                       break;
+                       change_mask |= MIXER_CONTROL_CHANGE_FLAG_MUTE;
                }
 
                if (self->events && self->events->value_changed)
@@ -90,239 +119,190 @@ control_changed (WpEndpoint * ep, guint32 control_id, struct audiomixer * self)
        }
 }
 
-/* called with self->lock locked */
 static void
-check_and_populate_controls (struct audiomixer * self)
+rescan_controls (struct audiomixer * self)
 {
-       g_autoptr (WpSession) session = NULL;
-       g_autoptr (WpEndpoint) def_ep = NULL;
-       guint32 def_id = 0;
-       struct mixer_control_impl *master_ctl = NULL;
-
-       /* find the session */
-       session = g_weak_ref_get (&self->session);
-
-       /* find the default audio sink endpoint */
-       if (session) {
-               g_autoptr (GPtrArray) arr =
-                               wp_object_manager_get_objects (self->endpoints_om, 0);
-               def_id = wp_session_get_default_endpoint (session,
-                               WP_DEFAULT_ENDPOINT_TYPE_AUDIO_SINK);
-
-               for (guint i = 0; i < arr->len; i++) {
-                       WpEndpoint *ep = g_ptr_array_index (arr, i);
-                       guint32 id = wp_proxy_get_global_id (WP_PROXY (ep));
-                       if (id != def_id)
-                               continue;
-                       def_ep = g_object_ref (ep);
-               }
+       g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
+       g_autoptr (WpIterator) it = NULL;
+       g_auto (GValue) val = G_VALUE_INIT;
+       guint32 id = -1;
+
+       g_debug ("rescan");
+
+       /* clear previous */
+       g_ptr_array_set_size (self->mixer_controls, 0);
+
+       /* add master controls */
+       g_signal_emit_by_name (self->default_nodes_api, "get-default-node",
+                       "Audio/Sink", &id);
+       if (id != (guint32)-1)
+               add_control (self, "Master Playback", id);
+
+       g_signal_emit_by_name (self->default_nodes_api, "get-default-node",
+                       "Audio/Source", &id);
+       if (id != (guint32)-1)
+               add_control (self, "Master Capture", id);
+
+       /* add endpoints */
+       it = wp_object_manager_new_iterator (self->om);
+       for (; wp_iterator_next (it, &val); g_value_unset (&val)) {
+               WpPipewireObject *ep = g_value_get_object (&val);
+               const gchar *name = wp_pipewire_object_get_property (ep, "endpoint.description");
+               const gchar *node = wp_pipewire_object_get_property (ep, "node.id");
+               id = node ? atoi(node) : 0;
+               if (name && id != 0 && id != (guint32)-1)
+                       add_control (self, name, id);
        }
 
-       /* find the audio sink endpoint that was the default before */
-       for (guint i = 0; i < self->mixer_controls->len; i++) {
-               struct mixer_control_impl *ctl = (struct mixer_control_impl *)
-                               g_ptr_array_index (self->mixer_controls, i);
-               if (ctl->is_master) {
-                       master_ctl = ctl;
-                       break;
-               }
-       }
+       /* notify subscribers */
+       if (self->events && self->events->controls_changed)
+               self->events->controls_changed (self->events_data);
+       g_cond_broadcast (&self->cond);
+}
 
-       g_debug ("check_and_populate: session:%p, def_ep:%p, def_id:%u, "
-                       "master_ctl:%p, master_id:%u", session, def_ep, def_id,
-                       master_ctl, master_ctl ? master_ctl->id : 0);
-
-       /* case 1: there is a default endpoint but it doesn't match
-                  what we currently expose -> clear and expose the new one */
-       if (def_ep && (!master_ctl || master_ctl->id != def_id)) {
-               struct mixer_control_impl *mixctl = NULL;
-               gfloat volume;
-               gboolean mute;
-
-               /* clear previous */
-               if (master_ctl)
-                       g_signal_handlers_disconnect_by_data (master_ctl->endpoint, self);
-               g_ptr_array_set_size (self->mixer_controls, 0);
-
-               /* get current master values */
-               wp_endpoint_get_control_float (def_ep, WP_ENDPOINT_CONTROL_VOLUME, &volume);
-               wp_endpoint_get_control_boolean (def_ep, WP_ENDPOINT_CONTROL_MUTE, &mute);
-
-               /* create the Master control */
-               mixctl = g_new0 (struct mixer_control_impl, 1);
-               strncpy (mixctl->pub.name, "Master", sizeof (mixctl->pub.name));
-               mixctl->pub.volume = volume;
-               mixctl->pub.mute = mute;
-               mixctl->is_master = TRUE;
-               mixctl->id = def_id;
-               mixctl->endpoint = g_object_ref (def_ep);
-               g_ptr_array_add (self->mixer_controls, mixctl);
-
-               /* track changes */
-               g_signal_connect (def_ep, "control-changed", (GCallback) control_changed,
-                       self);
-
-               /* wake up audiomixer_ensure_controls() */
-               g_cond_broadcast (&self->cond);
-
-               g_debug ("controls changed");
-
-               /* notify subscribers */
-               if (self->events && self->events->controls_changed)
-                       self->events->controls_changed (self->events_data);
+static void
+on_default_nodes_activated (WpObject * p, GAsyncResult * res, struct audiomixer * self)
+{
+       g_autoptr (GError) error = NULL;
+       if (!wp_object_activate_finish (p, res, &error)) {
+               g_warning ("%s", error->message);
        }
-       /* case 2: there is no default endpoint but something is exposed -> clear */
-       else if (!def_ep && master_ctl) {
-               g_signal_handlers_disconnect_by_data (master_ctl->endpoint, self);
-               g_ptr_array_set_size (self->mixer_controls, 0);
 
-               g_debug ("controls cleared");
+       if (wp_object_get_active_features (WP_OBJECT (self->mixer_api))
+                       & WP_PLUGIN_FEATURE_ENABLED)
+               wp_core_install_object_manager (self->core, self->om);
 
-               /* notify subscribers */
-               if (self->events && self->events->controls_changed)
-                       self->events->controls_changed (self->events_data);
-       }
+       g_signal_connect_swapped (self->default_nodes_api, "changed",
+               (GCallback) rescan_controls, self);
 }
 
 static void
-default_endpoint_changed (WpSession * session, WpDefaultEndpointType type,
-               guint32 new_id, struct audiomixer * self)
+on_mixer_activated (WpObject * p, GAsyncResult * res, struct audiomixer * self)
 {
-       if (type != WP_DEFAULT_ENDPOINT_TYPE_AUDIO_SINK)
-               return;
+       g_autoptr (GError) error = NULL;
+       if (!wp_object_activate_finish (p, res, &error)) {
+               g_warning ("%s", error->message);
+       }
 
-       g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
-       check_and_populate_controls (self);
+       if (wp_object_get_active_features (WP_OBJECT (self->default_nodes_api))
+                       & WP_PLUGIN_FEATURE_ENABLED)
+               wp_core_install_object_manager (self->core, self->om);
+
+       g_signal_connect_swapped (self->mixer_api, "changed",
+               (GCallback) volume_changed, self);
 }
 
 static void
-sessions_changed (WpObjectManager * sessions_om, struct audiomixer * self)
+on_core_connected (struct audiomixer * self)
 {
-       g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
-       g_autoptr (WpSession) old_session = NULL;
-       g_autoptr (WpSession) session = NULL;
-       g_autoptr (GPtrArray) arr = NULL;
-
-       old_session = g_weak_ref_get (&self->session);
-
-       /* normally there is only 1 session */
-       arr = wp_object_manager_get_objects (sessions_om, 0);
-       if (arr->len > 0)
-               session = WP_SESSION (g_object_ref (g_ptr_array_index (arr, 0)));
-
-       g_debug ("sessions changed, count:%d, session:%p, old_session:%p",
-                       arr->len, session, old_session);
-
-       if (session != old_session) {
-               if (old_session)
-                       g_signal_handlers_disconnect_by_data (old_session, self);
-               if (session)
-                       g_signal_connect (session, "default-endpoint-changed",
-                               (GCallback) default_endpoint_changed, self);
-
-               g_weak_ref_set (&self->session, session);
-       }
-
-       check_and_populate_controls (self);
+       self->om = wp_object_manager_new ();
+       wp_object_manager_add_interest (self->om, WP_TYPE_ENDPOINT,
+               WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY,
+               PW_KEY_MEDIA_CLASS, "#s", "Audio/*", NULL);
+       wp_object_manager_request_object_features (self->om,
+               WP_TYPE_ENDPOINT, WP_OBJECT_FEATURES_ALL);
+       g_signal_connect_swapped (self->om, "objects-changed",
+               (GCallback) rescan_controls, self);
+
+       wp_object_activate (WP_OBJECT (self->default_nodes_api),
+               WP_PLUGIN_FEATURE_ENABLED, NULL,
+               (GAsyncReadyCallback) on_default_nodes_activated, self);
+
+       wp_object_activate (WP_OBJECT (self->mixer_api),
+               WP_PLUGIN_FEATURE_ENABLED, NULL,
+               (GAsyncReadyCallback) on_mixer_activated, self);
 }
 
 static void
-endpoints_changed (WpObjectManager * endpoints_om, struct audiomixer * self)
+on_core_disconnected (struct audiomixer * self)
 {
-       g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
-       check_and_populate_controls (self);
+       g_ptr_array_set_size (self->mixer_controls, 0);
+       g_clear_object (&self->om);
+       g_signal_handlers_disconnect_by_data (self->default_nodes_api, self);
+       g_signal_handlers_disconnect_by_data (self->mixer_api, self);
+       wp_object_deactivate (WP_OBJECT (self->default_nodes_api), WP_PLUGIN_FEATURE_ENABLED);
+       wp_object_deactivate (WP_OBJECT (self->mixer_api), WP_PLUGIN_FEATURE_ENABLED);
 }
 
 static void
-remote_state_changed (WpCore *core, WpRemoteState state,
-       struct audiomixer * self)
+audiomixer_init_in_thread (struct audiomixer * self)
 {
        g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
-       self->state = state;
-       g_cond_broadcast (&self->cond);
-}
+       g_autoptr (GError) error = NULL;
 
-static gboolean
-connect_in_idle (struct audiomixer * self)
-{
-       wp_core_connect (self->core);
-       return G_SOURCE_REMOVE;
+       self->context = g_main_context_new ();
+       g_main_context_push_thread_default (self->context);
+
+       self->loop = g_main_loop_new (self->context, FALSE);
+       self->core = wp_core_new (self->context, NULL);
+
+       /* load required API modules */
+       if (!wp_core_load_component (self->core,
+                       "libwireplumber-module-default-nodes-api", "module", NULL, &error)) {
+               g_warning ("%s", error->message);
+               self->initialized = -1;
+               goto out;
+       }
+       if (!wp_core_load_component (self->core,
+                       "libwireplumber-module-mixer-api", "module", NULL, &error)) {
+               g_warning ("%s", error->message);
+               self->initialized = -1;
+               goto out;
+       }
+
+       self->default_nodes_api = wp_plugin_find (self->core, "default-nodes-api");
+       self->mixer_api = wp_plugin_find (self->core, "mixer-api");
+       g_object_set (G_OBJECT (self->mixer_api), "scale", 1 /* cubic */, NULL);
+
+       g_signal_connect_swapped (self->core, "connected",
+               G_CALLBACK (on_core_connected), self);
+       g_signal_connect_swapped (self->core, "disconnected",
+               G_CALLBACK (on_core_disconnected), self);
+
+       self->initialized = 1;
+
+out:
+       g_cond_broadcast (&self->cond);
 }
 
 static void *
 audiomixer_thread (struct audiomixer * self)
 {
-       g_autoptr (WpObjectManager) sessions_om =
-                       self->sessions_om = wp_object_manager_new ();
-       g_autoptr (WpObjectManager) endpoints_om =
-                       self->endpoints_om = wp_object_manager_new ();
-       GVariantBuilder b;
+       audiomixer_init_in_thread (self);
 
-       g_main_context_push_thread_default (self->context);
+       /* main loop for the thread; quits only when audiomixer_free() is called */
+       g_main_loop_run (self->loop);
 
-       /* install object manager for sessions */
-       wp_object_manager_add_proxy_interest (sessions_om, PW_TYPE_INTERFACE_Session,
-               NULL, WP_PROXY_FEATURE_INFO | WP_PROXY_SESSION_FEATURE_DEFAULT_ENDPOINT);
-       g_signal_connect (sessions_om, "objects-changed",
-               (GCallback) sessions_changed, self);
-       wp_core_install_object_manager (self->core, sessions_om);
-
-       /* install object manager for Audio/Sink endpoints */
-       g_variant_builder_init (&b, G_VARIANT_TYPE ("aa{sv}"));
-       g_variant_builder_open (&b, G_VARIANT_TYPE_VARDICT);
-       g_variant_builder_add (&b, "{sv}", "type",
-               g_variant_new_int32 (WP_OBJECT_MANAGER_CONSTRAINT_PW_GLOBAL_PROPERTY));
-       g_variant_builder_add (&b, "{sv}", "name",
-               g_variant_new_string (PW_KEY_MEDIA_CLASS));
-       g_variant_builder_add (&b, "{sv}", "value",
-               g_variant_new_string ("Audio/Sink"));
-       g_variant_builder_close (&b);
-
-       wp_object_manager_add_proxy_interest (endpoints_om,
-               PW_TYPE_INTERFACE_Endpoint,
-               g_variant_builder_end (&b),
-               WP_PROXY_FEATURE_INFO | WP_PROXY_ENDPOINT_FEATURE_CONTROLS);
-       g_signal_connect (endpoints_om, "objects-changed",
-               (GCallback) endpoints_changed, self);
-       wp_core_install_object_manager (self->core, endpoints_om);
-
-       g_signal_connect (self->core, "remote-state-changed",
-               (GCallback) remote_state_changed, self);
+       wp_core_disconnect (self->core);
+       g_clear_object (&self->default_nodes_api);
+       g_clear_object (&self->mixer_api);
+       g_object_unref (self->core);
 
-       g_main_loop_run (self->loop);
        g_main_context_pop_thread_default (self->context);
+       g_main_loop_unref (self->loop);
+       g_main_context_unref (self->context);
 
        return NULL;
 }
 
-static void
-mixer_control_impl_free (struct mixer_control_impl * impl)
-{
-       if (impl->endpoint)
-               g_signal_handlers_disconnect_matched (impl->endpoint,
-                       G_SIGNAL_MATCH_FUNC, 0, 0, NULL, control_changed, NULL);
-       g_clear_object (&impl->endpoint);
-       g_free (impl);
-}
-
 struct audiomixer *
 audiomixer_new (void)
 {
        struct audiomixer *self = calloc(1, sizeof(struct audiomixer));
 
+       wp_init (WP_INIT_ALL);
+
        g_mutex_init (&self->lock);
        g_cond_init (&self->cond);
-       self->context = g_main_context_new ();
-       self->loop = g_main_loop_new (self->context, FALSE);
-       self->core = wp_core_new (self->context, NULL);
-
-       self->state = WP_REMOTE_STATE_UNCONNECTED;
-       self->mixer_controls = g_ptr_array_new_with_free_func (
-               (GDestroyNotify) mixer_control_impl_free);
-       g_weak_ref_init (&self->session, NULL);
+       self->mixer_controls = g_ptr_array_new_with_free_func (g_free);
 
+       g_mutex_lock (&self->lock);
+       self->initialized = 0;
        self->thread = g_thread_new ("audiomixer", (GThreadFunc) audiomixer_thread,
                self);
+       while (self->initialized == 0)
+               g_cond_wait (&self->cond, &self->lock);
+       g_mutex_unlock (&self->lock);
 
        return self;
 }
@@ -333,11 +313,7 @@ audiomixer_free(struct audiomixer *self)
        g_main_loop_quit (self->loop);
        g_thread_join (self->thread);
 
-       g_weak_ref_clear (&self->session);
        g_ptr_array_unref (self->mixer_controls);
-       g_object_unref (self->core);
-       g_main_loop_unref (self->loop);
-       g_main_context_unref (self->context);
        g_cond_clear (&self->cond);
        g_mutex_clear (&self->lock);
 
@@ -356,34 +332,24 @@ audiomixer_unlock(struct audiomixer *self)
        g_mutex_unlock (&self->lock);
 }
 
-int
-audiomixer_ensure_connected(struct audiomixer *self, int timeout_sec)
+static gboolean
+do_connect (WpCore * core)
 {
-       gint64 end_time = g_get_monotonic_time () + timeout_sec * G_TIME_SPAN_SECOND;
-
-       /* already connected */
-       if (self->state == WP_REMOTE_STATE_CONNECTED)
-               return 0;
-
-       /* if disconnected, for any reason, try to connect again */
-       if (self->state != WP_REMOTE_STATE_CONNECTING)
-               wp_core_idle_add (self->core, (GSourceFunc) connect_in_idle, self, NULL);
-
-       while (true) {
-               if (!g_cond_wait_until (&self->cond, &self->lock, end_time))
-                       return -ETIMEDOUT;
-
-               if (self->state == WP_REMOTE_STATE_CONNECTED)
-                       return 0;
-               else if (self->state == WP_REMOTE_STATE_ERROR)
-                       return -EIO;
-       }
+       if (!wp_core_connect (core))
+               g_warning ("Failed to connect to PipeWire");
+       return G_SOURCE_REMOVE;
 }
 
 int
 audiomixer_ensure_controls(struct audiomixer *self, int timeout_sec)
 {
        gint64 end_time = g_get_monotonic_time () + timeout_sec * G_TIME_SPAN_SECOND;
+
+       g_return_val_if_fail (self->initialized == 1, -EIO);
+
+       if (!wp_core_is_connected (self->core))
+               g_main_context_invoke (self->context, (GSourceFunc) do_connect, self->core);
+
        while (self->mixer_controls->len == 0) {
                if (!g_cond_wait_until (&self->cond, &self->lock, end_time))
                        return -ETIMEDOUT;
@@ -425,17 +391,17 @@ audiomixer_add_event_listener(struct audiomixer *self,
 static gboolean
 do_change_volume (struct action * action)
 {
-       struct mixer_control_impl *ctl;
        struct audiomixer *self = action->audiomixer;
+       GVariantBuilder b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+       gboolean ret = FALSE;
+
+       g_variant_builder_add (&b, "{sv}", "volume",
+               g_variant_new_double (action->change_volume.volume));
+       g_signal_emit_by_name (self->mixer_api, "set-volume",
+               action->change_volume.id, g_variant_builder_end (&b), &ret);
+       if (!ret)
+               g_warning ("mixer api set-volume failed");
 
-       for (guint i = 0; i < self->mixer_controls->len; i++) {
-               ctl = g_ptr_array_index (self->mixer_controls, i);
-               if (ctl->id != action->change_volume.id)
-                       continue;
-               if (ctl->is_master)
-                       wp_endpoint_set_control_float (ctl->endpoint,
-                               WP_ENDPOINT_CONTROL_VOLUME, action->change_volume.volume);
-       }
        return G_SOURCE_REMOVE;
 }
 
@@ -448,29 +414,31 @@ audiomixer_change_volume(struct audiomixer *self,
                (const struct mixer_control_impl *) control;
        struct action * action;
 
+       g_return_if_fail (self->initialized == 1);
+
        /* schedule the action to run on the audiomixer thread */
        action = g_new0 (struct action, 1);
        action->audiomixer = self;
-       action->change_volume.id = impl->id;
+       action->change_volume.id = impl->node_id;
        action->change_volume.volume = (gfloat) volume;
-       wp_core_idle_add (self->core, (GSourceFunc) do_change_volume, action,
+       wp_core_idle_add (self->core, NULL, (GSourceFunc) do_change_volume, action,
                g_free);
 }
 
 static gboolean
 do_change_mute (struct action * action)
 {
-       struct mixer_control_impl *ctl;
        struct audiomixer *self = action->audiomixer;
+       GVariantBuilder b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+       gboolean ret = FALSE;
+
+       g_variant_builder_add (&b, "{sv}", "mute",
+               g_variant_new_double (action->change_mute.mute));
+       g_signal_emit_by_name (self->mixer_api, "set-volume",
+               action->change_mute.id, g_variant_builder_end (&b), &ret);
+       if (!ret)
+               g_warning ("mixer api set-volume failed");
 
-       for (guint i = 0; i < self->mixer_controls->len; i++) {
-               ctl = g_ptr_array_index (self->mixer_controls, i);
-               if (ctl->id != action->change_mute.id)
-                       continue;
-               if (ctl->is_master)
-                       wp_endpoint_set_control_boolean (ctl->endpoint,
-                               WP_ENDPOINT_CONTROL_MUTE, action->change_mute.mute);
-       }
        return G_SOURCE_REMOVE;
 }
 
@@ -483,11 +451,13 @@ audiomixer_change_mute(struct audiomixer *self,
                (const struct mixer_control_impl *) control;
        struct action * action;
 
+       g_return_if_fail (self->initialized == 1);
+
        /* schedule the action to run on the audiomixer thread */
        action = g_new0 (struct action, 1);
        action->audiomixer = self;
-       action->change_mute.id = impl->id;
+       action->change_mute.id = impl->node_id;
        action->change_mute.mute = mute;
-       wp_core_idle_add (self->core, (GSourceFunc) do_change_mute, action,
+       wp_core_idle_add (self->core, NULL, (GSourceFunc) do_change_mute, action,
                g_free);
 }
index f4e83c7..47bd703 100644 (file)
@@ -35,7 +35,6 @@ void audiomixer_free(struct audiomixer *self);
 void audiomixer_lock(struct audiomixer *self);
 void audiomixer_unlock(struct audiomixer *self);
 
-int audiomixer_ensure_connected(struct audiomixer *self, int timeout_sec);
 int audiomixer_ensure_controls(struct audiomixer *self, int timeout_sec);
 
 const struct mixer_control ** audiomixer_get_active_controls(
index 04501a8..09d91ae 100644 (file)
@@ -72,7 +72,7 @@ set (PKG_REQUIRED_LIST
        libsystemd>=222
        afb-daemon
        libpipewire-0.3
-       wireplumber-0.1
+       wireplumber-0.4
 )
 
 # Static constante definition