patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Demi Marie Obenour <demiobenour@gmail.com>
To: Spectrum OS Development <devel@spectrum-os.org>
Cc: Alyssa Ross <hi@alyssa.is>
Subject: [PATCH v5 8/8] img/app: Run PipeWire and WirePlumber in the VMs
Date: Sun, 20 Jul 2025 14:11:25 -0400	[thread overview]
Message-ID: <7f88f09b-5671-4f1e-86d7-df08dc2561eb@gmail.com> (raw)
In-Reply-To: <a7325ee5-eb7f-458c-9be6-404db676dd81@gmail.com>

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 bring a
log of unnecessary files into the VMs, which will hopefully be removed
later as part of a debloating effort.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
---
 img/app/Makefile                              |  17 +-
 img/app/default.nix                           |   3 +
 img/app/etc/mdev.conf                         |   3 +
 img/app/etc/pipewire/pipewire.conf            | 199 +++++++
 .../etc/s6-rc/app/dependencies.d/wireplumber  |   0
 .../s6-rc/pipewire/dependencies.d/directories |   0
 .../etc/s6-rc/pipewire/dependencies.d/mdevd   |   0
 img/app/etc/s6-rc/pipewire/notification-fd    |   1 +
 .../s6-rc/pipewire/notification-fd.license    |   2 +
 img/app/etc/s6-rc/pipewire/run                |  25 +
 img/app/etc/s6-rc/pipewire/type               |   1 +
 img/app/etc/s6-rc/pipewire/type.license       |   2 +
 .../etc/s6-rc/wireplumber/dependencies.d/dbus |   0
 .../s6-rc/wireplumber/dependencies.d/pipewire |   0
 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      | 536 ++++++++++++++++++
 18 files changed, 794 insertions(+), 2 deletions(-)
 create mode 100644 img/app/etc/pipewire/pipewire.conf
 create mode 100644 img/app/etc/s6-rc/app/dependencies.d/wireplumber
 create mode 100644 img/app/etc/s6-rc/pipewire/dependencies.d/directories
 create mode 100644 img/app/etc/s6-rc/pipewire/dependencies.d/mdevd
 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/wireplumber/dependencies.d/dbus
 create mode 100644 img/app/etc/s6-rc/wireplumber/dependencies.d/pipewire
 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 e11be09a3c6ca801d9211e49b58e3d05d57e344e..734a76f018dfc1deaa9bf2cfbd4fa0d6885f0546 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
@@ -84,6 +87,7 @@ VM_S6_RC_FILES = \
 	etc/s6-rc/app/dependencies.d/dbus \
 	etc/s6-rc/app/dependencies.d/directories \
 	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 \
@@ -100,10 +104,18 @@ VM_S6_RC_FILES = \
 	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/wayland-proxy-virtwl/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 $@)
@@ -150,6 +162,7 @@ run-qemu: $(imgdir)/appvm/blk/root.img start-vhost-user-net start-virtiofsd
 	    -chardev socket,id=virtiofsd,path=build/virtiofsd.sock \
 	    -device vhost-user-fs-pci,chardev=virtiofsd,tag=virtiofs0 \
 	    -device virtio-gpu-rutabaga-pci,cross-domain=on,hostmem=8G \
+	    -audio driver=pipewire,model=virtio \
 	    -object memory-backend-memfd,id=mem,size=256M,share=on \
 	    -numa node,memdev=mem \
 	    -device vhost-vsock-pci,guest-cid=3 \
diff --git a/img/app/default.nix b/img/app/default.nix
index 740643ac41f6473cdb6f6b0fd1f5f47f4493240d..d3eed1f0accdc8968d1ba5bdec74ab597789082f 100644
--- a/img/app/default.nix
+++ b/img/app/default.nix
@@ -48,6 +48,9 @@ let
       pkgs.xwayland
       pkgs.xdg-desktop-portal
       pkgs.xdg-desktop-portal-gtk
+      # Depends on pulseaudio libs
+      pkgs.pipewire
+      pkgs.wireplumber
     ];
   })).fhsenv;
 in
diff --git a/img/app/etc/mdev.conf b/img/app/etc/mdev.conf
index f2101e1f683c49808358b25520080c59ed2afa8e..80efb77c68cd533b4c46e9d15966807a3ff084dd 100644
--- a/img/app/etc/mdev.conf
+++ b/img/app/etc/mdev.conf
@@ -5,3 +5,6 @@
 $INTERFACE=.* 0:0 660 ! +/etc/mdev/iface
 $MODALIAS=virtio:d0000001Av.* 0:0 660 ! +/etc/mdev/virtiofs
 dri/card0 0:0 660 +background { /etc/mdev/listen card0 }
