Collaplex 音效
耳机方向的空间音效链路(PipeWire)。
链路
全局音频(应用输出) → collaplex_vsink(虚拟声卡) → 响度归一化(pipe DSP)
→ collaplex_hrtf_in(双耳 HRTF + 房间混响, 湿量 0.3)
→ EQ(三段) + 总音量放大(+6 dB)
→ 数字输出(S/PDIF)
安装
bash 脚本/安装.sh # 默认: 混响湿量 0.3, 总音量 +6 dB
VOL_DB=9 bash 脚本/安装.sh # 总音量改成 +9 dB
WET=0.4 bash 脚本/安装.sh # 混响湿量改成 0.4
脚本:探测数字输出 → 部署 pipe 插件入口 → 渲染两份配置 → 清掉残留 DSP(占住共享槽位会让新实例起不来) → 重载 PipeWire → 自检(4 个节点 + 2 个 DSP 进程)。
调 EQ
pipewire/20-collaplex-hrtf.conf 里 param_eq 节点(左右各一个, 两处一起改):
filters1 = [ { type = bq_lowshelf freq = 120 gain = 0.0 } # 低频架
{ type = bq_peaking freq = 1000 gain = 0.0 q = 1.0 } ] # 中频峰
filters2 = [ { type = bq_highshelf freq = 6000 gain = 0.0 } ] # 高频架
gain 单位 dB(正=提升), 改完重跑 安装.sh。
★ 坑: param_eq 的端口名带编号 —— In 1 / Out 1(走 filters1)、In 2 / Out 2(走 filters2)。
写成 :In / :Out 会报 unknown input port 并让整张图加载失败(模块静默降级成直通, 节点都不建)。
控制面板
bash 脚本/面板.sh # http://127.0.0.1:8789
- 电平归一化(响度目标)、总音量、32 段 EQ 全部是滑块:滚轮调节、Shift+滚轮微调(0.1 dB)、双击归零
- EQ 是 1/3 倍频程 32 段(20 Hz … 25 kHz),每段 ±15 dB,实时生效(DSP 从共享内存读参数,不用重装)
- 参数落点:
/dev/shm/collaplex-eq(前端写、DSP 读,版本号保证不成半更新);响度目标写在~/.local/state/cinema-spatial/loudness.json(归一化 DSP 每秒重读)
关于"放大"与削波(2026-09-19 修)
总音量级的输出不能硬削(原实现是 np.clip(out, -1.0, 1.0)): 硬削把波形削成平顶,
听感就是"炸麦"; 而被削掉的峰值不增加响度, 所以出现"声音没变大、却削峰了"。
改用软限幅 dsp/eq.py: soft_limit(): 上限 -1 dBFS, 幅值在 (上限−拐点) 以内完全透明,
超出部分用 tanh 平滑渐近上限 —— 不产生平顶, 波形其余部分不动, 响度真实提升。
实测(峰值 -12 dBFS 素材, 总音量 +12 dB): 输出峰值 -1.01 dBFS, 贴顶样本自 64.6% → 0%。
第二处放大 bug(2026-09-19 修)
20-collaplex-hrtf.conf 里残留了一级固定 +6 dB(volL/volR mixer)和一段用不上的 param_eq
—— 它和面板的总音量叠了两级, 把归一化守住的 −3 dBFS 顶到 +3 dBFS 再送进 EQ,
软限幅被迫一直工作, 听感就是"响度上不去、动态还被压"。已删除, 音量只由面板控制。
逐级实测排除法: 归一化输出峰值 ≤ −3.17 dBFS(从不超 0, 清白); HRIR 峰值 −17 dB、房间混响 IR −16.5 dB, 都不可能推高峰值 → 元凶就是那句残留增益。
修后端到端(峰值 −12 dBFS 素材):
| 总音量 | 出 RMS | 提升 | 出峰值 |
|---|---|---|---|
| +0 dB | −22.97 dBFS | — | −12.28 dBFS |
| +6 dB | −16.98 dBFS | +5.99 dB | −6.35 dBFS |
| +12 dB | −11.77 dBFS | +11.20 dB | −1.11 dBFS |
中低频沙沙声的根因(2026-09-19 修)
reverb/房间混响IR-96k.wav 是未归一化的原始 IR: 它的频响在 100~200 Hz 比中高频高约 20 dB
(|H| = 8.9 / 10.6, 而 8 kHz 只有 0.26)。湿路径(×0.3)因此在中低频 +10 dB, 与干信号相干相加后
整个 HRTF 级 +19 dB —— 归一化守住的 −14 dBFS 被顶到 +5 dBFS, EQ 的软限幅于是一直在工作,
生成 3 次谐波与宽带失真, 听感就是中低频沙沙。
修法: 给房间混响的 convolver 加线性增益 gain = 0.126(−18 dB)。
★ 坑: convolver 的 gain 是线性倍数, 不是 dB —— 写 -18.0 会变成"反相 ×18", 电平反而更高。
修后实测: EQ 入峰值 +5.16 → −15.57 dBFS; 600 Hz 谐波 −9.9 → −28.9 dB; 软限幅不再触发。
残留(未修, 待定): 归一化的增益是逐块更新的, 块与块之间的台阶仍会产生约 −29 dB 的谐波。 要彻底消掉需要做块间增益插值(逐样本平滑过渡)。音乐内容下一般被掩蔽, 放纯音仔细听才察觉得到。
实测(2026-09-19)
| 指标 | 数据 |
|---|---|
| 响度归一化 | 输入差 20.00 dB → 输出端差 0.00 dB(推子 −6.28 / +13.72) |
| HRTF 双耳化 | 出口左右相关系数 0.679(直通 = 1.000) |
| 混响 | 脉冲尾巴拖到 1.2 s(湿量 0.3) |
| 总音量 | 出口电平 −21.5 → −16.2 dBFS(+5.3 dB) |
试听
pw-play --target collaplex_hrtf_in 某个.wav # 只走 HRTF 核心(单曲)
全系统:默认输出就是 collaplex_vsink,Chrome/播放器/游戏直接出声即走整条链。
文件
| 路径 | 作用 |
|---|---|
dsp/loudness_norm.py |
响度归一化 DSP(pipe 插件载荷, 左右各一进程) |
dsp/状态.py |
读共享内存看实时推子/电平 |
bin/collaplex-loudness-norm |
pipe 插件入口模板(必须是 ASCII 路径) |
pipewire/10-collaplex-vsink.conf |
虚拟声卡 + 响度归一化 |
pipewire/20-collaplex-hrtf.conf |
HRTF 核心 + 混响 + EQ + 总音量 |
hrir/ reverb/ |
HRIR(12 个)与房间 IR |
脚本/安装.sh |
渲染 + 部署 + 重置 DSP + 自检 |