第二批反馈:引擎改造(限幅/SoundTouch不变调变速/长按快进/跳句/iOS闪光降级) + 6游戏状态BGM与试玩测试全绿

This commit is contained in:
SoundGame Dev
2026-08-30 16:45:42 +08:00
parent cc0f8c008d
commit 9a915d62c0
1163 changed files with 6346 additions and 208 deletions
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""爆音资产排查(R6):扫描 6 游戏全部 .ogg。
检测项:
1. 峰值 > -1dBFS(有损编码过冲/归一不足)
2. 首尾 5ms 内电平突变(无淡入淡出 → 爆豆声)。loop 类(bgm/ 与 sfx/amb_*)按
首尾交叉淡化工艺制造,边界连续是特性,只做 DC/峰值检查,不报爆豆、不修。
3. 直流偏置(|DC| > 0.001)
输出 JSON 行到 stdout 供汇总;--fix 模式见 fix_clicks.py 逻辑(本文件只扫)。
"""
import os
import sys
import numpy as np
import soundfile as sf
ROOT = "/home/ubuntu/SoundGame/web"
GAMES = ["storyline", "escape", "simon", "rhythm", "space", "rpg"]
EDGE_MS = 5
DC_THRESH = 0.001
PEAK_THRESH = 10 ** (-1.0 / 20) # -1dBFS
def is_loop(relpath):
parts = relpath.split(os.sep)
return "/bgm/" in "/" + relpath or os.path.basename(relpath).startswith("amb_")
def scan(path):
y, sr = sf.read(path, always_2d=True)
mono = y.mean(axis=1)
peak = float(np.max(np.abs(mono)))
dc = float(np.mean(mono))
n5 = max(1, int(sr * EDGE_MS / 1000))
n1 = max(1, int(sr * 1 / 1000))
n50 = max(1, int(sr * 50 / 1000))
head = float(np.max(np.abs(mono[:n5])))
tail = float(np.max(np.abs(mono[-n5:])))
# 爆豆判定(突变而非渐变):首/末 1ms 内的最大幅值 与 相邻 50ms 窗口峰值之比。
# 正常淡入(8ms cos²)首 1ms 幅值 < 局部峰值 15%;硬边/步进则趋近 1。
loc_h = float(np.max(np.abs(mono[:n50])))
loc_t = float(np.max(np.abs(mono[-n50:])))
FLOOR = 0.01 # 局部窗口低于 -40dB 视为静音区,不判爆豆(编解码底噪残余)
step_head = loc_h > FLOOR and float(np.max(np.abs(mono[:n1]))) > 0.15 * loc_h
step_tail = loc_t > FLOOR and float(np.max(np.abs(mono[-n1:]))) > 0.15 * loc_t
return {
"path": os.path.relpath(path, ROOT),
"loop": is_loop(os.path.relpath(path, ROOT)),
"peak_db": round(20 * np.log10(max(peak, 1e-9)), 2),
"dc": round(dc, 5),
"head5ms_db": round(20 * np.log10(max(head, 1e-9)), 2),
"tail5ms_db": round(20 * np.log10(max(tail, 1e-9)), 2),
"hot_peak": peak > PEAK_THRESH,
"dc_bad": abs(dc) > DC_THRESH,
"click_head": step_head,
"click_tail": step_tail,
}
def main():
results = []
for g in GAMES:
base = os.path.join(ROOT, g, "audio")
for dirpath, _d, files in os.walk(base):
for f in sorted(files):
if f.endswith(".ogg"):
results.append(scan(os.path.join(dirpath, f)))
import json
for r in results:
print(json.dumps(r, ensure_ascii=False))
return 0
if __name__ == "__main__":
sys.exit(main())