summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkotontrion <[email protected]>2024-08-20 11:50:05 +0200
committerkotontrion <[email protected]>2024-08-20 11:50:05 +0200
commit7706d24965a268b6075d3fb5557ef6b96c889483 (patch)
tree2a0a544c6b2e67d5e218d68c76c3282ce84889b2 /src
parent99dc6d3fbc99b955882c1d471c15e077cb44a45a (diff)
add property for linear or cubic volume scale
Diffstat (limited to 'src')
-rw-r--r--src/audio.c44
-rw-r--r--src/endpoint.c19
-rw-r--r--src/video.c40
-rw-r--r--src/wireplumber.c84
4 files changed, 122 insertions, 65 deletions
diff --git a/src/audio.c b/src/audio.c
index 9c2841b..15582d7 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -4,6 +4,7 @@
#include "device.h"
#include "endpoint.h"
+#include "glib-object.h"
#include "wp.h"
struct _AstalWpAudio {
@@ -382,34 +383,10 @@ static void astal_wp_audio_object_removed(AstalWpAudio *self, gpointer object) {
}
}
-/**
- * astal_wp_audio_get_default
- *
- * gets the default audio object.
- *
- * Returns: (nullable) (transfer none)
- */
-AstalWpAudio *astal_wp_audio_get_default() {
- static AstalWpAudio *self = NULL;
-
- if (self == NULL) self = g_object_new(ASTAL_WP_TYPE_AUDIO, NULL);
-
- return self;
-}
-
-/**
- * astal_wp_get_default_audio
- *
- * gets the default audio object. This function does the same as [[email protected]_default]
- *
- * Returns: (nullable) (transfer none)
- */
-AstalWpAudio *astal_wp_get_default_audio() { return astal_wp_audio_get_default(); }
-
-static void astal_wp_audio_init(AstalWpAudio *self) {
+AstalWpAudio *astal_wp_audio_new(AstalWpWp *wp) {
+ AstalWpAudio *self = g_object_new(ASTAL_WP_TYPE_AUDIO, NULL);
AstalWpAudioPrivate *priv = astal_wp_audio_get_instance_private(self);
-
- priv->wp = astal_wp_wp_get_default();
+ priv->wp = g_object_ref(wp);
g_signal_connect_swapped(priv->wp, "endpoint-added", G_CALLBACK(astal_wp_audio_object_added),
self);
@@ -419,11 +396,24 @@ static void astal_wp_audio_init(AstalWpAudio *self) {
self);
g_signal_connect_swapped(priv->wp, "device-removed", G_CALLBACK(astal_wp_audio_device_removed),
self);
+
+ return self;
+}
+
+static void astal_wp_audio_dispose(GObject *object) {
+ AstalWpAudio *self = ASTAL_WP_AUDIO(object);
+ AstalWpAudioPrivate *priv = astal_wp_audio_get_instance_private(self);
+ g_clear_object(&priv->wp);
+}
+
+static void astal_wp_audio_init(AstalWpAudio *self) {
+ AstalWpAudioPrivate *priv = astal_wp_audio_get_instance_private(self);
}
static void astal_wp_audio_class_init(AstalWpAudioClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS(class);
object_class->get_property = astal_wp_audio_get_property;
+ object_class->dispose = astal_wp_audio_dispose;
/**
* AstalWpAudio:microphones: (type GList(AstalWpEndpoint)) (transfer container)
diff --git a/src/endpoint.c b/src/endpoint.c
index 1a6f207..13979d1 100644
--- a/src/endpoint.c
+++ b/src/endpoint.c
@@ -26,6 +26,7 @@ typedef struct {
WpNode *node;
WpPlugin *mixer;
WpPlugin *defaults;
+ AstalWpWp *wp;
gboolean is_default_node;
AstalWpMediaClass media_class;
@@ -242,8 +243,7 @@ void astal_wp_endpoint_set_lock_channels(AstalWpEndpoint *self, gboolean lock_ch
}
const gchar *astal_wp_endpoint_get_volume_icon(AstalWpEndpoint *self) {
- AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
- if (priv->media_class == ASTAL_WP_MEDIA_CLASS_AUDIO_MICROPHONE) {
+ if (self->type == ASTAL_WP_MEDIA_CLASS_AUDIO_MICROPHONE) {
if (self->mute) return "microphone-sensitivity-muted-symbolic";
if (self->volume <= 0.33) return "microphone-sensitivity-low-symbolic";
if (self->volume <= 0.66) return "microphone-sensitivity-medium-symbolic";
@@ -362,7 +362,7 @@ static void astal_wp_endpoint_update_properties(AstalWpEndpoint *self) {
const gchar *dev =
wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(priv->node), "device.id");
guint device_id = g_ascii_strtoull(dev, NULL, 10);
- AstalWpDevice *device = astal_wp_wp_get_device(astal_wp_wp_get_default(), device_id);
+ AstalWpDevice *device = astal_wp_wp_get_device(priv->wp, device_id);
icon = astal_wp_device_get_icon(device);
if (icon == NULL) {
icon = self->type == ASTAL_WP_MEDIA_CLASS_AUDIO_SPEAKER
@@ -406,8 +406,7 @@ static void astal_wp_endpoint_default_changed_as_default(AstalWpEndpoint *self)
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);
+ AstalWpEndpoint *default_endpoint = astal_wp_wp_get_endpoint(priv->wp, defaultId);
if (default_endpoint != NULL &&
astal_wp_endpoint_get_media_class(default_endpoint) == priv->media_class) {
AstalWpEndpointPrivate *default_endpoint_priv =
@@ -441,7 +440,8 @@ static void astal_wp_endpoint_mixer_changed(AstalWpEndpoint *self, guint node_id
}
AstalWpEndpoint *astal_wp_endpoint_init_as_default(AstalWpEndpoint *self, WpPlugin *mixer,
- WpPlugin *defaults, AstalWpMediaClass type) {
+ WpPlugin *defaults, AstalWpMediaClass type,
+ AstalWpWp *wp) {
AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
priv->mixer = g_object_ref(mixer);
@@ -450,6 +450,7 @@ AstalWpEndpoint *astal_wp_endpoint_init_as_default(AstalWpEndpoint *self, WpPlug
priv->media_class = type;
priv->is_default_node = TRUE;
self->is_default = TRUE;
+ priv->wp = g_object_ref(wp);
priv->default_signal_handler_id = g_signal_connect_swapped(
priv->defaults, "changed", G_CALLBACK(astal_wp_endpoint_default_changed_as_default), self);
@@ -461,7 +462,8 @@ AstalWpEndpoint *astal_wp_endpoint_init_as_default(AstalWpEndpoint *self, WpPlug
return self;
}
-AstalWpEndpoint *astal_wp_endpoint_create(WpNode *node, WpPlugin *mixer, WpPlugin *defaults) {
+AstalWpEndpoint *astal_wp_endpoint_create(WpNode *node, WpPlugin *mixer, WpPlugin *defaults,
+ AstalWpWp *wp) {
AstalWpEndpoint *self = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
AstalWpEndpointPrivate *priv = astal_wp_endpoint_get_instance_private(self);
@@ -469,6 +471,7 @@ AstalWpEndpoint *astal_wp_endpoint_create(WpNode *node, WpPlugin *mixer, WpPlugi
priv->defaults = g_object_ref(defaults);
priv->node = g_object_ref(node);
priv->is_default_node = FALSE;
+ priv->wp = g_object_ref(wp);
priv->default_signal_handler_id = g_signal_connect_swapped(
priv->defaults, "changed", G_CALLBACK(astal_wp_endpoint_default_changed), self);
@@ -485,6 +488,7 @@ static void astal_wp_endpoint_init(AstalWpEndpoint *self) {
priv->node = NULL;
priv->mixer = NULL;
priv->defaults = NULL;
+ priv->wp = NULL;
self->volume = 0;
self->mute = TRUE;
@@ -502,6 +506,7 @@ static void astal_wp_endpoint_dispose(GObject *object) {
g_clear_object(&priv->node);
g_clear_object(&priv->mixer);
g_clear_object(&priv->defaults);
+ g_clear_object(&priv->wp);
}
static void astal_wp_endpoint_finalize(GObject *object) {
diff --git a/src/video.c b/src/video.c
index 1d75d30..00cdd82 100644
--- a/src/video.c
+++ b/src/video.c
@@ -321,31 +321,10 @@ static void astal_wp_video_object_removed(AstalWpAudio *self, gpointer object) {
}
}
-/**
- * astal_wp_video_get_default
- *
- * Returns: (nullable) (transfer none): gets the default video object.
- */
-AstalWpVideo *astal_wp_video_get_default() {
- static AstalWpVideo *self = NULL;
-
- if (self == NULL) self = g_object_new(ASTAL_WP_TYPE_VIDEO, NULL);
-
- return self;
-}
-
-/**
- * astal_wp_get_default_video
- *
- * Returns: (nullable) (transfer none): gets the default video object.
- */
-AstalWpVideo *astal_wp_get_default_video() { return astal_wp_video_get_default(); }
-
-static void astal_wp_video_init(AstalWpVideo *self) {
+AstalWpVideo *astal_wp_video_new(AstalWpWp *wp) {
+ AstalWpVideo *self = g_object_new(ASTAL_WP_TYPE_VIDEO, NULL);
AstalWpVideoPrivate *priv = astal_wp_video_get_instance_private(self);
-
- priv->wp = astal_wp_wp_get_default();
-
+ priv->wp = g_object_ref(wp);
g_signal_connect_swapped(priv->wp, "endpoint-added", G_CALLBACK(astal_wp_video_object_added),
self);
g_signal_connect_swapped(priv->wp, "endpoint-removed",
@@ -354,11 +333,24 @@ static void astal_wp_video_init(AstalWpVideo *self) {
self);
g_signal_connect_swapped(priv->wp, "device-removed", G_CALLBACK(astal_wp_video_device_removed),
self);
+
+ return self;
+}
+
+static void astal_wp_video_dispose(GObject *object) {
+ AstalWpVideo *self = ASTAL_WP_VIDEO(object);
+ AstalWpVideoPrivate *priv = astal_wp_video_get_instance_private(self);
+ g_clear_object(&priv->wp);
+}
+
+static void astal_wp_video_init(AstalWpVideo *self) {
+ AstalWpVideoPrivate *priv = astal_wp_video_get_instance_private(self);
}
static void astal_wp_video_class_init(AstalWpVideoClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS(class);
object_class->get_property = astal_wp_video_get_property;
+ object_class->dispose = astal_wp_video_dispose;
/**
* AstalWpVideo:sources: (type GList(AstalWpEndpoint)) (transfer container)
diff --git a/src/wireplumber.c b/src/wireplumber.c
index f08678a..f1fa516 100644
--- a/src/wireplumber.c
+++ b/src/wireplumber.c
@@ -3,6 +3,8 @@
#include "audio.h"
#include "device-private.h"
#include "endpoint-private.h"
+#include "glib-object.h"
+#include "glib.h"
#include "video.h"
#include "wp.h"
@@ -11,6 +13,11 @@ struct _AstalWpWp {
AstalWpEndpoint *default_speaker;
AstalWpEndpoint *default_microphone;
+
+ AstalWpAudio *audio;
+ AstalWpVideo *video;
+
+ AstalWpScale scale;
};
typedef struct {
@@ -27,6 +34,10 @@ typedef struct {
G_DEFINE_FINAL_TYPE_WITH_PRIVATE(AstalWpWp, astal_wp_wp, G_TYPE_OBJECT);
+G_DEFINE_ENUM_TYPE(AstalWpScale, astal_wp_scale,
+ G_DEFINE_ENUM_VALUE(ASTAL_WP_SCALE_LINEAR, "linear"),
+ G_DEFINE_ENUM_VALUE(ASTAL_WP_SCALE_CUBIC, "cubic"));
+
typedef enum {
ASTAL_WP_WP_SIGNAL_ENDPOINT_ADDED,
ASTAL_WP_WP_SIGNAL_ENDPOINT_REMOVED,
@@ -46,6 +57,7 @@ typedef enum {
ASTAL_WP_WP_PROP_DEVICES,
ASTAL_WP_WP_PROP_DEFAULT_SPEAKER,
ASTAL_WP_WP_PROP_DEFAULT_MICROPHONE,
+ ASTAL_WP_WP_PROP_SCALE,
ASTAL_WP_WP_N_PROPERTIES,
} AstalWpWpProperties;
@@ -110,14 +122,14 @@ GList *astal_wp_wp_get_devices(AstalWpWp *self) {
*
* Returns: (nullable) (transfer none): gets the audio object
*/
-AstalWpAudio *astal_wp_wp_get_audio() { return astal_wp_audio_get_default(); }
+AstalWpAudio *astal_wp_wp_get_audio(AstalWpWp *self) { return self->audio; }
/**
* astal_wp_wp_get_video
*
* Returns: (nullable) (transfer none): gets the video object
*/
-AstalWpVideo *astal_wp_wp_get_video() { return astal_wp_video_get_default(); }
+AstalWpVideo *astal_wp_wp_get_video(AstalWpWp *self) { return self->video; }
/**
* astal_wp_wp_get_default_speaker
@@ -135,6 +147,29 @@ AstalWpEndpoint *astal_wp_wp_get_default_microphone(AstalWpWp *self) {
return self->default_microphone;
}
+AstalWpScale astal_wp_wp_get_scale(AstalWpWp *self) { return self->scale; }
+
+void astal_wp_wp_set_scale(AstalWpWp *self, AstalWpScale scale) {
+ AstalWpWpPrivate *priv = astal_wp_wp_get_instance_private(self);
+ self->scale = scale;
+
+ if (priv->mixer == NULL) return;
+
+ g_object_set(priv->mixer, "scale", self->scale, NULL);
+
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init(&iter, priv->endpoints);
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ AstalWpEndpoint *ep = ASTAL_WP_ENDPOINT(value);
+ astal_wp_endpoint_update_volume(ep);
+ }
+
+ astal_wp_endpoint_update_volume(self->default_speaker);
+ astal_wp_endpoint_update_volume(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);
@@ -142,10 +177,10 @@ static void astal_wp_wp_get_property(GObject *object, guint property_id, GValue
switch (property_id) {
case ASTAL_WP_WP_PROP_AUDIO:
- g_value_set_object(value, astal_wp_wp_get_audio());
+ g_value_set_object(value, astal_wp_wp_get_audio(self));
break;
case ASTAL_WP_WP_PROP_VIDEO:
- g_value_set_object(value, astal_wp_wp_get_video());
+ g_value_set_object(value, astal_wp_wp_get_video(self));
break;
case ASTAL_WP_WP_PROP_ENDPOINTS:
g_value_set_pointer(value, g_hash_table_get_values(priv->endpoints));
@@ -159,6 +194,23 @@ static void astal_wp_wp_get_property(GObject *object, guint property_id, GValue
case ASTAL_WP_WP_PROP_DEFAULT_MICROPHONE:
g_value_set_object(value, self->default_microphone);
break;
+ case ASTAL_WP_WP_PROP_SCALE:
+ g_value_set_enum(value, self->scale);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ break;
+ }
+}
+
+static void astal_wp_wp_set_property(GObject *object, guint property_id, const GValue *value,
+ GParamSpec *pspec) {
+ AstalWpWp *self = ASTAL_WP_WP(object);
+
+ switch (property_id) {
+ case ASTAL_WP_WP_PROP_SCALE:
+ astal_wp_wp_set_scale(self, g_value_get_enum(value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
@@ -184,7 +236,8 @@ static void astal_wp_wp_object_added(AstalWpWp *self, gpointer object) {
if (WP_IS_NODE(object)) {
WpNode *node = WP_NODE(object);
- AstalWpEndpoint *endpoint = astal_wp_endpoint_create(node, priv->mixer, priv->defaults);
+ AstalWpEndpoint *endpoint =
+ astal_wp_endpoint_create(node, priv->mixer, priv->defaults, self);
g_hash_table_insert(priv->endpoints,
GUINT_TO_POINTER(wp_proxy_get_bound_id(WP_PROXY(node))), endpoint);
@@ -230,9 +283,9 @@ 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_MEDIA_CLASS_AUDIO_SPEAKER, self);
astal_wp_endpoint_init_as_default(self->default_microphone, priv->mixer, priv->defaults,
- ASTAL_WP_MEDIA_CLASS_AUDIO_MICROPHONE);
+ ASTAL_WP_MEDIA_CLASS_AUDIO_MICROPHONE, self);
}
static void astal_wp_wp_plugin_activated(WpObject *obj, GAsyncResult *result, AstalWpWp *self) {
@@ -248,6 +301,7 @@ static void astal_wp_wp_plugin_activated(WpObject *obj, GAsyncResult *result, As
if (--priv->pending_plugins == 0) {
priv->defaults = wp_plugin_find(priv->core, "default-nodes-api");
priv->mixer = wp_plugin_find(priv->core, "mixer-api");
+ g_object_set(priv->mixer, "scale", self->scale, NULL);
g_signal_connect_swapped(priv->obj_manager, "object-added",
G_CALLBACK(astal_wp_wp_object_added), self);
@@ -296,6 +350,9 @@ static void astal_wp_wp_dispose(GObject *object) {
AstalWpWp *self = ASTAL_WP_WP(object);
AstalWpWpPrivate *priv = astal_wp_wp_get_instance_private(self);
+ g_clear_object(&self->video);
+ g_clear_object(&self->audio);
+
wp_core_disconnect(priv->core);
g_clear_object(&self->default_speaker);
g_clear_object(&self->default_microphone);
@@ -326,6 +383,7 @@ static void astal_wp_wp_init(AstalWpWp *self) {
if (!wp_core_connect(priv->core)) {
g_critical("could not connect to PipeWire\n");
+ return;
}
priv->obj_manager = wp_object_manager_new();
@@ -365,6 +423,9 @@ static void astal_wp_wp_init(AstalWpWp *self) {
self->default_speaker = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
self->default_microphone = g_object_new(ASTAL_WP_TYPE_ENDPOINT, NULL);
+ self->audio = astal_wp_audio_new(self);
+ self->video = astal_wp_video_new(self);
+
priv->pending_plugins = 2;
wp_core_load_component(priv->core, "libwireplumber-module-default-nodes-api", "module", NULL,
"default-nodes-api", NULL,
@@ -378,11 +439,20 @@ static void astal_wp_wp_class_init(AstalWpWpClass *class) {
object_class->finalize = astal_wp_wp_finalize;
object_class->dispose = astal_wp_wp_dispose;
object_class->get_property = astal_wp_wp_get_property;
+ object_class->set_property = astal_wp_wp_set_property;
astal_wp_wp_properties[ASTAL_WP_WP_PROP_AUDIO] =
g_param_spec_object("audio", "audio", "audio", ASTAL_WP_TYPE_AUDIO, G_PARAM_READABLE);
astal_wp_wp_properties[ASTAL_WP_WP_PROP_VIDEO] =
g_param_spec_object("video", "video", "video", ASTAL_WP_TYPE_VIDEO, G_PARAM_READABLE);
+ /**
+ * AstalWpWp:scale: (type AstalWpScale)
+ *
+ * The scale used for the volume
+ */
+ astal_wp_wp_properties[ASTAL_WP_WP_PROP_SCALE] =
+ g_param_spec_enum("scale", "scale", "scale", ASTAL_WP_TYPE_SCALE, ASTAL_WP_SCALE_CUBIC,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
/**
* AstalWpWp:endpoints: (type GList(AstalWpEndpoint)) (transfer container)