From 1dc822c999d303770157a360c1329f3b0bb72766 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 13 Feb 2024 12:03:53 +0100 Subject: [PATCH] impl-device: add support for adapter Make it possible to wrap nodes created by a device in a wrapper such as adapter. Update the minimal.conf to use udev to detect and configure devices and nodes. Add a config switch to switch back to hardcoded config. --- src/daemon/minimal.conf.in | 40 ++++++++++++++++++++++++++++++++++++-- src/pipewire/impl-device.c | 34 +++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/daemon/minimal.conf.in b/src/daemon/minimal.conf.in index 38bd69726..b4e064b9d 100644 --- a/src/daemon/minimal.conf.in +++ b/src/daemon/minimal.conf.in @@ -44,6 +44,10 @@ context.properties = { vm.overrides = { default.clock.min-quantum = 1024 } + + # This config can use udev or hardcoded ALSA devices. Make sure to + # change the alsa device below when disabling udev + minimal.use-udev = true } context.spa-libs = { @@ -54,6 +58,7 @@ context.spa-libs = { # that factory. # audio.convert.* = audioconvert/libspa-audioconvert + audio.adapt = audioconvert/libspa-audioconvert api.alsa.* = alsa/libspa-alsa support.* = support/libspa-support } @@ -99,6 +104,8 @@ context.modules = [ # context of the PipeWire server. { name = libpipewire-module-spa-node-factory } + { name = libpipewire-module-spa-device-factory } + # Allows creating nodes that run in the context of the # client. Is used by all clients that want to provide # data to PipeWire. @@ -183,6 +190,33 @@ context.objects = [ } } + # This creates a ALSA udev device that will enumerate all + # ALSA devices. Because it is using ACP and has the audio-profile + # property set, this will enable a profile and create associated + # nodes, which will be automatically configured to their best + # configuration. + { factory = spa-device-factory + args = { + factory.name = api.alsa.enum.udev + alsa.use-acp = true + device.object.properties = { + api.acp.auto-profile = true + api.acp.auto-port = true + device.object.properties = { + node.adapter = audio.adapt + resample.disable = true + adapter.auto-port-config = { + mode = dsp + monitor = false + control = false + position = unknown # unknown, preserve + } + } + } + } + condition = [ { minimal.use-udev = true } ] + } + # This creates a single PCM source device for the given # alsa device path hw:0. You can change source to sink # to make a sink in the same way. @@ -222,7 +256,7 @@ context.objects = [ #channelmix.rear-delay = 12.0 #channelmix.stereo-widen = 0.0 #channelmix.hilbert-taps = 0 - channelmix.disable = true + #channelmix.disable = false #dither.noise = 0 #node.param.Props = { # params = [ @@ -248,6 +282,7 @@ context.objects = [ # } #} } + condition = [ { minimal.use-udev = false } ] } { factory = adapter args = { @@ -284,7 +319,7 @@ context.objects = [ #channelmix.rear-delay = 12.0 #channelmix.stereo-widen = 0.0 #channelmix.hilbert-taps = 0 - channelmix.disable = true + #channelmix.disable = false #dither.noise = 0 #node.param.Props = { # params = [ @@ -310,6 +345,7 @@ context.objects = [ # } #} } + condition = [ { minimal.use-udev = false } ] } # This creates a new Source node. It will have input ports # that you can link, to provide audio for this source. diff --git a/src/pipewire/impl-device.c b/src/pipewire/impl-device.c index eed83314e..7f50a1c3b 100644 --- a/src/pipewire/impl-device.c +++ b/src/pipewire/impl-device.c @@ -67,6 +67,7 @@ struct object_data { #define OBJECT_DEVICE 1 uint32_t type; struct spa_handle *handle; + struct spa_handle *subhandle; void *object; struct spa_hook listener; }; @@ -610,6 +611,8 @@ static void on_object_free(void *data) { struct object_data *od = data; pw_unload_spa_handle(od->handle); + if (od->subhandle) + pw_unload_spa_handle(od->subhandle); } static const struct pw_impl_node_events node_object_events = { @@ -765,11 +768,12 @@ static void device_add_object(struct pw_impl_device *device, uint32_t id, const struct spa_device_object_info *info) { struct pw_context *context = device->context; - struct spa_handle *handle; + struct spa_handle *handle, *subhandle = NULL; spa_autoptr(pw_properties) props = NULL; int res; void *iface; struct object_data *od = NULL; + const char *str; if (info->factory_name == NULL) { pw_log_debug("%p: missing factory name", device); @@ -801,6 +805,31 @@ static void device_add_object(struct pw_impl_device *device, uint32_t id, if (spa_streq(info->type, SPA_TYPE_INTERFACE_Node)) { struct pw_impl_node *node; + const struct pw_properties *p; + + p = pw_context_get_properties(context); + pw_properties_set(props, "clock.quantum-limit", + pw_properties_get(p, "default.clock.quantum-limit")); + + if ((str = pw_properties_get(props, "node.adapter")) != NULL) { + char name[64]; + snprintf(name, sizeof(name), "%s.follower", str); + pw_properties_setf(props, name, "pointer:%p", iface); + + subhandle = handle; + + handle = pw_context_load_spa_handle(context, str, &props->dict); + if (handle == NULL) { + pw_log_warn("%p: can't load handle %s: %m", device, str); + goto cleanup; + } + if ((res = spa_handle_get_interface(handle, info->type, &iface)) < 0) { + pw_log_error("%p: can't get %s interface: %s", device, info->type, + spa_strerror(res)); + goto cleanup; + } + } + node = pw_context_create_node(context, spa_steal_ptr(props), sizeof(struct object_data)); if (node == NULL) @@ -830,6 +859,7 @@ static void device_add_object(struct pw_impl_device *device, uint32_t id, if (od) { od->id = id; od->handle = handle; + od->subhandle = subhandle; spa_list_append(&device->object_list, &od->link); if (device->global) object_register(od, device->info.id); @@ -839,6 +869,8 @@ static void device_add_object(struct pw_impl_device *device, uint32_t id, cleanup: if (handle) pw_unload_spa_handle(handle); + if (subhandle) + pw_unload_spa_handle(subhandle); return; }