summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkotontrion <[email protected]>2024-07-19 09:31:05 +0200
committerkotontrion <[email protected]>2024-07-19 09:31:05 +0200
commit31776f401a9ff3124ca3feb1271cd17fb751081e (patch)
tree0374efeeedcaf0d94e2a8c9b9ca4ca4fdcc1d2bb /src
parent9eb113163187e1eec0d2e0ac763cd9eeb45cc9ae (diff)
add default speaker/microphone objects
Diffstat (limited to 'src')
-rw-r--r--src/audio.c48
-rw-r--r--src/endpoint.c101
-rw-r--r--src/wireplumber.c60
3 files changed, 187 insertions, 22 deletions
diff --git a/src/audio.c b/src/audio.c
index 630a09c..3782b2e 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -3,9 +3,7 @@
#include <wp/wp.h>
#include "endpoint.h"
-#include "glib-object.h"
-#include "glib.h"
-#include "wp.h"
+#include "wireplumber.h"
struct _AstalWpAudio {
GObject parent_instance;
@@ -39,6 +37,8 @@ typedef enum {
ASTAL_WP_AUDIO_PROP_SPEAKERS,
ASTAL_WP_AUDIO_PROP_STREAMS,
ASTAL_WP_AUDIO_PROP_RECORDERS,
+ ASTAL_WP_AUDIO_PROP_DEFAULT_SPEAKER,
+ ASTAL_WP_AUDIO_PROP_DEFAULT_MICROPHONE,
ASTAL_WP_AUDIO_N_PROPERTIES,
} AstalWpAudioProperties;
@@ -208,6 +208,26 @@ AstalWpEndpoint *astal_wp_audio_get_endpoint(AstalWpAudio *self, guint id) {
return endpoint;
}
+/**
+ * astal_wp_audio_get_default_speaker
+ *
+ * Returns: (nullable) (transfer none): gets the default speaker object
+ */
+AstalWpEndpoint *astal_wp_audio_get_default_speaker(AstalWpAudio *self) {
+ AstalWpAudioPrivate *priv = astal_wp_audio_get_instance_private(self);
+ return astal_wp_wp_get_default_speaker(priv->wp);
+}
+
+/**
+ * astal_wp_audio_get_default_microphone
+ *
+ * Returns: (nullable) (transfer none): gets the default microphone object
+ */
+AstalWpEndpoint *astal_wp_audio_get_default_microphone(AstalWpAudio *self) {
+ AstalWpAudioPrivate *priv = astal_wp_audio_get_instance_private(self);
+ return astal_wp_wp_get_default_microphone(priv->wp);
+}
+
static void astal_wp_audio_get_property(GObject *object, guint property_id, GValue *value,
GParamSpec *pspec) {
AstalWpAudio *self = ASTAL_WP_AUDIO(object);
@@ -225,6 +245,12 @@ static void astal_wp_audio_get_property(GObject *object, guint property_id, GVal
case ASTAL_WP_AUDIO_PROP_RECORDERS:
g_value_set_pointer(value, astal_wp_audio_get_recorders(self));
break;
+ case ASTAL_WP_AUDIO_PROP_DEFAULT_SPEAKER:
+ g_value_set_object(value, astal_wp_audio_get_default_speaker(self));
+ break;
+ case ASTAL_WP_AUDIO_PROP_DEFAULT_MICROPHONE:
+ g_value_set_object(value, astal_wp_audio_get_default_microphone(self));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
@@ -354,6 +380,22 @@ static void astal_wp_audio_class_init(AstalWpAudioClass *class) {
*/
astal_wp_audio_properties[ASTAL_WP_AUDIO_PROP_STREAMS] =
g_param_spec_pointer("streams", "streams", "streams", G_PARAM_READABLE);
+ /**
+ * AstalWpAudio:default-speaker:
+ *
+ * The AstalWndpoint object representing the default speaker
+ */
+ astal_wp_audio_properties[ASTAL_WP_AUDIO_PROP_DEFAULT_SPEAKER] =
+ g_param_spec_object("default-speaker", "default-speaker", "default-speaker",
+ ASTAL_WP_TYPE_ENDPOINT, G_PARAM_READABLE);
+ /**
+ * AstalWpAudio:default-microphone:
+ *
+ * The AstalWndpoint object representing the default speaker
+ */
+ astal_wp_audio_properties[ASTAL_WP_AUDIO_PROP_DEFAULT_MICROPHONE] =
+ g_param_spec_object("default-microphone", "default-microphone", "default-microphone",
+ ASTAL_WP_TYPE_ENDPOINT, G_PARAM_READABLE);
g_object_class_install_properties(object_class, ASTAL_WP_AUDIO_N_PROPERTIES,
astal_wp_audio_properties);
diff --git a/src/endpoint.c b/src/endpoint.c
index 0f5550c..2fbab5f 100644
--- a/src/endpoint.c
+++ b/src/endpoint.c
@@ -7,6 +7,7 @@
#include "glib-object.h"
#include "glib.h"
#include "glibconfig.h"
+#include "wp.h"
struct _AstalWpEndpoint {
GObject parent_instance;
@@ -24,6 +25,9 @@ typedef struct {
WpPlugin *mixer;
WpPlugin *defaults;
+ gboolean is_default_node;
+ AstalWpMediaClass media_class;
+
gulong signal_handler_id;
} AstalWpEndpointPrivate;
@@ -181,6 +185,62 @@ static void astal_wp_endpoint_set_property(GObject *object, guint property_id, c
}
}
+static void astal_wp_endpoint_update_properties(AstalWpEndpoint *self) {
+ AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
+ if (priv->node == NULL) return;
+ self->id = wp_proxy_get_bound_id(WP_PROXY(priv->node));
+
+ astal_wp_endpoint_update_volume(self);
+
+ const gchar *description =
+ wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(priv->node), "node.description");
+ if (description == NULL) {
+ description = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(priv->node), "node.nick");
+ }
+ if (description == NULL) {
+ description = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(priv->node), "node.name");
+ }
+ if (description == NULL) {
+ description = "unknown";
+ }
+ g_free(self->description);
+ self->description = g_strdup(description);
+
+ const gchar *type =
+ wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(priv->node), "media.class");
+ const GEnumClass *enum_class = g_type_class_ref(ASTAL_WP_TYPE_MEDIA_CLASS);
+ self->type = g_enum_get_value_by_nick(enum_class, type)->value;
+ g_type_class_unref(enum_class);
+
+ g_object_notify(G_OBJECT(self), "id");
+ g_object_notify(G_OBJECT(self), "description");
+ g_object_notify(G_OBJECT(self), "type");
+ g_signal_emit_by_name(self, "changed");
+}
+
+static void astal_wp_endpoint_default_changed_as_default(AstalWpEndpoint *self) {
+ AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
+
+ const GEnumClass *enum_class = g_type_class_ref(ASTAL_WP_TYPE_MEDIA_CLASS);
+ const gchar *media_class = g_enum_get_value(enum_class, priv->media_class)->value_nick;
+ guint defaultId;
+ g_signal_emit_by_name(priv->defaults, "get-default-node", media_class, &defaultId);
+ g_type_class_unref(enum_class);
+
+ if (defaultId != self->id) {
+ if (priv->node != NULL) g_object_unref(priv->node);
+ AstalWpEndpoint *default_endpoint =
+ astal_wp_wp_get_endpoint(astal_wp_wp_get_default(), defaultId);
+ if (default_endpoint != NULL &&
+ astal_wp_endpoint_get_media_class(default_endpoint) == priv->media_class) {
+ AstalWpEndpointPrivate *default_endpoint_priv =
+ astal_wp_endpoint_get_instance_private(default_endpoint);
+ priv->node = g_object_ref(default_endpoint_priv->node);
+ astal_wp_endpoint_update_properties(self);
+ }
+ }
+}
+
static void astal_wp_endpoint_default_changed(AstalWpEndpoint *self) {
AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
@@ -200,37 +260,38 @@ static void astal_wp_endpoint_default_changed(AstalWpEndpoint *self) {
}
}
-AstalWpEndpoint *astal_wp_endpoint_create(WpNode *node, WpPlugin *mixer, WpPlugin *defaults) {
- AstalWpEndpoint *self = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
+AstalWpEndpoint *astal_wp_endpoint_init_as_default(AstalWpEndpoint *self, WpPlugin *mixer,
+ WpPlugin *defaults, AstalWpMediaClass type) {
AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
priv->mixer = g_object_ref(mixer);
priv->defaults = g_object_ref(defaults);
- priv->node = g_object_ref(node);
- self->id = wp_proxy_get_bound_id(WP_PROXY(node));
+ priv->media_class = type;
+ priv->is_default_node = TRUE;
+ self->is_default = TRUE;
- astal_wp_endpoint_update_volume(self);
+ priv->signal_handler_id = g_signal_connect_swapped(
+ priv->defaults, "changed", G_CALLBACK(astal_wp_endpoint_default_changed_as_default), self);
- const gchar *description =
- wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.description");
- if (description == NULL) {
- description = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.nick");
- }
- if (description == NULL) {
- description = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.name");
- }
- if (description == NULL) {
- description = "unknown";
- }
- self->description = g_strdup(description);
+ astal_wp_endpoint_default_changed_as_default(self);
+ astal_wp_endpoint_update_properties(self);
+ return self;
+}
- const gchar *type = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "media.class");
- self->type = g_enum_get_value_by_nick(g_type_class_ref(ASTAL_WP_TYPE_MEDIA_CLASS), type)->value;
+AstalWpEndpoint *astal_wp_endpoint_create(WpNode *node, WpPlugin *mixer, WpPlugin *defaults) {
+ AstalWpEndpoint *self = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
+ AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
+
+ priv->mixer = g_object_ref(mixer);
+ priv->defaults = g_object_ref(defaults);
+ priv->node = g_object_ref(node);
+ priv->is_default_node = FALSE;
priv->signal_handler_id = g_signal_connect_swapped(
priv->defaults, "changed", G_CALLBACK(astal_wp_endpoint_default_changed), self);
+ astal_wp_endpoint_update_properties(self);
astal_wp_endpoint_default_changed(self);
return self;
}
@@ -252,6 +313,8 @@ static void astal_wp_endpoint_dispose(GObject *object) {
g_signal_handler_disconnect(priv->defaults, priv->signal_handler_id);
+ g_print("dispose: id: %u, name: %s\n", self->id, self->description);
+
g_clear_object(&priv->node);
g_clear_object(&priv->mixer);
g_clear_object(&priv->defaults);
diff --git a/src/wireplumber.c b/src/wireplumber.c
index d50da83..74794e6 100644
--- a/src/wireplumber.c
+++ b/src/wireplumber.c
@@ -2,12 +2,16 @@
#include "audio.h"
#include "endpoint-private.h"
+#include "endpoint.h"
#include "glib-object.h"
#include "glib.h"
#include "wp.h"
struct _AstalWpWp {
GObject parent_instance;
+
+ AstalWpEndpoint *default_speaker;
+ AstalWpEndpoint *default_microphone;
};
typedef struct {
@@ -37,6 +41,8 @@ static guint astal_wp_wp_signals[ASTAL_WP_WP_N_SIGNALS] = {
typedef enum {
ASTAL_WP_WP_PROP_AUDIO = 1,
ASTAL_WP_WP_PROP_ENDPOINTS,
+ ASTAL_WP_WP_PROP_DEFAULT_SPEAKER,
+ ASTAL_WP_WP_PROP_DEFAULT_MICROPHONE,
ASTAL_WP_WP_N_PROPERTIES,
} AstalWpWpProperties;
@@ -77,6 +83,22 @@ GList *astal_wp_wp_get_endpoints(AstalWpWp *self) {
*/
AstalWpAudio *astal_wp_wp_get_audio() { return astal_wp_audio_get_default(); }
+/**
+ * astal_wp_wp_get_default_speaker
+ *
+ * Returns: (nullable) (transfer none): gets the default speaker object
+ */
+AstalWpEndpoint *astal_wp_wp_get_default_speaker(AstalWpWp *self) { return self->default_speaker; }
+
+/**
+ * astal_wp_wp_get_default_microphone
+ *
+ * Returns: (nullable) (transfer none): gets the default microphone object
+ */
+AstalWpEndpoint *astal_wp_wp_get_default_microphone(AstalWpWp *self) {
+ return self->default_microphone;
+}
+
static void astal_wp_wp_get_property(GObject *object, guint property_id, GValue *value,
GParamSpec *pspec) {
AstalWpWp *self = ASTAL_WP_WP(object);
@@ -89,6 +111,12 @@ static void astal_wp_wp_get_property(GObject *object, guint property_id, GValue
case ASTAL_WP_WP_PROP_ENDPOINTS:
g_value_set_pointer(value, g_hash_table_get_values(priv->endpoints));
break;
+ case ASTAL_WP_WP_PROP_DEFAULT_SPEAKER:
+ g_value_set_object(value, self->default_speaker);
+ break;
+ case ASTAL_WP_WP_PROP_DEFAULT_MICROPHONE:
+ g_value_set_object(value, self->default_microphone);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
@@ -147,11 +175,21 @@ static void astal_wp_wp_mixer_changed(AstalWpWp *self, guint node_id) {
astal_wp_endpoint_update_volume(endpoint);
+ if (astal_wp_endpoint_get_id(self->default_speaker) == node_id)
+ astal_wp_endpoint_update_volume(self->default_speaker);
+ if (astal_wp_endpoint_get_id(self->default_microphone) == node_id)
+ astal_wp_endpoint_update_volume(self->default_microphone);
+
g_signal_emit_by_name(self, "changed");
}
static void astal_wp_wp_objm_installed(AstalWpWp *self) {
AstalWpWpPrivate *priv = astal_wp_wp_get_instance_private(self);
+
+ astal_wp_endpoint_init_as_default(self->default_speaker, priv->mixer, priv->defaults,
+ ASTAL_WP_MEDIA_CLASS_AUDIO_SPEAKER);
+ astal_wp_endpoint_init_as_default(self->default_microphone, priv->mixer, priv->defaults,
+ ASTAL_WP_MEDIA_CLASS_AUDIO_MICROPHONE);
}
static void astal_wp_wp_plugin_activated(WpObject *obj, GAsyncResult *result, AstalWpWp *self) {
@@ -177,6 +215,7 @@ static void astal_wp_wp_plugin_activated(WpObject *obj, GAsyncResult *result, As
G_CALLBACK(astal_wp_wp_object_added), self);
g_signal_connect_swapped(priv->obj_manager, "object-removed",
G_CALLBACK(astal_wp_wp_object_removed), self);
+
wp_core_install_object_manager(priv->core, priv->obj_manager);
}
}
@@ -220,6 +259,8 @@ static void astal_wp_wp_dispose(GObject *object) {
AstalWpWpPrivate *priv = astal_wp_wp_get_instance_private(self);
wp_core_disconnect(priv->core);
+ g_clear_object(&self->default_speaker);
+ g_clear_object(&self->default_microphone);
g_clear_object(&priv->mixer);
g_clear_object(&priv->defaults);
g_clear_object(&priv->obj_manager);
@@ -263,6 +304,9 @@ static void astal_wp_wp_init(AstalWpWp *self) {
g_signal_connect_swapped(priv->obj_manager, "installed", (GCallback)astal_wp_wp_objm_installed,
self);
+ self->default_speaker = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
+ self->default_microphone = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
+
priv->pending_plugins = 2;
wp_core_load_component(priv->core, "libwireplumber-module-default-nodes-api", "module", NULL,
"default-nodes-api", NULL,
@@ -287,6 +331,22 @@ static void astal_wp_wp_class_init(AstalWpWpClass *class) {
*/
astal_wp_wp_properties[ASTAL_WP_WP_PROP_ENDPOINTS] =
g_param_spec_pointer("endpoints", "endpoints", "endpoints", G_PARAM_READABLE);
+ /**
+ * AstalWpAudio:default-speaker:
+ *
+ * The AstalWndpoint object representing the default speaker
+ */
+ astal_wp_wp_properties[ASTAL_WP_WP_PROP_DEFAULT_SPEAKER] =
+ g_param_spec_object("default-speaker", "default-speaker", "default-speaker",
+ ASTAL_WP_TYPE_ENDPOINT, G_PARAM_READABLE);
+ /**
+ * AstalWpAudio:default-microphone:
+ *
+ * The AstalWndpoint object representing the default speaker
+ */
+ astal_wp_wp_properties[ASTAL_WP_WP_PROP_DEFAULT_MICROPHONE] =
+ g_param_spec_object("default-microphone", "default-microphone", "default-microphone",
+ ASTAL_WP_TYPE_ENDPOINT, G_PARAM_READABLE);
g_object_class_install_properties(object_class, ASTAL_WP_WP_N_PROPERTIES,
astal_wp_wp_properties);