+snd/pcmC0D0p 0:0 660 +background { /etc/mdev/listen pcmC0D0p }
+snd/pcmC0D0c 0:0 660 +background { /etc/mdev/listen pcmC0D0c }
+snd/controlC0 0:0 660 +background { /etc/mdev/listen controlC0 }
diff --git a/img/app/etc/pipewire/pipewire.conf b/img/app/etc/pipewire/pipewire.conf
new file mode 100644
index 0000000000000000000000000000000000000000..cbea25d2c7ca274db8a3c8772439b0d3a8279f13
--- /dev/null
+++ b/img/app/etc/pipewire/pipewire.conf
@@ -0,0 +1,199 @@
+# SPDX-License-Identifier: MIT
+
+# Copyright © 2018 Wim Taymans
+# Copyright © 2025 Demi Marie Obenour
+#
+# 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.
+
+# This file is based on the upstream default configuration.  This can be
+# found in upstream GitLab or in any distro with a recent version of PipeWire.
+# The following changes have been made:
+#
+# - Conditions that have known values in Spectrum VMs are omitted.
+# - Modules for hardware devices Spectrum VMs don't have are not loaded.
+# - The PulseAudio emulation server is loaded.
+# - Settings for VMs are applied unconditionally.
+# - Most comments in the upstream files have been removed.
+# - Device nodes for virtio-sound devices have been added.
+# - Integration with udev and logind is removed.
+context.properties = {
+    # Upstream defaults.
+    link.max-buffers = 16
+    core.daemon = true
+    core.name   = pipewire-0
+    # Account for running in a VM
+    default.clock.min-quantum = 1024
+}
+
+# Upstream defaults, with support for AVB, V4L2, libcamera
+# bluez, Vulkan, JACK, and video conversion omitted.
+context.spa-libs = {
+    audio.convert.* = audioconvert/libspa-audioconvert
+    api.alsa.*      = alsa/libspa-alsa
+    support.*       = support/libspa-support
+}
+
+context.modules = [
+    # Upstream defaults
+    { name = libpipewire-module-rt
+        args = { nice.level = -11, rt.prio = 88 }
+    }
+    { name = libpipewire-module-protocol-native  }
+    { name = libpipewire-module-metadata }
+    { name = libpipewire-module-spa-device-factory }
+    { name = libpipewire-module-spa-node-factory }
+    { name = libpipewire-module-client-node }
+    { name = libpipewire-module-access }
+    { name = libpipewire-module-client-device }
+    { name = libpipewire-module-portal }
+    { name = libpipewire-module-adapter }
+    { name = libpipewire-module-link-factory }
+    { name = libpipewire-module-session-manager }
+
+    # Load the PulseAudio server into PipeWire.
+    # This avoids needing a separate pipewire-pulse
+    # process.  The args are those used when running
+    # in a VM.
+    { name = libpipewire-module-protocol-pulse
+        args = {
+            server.address = [ "unix:native" ]
+            pulse.min.quantum = 1024/48000
+        }
+    }
+]
+
+context.objects = [
+    # Upstream defaults
+    { 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
+        }
+    }
+    { 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
+        }
+    }
+
+    # Spectrum doesn't use udev, so device nodes must be created statically.
+    # Creating them with pw-cli works as long as pw-cli is running, but
+    # the nodes are destroyed when pw-cli exits.
+    { factory = adapter
+        args = {
+            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,0,0"
+            api.alsa.pcm.card      = 0,
+            api.alsa.pcm.stream    = "playback"
+            audio.allowed-rates    = [ ]
+            audio.channels         = 2
+            audio.format           = "S32"
+            audio.position         = "FL,FR"
+            audio.rate             = 48000
+            factory.name           = "api.alsa.pcm.sink"
+            media.class            = "Audio/Sink"
+            node.name              = "alsa_output"
+            node.suspend-on-idle   = true
+        }
+    }
+    { factory = adapter
+        args = {
+            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,0,0"
+            api.alsa.pcm.card      = 0,
+            api.alsa.pcm.stream    = "capture"
+            audio.allowed-rates    = [ ]
+            audio.channels         = 2
+            audio.format           = "S32"
+            audio.position         = "FL,FR"
+            audio.rate             = 48000
+            factory.name           = "api.alsa.pcm.source"
+            media.class            = "Audio/Source"
+            node.name              = "alsa_input"
+            node.suspend-on-idle   = true
+        }
+    }
+]
+
+# Load the modules that are in the default config *except*
+# for ones whose job is to maintain state.
+pulse.cmd = [
+    { cmd = "load-module" args = "module-always-sink" flags = [ ] }
+    { cmd = "load-module" args = "module-device-manager" flags = [ ] }
+]
+
+# More default stuff.
+pulse.rules = [
+    {
+        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 ] }
+    }
+    {
+        matches = [ { application.process.binary = "firefox" } ]
+        actions = { quirks = [ remove-capture-dont-move ] }
+    }
+    {
+        matches = [ { application.name = "~speech-dispatcher.*" } ]
+        actions = {
+            update-props = {
+                pulse.min.req          = 512/48000
+                pulse.min.quantum      = 512/48000
+                pulse.idle.timeout     = 5
+            }
+        }
+    }
+]
+
+context.exec = []
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/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/mdevd b/img/app/etc/s6-rc/pipewire/dependencies.d/mdevd
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
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 <demiobenour@gmail.com>
diff --git a/img/app/etc/s6-rc/pipewire/run b/img/app/etc/s6-rc/pipewire/run
new file mode 100644
index 0000000000000000000000000000000000000000..2b3c001fb4f8b00e39aeeefe28ad6a4f7e4760a4
--- /dev/null
+++ b/img/app/etc/s6-rc/pipewire/run
@@ -0,0 +1,25 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2023-2024 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+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
+
+# Wait for sound devices to be available
+if { /etc/mdev/wait pcmC0D0p }
+if { /etc/mdev/wait pcmC0D0c }
+if { /etc/mdev/wait controlC0 }
+
+# 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 <demiobenour@gmail.com>
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/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/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 <demiobenour@gmail.com>
+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 <demiobenour@gmail.com>
diff --git a/img/app/etc/wireplumber/wireplumber.conf b/img/app/etc/wireplumber/wireplumber.conf
new file mode 100644
index 0000000000000000000000000000000000000000..68871e93083572976bba29b44f07eb35b86c0275
--- /dev/null
+++ b/img/app/etc/wireplumber/wireplumber.conf
@@ -0,0 +1,536 @@
+# 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.
+
+# This file is based on the upstream default configuration.  This can be
+# found in upstream GitLab or in any distro with a recent version of WirePlumber.
+# The following changes have been made:
+#
+# - Conditions that have known values in Spectrum VMs are omitted.
+# - Modules for hardware devices Spectrum VMs don't have are not loaded.
+# - Settings for VMs are applied unconditionally.
+# - Most comments in the upstream files have been removed.
+# - Integration with udev and logind is removed.
+# - RT scheduling for the data thread is enabled.
+# - The settings are those appropriate for a system-wide instance.
+
+context.spa-libs = {
+  api.alsa.*      = alsa/libspa-alsa
+  audio.convert.* = audioconvert/libspa-audioconvert
+  support.*       = support/libspa-support
+}
+
+# Upstream default, with RT scheduling enabled.
+context.modules = [
+  {
+    name = libpipewire-module-rt
+    args = { nice.level = -11, rt.prio = 88 }
+  }
+  { name = libpipewire-module-protocol-native }
+  { name = libpipewire-module-metadata }
+]
+
+wireplumber.profiles = {
+  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 = [
+  {
+    name = export-core, type = built-in
+    provides = support.export-core
+  }
+  {
+    name = libpipewire-module-client-node, type = pw-module
+    provides = pw.client-node
+    wants = [ support.export-core ]
+  }
+  {
+    name = libpipewire-module-client-device, type = pw-module
+    provides = pw.client-device
+    wants = [ support.export-core ]
+  }
+  {
+    name = libpipewire-module-spa-node-factory, type = pw-module
+    provides = pw.node-factory.spa
+    requires = [ pw.client-node ]
+  }
+  {
+    name = libpipewire-module-adapter, type = pw-module
+    provides = pw.node-factory.adapter
+    requires = [ pw.client-node ]
+  }
+  {
+    name = libwireplumber-module-settings, type = module
+    arguments = { metadata.name = sm-settings }
+    provides = metadata.sm-settings
+  }
+  {
+    name = settings-instance, type = built-in
+    arguments = { metadata.name = sm-settings }
+    provides = support.settings
+    after = [ metadata.sm-settings ]
+  }
+  {
+    name = libwireplumber-module-lua-scripting, type = module
+    provides = support.lua-scripting
+  }
+  {
+    name = libwireplumber-module-standard-event-source, type = module
+    provides = support.standard-event-source
+  }
+  {
+    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
+  }
+  {
+    name = libwireplumber-module-default-nodes-api, type = module
+    provides = api.default-nodes
+  }
+  {
+    name = libwireplumber-module-mixer-api, type = module
+    provides = api.mixer
+  }
+  {
+    name = metadata.lua, type = script/lua
+    arguments = { metadata.name = default }
+    provides = metadata.default
+  }
+  {
+    name = metadata.lua, type = script/lua
+    arguments = { metadata.name = filters }
+    provides = metadata.filters
+  }
+  {
+    name = sm-objects.lua, type = script/lua
+    provides = metadata.sm-objects
+  }
+  {
+    name = session-services.lua, type = script/lua
+    provides = support.session-services
+  }
+  {
+    name = monitors/alsa.lua, type = script/lua
+    provides = monitor.alsa
+    requires = [ support.export-core, pw.client-device ]
+  }
+  {
+    name = client/access-default.lua, type = script/lua
+    provides = script.client.access-default
+  }
+  {
+    type = virtual, provides = policy.client.access
+    wants = [ script.client.access-default ]
+  }
+  {
+    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 ]
+  }
+  {
+    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 ]
+  }
+  {
+    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 ]
+  }
+  {
+    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/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 ]
+  }
+  {
+    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 ]
+  }
+  {
+    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 ]
+  }
+  {
+    type = virtual, provides = hardware.audio
+    wants = [ monitor.alsa ]
+  }
+]
+
+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)

  parent reply	other threads:[~2025-07-20 18:11 UTC|newest]

