让 PipeWire 不再爆音
偶发的电音难在复现,也难在确认修好没有。把听感换成 pw-top 上的 ERR 计数之后,两次定位都有了依据:第一次是 memlock 权限,第二次是一份 WirePlumber 压根没读的配置文件。
音乐播着播着会突然「啪」一下,或者有那么一两秒像电流声,然后又正常了。
间歇出现,几分钟到几小时一次。重启没用,换线没用,换播放器也没用。
这类问题的麻烦有两层:复现不了,也没法确认修好了没有。
这个教程只是提供我自己的解决方案 实际效果因系统而异,不能确保可以 100% 解决你遇到的问题
先决条件
| 需要 | 说明 |
|---|---|
| PipeWire | 本文的命令都是它自带的:`pw-top` / `pw-metadata` / `wpctl` |
能改 `/etc/security/limits.d/` |
第一次修复动的是这里,要 root |
| 能重新登录 | 组权限只在登录时读一次 |
这套做法在什么环境上验证过
```plaintextCachyOS (Arch) | KDE Plasma 6 | Wayland PipeWire 1.6.7 | WirePlumber 0.5.17 声卡:3 个 HDA-Intel 设备(NVIDIA HDMI + 2 个 AMD)
```
| 项 | 是否通用 | 说明 |
|---|---|---|
`pw-top` / `pw-metadata` / `wpctl` |
✅ 通用 | PipeWire 自带,与发行版无关 |
`realtime` 组与 `memlock` |
⚠️ 因发行版而异 | Arch 系是 `/etc/security/limits.d/99-realtime-privileges.conf`;Debian 系组名和文件位置都不同,先查自己那份 |
`snd_hda_intel` 省电参数 |
⚠️ 只对 HDA 声卡 | USB 声卡、蓝牙耳机不适用 |
| WirePlumber 配置格式 | ❌ 0.5 起换过了 | 见「一份没有生效的配置」 |
| CPU governor | ⚠️ 因内核而异 | CachyOS 只有 `powersave` 和 `performance` 两档 |
三个自查命令:
```bashpw-top -b -n 3 # 看 ERR 列 cat /sys/module/snd_hda_intel/parameters/power_save # 省电秒数 grep -h '^@realtime' /etc/security/limits.d/*.conf # 组权限
```
第一步:把听感换成数字
`pw-top` 是 PipeWire 版的 `top`。要看的只有一列:ERR,也就是 xrun 的次数。
缓冲区没喂上,硬件只能拿旧数据顶上,听到的就是那声「啪」。所以 ERR 涨一次, 耳朵那边就响一次,两者是对得上的。
`-b -n 3` 是批处理模式,跑三次就退出,把输出留在终端里:
```bashpw-top -b -n 3
```
```plaintextS ID QUANT RATE WAIT BUSY W/Q B/Q ERR R 52 2048 48000 8.4us 26.2us 0.00 0.00 0 R 134 2400 48000 10.3us 14.3us 0.00 0.00 0
```
(原输出末尾还有 FORMAT 和 NAME 两列,这里截掉了。)
ERR 是 0,说明这段时间没出问题。数字一旦涨,后面就有可以对照的东西了: 改配置、再看 ERR、看它涨没涨。听感靠不住,计数靠得住。
顺带记住 QUANT 那一列,它是当前的缓冲区大小(单位是帧)。后面两次定位都和它有关。
第一次定位:ERR 有 25 个
现象
Proton 跑 Windows 游戏时出现电流声。`pw-top` 里 alsa_output 那一行的 ERR 是 25。
排查
同一个界面里还有一列能看:进程的实时优先级。当时 PipeWire 显示的是 `-`,
意思是没有实时优先级。
顺着权限往下查:
```bashgroups # 用户组里有没有 realtime grep -h '^@realtime' /etc/security/limits.d/*.conf grep -h '^@audio' /etc/security/limits.d/*.conf
```
拿到的结果:
```plaintext@realtime - rtprio 98 @realtime - memlock unlimited @realtime - nice -11
```
`@audio` 那份只给了 `rtprio`,没有 `memlock`。而用户当时在 `audio` 组里。
根因
`memlock unlimited` 决定进程能不能把音频缓冲区锁在物理内存里。
拿不到这个权限,缓冲区就可能被换出去,等要用的时候再换回来。换回来那一下的延迟, 就是一次 xrun。
所以在 `audio` 组不等于够用,音频线程要的是 `realtime` 组。
修复
```bashsudo usermod -aG realtime "$USER" # 注销重新登录,组权限只在登录时读一次
```
结果
ERR 从 25 降到 1,电流声消失。
第二次定位:还剩偶发
过了几天,音乐播放时又出现偶发的电音,游戏那边的电流声倒是没有了。
重新看 `pw-top`,ERR 不再是 0。
这一次没有单一根因,候选有四条:
| # | 候选 | 判断依据 |
|---|---|---|
| 1 | `snd_hda_intel` 省电 |
`power_save = 10`,10 秒没声音就让编解码器休眠,唤醒时爆音 |
| 2 | CPU governor | 所有核心跑在 `powersave`,音频线程拿不到实时性 |
| 3 | `min-quantum` 过低 |
值是 32,也就是 0.67 ms,任何抖动都会 xrun |
| 4 | WirePlumber 链路失败 | 日志里有 `1 of 1 PipeWire links failed to activate` |
四条里改了哪几条(2026-09 实测)
| 候选 | 档案里的方案 | 现在的状态 |
|---|---|---|
| 1 | `/etc/modprobe.d/audio-nosuspend.conf` |
❌ 文件不存在,内核参数仍是 `power_save=10` |
| 2 | `/etc/default/cpupower` + 启用服务 |
❌ 文件不存在,governor 仍是 `powersave` |
| 3 | `~/.config/pipewire/pipewire.conf.d/99-min-quantum.conf` |
✅ 已生效,`pw-metadata` 读出 `min-quantum = 256` |
| 4 | WirePlumber 规则 | ⚠️ 文件在,但没有被读,见下一节 |
一份没有生效的配置
回头再看这台机器时,WirePlumber 的日志里有这么两行:
```plaintextwireplumber: Old configuration file detected: ~/.config/wireplumber/main.lua.d/51-disable-suspension.lua wireplumber: Lua configuration files are NOT supported in WirePlumber 0.5. You need to port them to the new format if you want to use them.
```
那份配置是 0.4 时代的写法,内容是禁用音频设备的自动挂起。它一直在磁盘上, 看着像已经生效了,但 WirePlumber 0.5 起不再读 Lua 配置,所以从升级那天起, 它就是个死文件。
判断配置有没有生效,要看守护进程怎么说、设备上的属性是什么,不能看文件在不在。
里面两个属性名也没了
那份 Lua 规则用了三个属性,在 0.5 里的情况:
| 属性 | 0.5 里还在吗 |
|---|---|
`session.suspend-timeout-seconds` |
✅ 仍在 `suspend-node.lua` 里被读取,默认 5 秒 |
`api.alsa.disable-power-save` |
❌ 脚本和二进制模块里都搜不到 |
`api.alsa.disable-suspend` |
❌ 同上 |
所以就算把它转成新格式,能保留的也只有第一个。
新格式长什么样
WirePlumber 0.5 的配置放在 `~/.config/wireplumber/wireplumber.conf.d/*.conf`。
官方自带的 `alsa-vm.conf` 就是现成样例:
```plaintextmonitor.alsa.rules = [ { matches = [ { node.name = "~alsa_output.pci.*" } ] actions = { update-props = { api.alsa.period-size = 1024 api.alsa.headroom = 2048 } } } ]
```
照这个格式,那条禁用挂起的规则搬过来是:
```plaintext# ~/.config/wireplumber/wireplumber.conf.d/51-disable-suspend.conf monitor.alsa.rules = [ { matches = [ { node.name = "~alsa_output.*" } { node.name = "~alsa_input.*" } ] actions = { update-props = { session.suspend-timeout-seconds = 0 } } } ]
```
上面这一段没有在本机跑过。属性名确认过在 0.5 的脚本里还被读取, 文件格式照抄的是官方样例,但「改完到底生效没有」这一步没验。
怎么验证
① 看数字
```bashpw-top -b -n 3 # ERR 列
```
② 看配置有没有落到设备上
```bashpw-metadata -n settings | grep -iE 'quantum|rate'
```
本机现在的输出:
```plaintextupdate: id:0 key:'clock.rate' value:'48000' update: id:0 key:'clock.quantum' value:'1024' update: id:0 key:'clock.min-quantum' value:'256' update: id:0 key:'clock.max-quantum' value:'2048'
```
③ 看守护进程有没有抱怨
```bashjournalctl --user -u wireplumber --since '14 days ago' | grep -iE 'lua|not supported'
```
出现 `Old configuration file detected`,就说明那个文件是死的。
④ 回头看内核参数
```bashcat /sys/module/snd_hda_intel/parameters/power_save # 10 cat /sys/module/snd_hda_intel/parameters/power_save_controller # Y
```
这两个值本机至今没有改过。省电那一层本来是靠 WirePlumber 兜的, 而那份配置又没生效,所以这一层目前是空的。
现在的状态(2026-09-20 实测)
| 项 | 值 |
|---|---|
`pw-top` 的 ERR(两个活动节点) |
0 |
`clock.min-quantum` |
256(原为 32) |
| 用户组 | 含 `realtime` |
| 近 14 天 pipewire 日志里的 xrun | 0 条 |
近 14 天 wireplumber 的 `link failed` |
11 条 |
`snd_hda_intel power_save` |
10(未改) |
| CPU governor | powersave(未改) |
`link failed` 那条线还活着,说明候选 4 没有结。不过 ERR 是 0、日志里也没有 xrun,
所以它现在没有表现为听得见的爆音。
排错
| 现象 | 可能原因 | 下一步 |
|---|---|---|
| ERR 一直涨 | 缓冲区喂不上 | 先看 `pw-top` 的 QUANT 是不是太小,再提高 `min-quantum` |
| 只在游戏里出现 | Proton 的音频延迟 | Steam 启动项加 `PULSE_LATENCY_MSEC=60 %command%` |
| 改了配置没变化 | 服务没重启 | `systemctl --user restart wireplumber pipewire pipewire-pulse` |
| 配置写了却没生效 | 格式不对,0.5 不读 Lua | 看 `journalctl --user -u wireplumber` 有没有抱怨 |
| ERR 是 0 但还有杂音 | 不是 xrun 引起的 | 问题不在缓冲区,往硬件、线材、DSP 链查 |
参考
- `
pw-top` / `pw-metadata` / `wpctl`,都是 PipeWire 与 WirePlumber 自带的命令 - `
/etc/security/limits.d/99-realtime-privileges.conf`,`realtime` 组的权限来源 - `
/usr/share/wireplumber/wireplumber.conf.d/alsa-vm.conf`,0.5 配置格式的官方样例 - `
/usr/share/wireplumber/scripts/node/suspend-node.lua`,`session.suspend-timeout-seconds` 的读取处 - Arch Wiki: PipeWire