I’d like to share some things that have restored my sanity while solving the sound crackling / pw-top xruns / pipewire debug messages of “Client too slow!” errors…
My specific issue was that audio was fine-in-the-beginning-but-quickly-ramps-up-crackling-until-pure-robot-voices…
I found pw-top ERRs were skyrocketing, and found this to explain it:
api.alsa.headroom was the next target, and indeed it’s handled by wireplumber…
There’s a section, in /usr/share/wireplumber/main.lua.d/50-alsa-config.lua that defines what’ll happen if wireplumber detects it’s running in a VM:
-- These properties override node defaults when running in a virtual machine.
-- The rules below still override those.
["vm.node.defaults"] = {
["api.alsa.period-size"] = 256,
["api.alsa.headroom"] = 8192,
},
Guess what wireplumber detects, even when sys-audio is a HVM with PCI passthroughs?
Yup, a vm. You can verify this settings’ value with:
wpctl status
(to get the number of your default/primary Sink)
wpctl inspect ##
(sink number)
What does api.alsa.headroom do?
https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration/alsa.html
This adds extra delay between the hardware pointers and software pointers. In most cases this can be set to 0. For very bad devices or emulated devices (like in a VM) it might be necessary to increase the headroom value.
So how do we set it?
Well, the wireplumber version in the debian-12-minimum template is $(wireplumber -v)
[0.4.13] NOT the v5.5 that the current documentation is at, and there was a lua → SPA-JSON change to the formatting.
So we need to find the default configuration files that this version uses, and they’re located at:
/usr/share/wireplumber/main.lua.d/
They load in order of 01-whatever.lua to 90-enable-all.lua,
starting from /usr/share/wireplumber [shipped defaults]
then /etc/wireplumber [system wide settings]
then ~/.config/wireplumber [user settings]
With the latest read overriding the previous.
This can be set in the app-vm or template, by creating a .lua file in the above location of your choice (I’ve found that, aside from packages, all the audio modification I’ve needed to make can be done in the app-vm itself - making it quicker to Salt + deploy + test)
In my case, the crackling issues disappeared when I set api.alsa.headroom to 0, with the following file
/home/user/.config/wireplumber/main.lua.d/89-headroom.lua:
file.managed:
- mode: '0644'
- user: user
- group: user
- makedirs: True
- contents: |
alsa_monitor.properties = {
["vm.node.defaults"] = {
["api.alsa.headroom"] = 0,
},
}
Which seems counterintuitive at first, but the issue was data not getting to the right place at the right time - I thought the hardware needed a bigger buffer (quantum in pipewire terms), but it was being choked instead.
Now Easyeffects is running with plenty of audio modifications smooth as can be, and while I didn’t notice this at first, there’s no audio delay in daily videos as well! I do notice xruns or crackling but it’s sanely coming from large spikes in disk or CPU usage, whew.
You can set headroom, default volume, how profiles are loaded from pci and usb devices, and suspend in the same file, combining 40-device-defaults.lua and 50-alsa-config.lua modifications.
In Wireplumber, Devices are the hardware, and Nodes(Sinks) are the interface to that hardware.
/home/user/.config/wireplumber/main.lua.d/89-combined.lua:
file.managed:
- mode: '0644'
- user: user
- group: user
- makedirs: True
- contents: |
device_defaults.properties = {
["default-volume"] = 1.0,
["default-input-volume"] = 1.0,
}
alsa_monitor.enabled = true
alsa_monitor.properties = {
-- These properties override node defaults when running in a virtual machine.
-- The rules below still override those.
["vm.node.defaults"] = {
["api.alsa.headroom"] = 0,
},
}
alsa_monitor.rules = {
{
matches = {
{
{ "device.name", "matches", "alsa_card.pci*" },
},
},
apply_properties = {
["api.alsa.use-acp"] = true,
["api.alsa.use-ucm"] = true,
["api.acp.auto-profile"] = false,
["api.acp.auto-port"] = false,
},
},
{
matches = {
{
{ "device.name", "matches", "alsa_card.usb*" },
},
},
apply_properties = {
["api.alsa.use-acp"] = true,
["api.alsa.use-ucm"] = true,
["api.acp.auto-profile"] = false,
["api.acp.auto-port"] = false,
},
},
{
matches = {
{
-- Matches all sources.
{ "node.name", "matches", "alsa_input.*" },
},
{
-- Matches all sinks.
{ "node.name", "matches", "alsa_output.*" },
},
},
apply_properties = {
["api.alsa.headroom"] = 0,
["session.suspend-timeout-seconds"] = 0, -- 0 disables suspend
},
},
}
I believe the bulk of settings to change can be found in /usr/share/wireplumber/main.lua.d/50-alsa-config.lua and inserted under the last apply_properties section.
Hope this helps someone else