20 Commits

Author SHA1 Message Date
糖糖 d331128e88 输出设备不写死: 面板设备选择 + 自动挂载守护(治开机后"双重混响")
- 问题: 30-collaplex-eq.conf 的 node.target 是安装时探测一次写死的。开机时设备若还没枚举
  (USB 耳机晚到很常见: 09-24 实测开机 08:57、耳机 09:19:43 才出现; 09-23 是 21:01 → 21:03),
  建链时目标不存在 → 末段连到别处或干脆不连, 设备出现后不会自己改回来 → 听感"双重混响",
  只能 stop + start
- 新增 web/output_route.py: 设备清单(pw-dump) + 选择落盘(output.json) + 守护线程(每 3 秒核对
  collaplex_eq_out 只连选中设备, 不对就拆错的、按声道补对的) → 设备一出现自动挂上
- web/server.py: GET /api/devices, POST /api/device(落盘 + 立刻挂载), 快照带 output 段,
  main() 起守护线程
- web/index.html: 「输出设备」卡片(下拉 + 应用 + 状态 + 守护备注; 设备表变化才重建选项, 免得
  60 Hz 重建打断点击)
- 脚本/安装.sh: 优先用记住的设备(不在位也照样按它建链), 不再"猜第一个 digital"
- 实测: 面板真点应用切走再切回 ✓ / 连错 2 秒自动纠回 ✓ / 全拆 3 秒自动挂回 ✓ / start 自检
  打印 [在位] ✓ / 面板像素验收 ✓; pyright strict 0 errors 0 warnings
