v1.3.2: 面板补 favicon + 修响度曲线被压; 纠正"物理输出对齐虚拟声卡"的错误假设
面板: - 加内联 SVG favicon(深底 + 五根声波柱), 之前没有 favicon 浏览器显示默认破图标 - 响度归一化卡片: 高度 184 装不下(标题22+推子22+文字行20+间距 ≈ 70px), 而 #ln 只扣 46px -> canvas 溢出、上沿被「目标/判据」那行文字压住。改: 卡片 210px、#ln 扣 74px、 网格 430->456(把这 26px 补给上面两个电平表, 免得它们被压矮)。实测无重叠, #ln 126px ★ 重要纠正(2026-09-14 老板当场抓出): - 物理输出音量是**档位**, 不是"越大越响" —— 响度由 DSP 归一化保证(实测 0.35 就很好) - 上一版错误地把它"对齐"到虚拟声卡(1.0) -> 进削波保护、动态被压扁: 面板波形顶满 而听感反而更小(老 bug 复现)。已撤掉三处: · webui.py 桥启动的"对齐一次" -> 只兜静音 · 音频守护.py 第9项"物理≠虚拟就纠" -> 只在静音/近乎归零(<0.02)时才动手 · fix_phys_volume() 不再写正常档位 - config/组件配置单.json: 改记「档位 0.35」+ 写明教训
This commit is contained in:
+4
-3
@@ -59,9 +59,10 @@
|
||||
"匹配": "alsa_output.usb-EDIFIER_Technology_EDIFIER_Fit900NB_4250315939393214-00.iec958-stereo",
|
||||
"_匹配说明": "USB 耳机重启后 profile 可能在 iec958-stereo / analog-stereo 之间变, 名字会变; 所以按 EDIFIER 前缀匹配即可",
|
||||
"_路由": "数字(S/PDIF) 优先, 96kHz/24bit(S24LE); 实测音质优于模拟",
|
||||
"音量应与虚拟声卡一致": true,
|
||||
"容差": 0.03,
|
||||
"_音量说明": "★2026-09-14 教训: 断电隔夜重启后本级会从 WirePlumber 存档恢复成旧值(实测 0.35, 而虚拟声卡是 1.0), 链上每一项都对却声音凭空小 9 dB。约定 = 虚拟声卡是**音量真源**、这里是**执行器**, 两者应一致; 虚拟声卡自身 <0.1 时不判(那是真被调小了, 别乱纠)"
|
||||
"档位": 0.35,
|
||||
"_档位说明": "★★ 2026-09-14 修正(重要): 本级音量是一个**档位**, 实测 0.35 已经很响(响度由 DSP 归一化保证), **不是**\"越小越弱、越大越响\"。曾经错误地把它对齐到虚拟声卡(1.0) → 进削波保护、动态被压扁: 面板波形顶满而听感反而更小(老 bug 复现, 老板当场抓出)。看门狗只允许兜\"静音\"和\"近乎归零(<0.02)\", **绝不改正常档位**",
|
||||
"静音应被解除": true,
|
||||
"近乎归零阈值": 0.02
|
||||
},
|
||||
|
||||
"上混链": {
|
||||
|
||||
+11
-4
@@ -4,6 +4,11 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Collaplex 音效</title>
|
||||
<!-- ★ 图标(2026-09-14): 原来没有 favicon, 浏览器标签页显示默认破图标。
|
||||
用内联 SVG data URI: 深色圆角底 + 五根声波柱(与"音效"主题一致)。
|
||||
data URI 里的 # 必须写成 %23; 零文件依赖, 不占额外请求。 -->
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='7' fill='%230f0f0f'/><g fill='%234ade80'><rect x='5' y='13' width='2.6' height='6' rx='1.3'/><rect x='10.5' y='9' width='2.6' height='14' rx='1.3'/><rect x='16' y='12' width='2.6' height='8' rx='1.3'/><rect x='21.5' y='9' width='2.6' height='14' rx='1.3'/><rect x='27' y='13' width='2.6' height='6' rx='1.3'/></g></svg>">
|
||||
<link rel="apple-touch-icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='7' fill='%230f0f0f'/><g fill='%234ade80'><rect x='5' y='13' width='2.6' height='6' rx='1.3'/><rect x='10.5' y='9' width='2.6' height='14' rx='1.3'/><rect x='16' y='12' width='2.6' height='8' rx='1.3'/><rect x='21.5' y='9' width='2.6' height='14' rx='1.3'/><rect x='27' y='13' width='2.6' height='6' rx='1.3'/></g></svg>">
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0f0f0f; --card:#141414; --card2:#1a1a1a; --bd:#262626; --bd2:#333;
|
||||
@@ -51,7 +56,7 @@
|
||||
/* ★ 2026-09-13 加"响度归一化"框(固定 150px)后, 原 260px 会把两个电平表压成
|
||||
一条 10px 的缝(实测截图确认) —— 行高抬到 430, 两个电平表各得约 130px。 */
|
||||
.flow{display:grid;grid-template-columns:44% 4.5% 4.5% 15% 4.5% 6% 17% 4.5%;gap:0;
|
||||
align-items:stretch;height:430px;overflow:hidden;margin:0 16px 0}
|
||||
align-items:stretch;height:456px;overflow:hidden;margin:0 16px 0}
|
||||
.col{min-width:0;min-height:0;overflow:hidden}
|
||||
.meters{display:flex;flex-direction:column;gap:10px}
|
||||
.meter{flex:1;background:var(--card);border:1px solid var(--bd);border-radius:12px;
|
||||
@@ -72,8 +77,10 @@
|
||||
.lg i{width:9px;height:2px;border-radius:1px;display:inline-block}
|
||||
|
||||
/* ---------- 响度归一化: 推子 + 增益历史 ---------- */
|
||||
/* 高度 150 → 184: 框里加了「目标响度」滑块那一行, 不加高会把历史曲线 canvas 挤扁 */
|
||||
.lnbox{flex:0 0 auto;height:184px}
|
||||
/* ★ 2026-09-14 修: 高度 184 装不下(标题 22 + 推子 22 + 文字行 20 + 间距 = ~70px),
|
||||
而 #ln 只扣了 46px → canvas 溢出、上沿被「目标/判据」那行文字压住。
|
||||
现在: 卡片加高到 210, 扣减改 74px(与实际占用一致), canvas 归位并为 0dB 居中。 */
|
||||
.lnbox{flex:0 0 auto;height:210px}
|
||||
.lntgt{margin:6px 0 0;font-size:11px}
|
||||
.lntgt input[type=range]{flex:1;min-width:0}
|
||||
.lnfader{margin:8px 1px 0}
|
||||
@@ -87,7 +94,7 @@
|
||||
border-radius:50%;background:#e5e5e5;border:2px solid #0f0f0f;
|
||||
box-shadow:0 0 0 1px var(--bd);transition:left .12s linear}
|
||||
.lnfoot{display:flex;justify-content:space-between;font-size:11px;color:var(--mut);margin-top:6px}
|
||||
#ln{width:100%;display:block;height:calc(100% - 46px);min-height:30px;margin-top:5px}
|
||||
#ln{width:100%;display:block;height:calc(100% - 74px);min-height:60px;margin-top:6px}
|
||||
|
||||
/* 竖排开关 */
|
||||
.vsw{background:var(--card);border:1px solid var(--bd);border-radius:12px;cursor:pointer;
|
||||
|
||||
+6
-8
@@ -722,16 +722,14 @@ def vol_bridge_tick() -> str | None:
|
||||
# ★ 不能采信 VOL_FILE(存档): 重建/别处改动之后实况可能与存档不一致,
|
||||
# 那桥会把这个差值当成"菜单音量"折算一次 → 音量无故跳一下(2026-09-13 教训)。
|
||||
BRIDGE_REF = v if v > 0.01 else 0.35
|
||||
# ★★ 启动时顺手把**物理输出对齐一次**(2026-09-14 教训): 断电隔夜重启后物理输出会
|
||||
# 从 WirePlumber 存档恢复成旧值(实测 0.35 而虚拟声卡是 1.0) —— 桥只"监视变化",
|
||||
# 虚拟没变它就永远不动物理, 于是整晚/整天的声音都小 9 dB。启动时对齐一次即可。
|
||||
p0, pm0 = vol_of(BRIDGE_PID, timeout=4)
|
||||
# ★★ 2026-09-14 修正: **不要**在这里把物理输出"对齐"到虚拟声卡 —— 物理输出音量
|
||||
# 是一个**档位**(WirePlumber 存档里多少就是多少), 响度由 DSP 归一化负责。
|
||||
# 上一版就是这么把老板调好的 0.35 顶到 1.0 的, 结果进削波保护、动态被压扁:
|
||||
# 面板波形顶满而听感反而更小(老 bug 复现)。启动时**只兜静音**, 档位一律不动。
|
||||
_p0, pm0 = vol_of(BRIDGE_PID, timeout=4)
|
||||
if pm0:
|
||||
sh("wpctl", "set-mute", str(BRIDGE_PID), "0", timeout=4)
|
||||
return "启动同步: 物理输出原本静音 → 已解除"
|
||||
if p0 >= 0.0 and abs(p0 - BRIDGE_REF) > 0.02:
|
||||
msg = set_speaker(BRIDGE_REF)
|
||||
return "启动同步: 物理输出 %.3f → %.3f (对齐虚拟声卡) — %s" % (p0, BRIDGE_REF, msg)
|
||||
return "启动检查: 物理输出原本静音 → 已解除(档位不动)"
|
||||
return None
|
||||
ref = BRIDGE_REF
|
||||
p, _pm = vol_of(BRIDGE_PID, timeout=4)
|
||||
|
||||
@@ -334,21 +334,19 @@ def check_all(cfg: dict[str, Any]) -> list[str]:
|
||||
if not panel_ok():
|
||||
probs.append("🟢 面板 8788 无响应")
|
||||
|
||||
# 9) 物理输出音量 vs 虚拟声卡 —— 2026-09-14 教训: 断电重启后物理输出会从
|
||||
# WirePlumber 存档恢复成旧值(实测 0.35), 而虚拟声卡是 1.0, 链上全对
|
||||
# 却声音凭空小 9 dB。本项目约定「虚拟声卡 = 音量真源, 物理输出 = 执行器」,
|
||||
# 两者该一致。虚拟声卡自己很低(<0.1)时不判 —— 那是真被调小了, 别乱纠。
|
||||
vs = node_ids(want_sink)
|
||||
# 9) 物理输出被静音 / 近乎归零
|
||||
# ★★ 2026-09-14 修正: 上一版按"物理输出应等于虚拟声卡"去纠, 结果把老板调好的 0.35
|
||||
# 顶到 1.0 → 进削波保护、动态被压扁 —— 面板"波形顶满"而听感反而更小(老 bug 复现)。
|
||||
# 实际约定: **物理输出音量是个档位**(存档里多少就是多少, 0.35 就很响), 响度由 DSP
|
||||
# 归一化保证。所以这里只兜"静音 / 近乎归零"这种明显故障, **绝不改档位数值**。
|
||||
pn = phys_name()
|
||||
if vs and pn:
|
||||
vv, vm = vol_of(vs[0])
|
||||
if pn:
|
||||
for pid in node_ids(pn):
|
||||
pv, pm = vol_of(pid)
|
||||
if pm:
|
||||
probs.append("🔴 物理输出 %s 被静音 —— 链上全对却没声音" % pn)
|
||||
elif not vm and vv > 0.1 and pv >= 0.0 and abs(pv - vv) > 0.03:
|
||||
probs.append("🟡 物理输出音量 %.3f ≠ 虚拟声卡 %.3f "
|
||||
"—— 多半是重启后从存档恢复的旧值, 声音会小一大截" % (pv, vv))
|
||||
elif pv >= 0.0 and pv < 0.02:
|
||||
probs.append("🔴 物理输出音量 %.3f (近乎归零) —— 只剩这一级还有问题" % pv)
|
||||
break
|
||||
|
||||
return probs
|
||||
@@ -443,19 +441,14 @@ def _do_fix(key: str, cfg: dict[str, Any]) -> str:
|
||||
|
||||
|
||||
def fix_phys_volume(cfg: dict[str, Any]) -> str:
|
||||
"""把物理输出对齐到虚拟声卡。
|
||||
"""兜"物理输出被静音 / 近乎归零"。
|
||||
|
||||
本项目的音量真源是**虚拟声卡**(面板滑块、菜单媒体键、系统设置都写它), 物理输出
|
||||
只是执行器。断电重启后物理输出可能从 WirePlumber 存档恢复成旧值(2026-09-14 实测
|
||||
0.35 vs 虚拟声卡 1.0), 于是链上每一项都对却声音小 9 dB —— 就是这一项在兜底。
|
||||
★★ 2026-09-14 修正: 本函数**不再把物理输出"对齐"到虚拟声卡** —— 那是错的假设。
|
||||
物理输出音量是一个**档位**(WirePlumber 存档里多少就是多少, 实测 0.35 就很响),
|
||||
响度由 DSP 归一化负责。把它顶到 1.0 会进削波保护、动态被压扁: 面板波形顶满
|
||||
而听感反而更小(老板当场抓出来的老 bug 复现)。所以这里只解除静音, 且只在
|
||||
"近乎归零"时把档位救到一个安全值, 正常档位一律不动。
|
||||
"""
|
||||
want = str((cfg.get("默认输出") or {}).get("必须是", "cinema_spatial_up_sink"))
|
||||
vs = node_ids(want)
|
||||
if not vs:
|
||||
return "! 找不到虚拟声卡 %s" % want
|
||||
vv, vm = vol_of(vs[0])
|
||||
if vm or vv <= 0.01:
|
||||
return "! 虚拟声卡本身就不对(音量 %.3f, 静音=%s), 该先修它" % (vv, vm)
|
||||
pn = phys_name()
|
||||
if not pn:
|
||||
return "! 找不到物理输出"
|
||||
@@ -464,12 +457,17 @@ def fix_phys_volume(cfg: dict[str, Any]) -> str:
|
||||
return "! 物理输出节点不存在"
|
||||
pid = pids[0]
|
||||
before, bm = vol_of(pid)
|
||||
if bm:
|
||||
sh("wpctl", "set-mute", str(pid), "0")
|
||||
sh("wpctl", "set-volume", str(pid), "%.3f" % vv)
|
||||
after, am = vol_of(pid)
|
||||
if am or abs(after - vv) > 0.03:
|
||||
return "! 物理输出写不进去(仍 %.3f, 目标 %.3f)" % (after, vv)
|
||||
return "物理输出 %.3f%s -> %.3f (对齐虚拟声卡)" % (before, "(原本静音)" if bm else "", after)
|
||||
if am:
|
||||
return "! 静音解不掉(仍静音)"
|
||||
return "物理输出原本静音 -> 已解除(音量 %.3f 不动)" % after
|
||||
if 0.0 <= before < 0.02:
|
||||
sh("wpctl", "set-volume", str(pid), "0.35")
|
||||
after, _am = vol_of(pid)
|
||||
return "物理输出 %.3f 近乎归零 -> 救回安全档位 %.3f" % (before, after)
|
||||
return "物理输出 %.3f 正常, 无需处理(档位不归看门狗管)" % before
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
Reference in New Issue
Block a user