1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
| | # 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.
#
# PipeWire chooses the node with the highest priority.driver value as
# graph driver, which is the node that decides when the processing graph
# is going to run. If both the capture node and playback node are in
# the same graph, the capture node should be chosen as the driver. This
# is because the driver gets to choose the rate of the graph and so is
# much less likely to xrun. Since capture xruns result in corrupted
# audio recordings, while playback xruns just result in a glitch, it
# is more important to avoid capture xruns.
#
# When there are multiple sources or sinks that could be used,
# WirePlumber links application nodes to the one with the highest
# priority.session value. In the configuration created here,
# there are two valid audio sources: the virtio sound card's
# capture stream and the monitor of its playback stream. The
# capture stream is the correct choice, so its priority.session
# should be higher.
#
# The recommendation to give the capture device higher values
# for priority.driver and priority.session comes from George
# Kiagiadakis of Collabora, who also provided the values
# used (2000 and 1000) and why they must be different.
# The explanation for why it is more important to avoid
# capture xruns than playback xruns comes from past discussions
# that I (Demi Marie Obenour) had with Wim Taymans.
{ 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.pci-0000_00_01.0.analog-stereo"
node.suspend-on-idle = true
priority.driver = 1000
priority.session = 1000
}
}
{ 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.pci-0000_00_01.0.analog-stereo"
node.suspend-on-idle = true
priority.driver = 2000
priority.session = 2000
}
}
]
# 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 = []
|