- 坑: pw-dump 的 Link 键名是 output-port-id / input-port-id(写 output-port 一条连线都读不到);
  面板服务 enable --now 不会重启已在运行的实例(改 web/*.py 必须 restart)
2026-09-24 09:36:36 +08:00
lou 117a0c65d6 stop: 清锁改用通配 —— 锁文件是每声道一份(.lock0/.lock1), 只删基名会留残留 2026-09-23 21:11:18 +08:00
lou 717842f0a9 场所预设: 「电影院」「歌剧院」一键切 EQ + 混响
影院和歌剧院最本质的差别是混响时间(0.4~0.8 s vs 1.4~1.8 s), 光切 EQ 不够 ——
所以把这两个入口从 EQ 预设里移出来, 单独立一排「场所」按钮, 点一下同时切:

- 电影院: EQ(X-curve 高频滚降 + sub 低频) + 湿量 0.35
- 歌剧院: EQ(木质厅堂温暖 + presence) + 湿量 0.78

实现:
- PRESETS 里的两个条目搬到新的 VENUES(带 wet 字段); 抽出 applyEqPoints() 给两者共用
- 按钮互斥高亮: 点场所清掉 EQ 预设高亮, 反之亦然; 手动拖 EQ 滑块 / 湿量 / 全部归零都清
- 坑: wet 的 change handler 里是 `await post(...)` 之后才 markVenue(null), 属于异步 ——
  会在 applyVenue 点亮之后才跑, 把高亮清掉(第一轮 CDP 验收就是这么挂的)。用
  venueApplying 标志在 dispatch 那一刻记录意图, handler 据此决定清不清。
- applyVenue 里 markBusy("wet", 1500) 防 SSE 轮询把刚设的湿量弹回旧值

总音量不放进场所里 —— 那是个人听音响度, 不该切个场所就被改掉。

验收(CDP 真实点击): 点两个场所, 前端湿量与 /api/state 后端值一致(0.78 / 0.35),
EQ 32 段与设计逐位一致, 高亮 = 对应按钮名; 手动拖湿量后高亮清空。截图核过像素。
2026-09-20 17:15:59 +08:00
lou 2566307e6c EQ 预设加「电影院」「歌剧院」+ 预设按钮选中高亮
两个新预设按现实场所的声学设计, 不是随手画的微笑曲线:
- 电影院: 高频按 X-curve(ISO 2969 国际版, 2 kHz 以上约 -1.5 dB/oct, 实测 2k->4k->8k->16k
  逐段 -1.5)再叠银幕透声损失; 低频 25~60 Hz +2.5~3 dB(影院 sub / LFE 通道的体感)
  —— 厚, 暗, 有力
- 歌剧院: 中低频 150~400 Hz +2 dB(木质厅堂的温暖); 2~5 kHz +0.5~1 dB(没有扩声系统,
  歌手靠 presence 区投射, 这里不能削); 极低频 20~40 Hz -1 dB; 高频只随大空间空气吸收
  缓降 —— 暖, 亮, 有细节

两者实测差: 25~60 Hz 影院重 3.4 dB / 3.15~5 kHz 歌剧院足 3.0 dB / 8~16 kHz 歌剧院足 3.5 dB。

顺带修: `.presets button.on` 的样式一直定义了但没有任何代码加这个类 —— 点了预设没有选中
反馈。加 markPreset(): 点预设高亮对应按钮, 手动拖滑块 / 全部归零时同步更新。

验收(CDP 真实点击): 两个预设的 32 段值与设计逐位一致; 高亮 = 对应按钮名; 手动拖滑块后
高亮清空。README 加「EQ 预设的设计依据」节(含影院/歌剧院的混响湿量建议)。
2026-09-20 13:51:25 +08:00
lou 3b6ce2c57e 参数持久化: 面板改动落盘, 重启/开机后自动恢复
问题(09-20 实测复现): dsp/common.py 的 open_store() 是"文件不存在就零填充创建", 而
/dev/shm 是 tmpfs —— 重启后参数全变 0。听感上不是"参数没保存", 而是**"混响突然没了"**
(湿量 0), 极易被当成链路故障去查。

修法:
- common.py: 加 params.json 落盘(load_saved_params / save_params / restore_params);
  open_store() 检测到文件是"新建的"就从落盘恢复(带上下限夹取)。幂等 —— 两个声道进程
  同时判定为新建也没关系, 写进去的值一样。
- web/server.py: 面板每次改参数(POST /api/eq|volume|wet)后 save_params() 落盘; 值没变不写。
- 初始化.py: 改成"优先用落盘, 没有才用默认值", 并报告参数来源。

实测(rm /dev/shm/collaplex-eq* + 重启 pipewire): 参数自动恢复, 与落盘逐值一致
(总音量 +2.0 / 湿量 0.600 / EQ 非零 30 段)。

落盘位置 ~/.local/state/cinema-spatial/params.json(与 loudness.json 同目录)。
README 加「参数持久化」节 + 坑表条目。
2026-09-20 13:40:06 +08:00
lou 5c6a982210 修低频过量的四条: HRIR 均衡 + 房间 IR 校平 + LN_MAX_BOOST + EQ 平直
诊断见 README「实测(2026-09-20)」: 链路的**线性**部分干净(无驻波、失真 −116 dB),
"低频脏 + 沙沙"来自电平被顶穿之后软限幅常年介入 —— 归一化把内容提到满刻度, 宽频信号
一削, 互调产物堆在低频(听感"脏")、高频碎屑(听感"沙沙")。实测超软限幅阈值样本 50.49%、
峰值因子被从 14 dB 压到 2.0 dB。

四条修复:
1. LN_MAX_BOOST 20 -> 12(默认值): 20 dB 的提升上限让任何轻内容都被顶穿满刻度
2. HRIR 同相合并响应均衡(lowshelf 500Hz −7.2dB Q=0.707, 12 条共用一条曲线,
   方向线索 ILD/ITD 不受影响): 这套 HRIR 是原始测量数据、没做过漫反射场均衡。
   ★ 口径注意: 标准 DF-EQ 用各方向**功率平均**做参考, 只要求压 1.5 dB; 但真实内容
   L≈R 时两耳信号是**相加**的, 同相合并响应才对应实测的 7.5 dB
3. 房间 IR 低频校平(lowshelf 300Hz −21dB Q=0.70): 低频比中频高 24.5 dB -> 压到 +5.8 dB
   (留一点厚度); IR 峰值随之掉 3.7 dB, 湿路 gain 0.4 -> 0.61 补偿, 保持混响响度不变
4. EQ 微笑曲线(20~63Hz +2.5 / 高频 +3)改回平直

复测(同素材同脚本): 低频 20~200Hz **+13.6 -> +0.2 dB**; 整链峰值因子 **2.0 -> 12.2 dB**;
超阈值样本 **50.49% -> 0.00%**; 峰值/RMS −0.1/−2.1 -> −6.2/−18.4 dBFS。
响度目标回到 −14 dB 也不会再顶穿。

新增: 工具/分析均衡曲线.py(量曲线)、工具/均衡IR.py(施加校正, 可重跑);
README 加「IR 校正」节 + 修复前后对照 + 两条测量方法上的坑。
IR 备份: hrir/*.pre-eq.wav 与 reverb/*.wav.pre-eq.wav(频响校正前)、
reverb/*.orig.wav(剪直达声前)。重跑顺序: 先 重做混响IR.py, 再 均衡IR.py。
2026-09-20 13:05:10 +08:00
lou c9e9a5ac28 混响立体声丢失: 重做 IR 的工具把双声道压成了单声道 -> conf 改用 convolver 的 channel 按声道取
- 工具/重做混响IR.py 原来写 `x = x[:, 0]`, 只取左声道、把右声道丢掉, 写出去的 IR 成了
  单声道; 而 20-conf 里 revL/revR 读的是同一个文件 -> 两路湿声完全同源, 混响糊在正中央、
  没有宽度(原 IR 本身是双声道真立体声, 左右相关 0.884)
- 改为逐声道处理 + 双声道写回; 重新生成 reverb/房间混响IR-96k.wav (285888 x 2, 相关 0.9492)
- 20-conf: revL 用 channel = 0 / revR 用 channel = 1 —— PipeWire 的 convolver 支持该键,
  从多声道 IR 文件里按索引取声道。实测(通道探针: 左零右真的文件 + channel = 1)右耳尾巴
  不塌, 左右差仅 1.1 dB; 若读错声道会掉 40 dB 以上
- 新增取证脚本: 扫频测频响(Farina 反卷积, 确定性信号才测得准)、电平与失真诊断(只改电平的
  对照实验)、验证混响声道(文件级 + 物理级检查)、诊断低频沙沙(逐级旁路)
- 扫频脚本修了一个 bug: 湿量只在第一轮写, 后面几轮沿用被改过的值, 导致 B/C 两轮测的是同一配置
- README: 坑表加两条(双声道 IR + 验证判据的坑) + 2026-09-20 低频/沙沙诊断实测段
2026-09-20 12:55:51 +08:00
lou 43919f5f97 开机自启: 链路靠 PipeWire 自动加载 conf(不需要服务); 面板加 systemd --user 服务 collaplex-audio-panel.service(start 时自动装并 enable, Restart=no); 修 stop 漏杀按相对路径起的面板进程(端口被占导致新服务起不来); 打包带上 systemd/; README 加自启段 + 进程匹配要按后段的坑 2026-09-19 20:48:47 +08:00
lou a422792df2 修回声: 房间 IR 含 30ms 传播延迟(声源到麦克风 10.3m 的声程)且前两次反射与直达声同量级(30ms 处 0dB/40ms 处 -1.6dB), 与零延迟的干声相加 -> 湿声晚 30ms 还很响 = 回声; 处理 IR(裁前 22ms 静音 + 前 55ms 余弦渐入, 直达声段 0 -> -23.6dB)再把 gain 重标 0.126->0.4 补回混响量; 脉冲取证: 修前 120ms 处 -26dBFS 孤立峰(+12dB), 修后包络单调衰减无孤立峰; 新增 测试/test_echo.py 与 工具/重做混响IR.py, 原 IR 备份 .orig.wav 2026-09-19 20:32:07 +08:00
lou ddd642b772 主命令改成日常三个动作: start(接管系统音频) / stop(恢复默认输出+停面板) / web(起面板并打开浏览器); 安装脚本 /proc 进程消失竞态静默; README 与 deb 描述同步; 版本 1.0.1 2026-09-19 20:13:12 +08:00
lou 489a8a79c2 README: 第二代声明(对第一代 cinema-spatial 推倒重构) + 代际对比表(架构/EQ/响度/混响/控制/分发) 2026-09-19 20:04:12 +08:00
lou f1d6f84b2f 发布准备: 主命令 collaplex-audio(install/uninstall/status/panel) + 打包deb.sh(deb 1.9MB, Arch all, 依赖 python3-numpy/scipy, 装完不自动启用) + 安装脚本支持系统解释器(deb 场景无 venv) + 自检补湿量进程 + README 重写(链路图/面板/预设/数据来源/坑表/实测表/测试脚本清单) 2026-09-19 20:02:21 +08:00
lou 927309b1a9 README 补 SADIE II 来源(约克大学 H4, Zenodo DOI); 更新已修的残留描述(块间插值/限幅阈值); 记下'峰值失真纯音测不出'的教训 2026-09-19 19:28:21 +08:00
lou a81adb616b 修中低频沙沙: 房间混响 IR 未归一化(中低频频响 +20dB), 湿路径干湿相加使 HRTF 级 +19dB, 把归一化的 -14dBFS 顶到 +5dBFS 导致软限幅持续工作生成谐波; 给混响 convolver 加线性增益 0.126(-18dB); EQ入峰值 +5.16->-15.57 dBFS, 600Hz 谐波 -9.9->-28.9dB 2026-09-19 19:17:31 +08:00
lou b25770cd51 修第二处放大 bug: 删掉 20- 里残留的固定 +6dB 音量级和多余 param_eq(与面板总音量叠了两级, 把归一化的 -3dBFS 顶到 +3dBFS); 逐级实测排除归一化与混响; 修后 +0/+6/+12dB 输出峰值 -12.28/-6.35/-1.11 dBFS 2026-09-19 18:53:18 +08:00
lou e8b402472c 修削峰: 总音量输出级 np.clip 硬削 -> soft_limit 软限幅(上限-1dBFS, tanh 渐近); +12dB 时贴顶样本 64.6%->0%, 峰值 -1.01 dBFS 2026-09-19 18:45:12 +08:00
lou 5a4b11092c 前端: 抽公共层(dsp/common.py 共享内存协议)+ 32段EQ/总音量 DSP(dsp/eq.py, scipy sosfilt) + 本地控制面板(web/, 滑块+滚轮捕捉); 安装脚本接 30-collaplex-eq; pyright strict 0 error 2026-09-19 18:34:56 +08:00
lou f32c2cad24 核心后加 EQ(三段平直) + 总音量放大(+6dB, VOL_DB 可调); param_eq 端口须带编号(In 1/Out 1); README 补调参与坑 2026-09-19 18:24:18 +08:00
lou f1a3c80fd4 固化链路: 虚拟声卡 + 响度归一化 -> 数字输出 (安装脚本可复现部署) 2026-09-19 17:22:03 +08:00
lou 4ca821011b 初始化: collaplex音效 项目骨架 (venv + gitignore) 2026-09-19 17:07:19 +08:00