Files
SoundGame/HANDOVER.md
T
xsl ee5d4d2544 feat(playbook): 26 号交互手册全量落地——剩余 12 款深度重构 + 语音全量重制
storyline/escape/simon 首批之后的推广收官:15 款全部按 26 号模式运行
(发言权/回执/三档提示/迷茫接管/手势阶梯/断点复述),旧「双击重听/格挡/
菜单确认」等反模式口径全线废除,开场零规则念白(首句即动手)。

游戏层(12 款):rpg(格挡=前摇窗口内再单击)/ rhythm / space / radio /
dungeon / abyss / detective / cipher / teahouse / keeper / bridge /
hunt(联机切片):
- 每款 game.js 接引擎六模块 + makeInputPipeline(doubleTapWindow:0);
  四向玩法类 swipeCore + 长按帮助;双击是玩法的(space 护盾 / hunt
  旋风斩)按手势阶梯阶段 3 分档恢复双击窗口(doubletapCore)
- 每款 playtest 重写:玩法断言 + §6.2 乱点机器人(无声操作 / 被动
  原句重复 / 打断失败 = 0);15 款 3900+ 断言全绿
- 12 份 docs/design/26-apply-*.md 施工单 + tools/voice_lines_26/
  台词清单 + voice-ids 文档同步 + 转写会话剧本按新口径重写

引擎层(web/engine/,6 处修复):pipeline 增 doubletapCore;novice
teachLine 带 voice/ 前缀(此前教学句被静默丢弃);confusion.record
前置到新手门(锁定手势的乱点也计信号);InvalidHints 播完恢复解释前
状态(根治孤儿 BUSY);WaitPoint takeover(2) 台账去重;台账只在
speak 真正播出时登记。遗留引擎债登记 roadmap §3.1。

语音资产:台词表合并(新增 ~250 系统句 / 改 ~50 旧口径 / 删 ~110
废止句;合并器 tools/merge_voice_lines_26.py + 引用校验器
tools/validate_voice_refs.py),GPU 重录 1536 wav → 4204 ogg/m4a
落地,check_all_assets + validate_voice_refs 双绿;键盘变体同步扩表。

大厅/文档:15 款统一新交互口径(「新交互」徽标退役);README /
HANDOVER / rpg README 旧口径修正;roadmap P0-P3 置完成。
2026-10-02 12:33:20 +08:00

30 KiB
Raw Blame History

SoundGame 交接文档(HANDOVER)

接收方:即将接手开发、没参与过本项目的同事。读完本文 + 跟随链接即可独立上手。 本文是总入口:细节一律链接到专项文档,不复制。文中路径除注明外均为仓库相对路径(仓库根 = /home/ubuntu/SoundGame)。

0. 项目一句话

11 款纯音频 HTML5 游戏:屏幕只有封面图,操作只有 6 种手势(上/下/左/右滑、单击、双击),一切靠音频呈现。纯 vanilla JS ES module,无构建步骤、无 npm 依赖,手机浏览器打开即玩。全部对白由本地 Qwen3-TTS 双性别生成,音效由 Stable Audio 3 + 程序合成,BGM 由 Mureka API 生成。游戏阵容:6 款首发(rpg/storyline/escape/simon/rhythm/space)+《围猎》联机协作(纯音频 MMORPG 路线第一阶段切片)+ new8 四批 8 款 RPG(radio/dungeon/abyss/detective/cipher/teahouse/keeper/bridge,全部经多轮玩家代理迭代裁定可交付)。交互层按 26 号交互手册由引擎六模块统一承载(发言权/回执/三档提示/迷茫接管/手势阶梯/输入管道,web/engine/{floor,receipts,hints,confusion,novice,pipeline}.js;2026-10 全部 15 款完成深度落地,施工单见 docs/design/26-apply-*.md)。旧「向导层」guide.js 仍服务未迁移场景,其设计依据与验证过程见 research/。

1. 5 分钟上手

1.1 环境依赖清单

依赖 位置(仓库内) 说明
Node.js tools/node/bin/node 跑服务器与试玩测试;项目自带,无需装系统 node
Python venv(TTS) .venv-tts/ Qwen3-TTS 对白生成(需 GPU,13GB 模型在 models/hf/)
Python venv(SA3) tools/stable-audio-3/.venv/ Stable Audio 3 音效生成
ffmpeg tools/bin/ffmpeg 音频转码(ogg/m4a)
JDK 17 + Android SDK tools/jdk17/、tools/android-sdk/ 仅旧 Android 版用,Web 开发不需要

