Skip to content
Snippets Groups Projects
Commit e6d048c0 authored by George Kiagiadakis's avatar George Kiagiadakis
Browse files

Merge branch 'multistream-softdsp-ep'

See merge request !16
parents d6e64d27 184b293b
No related branches found
No related tags found
No related merge requests found
......@@ -54,7 +54,8 @@ shared_library(
'wireplumber-module-pw-audio-softdsp-endpoint',
[
'module-pw-audio-softdsp-endpoint.c',
],
'module-pw-audio-softdsp-endpoint/dsp.c',
],
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pw-audio-softdsp-endpoint"'],
install : true,
install_dir : wireplumber_module_dir,
......
......@@ -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);
}
......@@ -20,6 +20,7 @@ struct impl
WpModule *module;
WpRemotePipewire *remote_pipewire;
GHashTable *registered_endpoints;
GVariant *streams;
};
static void
......@@ -73,6 +74,8 @@ on_node_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
"media-class", g_variant_new_string (media_class));
g_variant_builder_add (&b, "{sv}",
"global-id", g_variant_new_uint32 (id));
g_variant_builder_add (&b, "{sv}",
"streams", impl->streams);
endpoint_props = g_variant_builder_end (&b);
/* Create the endpoint async */
......@@ -110,6 +113,8 @@ module_destroy (gpointer data)
g_hash_table_unref(impl->registered_endpoints);
impl->registered_endpoints = NULL;
g_clear_pointer (&impl->streams, g_variant_unref);
/* Clean up */
g_slice_free (struct impl, impl);
}
......@@ -134,6 +139,8 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
impl->remote_pipewire = rp;
impl->registered_endpoints = g_hash_table_new_full (g_direct_hash,
g_direct_equal, NULL, (GDestroyNotify)g_object_unref);
impl->streams = g_variant_lookup_value (args, "streams",
G_VARIANT_TYPE ("as"));
/* Set destroy callback for impl */
wp_module_set_destroy_callback (module, module_destroy, impl);
......
This diff is collapsed.
This diff is collapsed.
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <gio/gio.h>
#include <wp/wp.h>
#ifndef __WP_PW_AUDIO_DSP_H__
#define __WP_PW_AUDIO_DSP_H__
G_DECLARE_FINAL_TYPE (WpPwAudioDsp, wp_pw_audio_dsp,
WP_PW, AUDIO_DSP, GObject)
guint wp_pw_audio_dsp_id_encode (guint stream_id, guint control_id);
void wp_pw_audio_dsp_id_decode (guint id, guint *stream_id, guint *control_id);
void wp_pw_audio_dsp_new (WpEndpoint *endpoint, guint id, const char *name,
enum pw_direction direction, gboolean convert,
const struct pw_node_info *target, const struct spa_audio_info_raw *format,
GAsyncReadyCallback callback, gpointer user_data);
WpPwAudioDsp * wp_pw_audio_dsp_new_finish (GObject *initable, GAsyncResult *res,
GError **error);
const struct pw_node_info *wp_pw_audio_dsp_get_info (WpPwAudioDsp * self);
gboolean wp_pw_audio_dsp_prepare_link (WpPwAudioDsp * self,
GVariant ** properties, GError ** error);
GVariant * wp_pw_audio_dsp_get_control_value (WpPwAudioDsp * self,
guint32 control_id);
gboolean wp_pw_audio_dsp_set_control_value (WpPwAudioDsp * self,
guint32 control_id, GVariant * value);
#endif
......@@ -316,44 +316,45 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
if (!ptr_array)
return NULL;
/* TODO: for now we statically return the first stream
* we should be looking into the media.role eventually */
g_variant_lookup (props, "media.role", "&s", &role);
if (!g_strcmp0 (action, "mixer") && !g_strcmp0 (role, "Master"))
*stream_id = WP_STREAM_ID_NONE;
else
*stream_id = 0;
/* Find and return the "selected" endpoint */
/* FIXME: fix the endpoint API, this is terrible */
for (i = 0; i < ptr_array->len; i++) {
ep = g_ptr_array_index (ptr_array, i);
GVariantIter iter;
g_autoptr (GVariant) controls = NULL;
g_autoptr (GVariant) value = NULL;
const gchar *name;
guint id;
controls = wp_endpoint_list_controls (ep);
g_variant_iter_init (&iter, controls);
while ((value = g_variant_iter_next_value (&iter))) {
if (!g_variant_lookup (value, "name", "&s", &name)
|| !g_str_equal (name, "selected")) {
g_variant_unref (value);
continue;
}
g_variant_lookup (value, "id", "u", &id);
g_variant_unref (value);
}
id = wp_endpoint_find_control (ep, WP_STREAM_ID_NONE, "selected");
if (id == WP_CONTROL_ID_NONE)
continue;
value = wp_endpoint_get_control_value (ep, id);
if (value && g_variant_get_boolean (value))
return g_object_ref (ep);
if (value && g_variant_get_boolean (value)) {
g_object_ref (ep);
goto select_stream;
}
}
/* If not found, return the first endpoint */
return (ptr_array->len > 1) ?
ep = (ptr_array->len > 1) ?
g_object_ref (g_ptr_array_index (ptr_array, 0)) : NULL;
select_stream:
g_variant_lookup (props, "media.role", "&s", &role);
if (!g_strcmp0 (action, "mixer") && !g_strcmp0 (role, "Master"))
*stream_id = WP_STREAM_ID_NONE;
else if (ep) {
/* the default role is "Multimedia" */
if (!role)
role = "Multimedia";
*stream_id = wp_endpoint_find_stream (ep, role);
/* role not found, try the first stream */
if (*stream_id == WP_STREAM_ID_NONE) {
g_warning ("role '%s' not found in endpoint", role);
*stream_id = 0;
}
}
return ep;
}
static void
......
......@@ -9,11 +9,20 @@ 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
load-module C libwireplumber-module-pw-alsa-udev
# 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"]>
}
# Implements linking clients to devices and maintains
# information about the devices to be used.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment