diff options
author | Scott Murray <scott.murray@konsulko.com> | 2022-06-17 20:24:27 -0400 |
---|---|---|
committer | Scott Murray <scott.murray@konsulko.com> | 2022-07-04 22:29:37 +0000 |
commit | 509105d48cd1524cba1f20c4e0641d8c2d3a9cb4 (patch) | |
tree | c9739f3e7bc741efb8c2760bf0dc917e920f5824 /binding | |
parent | dadd3d771d5b27a455afffed8437c0a2e6db26b2 (diff) |
Repurpose into VIS clientneedlefish_13.93.0needlefish/13.93.013.93.0
Repurpose repository for a spiritual successor of the previous
binding. The replacement is a daemon that demonstrates servicing
the volume actuator from the VSS schema via VIS signals from
KUKSA.val. Currently the connection to KUKSA.val is websocket based
using the boost::asio framework, but the plan is to migrate to gRPC
as that becomes more robust in KUKSA.val. As well, this new code
will serve as the base for implementing a gRPC API to expose the full
set of WirePlumber controls as was done with the previous binding.
Notable changes:
- New code is completely C++, partly to leverage using Boost, but
also to futureproof future work with gRPC. The WirePlumber
interfacing code that has been kept from the old binding is
still C for now, converting it to C++ is a planned future
rework.
- Switch from CMake to meson for ease of development and some
degree of futureproofing.
- Use with systemd is assumed; behavior follows the systemd
daemon guidelines barring the use of journald logging prefixes,
which may be addressed with future work. A systemd unit is
also installed as part of the build.
- SPDX license headers using SPDX "short identifiers" are used in
source files rather than the full copyright headers used in the
previous codebase. This follows the direction that projects such
as the Linux kernel are going in.
Bug-AGL: SPEC-4409
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
Change-Id: Ibb7091c4354432bb094147d1419ab475486a4abc
(cherry picked from commit 298bbf445a731b85cb8d5d19a3b595e8870d8701)
Diffstat (limited to 'binding')
-rw-r--r-- | binding/CMakeLists.txt | 19 | ||||
-rw-r--r-- | binding/audiomixer-binding.c | 402 | ||||
-rw-r--r-- | binding/audiomixer.c | 463 | ||||
-rw-r--r-- | binding/audiomixer.h | 59 |
4 files changed, 0 insertions, 943 deletions
diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt deleted file mode 100644 index c199ad2..0000000 --- a/binding/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -PROJECT_TARGET_ADD(audiomixer-binding) - - add_definitions(-DAFB_BINDING_VERSION=3) - - set(audiomixer_SOURCES - audiomixer-binding.c - audiomixer.c - ) - - add_library(${TARGET_NAME} MODULE ${audiomixer_SOURCES}) - - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "libafm-" - LABELS "BINDING" - LINK_FLAGS ${BINDINGS_LINK_FLAG} - OUTPUT_NAME ${TARGET_NAME} - ) - - TARGET_LINK_LIBRARIES(${TARGET_NAME} ${link_libraries}) diff --git a/binding/audiomixer-binding.c b/binding/audiomixer-binding.c deleted file mode 100644 index 2c8981a..0000000 --- a/binding/audiomixer-binding.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd. - * Copyright © 2019 Konsulko Group - * @author George Kiagiadakis <george.kiagiadakis@collabora.com> - * - * SPDX-License-Identifier: MIT - */ - -#include <string.h> -#include <json-c/json.h> -#include <afb/afb-binding.h> -#include <systemd/sd-event.h> -#include "audiomixer.h" - -static struct audiomixer *audiomixer; -static afb_event_t controls_changed; -static afb_event_t volume_changed; -static afb_event_t mute_changed; - -static const char *signalcomposer_events[] = { - "event.volume.up", - "event.volume.down", - "event.volume.mute", - NULL, -}; - -static void -audiomixer_controls_changed_deferred(int signum, void *arg) -{ - AFB_DEBUG("controls changed"); - afb_event_push(controls_changed, NULL); -} - -struct value_changed_data -{ - unsigned int change_mask; - struct mixer_control control; -}; - -static void -audiomixer_value_changed_deferred(int signum, void *data) -{ - struct value_changed_data *d = data; - json_object *json; - - AFB_DEBUG("value changed"); - - json = json_object_new_object(); - json_object_object_add(json, "control", - json_object_new_string(d->control.name)); - - if (d->change_mask & MIXER_CONTROL_CHANGE_FLAG_VOLUME) { - json_object_object_add(json, "value", - json_object_new_double(d->control.volume)); - afb_event_push(volume_changed, json); - } else if (d->change_mask & MIXER_CONTROL_CHANGE_FLAG_MUTE) { - json_object_object_add(json, "value", - json_object_new_int(d->control.mute)); - afb_event_push(mute_changed, json); - } - - free(d); -} - -/* called in audiomixer's thread */ -static void -audiomixer_controls_changed(void *data) -{ - afb_api_t api = data; - afb_api_queue_job (api, audiomixer_controls_changed_deferred, NULL, - (void *) 0x1, 0); -} - - -/* called in audiomixer's thread */ -static void -audiomixer_value_changed(void *data, - unsigned int change_mask, - const struct mixer_control *control) -{ - afb_api_t api = data; - struct value_changed_data *d = calloc(1, sizeof(*d)); - - d->change_mask = change_mask; - d->control = *control; - - afb_api_queue_job (api, audiomixer_value_changed_deferred, d, - (void *) 0x1, 0); -} - -static const struct audiomixer_events audiomixer_events = { - .controls_changed = audiomixer_controls_changed, - .value_changed = audiomixer_value_changed, -}; - -static int -cleanup(sd_event_source *s, void *data) -{ - audiomixer_free(audiomixer); - audiomixer = NULL; - return 0; -} - -static int -init(afb_api_t api) -{ - int ret; - - ret = afb_daemon_require_api("signal-composer", 1); - if (ret) { - AFB_WARNING("unable to initialize signal-composer binding"); - } else { - const char **tmp = signalcomposer_events; - json_object *args = json_object_new_object(); - json_object *signals = json_object_new_array(); - - while (*tmp) { - json_object_array_add(signals, json_object_new_string(*tmp++)); - } - json_object_object_add(args, "signal", signals); - if(json_object_array_length(signals)) { - afb_api_call_sync(api, "signal-composer", "subscribe", - args, NULL, NULL, NULL); - } else { - json_object_put(args); - } - } - - sd_event *e = afb_daemon_get_event_loop(); - - controls_changed = afb_api_make_event(api, "controls_changed"); - volume_changed = afb_api_make_event(api, "volume_changed"); - mute_changed = afb_api_make_event(api, "mute_changed"); - - audiomixer = audiomixer_new(); - sd_event_add_exit(e, NULL, cleanup, NULL); - - audiomixer_add_event_listener(audiomixer, &audiomixer_events, api); - - return 0; -} - -static void -list_controls_cb(afb_req_t request) -{ - json_object *ret_json, *nest_json; - const struct mixer_control **ctls; - unsigned int n_controls, i; - - audiomixer_lock(audiomixer); - - if (audiomixer_ensure_controls(audiomixer, 3) < 0) { - AFB_REQ_NOTICE(request, "No mixer controls were exposed " - "in PipeWire after 3 seconds"); - } - - ctls = audiomixer_get_active_controls(audiomixer, &n_controls); - - ret_json = json_object_new_array(); - for (i = 0; i < n_controls; i++) { - nest_json = json_object_new_object(); - json_object_object_add(nest_json, "control", - json_object_new_string(ctls[i]->name)); - json_object_object_add(nest_json, "volume", - json_object_new_double(ctls[i]->volume)); - json_object_object_add(nest_json, "mute", - json_object_new_int(ctls[i]->mute)); - json_object_array_add(ret_json, nest_json); - } - afb_req_success(request, ret_json, NULL); - - audiomixer_unlock(audiomixer); -} - -static void -volume_cb(afb_req_t request) -{ - json_object *ret_json; - const char *control = afb_req_value(request, "control"); - const char *value = afb_req_value(request, "value"); - const struct mixer_control *ctl; - double volume; - - audiomixer_lock(audiomixer); - - if (!control) { - afb_req_fail(request, "failed", - "Invalid arguments: missing 'control'"); - goto unlock; - } - - if (audiomixer_ensure_controls(audiomixer, 3) < 0) { - AFB_REQ_NOTICE(request, "No mixer controls were exposed " - "in PipeWire after 3 seconds"); - } - - ctl = audiomixer_find_control(audiomixer, control); - if (!ctl) { - afb_req_fail(request, "failed", "Could not find control"); - goto unlock; - } - - if(value) { - char *endptr; - volume = strtod(value, &endptr); - if (endptr == value || volume < -0.00001 || volume > 1.00001) { - afb_req_fail(request, "failed", - "Invalid volume value (must be between 0.0 and 1.0)"); - goto unlock; - } - - audiomixer_change_volume(audiomixer, ctl, volume); - } else { - volume = ctl->volume; - } - - ret_json = json_object_new_object(); - json_object_object_add(ret_json, "volume", json_object_new_double(volume)); - afb_req_success(request, ret_json, NULL); - -unlock: - audiomixer_unlock(audiomixer); -} - -static void -mute_cb(afb_req_t request) -{ - json_object *ret_json; - const char *control = afb_req_value(request, "control"); - const char *value = afb_req_value(request, "value"); - const struct mixer_control *ctl; - int mute; - - audiomixer_lock(audiomixer); - - if (!control) { - afb_req_fail(request, "failed", - "Invalid arguments: missing 'control'"); - goto unlock; - } - - if (audiomixer_ensure_controls(audiomixer, 3) < 0) { - AFB_REQ_NOTICE(request, "No mixer controls were exposed " - "in PipeWire after 3 seconds"); - } - - ctl = audiomixer_find_control(audiomixer, control); - if (!ctl) { - afb_req_fail(request, "failed", "Could not find control"); - goto unlock; - } - - if(value) { - char *endptr; - mute = (int) strtol(value, &endptr, 10); - if (endptr == value || mute < 0 || mute > 1) { - afb_req_fail(request, "failed", - "Invalid mute value (must be integer 0 or 1)"); - goto unlock; - } - - audiomixer_change_mute(audiomixer, ctl, mute); - } else { - mute = ctl->mute; - } - - ret_json = json_object_new_object(); - json_object_object_add(ret_json, "mute", json_object_new_int(mute)); - afb_req_success(request, ret_json, NULL); - -unlock: - audiomixer_unlock(audiomixer); -} - -static void -subscribe_cb(afb_req_t request) -{ - const char *eventstr = afb_req_value(request, "event"); - afb_event_t event; - - if (!eventstr) { - afb_req_fail(request, "failed", - "Invalid arguments: missing 'event'"); - return; - } - - if (!strcmp(eventstr, "controls_changed")) - event = controls_changed; - else if (!strcmp(eventstr, "volume_changed")) - event = volume_changed; - else if (!strcmp(eventstr, "mute_changed")) - event = mute_changed; - else { - afb_req_fail(request, "failed", "Invalid event name"); - return; - } - - if (afb_req_subscribe(request, event) != 0) - afb_req_fail(request, "failed", "Failed to subscribe to event"); - else - afb_req_success(request, NULL, "Subscribed"); -} - -static void -unsubscribe_cb(afb_req_t request) -{ - const char *eventstr = afb_req_value(request, "event"); - afb_event_t event; - - if (!eventstr) { - afb_req_fail(request, "failed", - "Invalid arguments: missing 'event'"); - return; - } - - if (!strcmp(eventstr, "controls_changed")) - event = controls_changed; - else if (!strcmp(eventstr, "volume_changed")) - event = volume_changed; - else if (!strcmp(eventstr, "mute_changed")) - event = mute_changed; - else { - afb_req_fail(request, "failed", "Invalid event name"); - return; - } - - if (afb_req_unsubscribe(request, event) != 0) - afb_req_fail(request, "failed", "Failed to unsubscribe from event"); - else - afb_req_success(request, NULL, "Unsubscribed"); -} - -static void -onevent(afb_api_t api, const char *event, struct json_object *object) -{ - const struct mixer_control *ctl; - json_object *tmp = NULL; - const char *uid; - const char *value; - - json_object_object_get_ex(object, "uid", &tmp); - if (tmp == NULL) - return; - - uid = json_object_get_string(tmp); - if (strncmp(uid, "event.volume.", 13)) - return; - - json_object_object_get_ex(object, "value", &tmp); - if (tmp == NULL) - return; - - value = json_object_get_string(tmp); - if (strncmp(value, "true", 4)) - return; - - audiomixer_lock(audiomixer); - - ctl = audiomixer_find_control(audiomixer, "Master Playback"); - if (!ctl) - goto unlock; - - if (!strcmp(uid, "event.volume.mute")) { - audiomixer_change_mute(audiomixer, ctl, !ctl->mute); - } else { - double volume = ctl->volume; - - if (!strcmp(uid, "event.volume.up")) { - volume += 0.05; // up 5% - if (volume > 1.0) - volume = 1.0; // clamp to 100% - } else if (!strcmp(uid, "event.volume.down")) { - volume -= 0.05; // down 5% - if (volume < 0.0) - volume = 0.0; // clamp to 0% - } else { - AFB_WARNING("Unhandled signal-composer uid '%s'", uid); - goto unlock; - } - audiomixer_change_volume(audiomixer, ctl, volume); - } - -unlock: - audiomixer_unlock(audiomixer); -} - -static const afb_verb_t verbs[]= { - { .verb = "list_controls", .callback = list_controls_cb, .info = "List the available controls" }, - { .verb = "volume", .callback = volume_cb, .info = "Get/Set volume" }, - { .verb = "mute", .callback = mute_cb, .info = "Get/Set mute" }, - { .verb = "subscribe", .callback = subscribe_cb, .info = "Subscribe to mixer events" }, - { .verb = "unsubscribe", .callback = unsubscribe_cb, .info = "Unsubscribe from mixer events" }, - { } -}; - -const afb_binding_t afbBindingV3 = { - .api = "audiomixer", - .specification = "AudioMixer API", - .verbs = verbs, - .onevent = onevent, - .init = init, -}; diff --git a/binding/audiomixer.c b/binding/audiomixer.c deleted file mode 100644 index 97ad622..0000000 --- a/binding/audiomixer.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis <george.kiagiadakis@collabora.com> - * - * SPDX-License-Identifier: MIT - */ - -#include "audiomixer.h" -#include <wp/wp.h> -#include <pipewire/pipewire.h> - -struct audiomixer -{ - WpCore *core; - GMainLoop *loop; - GMainContext *context; - GThread *thread; - GMutex lock; - GCond cond; - - GPtrArray *mixer_controls; - - gint initialized; - WpObjectManager *om; - WpPlugin *default_nodes_api; - WpPlugin *mixer_api; - - const struct audiomixer_events *events; - void *events_data; -}; - -struct mixer_control_impl -{ - struct mixer_control pub; - guint32 node_id; -}; - -struct action -{ - struct audiomixer *audiomixer; - union { - struct { - guint32 id; - gfloat volume; - } change_volume; - struct { - guint32 id; - gboolean mute; - } change_mute; - }; -}; - -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) && - g_variant_lookup (v, "mute", "b", mute); -} - -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 -volume_changed (struct audiomixer * self, guint32 node_id) -{ - 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->node_id != node_id) - continue; - - if (!get_mixer_controls (self, node_id, &vol, &mute)) { - g_warning ("failed to get object controls when volume changed"); - return; - } - - if ((ctl->pub.volume - 0.01f) > vol || (ctl->pub.volume + 0.01f) < vol) { - ctl->pub.volume = vol; - change_mask |= MIXER_CONTROL_CHANGE_FLAG_VOLUME; - } - if (ctl->pub.mute != mute) { - ctl->pub.mute = mute; - change_mask |= MIXER_CONTROL_CHANGE_FLAG_MUTE; - } - - if (self->events && self->events->value_changed) - self->events->value_changed (self->events_data, change_mask, &ctl->pub); - break; - } -} - -static void -rescan_controls (struct audiomixer * self) -{ - 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); - } - - /* notify subscribers */ - if (self->events && self->events->controls_changed) - self->events->controls_changed (self->events_data); - g_cond_broadcast (&self->cond); -} - -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); - } - - if (wp_object_get_active_features (WP_OBJECT (self->mixer_api)) - & WP_PLUGIN_FEATURE_ENABLED) - wp_core_install_object_manager (self->core, self->om); - - g_signal_connect_swapped (self->default_nodes_api, "changed", - (GCallback) rescan_controls, self); -} - -static void -on_mixer_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); - } - - 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 -on_core_connected (struct audiomixer * 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 -on_core_disconnected (struct audiomixer * 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 -audiomixer_init_in_thread (struct audiomixer * self) -{ - g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock); - g_autoptr (GError) error = NULL; - - 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) -{ - audiomixer_init_in_thread (self); - - /* main loop for the thread; quits only when audiomixer_free() is called */ - g_main_loop_run (self->loop); - - 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_context_pop_thread_default (self->context); - g_main_loop_unref (self->loop); - g_main_context_unref (self->context); - - return NULL; -} - -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->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; -} - -void -audiomixer_free(struct audiomixer *self) -{ - g_main_loop_quit (self->loop); - g_thread_join (self->thread); - - g_ptr_array_unref (self->mixer_controls); - g_cond_clear (&self->cond); - g_mutex_clear (&self->lock); - - free (self); -} - -void -audiomixer_lock(struct audiomixer *self) -{ - g_mutex_lock (&self->lock); -} - -void -audiomixer_unlock(struct audiomixer *self) -{ - g_mutex_unlock (&self->lock); -} - -static gboolean -do_connect (WpCore * core) -{ - 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; - } - return 0; -} - -const struct mixer_control ** -audiomixer_get_active_controls(struct audiomixer *self, - unsigned int *n_controls) -{ - *n_controls = self->mixer_controls->len; - return (const struct mixer_control **) self->mixer_controls->pdata; -} - -const struct mixer_control * -audiomixer_find_control(struct audiomixer *self, const char *name) -{ - struct mixer_control *ctl; - - for (guint i = 0; i < self->mixer_controls->len; i++) { - ctl = g_ptr_array_index (self->mixer_controls, i); - if (!strcmp(ctl->name, name)) { - return ctl; - } - } - return NULL; -} - -void -audiomixer_add_event_listener(struct audiomixer *self, - const struct audiomixer_events *events, - void *data) -{ - self->events = events; - self->events_data = data; -} - -static gboolean -do_change_volume (struct action * action) -{ - 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"); - - return G_SOURCE_REMOVE; -} - -void -audiomixer_change_volume(struct audiomixer *self, - const struct mixer_control *control, - double volume) -{ - const struct mixer_control_impl *impl = - (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->node_id; - action->change_volume.volume = (gfloat) volume; - wp_core_idle_add (self->core, NULL, (GSourceFunc) do_change_volume, action, - g_free); -} - -static gboolean -do_change_mute (struct action * action) -{ - 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"); - - return G_SOURCE_REMOVE; -} - -void -audiomixer_change_mute(struct audiomixer *self, - const struct mixer_control *control, - bool mute) -{ - const struct mixer_control_impl *impl = - (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->node_id; - action->change_mute.mute = mute; - wp_core_idle_add (self->core, NULL, (GSourceFunc) do_change_mute, action, - g_free); -} diff --git a/binding/audiomixer.h b/binding/audiomixer.h deleted file mode 100644 index 47bd703..0000000 --- a/binding/audiomixer.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis <george.kiagiadakis@collabora.com> - * - * SPDX-License-Identifier: MIT - */ - -#include <stdbool.h> - -struct audiomixer; - -struct mixer_control -{ - char name[32]; - double volume; - bool mute; -}; - -struct audiomixer_events -{ - void (*controls_changed) (void *data); - - void (*value_changed) (void *data, -#define MIXER_CONTROL_CHANGE_FLAG_VOLUME (1<<0) -#define MIXER_CONTROL_CHANGE_FLAG_MUTE (1<<1) - unsigned int change_mask, - const struct mixer_control *control); -}; - -struct audiomixer * audiomixer_new(void); -void audiomixer_free(struct audiomixer *self); - -/* locking is required to call any of the methods below - * and to access any structure maintained by audiomixer */ -void audiomixer_lock(struct audiomixer *self); -void audiomixer_unlock(struct audiomixer *self); - -int audiomixer_ensure_controls(struct audiomixer *self, int timeout_sec); - -const struct mixer_control ** audiomixer_get_active_controls( - struct audiomixer *self, - unsigned int *n_controls); - -const struct mixer_control * audiomixer_find_control( - struct audiomixer *self, - const char *name); - -void audiomixer_add_event_listener(struct audiomixer *self, - const struct audiomixer_events *events, - void *data); - -void audiomixer_change_volume(struct audiomixer *self, - const struct mixer_control *control, - double volume); - -void audiomixer_change_mute(struct audiomixer *self, - const struct mixer_control *control, - bool mute); - |