全部工具是用户态安装在 tools/ 下、不入库(见 §8)。从零复现整机环境的完整手册:docs/setup.md。

1.2 密钥(只在需要重新生成资产时才配)

密钥文件被 .gitignore 排除,绝不入库;模板里有获取链接:

cp env.d/sfx.example.sh env.d/sfx.sh       # HF token:https://huggingface.co/settings/tokens
                                           # (需先接受 stable-audio-3-small-sfx 的 gated 许可)
cp env.d/mureka.example.sh env.d/mureka.sh # Mureka key:https://platform.mureka.ai

TTS 环境(env.d/tts.sh)不含密钥,直接 source 即可。三个 env 脚本的作用:tts.sh = HF_HOME/镜像;sfx.sh = HF token + uv 路径;mureka.sh = Mureka API key。详见 env.d/tts.md、env.d/sfx.md。

1.3 跑起来

bash web/serve.sh
# 打印:本机 http://localhost:8000/  局域网 http://192.168.x.x:8000/
  • 桌面浏览器开 http://localhost:8000/ 即游戏大厅;鼠标拖拽/点击可模拟全部手势。
  • 手机访问:手机与电脑连同一 WiFi,开打印出的局域网地址。WSL2 需先在 Windows 做端口转发(命令在 web/serve.sh:9 注释里:netsh interface portproxy add v4tov4 ...)。
  • 服务器 = web/server/server.js(Node 零依赖,静态托管 + 用户系统 API 一体),细节见 web/server/README.md。

1.4 跑一遍试玩测试

每款游戏都有无头试玩测试(mock 浏览器 + 虚拟时钟,模拟玩家通关,见 §5):

for g in storyline escape simon rhythm space rpg radio dungeon; do
  tools/node/bin/node web/$g/test/playtest.mjs || echo "FAILED: $g"
done
tools/node/bin/node web/harness/guide.test.mjs   # 向导层专项(guide=full 行为断言)
tools/node/bin/node web/harness/engine-voice.test.mjs   # 引擎 voice 句尾保真专项(见 §6)

退出码 0 = 全绿。当前 6 款 + 向导专项全部通过。注意:playtest 一律在 setup 里 settings.set({guide:'off'}) 验证纯玩法逻辑;向导行为断言只住在 guide.test.mjs。

1.4.1 玩家感知转写器(交互研究的测量仪器)

tools/node/bin/node web/harness/transcribe.mjs <game>   # 产物: research/04-playtests/transcripts/<game>.md

把一局游戏对玩家的全部输出(每句语音全文/音效/BGM/震动/静默段/缺资产)转成时间轴日志——无法真听音频的评审(人或 agent)靠它审交互。会话剧本在 web/harness/sessions/<game>.mjs(describe=状态行话术 + session=新手动作剧本)。规矩:剧本必须以完整弧线收尾、driver 要对提示做人类级反应、台词全文映射跑 tools/extract_voice_text.py 刷新。

1.5 顺手验证:单条语音再生成冒烟

source env.d/tts.sh
flock .gpu.lock .venv-tts/bin/python tools/tts_smoke_test.py
# 输出 tools/samples/tts_test.wav(约 2s),能出声即 TTS 环境完好

1.6 常见任务速查

