6.2 KiB
6.2 KiB
Cinema Spatial —— 耳机上的电影院空间音频
把多声道音轨(5.1/7.1)用 HRTF 渲染成双耳,耳机上得到接近电影院 C 位的环绕包围感。 立体声源会先经过矩阵上混(杜比 Pro Logic 原理)再渲染,所以浏览器、音乐播放器同样有空间感。
原理
多声道源 ──┐
├─> [矩阵上混 5.1] ─> 6 路 ─> [每路与人头脉冲响应卷积] ─> 双耳混音 ─> 耳机
立体声源 ──┘ (SADIE-II 真人 HRTF)
- HRTF(Head-Related Transfer Function,头相关传输函数):同一方向的声音到达两耳时, 会被人头、耳廓滤波,产生不同的时间差、强度差和频谱染色 —— 大脑靠这些线索判断方位。 把这些滤波做成脉冲响应(IR),与声道信号卷积,就能在耳机上"伪造"出方位。
- 虚拟声卡:用 PipeWire 的
filter-chain模块建两个Audio/Sink,程序把声音送进来, 内部渲染完再输出到真实设备。全局生效,任何播放器都能用。
特性
- 全链路 96kHz / 24bit(HRTF 采用 SADIE-II
96K/24bit/512tap真人数据) - 立体声源自动矩阵上混,不需要多声道片源
- 提供 deb 包,系统级安装
- 纯脚本 + 数据,无编译,
Architecture: all
安装
方式一:deb 包
sudo dpkg -i collaplex-cinema-spatial_1.0.0_all.deb
# 依赖: pipewire libmysofa1 mpv
配置装在 /usr/share/pipewire/pipewire.conf.d/,对所有用户生效。
生效:systemctl --user restart pipewire pipewire-pulse wireplumber(或注销重登)
方式二:从源码
# 1. 下载 HRTF 数据集(不随仓库分发)
mkdir -p sofa && cd sofa
curl -L -O https://zenodo.org/records/12092466/files/H4_HRIR_SOFA.zip
unzip H4_HRIR_SOFA.zip && cd ..
# 2. 从 SOFA 提取脉冲响应(12 个 = 6 方向 x 左右耳)
bash 换HRTF.sh "$PWD/sofa/H4_HRIR_SOFA/H4_HRIR_SOFA/H4_96K_24bit_512tap_FIR_SOFA.sofa" H4-96k -16
ln -sfn H4-96k hrir/current
# 3. 生成 PipeWire 配置
python3 生成配置.py
systemctl --user restart pipewire pipewire-pulse wireplumber
用法
空间音频 开 # 全局切到立体声上混版(推荐)
空间音频 开5.1 # 全局切到 5.1 直通版
空间音频 关 # 切回物理设备
空间音频 状态 # 查看两个虚拟声卡和当前默认
mpv-影院 电影.mkv # 只给 mpv 用, 不依赖全局设置(适合 A/B 对比)
也可以直接在桌面环境的"声音设置"里选 电影院空间音频。
文件说明
| 文件 | 作用 |
|---|---|
空间音频 |
开关命令 |
mpv-影院 |
mpv 专用的 HRTF 播放包装 |
生成配置.py |
生成 PipeWire filter-chain 配置 |
换HRTF.sh |
从 SOFA 提取脉冲响应(可换任意 HRTF 模型) |
提取HRIR.sh |
早期版本(已被 换HRTF.sh 取代) |
打包deb.sh |
打包 collaplex-cinema-spatial |
验收测试.sh |
安装后自动验证 |
demo.sh / 响度校准.sh / 方向测试.sh |
开发期验证工具 |
对比分析.py / 排查.sh |
开发期排查工具 |
踩过的坑(均为实测)
- 增益必须按"整链"算:矩阵上混出来的 6 路会在混音器里相加,实测整链净增益约 +10.5dB。 只按单路脉冲响应峰值校准,一听真实内容就削波爆音。
- 校准素材必须接近满刻度:自造测试信号(峰值 -12dB)与真实母带(≈0dBFS)差 10dB 以上, 用它校出的增益必然炸。
- 不同 SOFA 的电平差异极大:同一数据集内,D1(假头)IR 峰值 -11.6dB、H4(真人)-3.1dB。 换模型必须重新量峰值,目标压在 -12dB 左右。
sofalizer按 SOFA 自身的采样率输出:不管输入 48k 还是 96k,只要 SOFA 是 44.1k, 输出就掉到 44.1k —— 这是"采样率偏低"的根因。要么在滤镜链尾aresample拉回, 要么用原生高采样率的数据集。convolver按当前时钟率重采样脉冲响应:主时钟还是 48k 的话,96k/512tap 的 IR 会被降成等效 256tap,等于白换。必须把default.clock.rate一起提到 96k。node.passive = true必须配node.target:否则 filter-chain 的输出端不连任何设备, 声音凭空消失。- ffmpeg 滤镜表达式里的
,和|要用单引号包住,否则会被当分隔符解析。 adelay+join合成多声道素材不可靠:曾静默产出 0.25 秒的单声道文件。 改用aevalsrc直接合成。type=time是时域卷积(保真);默认的type=freq走 FFT,会削高频。- ★ 测试素材必须匹配真实内容的"峰值因子":用平稳的粉噪做压力测试,看着余量充足, 一放电影里爆炸/撞击那种高峰值因子瞬态就削 —— 同样听感响度下,瞬态峰值能再高出 10dB 以上。 校准要用真实片源,或至少混入冲击型素材。
- ★ 每一层音量都吃动态余量,改一处就要记账:虚拟声卡音量与设备(耳机)音量 串联在同一条链上。曾把设备音量从 0.66 提到 1.00(+3.6dB)却没算进余量账, 结果一有瞬态就削。设备自身的出厂音量是预留余量,不要顺手拉满。
- ★ USB 音频设备重启后 profile 会变(
iec958-stereo↔analog-stereo), 设备node.name随之后缀改变 → 配置里记的node.target失效 → WirePlumber 回落到其它输出 (实测落到 HDMI,表现为"没声音"或"声音从显示器出来")。 本项目的空间音频 开已带自检:设备名失效时自动重新探测并重建配置。 - 判定设备是否存在不能用
pw-cli info:它对不存在的名字也返回成功。要用pw-dump按node.name精确比对。
数据来源
HRTF 采用 SADIE II
(University of York,Zenodo DOI 10.5281/zenodo.12092466),
使用其中真人受试者 H4 的 96K/24bit/512tap 版本。
引用方式见数据集页面。数据集不随本仓库分发,请从 Zenodo 自行下载。
License
MIT