让 PipeWire 不再爆音

偶发的电音难在复现,也难在确认修好没有。把听感换成 pw-top 上的 ERR 计数之后,两次定位都有了依据:第一次是 memlock 权限,第二次是一份 WirePlumber 压根没读的配置文件。

音乐播着播着会突然「啪」一下,或者有那么一两秒像电流声,然后又正常了。

间歇出现,几分钟到几小时一次。重启没用,换线没用,换播放器也没用。

这类问题的麻烦有两层:复现不了,也没法确认修好了没有。

这个教程只是提供我自己的解决方案 实际效果因系统而异,不能确保可以 100% 解决你遇到的问题

先决条件

需要 说明
PipeWire 本文的命令都是它自带的:`pw-top` / `pw-metadata` / `wpctl`
能改 `/etc/security/limits.d/` 第一次修复动的是这里,要 root
能重新登录 组权限只在登录时读一次

这套做法在什么环境上验证过

```plaintext
CachyOS (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` 两档

三个自查命令:

```bash
pw-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` 是批处理模式,跑三次就退出,把输出留在终端里:

```bash
pw-top -b -n 3
```
```plaintext
S   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 显示的是 `-`, 意思是没有实时优先级。

顺着权限往下查:

```bash
groups                                    # 用户组里有没有 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` 组。

修复

```bash
sudo 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 的日志里有这么两行:

```plaintext
wireplumber: 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` 就是现成样例:

```plaintext
monitor.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 的脚本里还被读取, 文件格式照抄的是官方样例,但「改完到底生效没有」这一步没验。

怎么验证

① 看数字

```bash
pw-top -b -n 3                            # ERR 列
```

② 看配置有没有落到设备上

```bash
pw-metadata -n settings | grep -iE 'quantum|rate'
```

本机现在的输出:

```plaintext
update: 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'
```

③ 看守护进程有没有抱怨

```bash
journalctl --user -u wireplumber --since '14 days ago' | grep -iE 'lua|not supported'
```

出现 `Old configuration file detected`,就说明那个文件是死的。

④ 回头看内核参数

```bash
cat /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 链查

参考