我要做什么 怎么做
加/改一句语音 ① 台词写进对应 docs/design/*-voice-ids.md(id+全文+instruct,数字改档位整句)→ ② 条目加进 tools/gen_voice_dual.py 台词表 → ③ flock .gpu.lock .venv-tts/bin/python tools/gen_voice_dual.py --game <g> --only <id> → ④ 跑 tools/check_all_assets.py
加一款新游戏 复制模板:cp -r web/template web/<name>;接线方式与目录约定见 docs/web-engine-api.md §快速开始;手势/震动/菜单遵守 00-global-ux;大厅 web/index.html 加卡片;写 web/<name>/test/playtest.mjs(mock 层抄现有任一)
修一个爆音/咔哒音 tools/scan_clicks.py 定位 → tools/fix_clicks.py 修复(标准:one-shot 真峰值 ≤-3dBFS、首尾 8ms cos² 淡化、去直流;§3.2 的 MDCT 坑必读)→ 终检
改全局设置项/加设置 web/engine/settings.js(VALID 白名单)+ 大厅 web/index.html 设置面板 + 00 文档 §1 语义表,三处同步
生成一首 BGM prompt 参考 10 号文档 §8 草稿 → tools/mureka_gen.py → tools/bpm_precise.py 实测 BPM(节奏类必须)→ 无缝循环处理(90s/强 onset 起点/3s crossfade)→ 记 tools/mureka_log.md 台账
调真机听感参数 改完必须真机戴耳机验证(roadmap §4 checklist);模拟器/桌面听不出手感

1.7 排障速查

症状 排查
游戏全程无声 ① 是否点了启动遮罩(音频必须在真实手势中解锁)② iOS 上 .ogg 解码失败——确认资产有 .m4a 副本 ③ 开 DevTools 控制台看 [WebAudioEngine] 日志(资产缺失只 console.error 不崩)
手机打不开页面 同一 WiFi?WSL2 做了端口转发(web/serve.sh:9 注释)?防火墙放行 8000?
TTS/SA3 脚本抢 GPU 失败或结果损坏 所有 GPU 任务必须 flock .gpu.lock 串行;模型下载失败看 env.d/sfx.sh 注释(gated 模型必须直连官方端点,hf-mirror 会 308)
测试挂了但游戏没改 看是不是动了引擎(engine 是 6 游戏共享的);playtest 用虚拟时钟,真实 setTimeout 依赖会失效
语音变了性别/音色 voice 缓存键含性别,切换设置后下一句生效;检查资产是否误放进错误的 gender 目录(角色音应平铺,见 §4 核查结论 12 号文档 §4)

1.8 第一周建议路径

  1. 第 1 天:跑通 §1.3 + §1.4;大厅里把 6 款游戏各玩 5 分钟(戴耳机)。
  2. 第 2 天:通读 docs/web-engine-api.md + 00-global-ux,对照 web/template/ 跑一个 hello-game。
  3. 第 3 天:挑一款游戏读 game.js + 对应设计文档 + playtest.mjs,改一个小参数跑测试验证。
  4. 第 4-5 天:读 §3 资产管线,跑通 §1.5 冒烟 + 一次 --only 单条重录 + 终检。

2. 技术架构

2.0 顶层目录速览

web/            # ★ 全部现役工作在这里:大厅 + 引擎 + 6 游戏 + 服务器
app/            # 旧 Android 版(已停迭代,§2.5)
docs/           # 文档:setup / 引擎手册 / design/(设计规范与审查档案)/ audio/(资产台账与轮次报告)
env.d/          # 环境注入脚本(tts/sfx/mureka/android)+ 密钥模板(*.example.sh)
tools/          # 用户态工具链(node/jdk17/android-sdk/ffmpeg/bin)+ 资产管线脚本(*.py/*.sh)
models/hf/      # HF 模型缓存(13GB,不入库)
.venv-tts/      # TTS venv;tools/stable-audio-3/.venv/ 是 SA3 venv

一次手势的完整调用链(以"滑动移动"为例):

触摸 → gesture.js(滑动识别,内置 swipe 震动)
    → pipeline.js 输入管道(26 号:新手门→迷茫信号→防连点→发言权→等待点限制)
    → game.onGesture('left')(游戏逻辑:查移动)
    → audio.play('sfx/parry')(sfx 层即发即弃)+ vibrate()(语义震动)
    → 状态推进(计时器 / voice onComplete 链 / setState)

2.1 架构图

浏览器(手机/桌面)
└── web/index.html          游戏大厅:设置面板(音色/语速/BGM 音量)+ 登录 + 进度卡片
└── web/<game>/             每游戏一个目录:index.html + game.js + cover.png + audio/
    └── 共享引擎 web/engine/
        ├── gesture.js      手势识别 → 7 种事件(6 手势 + longpress/longpressend)
        │                   内置基础震动(滑动→swipe、点按→confirm)
        ├── audio.js        Web Audio 三层(bgm/voice/sfx)+ ducking + 交叉淡入
        │                   voice 走 SoundTouch 变速不变调管线(engine/vendor/soundtouch.js)
        │                   总线挂 DynamicsCompressor 限幅器(-6dB/12:1)防爆音
        │                   长按事件 → voice 临时 1.7× 快进(setRateBoostEnabled 可按游戏豁免)
        │                   isVoicePlaying() 公开查询
        ├── guide.js        ★ 向导层:状态签名 earcon(8 音运行时合成)+ turn cue +
        │                   静默看门狗 + 纠错;settings.guide 三档(full/standard/off),
        │                   off=全静且零定时器;插播一律 queue 不顶对白,error/watchdog
        │                   语音空闲才播(过期丢弃)。设计裁定:docs/design/14-guide-layer.md
        ├── settings.js     四项全局设置(voice/speed/bgm/guide),localStorage 持久化
        │                   (speed 含 2.2 视障档)
        ├── haptics.js      navigator.vibrate 封装,10 种预定义语义震型
        └── game.js         Game / StateMachineGame 基类
└── web/server/server.js    Node 零依赖:静态文件 + 用户系统 API
    └── data/               users.json(PIN 明文!)、sessions.json、<username>.json(进度)

引擎 API 手册(开发新游戏必读):docs/web-engine-api.md。整体架构与音频时序:docs/architecture.md。

2.2 服务器 API

POST /api/register / POST /api/login / GET /api/progress?game=x / PUT /api/progress {game, data}。进度 data 自由格式,但必须带 summary 字符串(大厅卡片直接显示);_settings 是设置同步的保留键。

⚠ 玩具级认证(web/server/README 有显著警告):PIN 明文存储、token 永不过期、HTTP 明文。仅适用同一 WiFi 家庭内网,勿暴露公网。

2.3 资产目录约定

web/<game>/audio/
├── bgm/*.ogg|m4a         背景/状态音乐(循环)
├── voice/male|female/    旁白/系统音双性别(两目录 id 集合完全一致)
├── voice/<id>.*          角色/NPC 台词平铺(固定单音色,引擎回退命中)
└── sfx/*.ogg|m4a         音效(即发即弃)

2.4 关键设计决策(为什么)

  • 为什么无构建纯 vanilla JS:游戏逻辑全部是"手势→音频状态机",无 UI 框架需求;无构建 = 任何静态服务器可托管、任何同事改完刷新即验、无依赖腐烂风险。
  • 为什么 ogg + m4a 双格式:iOS Safari 对 Ogg Vorbis 解码支持差,引擎按 .ogg → .m4a → .mp3 → .wav 顺序回退;Android/Chrome 用 ogg(体积/音质比好),iOS 自动落到 m4a(AAC)。
  • 为什么单击有 ~300ms 延迟:引擎要等 300ms 双击窗口关闭才能确定单击。26 号落地后,叙事/探索类游戏一律 doubleTapWindow: 0(单击零延迟、无双击);双击是核心玩法的游戏(space 护盾、hunt 旋风斩)在手势阶梯阶段 ≥3 后才恢复双击窗口(pipeline 的 doubletapCore),此时单击延迟是该玩法的既定代价。
  • 为什么引擎管手势震动、游戏管语义震动:gesture.js 对"识别成功"内置 swipe/confirm 震动;游戏层只在语义事件(error/success/warning/pickup/hit/beat/danger)调 vibrate()。分层防止双重震动,也让 6 款游戏的触觉语言一致(设计规范:docs/design/00-global-ux.md §2)。
  • 为什么 voice 双性别目录而不是运行时变调:变速变调会毁音色;每句台词用两个 speaker(male=Uncle_Fu / female=见 §3.1 分配表)各生成一遍。角色/NPC 音固定不随设置(只有旁白/系统音跟随)。

2.5 Android 旧版状态

app/ 下 3 款早期 Android flavor(storyline/reflex/escape,Gradle 构建)已停止迭代:reflex《声东击西》因质量差已在 Web 版删除,另外两款以 Web 版为准(Web 版 escape 已重设计,逻辑不再一致)。构建命令见 README 与 docs/engine-api.md。新工作全部发生在 web/。

3. 音频资产制作流水线(接手重点)

总规范:docs/asset-pipeline.md;资产总账与音色分配:docs/audio/asset-manifest.md;各轮生产报告(round1-6):docs/audio/。设计侧台词表(id + 全文 + instruct,录什么看这些):docs/design/*-voice-ids.md(03/04/05 号系列)。

3.1 TTS 对白(Qwen3-TTS CustomVoice,本地 GPU)

source env.d/tts.sh
# 串行保护 GPU(所有 GPU 任务都要 flock)
flock .gpu.lock .venv-tts/bin/python tools/gen_voice_dual.py              # 全量(断点续跑,已有合格 wav 跳过)
flock .gpu.lock .venv-tts/bin/python tools/gen_voice_dual.py --game rpg --gender female --only tut_battle_1  # 单条重录
flock .gpu.lock .venv-tts/bin/python tools/gen_voice_dual.py --check      # 校验
  • 台词表内嵌在 tools/gen_voice_dual.py:加台词 = 在表里加 (id, speaker组, 全文) 条目再跑。staging wav 在 tools/gen_voice_dual_wav/,脚本自动 ffmpeg 转双格式。
  • 音色分配表(游戏 × 性别 → speaker):storyline m=Uncle_Fu/f=Vivian;escape/simon/space/rpg m=Uncle_Fu/f=Serena;rhythm m=Uncle_Fu/f=Vivian。RPG NPC 单音色(村长 Uncle_Fu·慈和 / 老猎人 Eric / 头目 Dylan / 寨主 Uncle_Fu·阴狠)。完整 instruct 模板见 asset-manifest.md。
  • instruct 规范(00 文档 §3):同游戏同常量、不含语速词(语速由引擎 SoundTouch 运行时控制)、一句一文件整句生成、禁止数字碎片拼接(数字信息按档位预生成整句,如 time_band1..4)。
  • 多音字避坑:"重听"→写"重新听";风险清单见 asset-manifest.md;确认误读后改台词表重跑单条(--only)。
  • QC 标准:时长 >0.3s、峰值 >0.05、峰值归一 0.89(voice 层策略,R6 起不改)、两性时长离群 >3 倍重生成。

3.2 音效(Stable Audio 3 + 程序合成)

source env.d/sfx.sh
cd tools/stable-audio-3 && flock ../../.gpu.lock .venv/bin/python -m stable_audio_tools ...  # CLI 用法见 env.d/sfx.md
  • prompt 一律英文;各游戏音效的需求清单与 prompt 草稿:docs/design/05-rpg-chapter1.md §7.2 是范本。
  • 程序合成兜底:numpy 合成脚本在各游戏 gen/ 目录(如 tools/gen_rpg_syn.py)。
  • ⚠ MDCT 编码过冲的坑(踩过的雷,R6 报告 §1):SA3 输出的近满幅高密度 one-shot,经 vorbis/aac 等 MDCT 有损编码后重建峰值会过冲 +4~8dB;而 ffmpeg volumedetect 走 s16 中间格式会把超幅削平显示为 0.0dB——用 volumedetect 做 QC 完全测不出来。正确校验法是**"解码回读真峰值"**:编码后再解码成浮点 PCM 读真实峰值。成品标准:one-shot 真峰值 ≤ -3dBFS + 去直流 + 首尾 8ms cos² 淡入淡出;loop 类(amb/bgm)只去直流(首尾淡化会破坏无缝循环)。编码管线内置"超 -3dB 则降增益 + 首轮 18kHz 低通 → 重编"(最多 3 次迭代)。
  • 排查工具:tools/scan_clicks.py(扫描爆音/直流/突变)、tools/fix_clicks.py(修复),原件备份在 tools/asset_fix_backup/。

3.3 BGM(Mureka API)

source env.d/mureka.sh
.venv-tts/bin/python tools/mureka_gen.py   # 调用/选候选/下载,用法见脚本头注释
  • 全流程与每次调用记录(task id、成本):tools/mureka_log.md。预算台账:累计 $0.63 / $2.00(R6 后)。
  • BPM 实测:节奏类 BGM 必须用 tools/bpm_precise.py 实测(AI 生成曲目 BPM 不精确会毁玩法;rhythm 三曲目 90/105/120 都是归一化过的)。
  • 无缝循环处理参数:90s 时长、强 onset 起点、首尾 3s crossfade 叠化、ogg q3 + m4a 128k。
  • 状态 BGM 规范(每游戏状态→曲目映射、切换淡入淡出毫秒数):docs/design/10-feedback2-state-bgm.md。

3.4 资产终检

.venv-tts/bin/python tools/check_all_assets.py   # 全项目 928×2 文件:双格式/0字节/解码/性别集合一致性

最近一次全绿报告:docs/audio/final-asset-check.md。改任何资产后必跑。

4. 六款游戏逐个简介

手势全局约定(26 号宪法):单击=继续/确定、左右滑=选择/移动、上滑=我在哪(阶段 1 解锁)、下滑=菜单(阶段 2)、双击=重听(阶段 3;未解锁按单击处理);四向滑动是玩法的游戏帮助换到长按(00 文档 §5 / 26 号 §4.3)。

游戏 玩法一句话 手势要点 目录 设计文档 试玩测试覆盖
剑与声 rpg 纯音频回合制武侠 RPG:节点图探索 + 听前摇双击格挡的回合制战斗 + 升级装备三章剧情 左右滑=选选项/招式,单击=确认/出手,双击=格挡/重听,上滑=状态,下滑=菜单/逃跑 web/rpg/ 05-rpg.md + chapter1/voice-ids/addendum + 11 战斗重构 第一章通关全链(村庄→商店→教学战锁血→支线→连战→Boss 二阶段)、格挡 0.3 减伤、存档继续
声命线 storyline 末世互动广播剧:3 幕 8 抉择 4 结局,左右滑做选择 单击=继续,双击=重听节点,左右滑=抉择,上滑=帮助+进度,下滑=菜单 web/storyline/ docs/games/storyline.md(旧)+ 打磨项见 roadmap §2 抉择路径到达结局、双击重听、菜单返回不丢节点、剧情标记断言
暗室 escape 3x3 黑暗房间密室逃脱:引导式开场 + 三级卡关提示,钥匙→铁柜→保险丝→电箱→大门 四向滑=移动,单击=检查/重听环境音,双击=使用物品/背包兜底 web/escape/ 01-escape-redesign.md 引导 8 步、解谜链、撞墙、卡关提示计时、双击兜底
音序回响 simon 音频记忆:听手势音序列复现,复活机制(机会重听),全程无 BGM 六手势全是答题输入;input 阶段双击=重听(期望 doubletap 时=答题,round2 裁决①) web/simon/ 02-simon-redesign.md 教学 6 手势、复活序列不变、双击上下文判定、速度曲线
节拍行者 rhythm 跟随领奏:听 1 小节节奏型在下一小节跟打,3 曲目(90/105/120 BPM)解锁制 四向滑+单击+双击全是演奏输入;无临近 down 音符时下滑=菜单(折中) web/rhythm/ 03-rhythm-rebuild.md + voice-ids Perfect/Good/Miss 窗口、tap 300ms 补偿、菜单暂停 BGM 断点续播、下滑折中
盲航 space 太空盲飞:听方位呼啸同向滑闪避陨石,单击加速双击护盾,60s 保护期 四向滑=同向闪避;无下方陨石时下滑=菜单(折中) web/space/ 04-space-rebuild.md + voice-ids 演练做错重教、保护期撞不掉血、同向判定、归因语音、里程碑

每游戏目录下还有自带 README.md(玩法说明)。试玩通过标准(测试断言的设计依据):docs/design/13-playtest-criteria.md。

5. 测试与质量

5.1 无头试玩测试原理

web/<game>/test/playtest.mjs(范本注释见 rpg 版头部):用 Node 直接加载真实的 game.js 和真实的引擎模块(engine/audio.js、settings.js、haptics.js、vendor/soundtouch.js),只 mock 浏览器底座——window/document/navigator/localStorage/fetch/AudioContext 最小接口(fetch 返回带时长的假 buffer,voice 链由引擎真实的结束定时器推进)。虚拟时钟 patch setTimeout/setInterval/performance.now/ctx.currentTime(可快进卡关计时器/保护期,零真实等待)。手势直接注入 game.onGesture()(引擎回调路径),随机性用种子 PRNG 保证可重复。退出码 0 = 全绿。

5.2 怎么加断言

  • 新功能:先查 13-playtest-criteria.md 对应游戏的章节有没有该行为的检查单条目;没有就按"关键路径 / 边界断言 / 全局通用"三类补一条,再在 playtest.mjs 里实现。
  • 新游戏:复制任一现有 playtest.mjs 的 mock 层(虚拟时钟 + 引擎 mock 是通用的),只重写游戏特定的路径断言;断言清单先在 13 号文档登记。
  • 重构已知偏差时:13 号文档里有 known-issue 标记的预期失败断言,修正落地后翻正。

5.3 其他质量手段

  • JS 语法校验:tools/node/bin/node --check web/<game>/game.js。
  • 引擎 voice 句尾保真:tools/node/bin/node web/harness/engine-voice.test.mjs (I1 源未耗尽不得结算 / I2 耗尽后排空窗才断开 / I3 mock 零消耗走旧路径; 2026-09 真机人耳反馈固化,改动 audio.js _finishVoice 必跑)。
  • voice 资产尾部静默审计:.venv-tts/bin/python tools/audit_voice_tails.py (量化尾部裁剪松紧;注意判读口径——尾静默紧≠剪字,句尾完整性验收在引擎层)。
  • 资产终检:tools/check_all_assets.py(§3.4)。
  • 真机听测 checklist(听感参数只能真机验):docs/design/roadmap.md §4。

6. 已知问题与限制

问题 现状 后续处理建议
引擎无公开 isVoicePlaying 已解决(R7):audio.isVoicePlaying() 公开,游戏层全部收编 —
长按快进无 per-game 开关 已解决(R7):audio.setRateBoostEnabled(false),rhythm/space 演奏期已豁免 —
voice 句尾剪字(每句末尾音节缺失) 已根治(2026-09 真机人耳反馈,三修):一修固定排空窗(手机端未交付量浮动,不够);二修 onEnd 完成信号(仍不解决交付缺失);三修定位真根因——SoundTouch Stretch 源结束不冲刷最后一个小节,句尾 0.1-0.25s 任何平台都不会播出,根治 = 源尾垫 0.25s 静音推出真实句尾 + onEnd 驱动完成 + 1.5s 兜底。回归 web/harness/engine-voice.test.mjs(PAD+I1-I3);live 验证 span 8.57s→9.04s(≥文件时长 8.80s);全 8 游戏 live 巡检 225 句对账:自然结束句 210/210 完整,15 条短跨度逐条分诊=输入抢占(space 教学错手重教×12/simon 单击跳句×2,零手势静听复测完整)或探针对齐容差(rpg×1 差 0.06s),引擎零截断 引擎层已固化,新游戏零改动继承;live 探针同步适配(清扫器守卫);合成侧经 ASR 抽查排除剪字(尾静默紧是裁剪工艺,判读口径见 tools/audit_voice_tails.py 头注)
向导 earcon 为运行时合成音 音色是「可用」级不是「精美」级(正弦/三角/方波包络) 若升级为录制品 earcon:保持 8 音词表与参数边界不变(14 号文档 §2),只换渲染后端
escape「需要帮助吗」问而不等 P1 终评唯一「擦边当白痴」时刻 v2 手势宪法专项:真问答 + PLAY 期目标重听入口
space/simon 无「局」与结算仪式;storyline 链尾无轻量 turn 信号、二周目重播开场教学;rhythm 教学首误先震后教 round4 终评 P0 全部裁定入 v2(research/04-playtests/round4/synthesis.md §2) L5 会话层专项 + 微 polish 清单
语速上限 2.2(视障档) P1 要求 3x+ 需真机人耳验证 SoundTouch 高 tempo 质量,勿盲上
iOS 无震动 haptics.js 特性检测静默 no-op;设计裁定:绝不回退提示音(提示音混淆是用户反馈⑦根因) RPG 无声敌人 iOS 不可格挡由「防御指令」兜底(05-rpg-addendum §2.2);保持现状
SoundTouch 走 ScriptProcessorNode 有固定处理延迟;voice 变速不变调用它(engine/vendor/soundtouch.js,LGPL 许可) 功能正常不优先动;若换 AudioWorklet 需重测语速档;商业化注意 LGPL 声明义务(§7)
菜单约定 RPG 豁免 全球模态菜单=单击循环/双击确认;RPG 探索态菜单刻意保持左右滑+单击(与探索范式同构,round2 裁决②) 不是缺陷,不要"统一"它;测试断言按豁免写
服务器玩具级认证 PIN 明文、token 不过期、HTTP 明文 家庭内网可接受;公网化前必须重做(PIN 哈希、token 过期、HTTPS),见 roadmap §5

7. 路线图

完整版:docs/design/roadmap.md。摘要:

  1. 交互研究 v2 清单(round4 终评裁定移交,详见 research/04-playtests/round4/synthesis.md §5):
    • 会话层专项:space/simon 定义「局」与结算仪式(参照 rhythm 范本);storyline 链尾轻量 turn earcon + 二周目跳过已听教学;rhythm 教学首误免震先教。
    • 手势宪法专项:escape PLAY 期目标重听入口;「需要帮助吗」改真问答;全线重听键统一评估(longpress 候选)。
    • 场景参数包 A/B/C/D(research/03-schemes/schemes.md §3 分期裁定):通勤两遍式播报 / 摸鱼震动语言 / 睡前定时器 / 下班容错档——入口显式选择+记忆,不做自动切换。
    • 语速 3x+:真机人耳验证 SoundTouch 高 tempo 质量后再上。
  2. RPG 第二/三章(内容灌入,模式已定型):ch2《腐沼泽》(毒状态、蛊师先攻、Boss 召唤二阶段);ch3《无瞳峰》(无声前摇敌人→终 Boss 三阶段全无声,震动格挡是高潮机制)。需策划补发两章台词表 → 音频录制 → 开发灌入。新台词必须同步向导规范(00 文档 §6)。
  3. storyline 打磨:菜单统一到全球约定;knowTrap 死标记修复(只写不读,需在北区节点接入或删除);ui_confirm/ui_menu 提示音排查替换;结局 X/4 轻量进度。
  4. 存量打磨小项:rhythm 结算末位语音修正(no_more_song 补录)、escape PLAY 中回大厅入口、大厅设置试听句。
  5. 商业化前置清单:Stable Audio 3 / Mureka / Qwen3-TTS / NotoSansSC(OFL)四项许可确认与凭证落盘 + soundtouch.js 的 LGPL 声明义务(About 页标注库名与许可)+ 隐私政策(用户系统公网化前置)。

8. 协作约定

8.1 git 规范

  • 密钥绝不入库:env.d/sfx.sh、env.d/mureka.sh(真实 token/key)已被 .gitignore 排除;只提交 *.example.sh 模板。key 泄露去对应平台 Dashboard 吊销换新。
  • .gitignore 还排除:models/(13GB HF 缓存)、.venv-tts/、tools/stable-audio-3/.venv/、tools/bin/(ffmpeg)、tools/jdk17/、tools/android-sdk/、tools/node/、tools/bgm_backup/、tools/mureka_out/(原始 flac)、tools/samples/、.gpu.lock、Android 构建产物。
  • 中间产物不入库:staging wav(tools/gen_voice_dual_wav/)、tools/asset_fix_backup/ 等留本地供断点续跑,不进仓库(已 ignore 或保持 untracked,提交前 git status 自查)。

8.2 目录红线

  • 资产生成脚本放各游戏 gen/ 或 tools/,可复现是硬要求——任何入库音频都必须能从脚本重新生成。
  • web/engine/ 是共享引擎,改动要同步更新 docs/web-engine-api.md 并跑全部 6 款试玩测试。
  • 不改 docs/design/ 下的裁定记录(round*-review),新结论写新文档。

8.3 文档更新义务

  • 改玩法/手势/震动语义 → 更新 docs/design/00-global-ux.md 与对应游戏设计文档。
  • 新增/重录语音 → 台词先进对应 *-voice-ids.md(id + 全文 + instruct),再进 gen_voice_dual.py 台词表;两表必须一致。
  • 音频轮次结束 → 写 docs/audio/roundN-report.md(统计、QC、给开发的接线约定),并跑终检更新 final-asset-check.md。
  • 资产清单变动 → 更新 docs/audio/asset-manifest.md。

本文最后更新对应 commit 9a915d6 之后的仓库状态(2026-08-30)。如发现与代码不符,以代码为准并修正本文。