From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from atuin.qyliss.net (localhost [IPv6:::1]) by atuin.qyliss.net (Postfix) with ESMTP id 21B533791; Fri, 18 Jul 2025 02:15:00 +0000 (UTC) Received: by atuin.qyliss.net (Postfix, from userid 993) id C55A13720; Fri, 18 Jul 2025 02:14:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on atuin.qyliss.net X-Spam-Level: * X-Spam-Status: No, score=1.9 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DMARC_PASS,FREEMAIL_FROM,PDS_OTHER_BAD_TLD, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE autolearn=no autolearn_force=no version=4.0.1 Received: from mail-qt1-x831.google.com (mail-qt1-x831.google.com [IPv6:2607:f8b0:4864:20::831]) by atuin.qyliss.net (Postfix) with ESMTPS id 220F5378D for ; Fri, 18 Jul 2025 02:14:53 +0000 (UTC) Received: by mail-qt1-x831.google.com with SMTP id d75a77b69052e-4ab58720d04so19823141cf.0 for ; Thu, 17 Jul 2025 19:14:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1752804892; x=1753409692; darn=spectrum-os.org; h=content-transfer-encoding:autocrypt:to:in-reply-to:content-language :reply-to:references:subject:from:user-agent:mime-version:date :message-id:from:to:cc:subject:date:message-id:reply-to; bh=X8ncRFtGKYf0e9ncRKK77B/JPrb0XvTpbvBVbv8HYKE=; b=Qj4PNFyQ1SSimLpW832apo8resV06KAzBP1c+5lrFvpz8ylScHbe+CBncr6DYhWiVj qNISdcHTffArNQGKqLGkSPlsa0ZcpOB/PNA+I+cxX6JpxPDxtc7LVWgbmBR1BTfCQyNG WvwsHirrXETyrjwkQwhxYKbx2xuJflruvVC/87p/wqf/uvOMZ5Ff1AgiKlZSEQHNrcGA RayyXBO4SNMxB4uVY830CeJDgVpofXiHuKn0dGBy83yKoYQFvc080KCnJgbGbjwN+UJP 68Mq0S1MOeQHamcRiKzflW89jseLwmtdpoIvmPyXkkFzCCoyWf49daRrD4JtspVaocq1 jf6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752804892; x=1753409692; h=content-transfer-encoding:autocrypt:to:in-reply-to:content-language :reply-to:references:subject:from:user-agent:mime-version:date :message-id:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=X8ncRFtGKYf0e9ncRKK77B/JPrb0XvTpbvBVbv8HYKE=; b=i/in2jQIzAN+O6ZHC3KVXAq79XBsFVnPUUOGwDCUxI/gvwtTKx5Nnb7buYH5Khv+sc oWYV/gg3r7CsBlM4wpL6NC7PDha4Zr/+xeJt3nk/DxD27UGMRe4N7ehAKEVrf/5cFxpv XMoyE7+Dm0MUgJJiN48A6EIlEwKfrXSn7kvMpTEqN5Uq2Xqaqlt66Ljn35sHzpX68VaG H8UUUzF7R6msN+qly52NxS5+MEpkbahw2Xo2vmzMAdS3FbyyX1/3RX6dS+QOTIspIf1F Rawk2EzS2528y1UrvbubKzTGyb3hZLyostgLCHbOOEvCnIuZ/GK97bLBQsomVn2ikDcB r8ww== X-Gm-Message-State: AOJu0YzQ/9CmkjtuUO6/gOLv79Am/e3b2fF/3A3MX2kvxONW0qTQDpFg RpyAcoBY+RJp6I0Nu4JQ4I1hKQS0obv9Q/5+kmBVWUUH6OeQWDm6fyeq0J3iZpIh X-Gm-Gg: ASbGncsCsCqhyT8SwmE5lekogF3rwF3qX4xnuBm7ByJDuq0+USz1SFJfDtPCfGHVexw 4veMhfsIXHstV8SuO0scIZo6qIf39UvW5YnhLUWB+DX90Ntk5YNGgCymLbO3E/Mb0X380OBbDly l2nwquCCNgEaT70D0dfJejBmyqCheFlVGb7VdvRp/O8/3Q9E4atfNUklytcQiUAt/RzhXs4BoCN 2Mqcf6rj1uxOdFri+tZiTLwTYd2U7X4CJwopfs6/G953Tu7LtTdAWtCubzVzfuLDtQrmoKNaN78 gRlyZ7800vYWGWpFijtX+rgnqHo5Sjds9BSa2073AdTW5wDXXuJtuvp/8B2cMJWDMBsEDKrTC3O la2LyEmUXLMj4OLOZPTG+KkweKZJR/g33d+kG6w== X-Google-Smtp-Source: AGHT+IG6XJCsXYVbneAKHuQaCllWMdnI087tbliHPGJ3ERieBkCmP0N/bxr6yuhYtdy5Rnb9+WHDyQ== X-Received: by 2002:ac8:590c:0:b0:4ab:82ab:727 with SMTP id d75a77b69052e-4ab93df2971mr123953771cf.48.1752804891437; Thu, 17 Jul 2025 19:14:51 -0700 (PDT) Received: from [10.138.10.6] ([89.187.178.201]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4abb4b46b27sm2051611cf.64.2025.07.17.19.14.50 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 17 Jul 2025 19:14:50 -0700 (PDT) Message-ID: <25197dbe-d642-4283-80b7-be66dcc4f4eb@gmail.com> Date: Thu, 17 Jul 2025 22:14:49 -0400 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: Demi Marie Obenour Subject: [PATCH v4 3/3] Run PipeWire and WirePlumber in the VMs References: <6ab772e0-62b0-430f-8d3c-0bba79a3282c@gmail.com> Content-Language: en-US In-Reply-To: <6ab772e0-62b0-430f-8d3c-0bba79a3282c@gmail.com> To: Spectrum OS Development Autocrypt: addr=demiobenour@gmail.com; keydata= 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 +MYSfkEjBz0E8CLOcAw7JIwAaeBT Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-ID-Hash: O53MQSC6WJYX4GWFXJYDAR5NHUVANFKS X-Message-ID-Hash: O53MQSC6WJYX4GWFXJYDAR5NHUVANFKS X-MailFrom: demiobenour@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-devel.spectrum-os.org-0; header-match-devel.spectrum-os.org-1; header-match-devel.spectrum-os.org-2; header-match-devel.spectrum-os.org-3; header-match-devel.spectrum-os.org-4; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.9 Precedence: list Reply-To: Demi Marie Obenour List-Id: Patches and low-level development discussion Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: WirePlumber is completely overkill as a session manager here, and ideally a trivial session manager would be used instead. I did build a Spectrum OS image and found that PipeWire and WirePlumber both successfully started. PipeWire is configured to listen on the PulseAudio socket, so PulseAudio compatibility works. This does inject a large number of completely unnecessary files into the VM, notably for libcamera and Bluetooth support. Signed-off-by: Demi Marie Obenour --- Notes: I tested this with QEMU virtio-sound, but I am not sure if the paramters are correct. In particular, the sample rate might well be wrong. img/app/Makefile | 28 +- img/app/default.nix | 18 + img/app/etc/pipewire/pipewire.conf | 291 ++++++++ .../etc/s6-rc/app/dependencies.d/directories | 0 .../app/dependencies.d/directories.license | 2 + .../etc/s6-rc/app/dependencies.d/wireplumber | 0 .../app/dependencies.d/wireplumber.license | 2 + .../etc/s6-rc/dbus/dependencies.d/directories | 0 .../dbus/dependencies.d/directories.license | 2 + img/app/etc/s6-rc/directories/type | 1 + img/app/etc/s6-rc/directories/type.license | 2 + img/app/etc/s6-rc/directories/up | 11 + .../s6-rc/pipewire/dependencies.d/directories | 0 .../dependencies.d/directories.license | 2 + img/app/etc/s6-rc/pipewire/notification-fd | 1 + .../s6-rc/pipewire/notification-fd.license | 2 + img/app/etc/s6-rc/pipewire/run | 20 + img/app/etc/s6-rc/pipewire/type | 1 + img/app/etc/s6-rc/pipewire/type.license | 2 + .../dependencies.d/directories | 0 .../dependencies.d/directories.license | 2 + img/app/etc/s6-rc/wayland-proxy-virtwl/run | 11 - .../etc/s6-rc/wireplumber/dependencies.d/dbus | 0 .../wireplumber/dependencies.d/dbus.license | 2 + .../s6-rc/wireplumber/dependencies.d/pipewire | 0 .../dependencies.d/pipewire.license | 2 + img/app/etc/s6-rc/wireplumber/run | 4 + img/app/etc/s6-rc/wireplumber/type | 1 + img/app/etc/s6-rc/wireplumber/type.license | 2 + img/app/etc/wireplumber/wireplumber.conf | 676 ++++++++++++++++++ 30 files changed, 1069 insertions(+), 16 deletions(-) create mode 100644 img/app/etc/pipewire/pipewire.conf create mode 100644 img/app/etc/s6-rc/app/dependencies.d/directories create mode 100644 img/app/etc/s6-rc/app/dependencies.d/directories.license create mode 100644 img/app/etc/s6-rc/app/dependencies.d/wireplumber create mode 100644 img/app/etc/s6-rc/app/dependencies.d/wireplumber.license create mode 100644 img/app/etc/s6-rc/dbus/dependencies.d/directories create mode 100644 img/app/etc/s6-rc/dbus/dependencies.d/directories.license create mode 100644 img/app/etc/s6-rc/directories/type create mode 100644 img/app/etc/s6-rc/directories/type.license create mode 100644 img/app/etc/s6-rc/directories/up create mode 100644 img/app/etc/s6-rc/pipewire/dependencies.d/directories create mode 100644 img/app/etc/s6-rc/pipewire/dependencies.d/directories.license create mode 100644 img/app/etc/s6-rc/pipewire/notification-fd create mode 100644 img/app/etc/s6-rc/pipewire/notification-fd.license create mode 100644 img/app/etc/s6-rc/pipewire/run create mode 100644 img/app/etc/s6-rc/pipewire/type create mode 100644 img/app/etc/s6-rc/pipewire/type.license create mode 100644 img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories create mode 100644 img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories.license create mode 100644 img/app/etc/s6-rc/wireplumber/dependencies.d/dbus create mode 100644 img/app/etc/s6-rc/wireplumber/dependencies.d/dbus.license create mode 100644 img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire create mode 100644 img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire.license create mode 100644 img/app/etc/s6-rc/wireplumber/run create mode 100644 img/app/etc/s6-rc/wireplumber/type create mode 100644 img/app/etc/s6-rc/wireplumber/type.license create mode 100644 img/app/etc/wireplumber/wireplumber.conf diff --git a/img/app/Makefile b/img/app/Makefile index d81337f99e2e83cf03ac73b9fc96fae6ce118537..691db75c287294104bb2fa332e002792bfa61f6e 100644 --- a/img/app/Makefile +++ b/img/app/Makefile @@ -53,7 +53,10 @@ VM_FILES = \ etc/s6-linux-init/scripts/rc.init \ etc/s6-linux-init/scripts/rc.shutdown \ etc/s6-linux-init/scripts/rc.shutdown.final \ - etc/xdg/xdg-desktop-portal/portals.conf + etc/xdg/xdg-desktop-portal/portals.conf \ + etc/pipewire/pipewire.conf \ + etc/wireplumber/wireplumber.conf + VM_DIRS = dev run proc sys tmp \ etc/s6-linux-init/run-image/service VM_FIFOS = etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/fifo @@ -81,24 +84,38 @@ build/rootfs.erofs: ../../scripts/make-erofs.sh $(VM_FILES) $(VM_BUILD_FILES) bu ) | ../../scripts/make-erofs.sh $@ VM_S6_RC_FILES = \ + etc/s6-rc/app/dependencies.d/directories \ etc/s6-rc/app/dependencies.d/dbus \ etc/s6-rc/app/dependencies.d/wayland-proxy-virtwl \ + etc/s6-rc/app/dependencies.d/wireplumber \ etc/s6-rc/app/run \ etc/s6-rc/app/type \ + etc/s6-rc/dbus/dependencies.d/directories \ etc/s6-rc/dbus/notification-fd \ etc/s6-rc/dbus/run \ etc/s6-rc/dbus/type \ + etc/s6-rc/directories/type \ + etc/s6-rc/directories/up \ etc/s6-rc/mdevd-coldplug/dependencies \ etc/s6-rc/mdevd-coldplug/type \ etc/s6-rc/mdevd-coldplug/up \ etc/s6-rc/mdevd/notification-fd \ etc/s6-rc/mdevd/run \ etc/s6-rc/mdevd/type \ + etc/s6-rc/ok-all/contents \ + etc/s6-rc/ok-all/type \ + etc/s6-rc/pipewire/dependencies.d/directories \ + etc/s6-rc/pipewire/notification-fd \ + etc/s6-rc/pipewire/run \ + etc/s6-rc/pipewire/type \ + etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories \ etc/s6-rc/wayland-proxy-virtwl/notification-fd \ etc/s6-rc/wayland-proxy-virtwl/run \ etc/s6-rc/wayland-proxy-virtwl/type \ - etc/s6-rc/ok-all/contents \ - etc/s6-rc/ok-all/type + etc/s6-rc/wireplumber/dependencies.d/dbus \ + etc/s6-rc/wireplumber/dependencies.d/pipewire \ + etc/s6-rc/wireplumber/run \ + etc/s6-rc/wireplumber/type build/etc/s6-rc: $(VM_S6_RC_FILES) mkdir -p $$(dirname $@) @@ -135,7 +152,7 @@ start-virtiofsd: scripts/start-virtiofsd.elb .PHONY: start-virtiofsd run-qemu: $(imgdir)/appvm/blk/root.img start-vhost-user-net start-virtiofsd - @../../scripts/run-qemu.sh -m 256 -cpu host -kernel $(KERNEL) -vga none \ + @../../scripts/run-qemu.sh -m 256 -kernel $(KERNEL) -vga none \ -drive file=$(imgdir)/appvm/blk/root.img,if=virtio,format=raw,readonly=on \ -append "root=PARTLABEL=root nokaslr" \ -gdb unix:build/gdb.sock,server,nowait \ @@ -151,7 +168,8 @@ run-qemu: $(imgdir)/appvm/blk/root.img start-vhost-user-net start-virtiofsd -parallel none \ -chardev vc,id=virtiocon0 \ -device virtio-serial-pci \ - -device virtconsole,chardev=virtiocon0 + -device virtconsole,chardev=virtiocon0 \ + -audio driver=pipewire,model=virtio .PHONY: run-qemu run-cloud-hypervisor: $(imgdir)/appvm/blk/root.img start-vhost-user-gpu start-vhost-user-net start-virtiofsd diff --git a/img/app/default.nix b/img/app/default.nix index 740643ac41f6473cdb6f6b0fd1f5f47f4493240d..8c469ee5b9a672bf6504600b09ff1f57fb87f2d9 100644 --- a/img/app/default.nix +++ b/img/app/default.nix @@ -26,6 +26,20 @@ let CONFIG_FEATURE_IP_ROUTE y CONFIG_INIT n CONFIG_IP y + CONFIG_FACTOR n + CONFIG_FOLD n + CONFIG_LSSCSI n + CONFIG_NANDWRITE n + CONFIG_NANDDUMP n + CONFIG_RAIDAUTORUN n + CONFIG_RFKILL n + CONFIG_UBIATTACH n + CONFIG_UBIDETACH n + CONFIG_UBIMKVOL n + CONFIG_UBIRMVOL n + CONFIG_UBIRSVOL n + CONFIG_UBIUPDATEVOL n + CONFIG_UBIRENAME n ''; }) @@ -48,6 +62,10 @@ let pkgs.xwayland pkgs.xdg-desktop-portal pkgs.xdg-desktop-portal-gtk + # Depends on pulseaudio libs + pkgs.pipewire + pkgs.wireplumber + pkgs.pulseaudio ]; })).fhsenv; in diff --git a/img/app/etc/pipewire/pipewire.conf b/img/app/etc/pipewire/pipewire.conf new file mode 100644 index 0000000000000000000000000000000000000000..30f289e11d6b075c592d36c17ebc681fd7599bea --- /dev/null +++ b/img/app/etc/pipewire/pipewire.conf @@ -0,0 +1,291 @@ +# SPDX-License-Identifier: MIT + +# Copyright © 2018 Wim Taymans +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +context.properties = { + ## Configure properties in the system. + link.max-buffers = 16 # version < 3 clients can't handle more + + core.daemon = true # listening for socket connections + core.name = pipewire-0 # core name and socket name + default.clock.min-quantum = 1024 # account for running in a VM +} + +context.spa-libs = { + # = + # + # Used to find spa factory names. It maps an spa factory name + # regular expression to a library name that should contain + # that factory. + # + audio.convert.* = audioconvert/libspa-audioconvert + api.alsa.* = alsa/libspa-alsa + support.* = support/libspa-support +} + +context.modules = [ + # Uses realtime scheduling to boost the audio thread priorities. This uses + # RTKit if the user doesn't have permission to use regular realtime + # scheduling. You can also clamp utilisation values to improve scheduling + # on embedded and heterogeneous systems, e.g. Arm big.LITTLE devices. + # use module.rt.args = { ... } to override the arguments. + { name = libpipewire-module-rt + args = { + nice.level = -11 + rt.prio = 88 + } + flags = [ ifexists nofail ] + condition = [ { module.rt = !false } ] + } + + # The native communication protocol. + { name = libpipewire-module-protocol-native + args = { + # List of server Unix sockets, and optionally permissions + #sockets = [ { name = "pipewire-0" }, { name = "pipewire-0-manager" } ] + } + } + + # Allows applications to create metadata objects. It creates + # a factory for Metadata objects. + { name = libpipewire-module-metadata + condition = [ { module.metadata = !false } ] + } + + # Creates a factory for making devices that run in the + # context of the PipeWire server. + { name = libpipewire-module-spa-device-factory + condition = [ { module.spa-device-factory = !false } ] + } + + # Creates a factory for making nodes that run in the + # context of the PipeWire server. + { name = libpipewire-module-spa-node-factory + condition = [ { module.spa-node-factory = !false } ] + } + + # Allows creating nodes that run in the context of the + # client. Is used by all clients that want to provide + # data to PipeWire. + { name = libpipewire-module-client-node + condition = [ { module.client-node = !false } ] + } + + # Allows creating devices that run in the context of the + # client. Is used by the session manager. + { name = libpipewire-module-client-device + condition = [ { module.client-device = !false } ] + } + + # The portal module monitors the PID of the portal process + # and tags connections with the same PID as portal + # connections. + { name = libpipewire-module-portal + flags = [ ifexists nofail ] + condition = [ { module.portal = !false } ] + } + + # The access module can perform access checks and block + # new clients. + { name = libpipewire-module-access + args = { + # Socket-specific access permissions + #access.socket = { pipewire-0 = "default", pipewire-0-manager = "unrestricted" } + + # Deprecated legacy mode (not socket-based), + # for now enabled by default if access.socket is not specified + #access.legacy = true + } + condition = [ { module.access = !false } ] + } + + # Makes a factory for wrapping nodes in an adapter with a + # converter and resampler. + { name = libpipewire-module-adapter + condition = [ { module.adapter = !false } ] + } + + # Makes a factory for creating links between ports. + # use module.link-factory.args = { ... } to override the arguments. + { name = libpipewire-module-link-factory + args = { + #allow.link.passive = false + } + condition = [ { module.link-factory = !false } ] + } + + # Provides factories to make session manager objects. + { name = libpipewire-module-session-manager + condition = [ { module.session-manager = !false } ] + } + + # The PulseAudio communication protocol + { name = libpipewire-module-protocol-pulse + args = { + # the addresses this server listens on + server.address = [ "unix:native" ] + } + } +] + +context.objects = [ + # A default dummy driver. This handles nodes marked with the "node.always-process" + # property when no other driver is currently active. JACK clients need this. + { factory = spa-node-factory + args = { + factory.name = support.node.driver + node.name = Dummy-Driver + node.group = pipewire.dummy + node.sync-group = sync.dummy + priority.driver = 200000 + #clock.id = monotonic # realtime | tai | monotonic-raw | boottime + #clock.name = "clock.system.monotonic" + } + condition = [ { factory.dummy-driver = !false } ] + } + { factory = spa-node-factory + args = { + factory.name = support.node.driver + node.name = Freewheel-Driver + priority.driver = 190000 + node.group = pipewire.freewheel + node.sync-group = sync.dummy + node.freewheel = true + #freewheel.wait = 10 + } + condition = [ { factory.freewheel-driver = !false } ] + } + + { factory = adapter + args = { + factory.name = "api.alsa.pcm.source" + node.name = "alsa_input" + media.class = "Audio/Source" + alsa.card = 0, + alsa.card_name = "VirtIO SoundCard" + alsa.device = 0 + alsa.driver_name = "virtio_snd" + alsa.id = "SoundCard" + alsa.long_card_name = "VirtIO SoundCard at pci/0000:00:01.0/virtio0" + alsa.name = "VirtIO SoundCard" + alsa.subdevice = 1 + alsa.subdevice_name = "subdevice #1" + api.alsa.card.longname = "VirtIO SoundCard at pci/0000:00:01.0/virtio0" + api.alsa.card.name = "VirtIO SoundCard" + api.alsa.headroom = 0 + api.alsa.path = "hw:0" + api.alsa.pcm.card = 0, + api.alsa.pcm.stream = "capture" + media.class = "Audio/Source" + api.alsa.path = "hw:0" + node.suspend-on-idle = true + audio.format = "S32" + audio.rate = 48000 + audio.allowed-rates = [ ] + audio.channels = 2 + audio.position = "FL,FR" + } + } + { factory = adapter + args = { + factory.name = "api.alsa.pcm.sink" + node.name = "alsa_output" + media.class = "Audio/Sink" + alsa.card = 0, + alsa.card_name = "VirtIO SoundCard" + alsa.device = 0 + alsa.driver_name = "virtio_snd" + alsa.id = "SoundCard" + alsa.long_card_name = "VirtIO SoundCard at pci/0000:00:01.0/virtio0" + alsa.name = "VirtIO SoundCard" + alsa.subdevice = 0 + alsa.subdevice_name = "subdevice #0" + api.alsa.card.longname = "VirtIO SoundCard at pci/0000:00:01.0/virtio0" + api.alsa.card.name = "VirtIO SoundCard" + api.alsa.headroom = 0 + api.alsa.path = "hw:0" + api.alsa.pcm.card = 0, + api.alsa.pcm.stream = "playback" + node.suspend-on-idle = true + audio.format = "S32" + audio.rate = 48000 + audio.allowed-rates = [ ] + audio.channels = 2 + audio.position = "FL,FR" + } + } +] + +pulse.cmd = [ + { cmd = "load-module" args = "module-always-sink" flags = [ ] + condition = [ { pulse.cmd.always-sink = !false } ] } + { cmd = "load-module" args = "module-device-manager" flags = [ ] + condition = [ { pulse.cmd.device-manager = !false } ] } + { cmd = "load-module" args = "module-device-restore" flags = [ ] + condition = [ { pulse.cmd.device-restore = !false } ] } + { cmd = "load-module" args = "module-stream-restore" flags = [ ] + condition = [ { pulse.cmd.stream-restore = !false } ] } +] + +pulse.properties.rules = [ + { matches = [ { cpu.vm.name = !null } ] + actions = { + update-props = { + # These overrides are only applied when running in a vm. + pulse.min.quantum = 1024/48000 # 22ms + } + } + } +] + +# client/stream specific properties +pulse.rules = [ + { + # skype does not want to use devices that don't have an S16 sample format. + matches = [ + { application.process.binary = "teams" } + { application.process.binary = "teams-insiders" } + { application.process.binary = "teams-for-linux" } + { application.process.binary = "skypeforlinux" } + ] + actions = { quirks = [ force-s16-info ] } + } + { + # firefox marks the capture streams as don't move and then they + # can't be moved with pavucontrol or other tools. + matches = [ { application.process.binary = "firefox" } ] + actions = { quirks = [ remove-capture-dont-move ] } + } + { + # speech dispatcher asks for too small latency and then underruns. + matches = [ { application.name = "~speech-dispatcher.*" } ] + actions = { + update-props = { + pulse.min.req = 512/48000 # 10.6ms + pulse.min.quantum = 512/48000 # 10.6ms + pulse.idle.timeout = 5 # pause after 5 seconds of underrun + } + } + } +] + +context.exec = [] diff --git a/img/app/etc/s6-rc/app/dependencies.d/directories b/img/app/etc/s6-rc/app/dependencies.d/directories new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/app/dependencies.d/directories.license b/img/app/etc/s6-rc/app/dependencies.d/directories.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/app/dependencies.d/directories.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/app/dependencies.d/wireplumber b/img/app/etc/s6-rc/app/dependencies.d/wireplumber new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/app/dependencies.d/wireplumber.license b/img/app/etc/s6-rc/app/dependencies.d/wireplumber.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/app/dependencies.d/wireplumber.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/dbus/dependencies.d/directories b/img/app/etc/s6-rc/dbus/dependencies.d/directories new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/dbus/dependencies.d/directories.license b/img/app/etc/s6-rc/dbus/dependencies.d/directories.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/dbus/dependencies.d/directories.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/directories/type b/img/app/etc/s6-rc/directories/type new file mode 100644 index 0000000000000000000000000000000000000000..bdd22a1850ae6c03a414eeb8084998679a2cdf92 --- /dev/null +++ b/img/app/etc/s6-rc/directories/type @@ -0,0 +1 @@ +oneshot diff --git a/img/app/etc/s6-rc/directories/type.license b/img/app/etc/s6-rc/directories/type.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/directories/type.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/directories/up b/img/app/etc/s6-rc/directories/up new file mode 100644 index 0000000000000000000000000000000000000000..2396fad42ac695afe924119b38b36c6654f2a504 --- /dev/null +++ b/img/app/etc/s6-rc/directories/up @@ -0,0 +1,11 @@ +#!/bin/execlineb -P +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2023-2024 Alyssa Ross +# +# Directory creation (if it's copyrightable): +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2022 Unikie + +if { mkdir -m 1755 /tmp/.X11-unix } +if { mkdir -m 0755 /run/user } +if { mkdir -m 0700 /run/user/0 } diff --git a/img/app/etc/s6-rc/pipewire/dependencies.d/directories b/img/app/etc/s6-rc/pipewire/dependencies.d/directories new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/pipewire/dependencies.d/directories.license b/img/app/etc/s6-rc/pipewire/dependencies.d/directories.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/pipewire/dependencies.d/directories.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/pipewire/notification-fd b/img/app/etc/s6-rc/pipewire/notification-fd new file mode 100644 index 0000000000000000000000000000000000000000..7ed6ff82de6bcc2a78243fc9c54d3ef5ac14da69 --- /dev/null +++ b/img/app/etc/s6-rc/pipewire/notification-fd @@ -0,0 +1 @@ +5 diff --git a/img/app/etc/s6-rc/pipewire/notification-fd.license b/img/app/etc/s6-rc/pipewire/notification-fd.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/pipewire/notification-fd.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/pipewire/run b/img/app/etc/s6-rc/pipewire/run new file mode 100644 index 0000000000000000000000000000000000000000..c0b55a152ed2d926d460ff95b6ef242199609e02 --- /dev/null +++ b/img/app/etc/s6-rc/pipewire/run @@ -0,0 +1,20 @@ +#!/bin/execlineb -P +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2023-2024 Alyssa Ross +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +s6-ipcserver-socketbinder -B /run/user/0/pipewire-0 +fdmove -c 3 0 + +s6-ipcserver-socketbinder -B /run/user/0/pipewire-0-manager +fdmove -c 4 0 + +redirfd -r 0 /dev/null + +# Notify readiness. +if { fdmove 1 5 echo } +fdclose 5 + +export LISTEN_FDS 2 +getpid LISTEN_PID +pipewire diff --git a/img/app/etc/s6-rc/pipewire/type b/img/app/etc/s6-rc/pipewire/type new file mode 100644 index 0000000000000000000000000000000000000000..5883cff0cd1514b2836f4ffa39fdac769a5213cb --- /dev/null +++ b/img/app/etc/s6-rc/pipewire/type @@ -0,0 +1 @@ +longrun diff --git a/img/app/etc/s6-rc/pipewire/type.license b/img/app/etc/s6-rc/pipewire/type.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/pipewire/type.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories b/img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories.license b/img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/wayland-proxy-virtwl/dependencies.d/directories.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/wayland-proxy-virtwl/run b/img/app/etc/s6-rc/wayland-proxy-virtwl/run index 7b8034368f547cfaf83a81a3b5d73ab864edafff..c1e0e088c789ab8c5fde7e50c9f4b856fff0e477 100755 --- a/img/app/etc/s6-rc/wayland-proxy-virtwl/run +++ b/img/app/etc/s6-rc/wayland-proxy-virtwl/run @@ -1,17 +1,6 @@ #!/bin/execlineb -P # SPDX-License-Identifier: EUPL-1.2+ # SPDX-FileCopyrightText: 2023-2024 Alyssa Ross -# -# Directory creation (if it's copyrightable): -# SPDX-License-Identifier: MIT -# SPDX-FileCopyrightText: 2022 Unikie - -foreground { mkdir /tmp/.X11-unix } -foreground { mkdir /run/user } -foreground { - umask 077 - mkdir /run/user/0 -} s6-ipcserver-socketbinder -B /run/user/0/wayland-0 fdmove -c 3 0 diff --git a/img/app/etc/s6-rc/wireplumber/dependencies.d/dbus b/img/app/etc/s6-rc/wireplumber/dependencies.d/dbus new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/wireplumber/dependencies.d/dbus.license b/img/app/etc/s6-rc/wireplumber/dependencies.d/dbus.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/wireplumber/dependencies.d/dbus.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire b/img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire.license b/img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/s6-rc/wireplumber/run b/img/app/etc/s6-rc/wireplumber/run new file mode 100644 index 0000000000000000000000000000000000000000..e721d8df9b97f05812d63520c1b450092986be96 --- /dev/null +++ b/img/app/etc/s6-rc/wireplumber/run @@ -0,0 +1,4 @@ +#!/bin/execlineb -P +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour +wireplumber diff --git a/img/app/etc/s6-rc/wireplumber/type b/img/app/etc/s6-rc/wireplumber/type new file mode 100644 index 0000000000000000000000000000000000000000..5883cff0cd1514b2836f4ffa39fdac769a5213cb --- /dev/null +++ b/img/app/etc/s6-rc/wireplumber/type @@ -0,0 +1 @@ +longrun diff --git a/img/app/etc/s6-rc/wireplumber/type.license b/img/app/etc/s6-rc/wireplumber/type.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/img/app/etc/s6-rc/wireplumber/type.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/img/app/etc/wireplumber/wireplumber.conf b/img/app/etc/wireplumber/wireplumber.conf new file mode 100644 index 0000000000000000000000000000000000000000..492515df792e309b667804de2c2cadac8f69210a --- /dev/null +++ b/img/app/etc/wireplumber/wireplumber.conf @@ -0,0 +1,676 @@ +# SPDX-License-Identifier: MIT + +# Copyright © 2019-2021 Collabora Ltd. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +context.spa-libs = { + ## SPA factory name to library mappings + ## Used to find SPA factory names. It maps a SPA factory name regular + ## expression to a library name that should contain that factory. + ## + ## Syntax: + ## = + + api.alsa.* = alsa/libspa-alsa + audio.convert.* = audioconvert/libspa-audioconvert + support.* = support/libspa-support +} + +context.modules = [ + ## PipeWire modules to load. + ## These modules are loaded before a connection to pipewire is attempted. + ## This section should be kept minimal and load only the modules that are + ## necessary for the protocol to work. + ## + ## If ifexists is given, the module is ignored when it is not found. + ## If nofail is given, module initialization failures are ignored. + ## + ## Syntax: + ## { + ## name = + ## [ args = { = ... } ] + ## [ flags = [ ifexists | nofail ] ] + ## } + + # Uses RTKit to boost the data thread priority. Also allows clamping + # of utilisation when using the Completely Fair Scheduler on Linux. + { + name = libpipewire-module-rt + args = { + nice.level = -11 + # rt.prio = 88 + # rt.time.soft = -1 + # rt.time.hard = -1 + # uclamp.min = 0 + # uclamp.max = 1024 + } + flags = [ ifexists, nofail ] + } + + ## The native communication protocol. + { name = libpipewire-module-protocol-native } + + ## Support for metadata objects + { name = libpipewire-module-metadata } +] + +wireplumber.profiles = { + ## Syntax: + ## = { + ## inherits = [ other, profile, names ] # optional + ## # optional is the default + ## = [ required | optional | disabled ] + ## ... + ## } + + # The default profile + main = { + metadata.sm-settings = required + metadata.sm-objects = required + + policy.standard = required + + hardware.audio = required + + # PipeWire Media Session is not running. + # Do not bother to check. + check.no-media-session = disabled + + # No Bluetooth + hardware.bluetooth = disabled + bluetooth.use-persistent-storage = disabled + bluetooth.autoswitch-to-headset-profile = disabled + + # No video capture + hardware.video-capture = disabled + + # There is only one instance of WirePlumber, so + # pretend that this is a system-wide instance. + # See mixin.systemwide-session in the upstream wireplumber.conf. + support.reserve-device = disabled + monitor.alsa.reserve-device = disabled + support.portal-permissionstore = disabled + script.client.access-portal = disabled + support.logind = disabled + monitor.bluez.seat-monitoring = disabled + + # Disable anything that could store state. + # See mixin.stateless in the upstream wireplumber.conf. + hooks.device.profile.state = disabled + hooks.device.routes.state = disabled + hooks.default-nodes.state = disabled + hooks.stream.state = disabled + } +} + +wireplumber.components = [ + ## WirePlumber components to load. + ## These components are loaded after a connection to pipewire is established. + ## type is mandatory; rest of the tags are optional + ## + ## Syntax: + ## { + ## name = + ## type = + ## arguments = { } + ## + ## # Feature that this component provides + ## provides = + ## + ## # List of features that must be provided before this component is loaded + ## requires = [ ] + ## + ## # List of features that would offer additional functionality if provided + ## # but are not strictly required + ## wants = [ ] + ## } + + ## Makes a secondary connection to PipeWire for exporting objects + { + name = export-core, type = built-in + provides = support.export-core + } + + ## Enables creating local nodes that are exported to pipewire + ## This is needed for LocalNode() / WpImplNode + ## This should be used with the export-core to avoid protocol deadlocks, + ## unless you know what you are doing + { + name = libpipewire-module-client-node, type = pw-module + provides = pw.client-node + wants = [ support.export-core ] + } + + ## Enables creating local devices that are exported to pipewire + ## This is needed for SpaDevice() / WpSpaDevice + ## This should be used with the export-core to avoid protocol deadlocks, + ## unless you know what you are doing + { + name = libpipewire-module-client-device, type = pw-module + provides = pw.client-device + wants = [ support.export-core ] + } + + # Provides a node factory to create SPA nodes + # You need this to use LocalNode("spa-node-factory", ...) + { + name = libpipewire-module-spa-node-factory, type = pw-module + provides = pw.node-factory.spa + requires = [ pw.client-node ] + } + + ## Provides a node factory to create SPA nodes wrapped in an adapter + ## You need this to use LocalNode("adapter", ...) + { + name = libpipewire-module-adapter, type = pw-module + provides = pw.node-factory.adapter + requires = [ pw.client-node ] + } + + ## Provides the "sm-settings" metadata object + { + name = libwireplumber-module-settings, type = module + arguments = { metadata.name = sm-settings } + provides = metadata.sm-settings + } + + ## Activates a global WpSettings instance, providing settings from + ## the sm-settings metadata object. Note that this blocks and waits for the + ## sm-settings metadata object to become available, so one instance must + ## provide that, while others should only load this to access settings + { + name = settings-instance, type = built-in + arguments = { metadata.name = sm-settings } + provides = support.settings + after = [ metadata.sm-settings ] + } + + ## Log level settings + { + name = libwireplumber-module-log-settings, type = module + provides = support.log-settings + } + + ## The lua scripting engine + { + name = libwireplumber-module-lua-scripting, type = module + provides = support.lua-scripting + } + + ## Module listening for pipewire objects to push events + { + name = libwireplumber-module-standard-event-source, type = module + provides = support.standard-event-source + } + + ## Session item factories + { + name = libwireplumber-module-si-node, type = module + provides = si.node + } + { + name = libwireplumber-module-si-audio-adapter, type = module + provides = si.audio-adapter + } + { + name = libwireplumber-module-si-standard-link, type = module + provides = si.standard-link + } + + ## API to access default nodes from scripts + { + name = libwireplumber-module-default-nodes-api, type = module + provides = api.default-nodes + } + + ## API to access mixer controls + { + name = libwireplumber-module-mixer-api, type = module + provides = api.mixer + } + + ## API to get notified about file changes + { + name = libwireplumber-module-file-monitor-api, type = module + provides = api.file-monitor + } + + ## Provide the "default" pw_metadata + { + name = metadata.lua, type = script/lua + arguments = { metadata.name = default } + provides = metadata.default + } + + ## Provide the "filters" pw_metadata + { + name = metadata.lua, type = script/lua + arguments = { metadata.name = filters } + provides = metadata.filters + } + + ## Provide the "sm-objects" pw_metadata, supporting dynamic loadable objects + { + name = sm-objects.lua, type = script/lua + provides = metadata.sm-objects + } + + ## Populates the "session.services" property on the WirePlumber client object + { + name = session-services.lua, type = script/lua + provides = support.session-services + } + + ## Device monitors' optional features + { + type = virtual, provides = monitor.alsa-midi.monitoring, + requires = [ api.file-monitor ] + } + + ## Device monitors + { + name = monitors/alsa.lua, type = script/lua + provides = monitor.alsa + requires = [ support.export-core, pw.client-device ] + } + { + name = monitors/alsa-midi.lua, type = script/lua + provides = monitor.alsa-midi + wants = [ monitor.alsa-midi.monitoring ] + } + + ## Client access configuration hooks + { + name = client/access-default.lua, type = script/lua + provides = script.client.access-default + } + { + type = virtual, provides = policy.client.access + wants = [ script.client.access-default ] + } + + ## Device profile selection hooks + { + name = device/select-profile.lua, type = script/lua + provides = hooks.device.profile.select + } + { + name = device/find-preferred-profile.lua, type = script/lua + provides = hooks.device.profile.find-preferred + } + { + name = device/find-best-profile.lua, type = script/lua + provides = hooks.device.profile.find-best + } + { + name = device/apply-profile.lua, type = script/lua + provides = hooks.device.profile.apply + } + { + type = virtual, provides = policy.device.profile + requires = [ hooks.device.profile.select, + hooks.device.profile.apply ] + wants = [ hooks.device.profile.find-best, hooks.device.profile.find-preferred ] + } + + # Device route selection hooks + { + name = device/select-routes.lua, type = script/lua + provides = hooks.device.routes.select + } + { + name = device/find-best-routes.lua, type = script/lua + provides = hooks.device.routes.find-best + } + { + name = device/apply-routes.lua, type = script/lua + provides = hooks.device.routes.apply + } + { + type = virtual, provides = policy.device.routes + requires = [ hooks.device.routes.select, + hooks.device.routes.apply ] + wants = [ hooks.device.routes.find-best ] + } + + ## Default nodes selection hooks + { + name = default-nodes/rescan.lua, type = script/lua + provides = hooks.default-nodes.rescan + } + { + name = default-nodes/find-selected-default-node.lua, type = script/lua + provides = hooks.default-nodes.find-selected + requires = [ metadata.default ] + } + { + name = default-nodes/find-best-default-node.lua, type = script/lua + provides = hooks.default-nodes.find-best + } + { + name = default-nodes/apply-default-node.lua, type = script/lua, + provides = hooks.default-nodes.apply + requires = [ metadata.default ] + } + { + type = virtual, provides = policy.default-nodes + requires = [ hooks.default-nodes.rescan, + hooks.default-nodes.apply ] + wants = [ hooks.default-nodes.find-selected, + hooks.default-nodes.find-best ] + } + + ## Node configuration hooks + { + name = node/create-item.lua, type = script/lua + provides = hooks.node.create-session-item + requires = [ si.audio-adapter, si.node ] + } + { + name = node/suspend-node.lua, type = script/lua + provides = hooks.node.suspend + } + { + name = node/filter-forward-format.lua, type = script/lua + provides = hooks.filter.forward-format + } + { + type = virtual, provides = policy.node + requires = [ hooks.node.create-session-item ] + wants = [ hooks.node.suspend + hooks.filter.forward-format ] + } + { + name = node/software-dsp.lua, type = script/lua + provides = node.software-dsp + } + { + name = node/audio-group.lua, type = script/lua + provides = node.audio-group + } + + ## Linking hooks + { + name = linking/rescan.lua, type = script/lua + provides = hooks.linking.rescan + } + { + name = linking/find-media-role-target.lua, type = script/lua + provides = hooks.linking.target.find-media-role + } + { + name = linking/find-defined-target.lua, type = script/lua + provides = hooks.linking.target.find-defined + } + { + name = linking/find-audio-group-target.lua, type = script/lua + provides = hooks.linking.target.find-audio-group + requires = [ node.audio-group ] + } + { + name = linking/find-filter-target.lua, type = script/lua + provides = hooks.linking.target.find-filter + requires = [ metadata.filters ] + } + { + name = linking/find-default-target.lua, type = script/lua + provides = hooks.linking.target.find-default + requires = [ api.default-nodes ] + } + { + name = linking/find-best-target.lua, type = script/lua + provides = hooks.linking.target.find-best + requires = [ metadata.filters ] + } + { + name = linking/get-filter-from-target.lua, type = script/lua + provides = hooks.linking.target.get-filter-from + requires = [ metadata.filters ] + } + { + name = linking/prepare-link.lua, type = script/lua + provides = hooks.linking.target.prepare-link + requires = [ api.default-nodes ] + } + { + name = linking/link-target.lua, type = script/lua + provides = hooks.linking.target.link + requires = [ si.standard-link ] + } + { + type = virtual, provides = policy.linking.standard + requires = [ hooks.linking.rescan, + hooks.linking.target.prepare-link, + hooks.linking.target.link ] + wants = [ hooks.linking.target.find-media-role, + hooks.linking.target.find-defined, + hooks.linking.target.find-audio-group, + hooks.linking.target.find-filter, + hooks.linking.target.find-default, + hooks.linking.target.find-best, + hooks.linking.target.get-filter-from ] + } + + ## Linking: Role-based priority system + { + name = linking/rescan-media-role-links.lua, type = script/lua + provides = hooks.linking.role-based.rescan + requires = [ api.mixer ] + } + { + type = virtual, provides = policy.linking.role-based + requires = [ policy.linking.standard, + hooks.linking.role-based.rescan ] + } + + ## Standard policy definition + { + type = virtual, provides = policy.standard + requires = [ policy.client.access + policy.device.profile + policy.device.routes + policy.default-nodes + policy.linking.standard + policy.linking.role-based + policy.node + support.standard-event-source ] + } + + ## Load targets + { + type = virtual, provides = hardware.audio + wants = [ monitor.alsa, monitor.alsa-midi ] + } +] + +wireplumber.components.rules = [ + ## Rules to apply on top of wireplumber.components + { + matches = [ + { + type = "script/lua" + } + ] + actions = { + merge = { + requires = [ support.lua-scripting ] + } + } + } + { + matches = [ + { + provides = "~hooks.*" + name = "!~monitors/.*" + } + ] + actions = { + merge = { + before = [ support.standard-event-source ] + } + } + } + { + matches = [ + { provides = "~monitor.*" } + ] + actions = { + merge = { + after = [ support.standard-event-source ] + } + } + } + # session-services.lua must execute at the very end + { + matches = [ + { name = "!session-services.lua" } + ] + actions = { + merge = { + before = [ support.session-services ] + } + } + } +] + +wireplumber.settings.schema = { + ## Bluetooth + bluetooth.use-persistent-storage = { + description = "Whether to use persistent BT storage or not" + type = "bool" + default = true + } + bluetooth.autoswitch-to-headset-profile = { + description = "Whether to autoswitch to BT headset profile or not" + type = "bool" + default = true + } + + ## Device + device.restore-profile = { + description = "Whether to restore device profile or not" + type = "bool" + default = true + } + device.restore-routes = { + description = "Whether to restore device routes or not" + type = "bool" + default = true + } + device.routes.default-sink-volume = { + description = "The default volume for sink devices" + type = "float" + default = 0.064 + min = 0.0 + max = 1.0 + } + device.routes.default-source-volume = { + description = "The default volume for source devices" + type = "float" + default = 1.0 + min = 0.0 + max = 1.0 + } + + ## Linking + linking.role-based.duck-level = { + description = "The volume level to apply when ducking (= reducing volume for a higher priority stream to be audible) in the role-based linking policy" + type = "float" + default = 0.3 + min = 0.0 + max = 1.0 + } + linking.allow-moving-streams = { + description = "Whether to allow metadata to move streams at runtime or not" + type = "bool" + default = true + } + linking.follow-default-target = { + description = "Whether to allow streams follow the default device or not" + type = "bool" + default = true + } + + ## Monitor + monitor.camera-discovery-timeout = { + description = "The camera discovery timeout in milliseconds" + type = "int" + default = 1000 + min = 0 + max = 60000 + } + + ## Node + node.features.audio.no-dsp = { + description = "Whether to never convert audio to F32 format or not" + type = "bool" + default = false + } + node.features.audio.monitor-ports = { + description = "Whether to enable monitor ports on audio nodes or not" + type = "bool" + default = true + } + node.features.audio.control-port = { + description = "Whether to enable control ports on audio nodes or not" + type = "bool" + default = false + } + node.stream.restore-props = { + description = "Whether to restore properties on stream nodes or not" + type = "bool" + default = true + } + node.stream.restore-target = { + description = "Whether to restore target on stream nodes or not" + type = "bool" + default = true + } + node.stream.default-playback-volume = { + description = "The default volume for playback nodes" + type = "float" + default = 1.0 + min = 0.0 + max = 1.0 + } + node.stream.default-capture-volume = { + description = "The default volume for capture nodes" + type = "float" + default = 1.0 + min = 0.0 + max = 1.0 + } + node.stream.default-media-role = { + description = "A media.role to assign on streams that have none specified" + type = "string" + default = null + } + node.filter.forward-format = { + description = "Whether to forward format on filter nodes or not" + type = "bool" + default = false + } + node.restore-default-targets = { + description = "Whether to restore default targets or not" + type = "bool" + default = true + } +} -- Sincerely, Demi Marie Obenour (she/her/hers)