Thread overview: 81+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-11  2:44 [PATCH v3] Run PipeWire and WirePlumber in the VMs Demi Marie Obenour
2025-07-14 14:54 ` Alyssa Ross
2025-07-15 20:22   ` Demi Marie Obenour
2025-07-16 10:26     ` Alyssa Ross
2025-07-16 21:16   ` Demi Marie Obenour
2025-07-16 21:27     ` Demi Marie Obenour
2025-07-18 12:16       ` Alyssa Ross
2025-07-17  5:53   ` Demi Marie Obenour
2025-07-18 10:02     ` Alyssa Ross
2025-07-18 10:19       ` Alyssa Ross
2025-07-18  2:07 ` [PATCH v4 0/3] Sound support in Spectrum VMs Demi Marie Obenour
2025-07-18  2:13   ` [PATCH v4 1/3] Rebuild the root filesystem when the makefile changes Demi Marie Obenour
2025-07-18 11:14     ` Alyssa Ross
2025-07-18  2:13   ` [PATCH v4 2/3] Fix permissions on /tmp Demi Marie Obenour
2025-07-18 11:51     ` Alyssa Ross
2025-07-18 11:51     ` Alyssa Ross
2025-07-18 11:53     ` Alyssa Ross
2025-07-18  2:14   ` [PATCH v4 3/3] Run PipeWire and WirePlumber in the VMs Demi Marie Obenour
2025-07-18 11:27     ` Alyssa Ross
2025-07-18 17:59       ` Demi Marie Obenour
2025-07-19  9:22         ` Alyssa Ross
2025-07-19 20:05           ` Demi Marie Obenour
2025-07-19  8:06     ` Alyssa Ross
2025-07-19 20:03       ` Demi Marie Obenour
2025-07-19 20:07         ` Demi Marie Obenour
2025-07-20  7:50           ` Alyssa Ross
2025-07-20 17:58   ` [PATCH v5 0/8] Sound support in Spectrum VMs Demi Marie Obenour
2025-07-20 18:02     ` [PATCH v5 1/8] Revert "img/app: fix permissions on /tmp" Demi Marie Obenour
2025-07-21  9:34       ` Alyssa Ross
2025-07-20 18:03     ` [PATCH v5 2/8] img/app: Use separate service to create directories Demi Marie Obenour
2025-07-21  9:21       ` Alyssa Ross
2025-07-22 23:48         ` Demi Marie Obenour
2025-07-20 18:04     ` [PATCH v5 3/8] img/app: Fix permissions of /tmp/.X11-unix Demi Marie Obenour
2025-07-20 18:05     ` [PATCH v5 4/8] img/app: Create other X11 directories Demi Marie Obenour
2025-07-21  9:23       ` Alyssa Ross
2025-07-21 19:03         ` Demi Marie Obenour
2025-07-20 18:06     ` [PATCH v5 5/8] img/app: Be explicit about directory modes Demi Marie Obenour
2025-07-20 18:08     ` [PATCH v5 6/8] img/app: create /run/user and /run/wait very early in boot Demi Marie Obenour
2025-07-21  9:23       ` Alyssa Ross
2025-07-20 18:10     ` [PATCH v5 7/8] host/rootfs: " Demi Marie Obenour
2025-07-20 18:11     ` Demi Marie Obenour [this message]
2025-07-21  9:42       ` [PATCH v5 8/8] img/app: Run PipeWire and WirePlumber in the VMs Alyssa Ross
2025-07-21 19:09         ` Demi Marie Obenour
2025-07-26 10:11           ` Alyssa Ross
2025-07-21 19:10         ` Demi Marie Obenour
2025-07-24 22:15     ` [PATCH v6 0/5] Sound support in Spectrum VMs Demi Marie Obenour
2025-07-24 22:30       ` [PATCH v6 1/5] host/rootfs: Create /run/user and /run/wait via run-image Demi Marie Obenour
2025-07-26 10:46         ` Alyssa Ross
2025-07-24 22:32       ` [PATCH v6 2/5] img/app: " Demi Marie Obenour
2025-07-24 22:33       ` [PATCH v6 3/5] img/app: tell mount(8) to create directories Demi Marie Obenour
2025-07-26 11:20         ` Alyssa Ross
2025-07-26 11:26         ` Alyssa Ross
2025-07-24 22:35       ` [PATCH v6 4/5] img/app: Create needed directories in early boot Demi Marie Obenour
2025-07-26 10:24         ` Alyssa Ross
2025-07-27 20:13           ` Demi Marie Obenour
2025-07-24 22:36       ` [PATCH v6 5/5] img/app: Run PipeWire and WirePlumber in the VMs Demi Marie Obenour
2025-07-26 11:29         ` Alyssa Ross
2025-07-26 10:57       ` [PATCH v6 0/5] Sound support in Spectrum VMs Alyssa Ross
2025-07-28  5:57       ` [PATCH v7 0/2] " Demi Marie Obenour
2025-07-28  6:01         ` [PATCH v7 1/2] img/app: Create needed directories in early boot Demi Marie Obenour
2025-07-28  6:03         ` [PATCH v7 2/2] img/app: Run PipeWire and WirePlumber in the VMs Demi Marie Obenour
2025-07-28  6:18           ` Demi Marie Obenour
2025-07-28 23:13         ` [PATCH v8 0/2] Sound support in Spectrum VMs Demi Marie Obenour
2025-07-29  0:32           ` [PATCH v9 " Demi Marie Obenour
2025-07-29  0:33             ` [PATCH v9 1/2] img/app: Create needed directories in early boot Demi Marie Obenour
2025-07-29 12:44               ` Alyssa Ross
2025-07-29  0:33             ` [PATCH v9 2/2] img/app: Run PipeWire and WirePlumber in the VMs Demi Marie Obenour
2025-07-29 13:08               ` Alyssa Ross
2025-07-29 21:17                 ` Demi Marie Obenour
2025-07-30  8:10                   ` Alyssa Ross
2025-07-30  9:59             ` [PATCH v10] " Demi Marie Obenour
2025-07-31  9:12               ` Alyssa Ross
2025-07-31  9:40               ` Alyssa Ross
2025-07-31 17:06               ` [PATCH v11] " Demi Marie Obenour
2025-08-01 17:53                 ` Alyssa Ross
2025-08-02  7:54                 ` Alyssa Ross
2025-07-28 23:13         ` [PATCH v8 1/2] img/app: Create needed directories in early boot Demi Marie Obenour
2025-07-28 23:19           ` Demi Marie Obenour
2025-07-28 23:13         ` [PATCH v8 2/2] img/app: Run PipeWire and WirePlumber in the VMs Demi Marie Obenour
2025-07-29 12:41         ` [PATCH v7 0/2] Sound support in Spectrum VMs Alyssa Ross
2025-07-24 22:23     ` [PATCH v6 1/5] host/rootfs: Create /run/user and /run/wait via run-image Demi Marie Obenour

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7f88f09b-5671-4f1e-86d7-df08dc2561eb@gmail.com \
    --to=demiobenour@gmail.com \
    --cc=devel@spectrum-os.org \
    --cc=hi@alyssa.is \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/crosvm
	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/nixpkgs
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock
	https://spectrum-os.org/git/www

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).