Files
SoundGame/docs/asset-pipeline.md
T
xsl 24c526af68 feat(assets): SA3 音效补齐 radio 11+1 与 dungeon 13源+15pan(token 走 hf-mirror 实测可用)
- 模型 17/17 文件经镜像全量缓存(gated+token,直连本机不通;sfx.md/setup.md/asset-pipeline.md 镜像结论同步更新)
- 修复迁移遗留:stable_audio_3/models/ 源码子包被 .gitignore models/ 规则误伤丢失,从上游恢复并加白名单
- radio:arg_muffled/brake_screech 等叙事件落地(接线原有 L111/L154 零改动)+ phone_ring SYN→SA3 升级;dungeon 13 源 A/B 对齐(RMS 偏差 ≤0.3dB)全量同名替换,SYN 原版备份 tools/asset_fix_backup/sa3_ab_20260902/
- 关键玩法语义达标:echo 距离档 0.83<1.43<2.18<2.98s 严格递增;one-shot 真峰值全 ≤-3dBFS(解码回读法)
- check_all_assets 8 游戏全绿(radio 254/254、dungeon 303/303);playtest radio 221/dungeon 1610 全绿
2026-09-02 19:35:38 +08:00

137 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SoundGame — 资产生产流水线
三类音频资产(对白 voice / 音效 sfx / 音乐 bgm)的完整生成与再生成流程。
所有脚本都在 `app/src/<flavor>/gen/` 下,可随时复现全部资产。
通用约定:
- **GPU 任务必须串行**:`flock /home/ubuntu/SoundGame/.gpu.lock <命令>`
(TTS 与 Stable Audio 3 共用一块 RTX 3090)。
- 环境脚本:`source env.d/tts.sh`(HF_HOME + hf-mirror 镜像)、
`source env.d/android.sh`(ffmpeg 在 PATH)、`source env.d/sfx.sh`(HF_TOKEN,
含密钥不入库,模板见 `env.d/sfx.example.sh`)。
- 中间产物 wav 输出到各 gen 目录或 `/tmp/slg/`(不入库),最终 ogg 进
`app/src/<flavor>/assets/audio/{bgm,voice,sfx}/`。
## 1. TTS 对白(Qwen3-TTS CustomVoice)
引擎与用法详见 `env.d/tts.md`(CustomVoice 预置音色 + instruct 情感指令、
VoiceDesign、预置音色表)。流水线层面:
```bash
source env.d/tts.sh
# 《声命线》:48 条,6 条一批;输出 /tmp/slg/wav/,可传文件名参数只重生指定条目
flock .gpu.lock .venv-tts/bin/python app/src/storyline/gen/gen_voice.py [文件id...]
bash app/src/storyline/gen/convert.sh # wav -> ogg 进 assets(含电台带通效果)
# 《声东击西》:33 条,逐条生成;输出 gen/wav_voice/
cd app/src/reflex/gen
flock /home/ubuntu/SoundGame/.gpu.lock /home/ubuntu/SoundGame/.venv-tts/bin/python gen_voice.py
# 《暗室》:41 条,8 条一批,统一峰值归一 0.89;输出 gen/wav/voice/
flock .gpu.lock .venv-tts/bin/python app/src/escape/gen/gen_voice.py
```
- 每条生成后自动校验:**时长 > 0.3s 且峰值 > 0.05**,不达标直接 assert 失败。
- 音色分配见各游戏文档(docs/games/*.md):旁白 Uncle_Fu、林晚 Serena、
猎狗 Dylan、小满/广播 Vivian;escape 全员 Uncle_Fu 靠 instruct 区分语气。
- 已知不稳条目:reflex 的 `num_6`("六"),单独用
`app/src/reflex/gen/fix_num6.py` 重试生成(最多 4 次取时长合格的一条)。
- 改台词只需改 gen_voice.py 里的 `LINES` 表再重跑;文件名即游戏代码引用的 id。
## 2. 音效(Stable Audio 3 + 程序合成)
### 2a. Stable Audio 3 small-sfx(主力,AI 生成)
```bash
source env.d/sfx.sh # HF_TOKEN(gated 模型;本机直连不通,实测 gated+token 走 hf-mirror 可用,2026-09-02)
cd tools/stable-audio-3
uv run stable-audio --model small-sfx \
-p "footsteps on gravel, slow walking" --duration 3 \
-o /home/ubuntu/SoundGame/tools/samples/sfx_test.wav
```
- 提示词必须英文;`--duration` ≤ 120s;`--steps` 默认 8;small 模型峰值显存
约 2.4GB。批量:`-p "p1" "p2" --duration 3 5`。
- 首次使用需完成 gated 许可 + token 流程,详见 `env.d/sfx.md` / `docs/setup.md`。
- 生成后用 ffmpeg 转 ogg 进 assets(规格见下文"资产规格")。
### 2b. 程序合成(保底/已在线方案,numpy + soundfile,纯 CPU 无需 GPU 锁)
| 游戏 | 脚本 | 输出 |
|---|---|---|
| storyline | `app/src/storyline/gen/gen_sfx.py` | `/tmp/slg/wav/sfx/`(24kHz),`convert.sh` 转 ogg |
| reflex | `app/src/reflex/gen/gen_sfx_bgm.py` | `gen/wav_sfx/`(44.1kHz),`convert_audio.py` 转 ogg |
| escape | `app/src/escape/gen/gen_sfx.py` | `gen/wav/sfx/`(44.1kHz),手动 ffmpeg 转 ogg |
当前线上资产全部为程序合成版;Stable Audio 3 环境已就绪,可按
README 待办逐步替换(替换时保持同名同目录,游戏代码零改动)。
## 3. BGM
### 3a. Mureka API(storyline / escape)
```bash
source env.d/mureka.sh # MUREKA_API_KEY(含密钥不入库,模板 env.d/mureka.example.sh)
python3 tools/mureka_gen.py \
--prompt "dark ambient horror game background music, low drone, ..., seamless loop, instrumental" \
--out tools/mureka_out/storyline # 提交→轮询→下载全部候选 flac
python3 tools/mureka_gen.py --task-id <id> --out tools/mureka_out/xxx # 只轮询已有任务
```
后处理(两条已上线 BGM 相同参数,记录见 `tools/mureka_log.md`):
```
裁剪前 120s → 首尾 3s acrossfade(tri) 无缝循环化 → loudnorm I=-20/TP=-2/LRA=11
→ aresample 44.1kHz 立体声 → libvorbis -q:a 3
```
成品 117.00s,保持原文件名(`dark_drone.ogg` / `drone.ogg`)直接替换 assets。
原程序合成版备份在 `tools/bgm_backup/`。
**成本参考**:`POST /v1/instrumental/generate` 每条任务 **$0.03**(一次出 2 个
候选),项目预算上限 $0.50,已用 $0.06(2 条任务均一次成功)。每次调用必须
登记到 `tools/mureka_log.md`。
### 3b. 程序合成(reflex,有意保留)
`app/src/reflex/gen/gen_sfx_bgm.py` 的 `make_pulse_bgm(bpm, bars=8)`:
心跳底鼓 + 低音脉冲音序 + 镲噪声 + 不协和 pad,100/128/152 BPM 三档各 8 小节。
玩法要求 BPM 随波次精确切换,AI 生成曲目 BPM 不精确会破坏机制,**不要用
Mureka 替换 reflex 的 BGM**。
## 资产规格
| 项 | 标准 |
|---|---|
| 容器/编码 | ogg vorbis(`-c:a libvorbis`) |
| 码率 | voice/sfx `-q:a 4`;bgm `-q:a 3`(控制体积) |
| 采样率 | TTS 源 24kHz;程序合成 reflex/escape 44.1kHz;BGM 统一 aresample 44.1kHz |
| BGM 响度 | loudnorm **I=-20 LUFS / TP=-2 / LRA=11** |
| BGM 循环 | 首尾交叉淡化无缝循环(Mureka 版 3s acrossfade;合成版 2s/4s 等功率交叉) |
| 时长校验 | 每条 > 0.3s(数字语音等短条目除外,fix_num6 放宽到 0.3~1.5s 区间判定) |
| 峰值校验 | 生成后 assert 峰值 > 0.05(防静音/失败条目混入) |
| 峰值归一 | storyline 合成 sfx 0.85;escape voice 0.89 / sfx 0.85(环境音 0.55-0.7);reflex voice 0.6 / sfx 0.7-0.95 |
## 目录约定
```
app/src/<flavor>/
assets/audio/{bgm,voice,sfx}/*.ogg # 最终资产(入库)
gen/ # 生成脚本(入库)+ 中间 wav/字体(不入库)
tools/
mureka_gen.py / mureka_log.md # Mureka 调用脚本与台账(入库)
mureka_out/ bgm_backup/ samples/ # 原始下载/备份/冒烟输出(不入库)
stable-audio-3/ # SFX 仓库(代码入库,.venv 不入库)
```
引擎按 id 前缀(`bgm/` `voice/` `sfx/`)分发到三个播放层,文件名即代码中的
引用 id;新增资产保持"同名替换、新增先改代码引用"的原则。
## 封面图
- storyline:`gen/make_cover.py`(PIL,字体 /tmp/slg/NotoSansSC.ttf)
- escape:`gen/gen_cover.py`(PIL,字体 gen/font.otf,不入库需自备)
- reflex:封面无独立生成脚本(骨架渐变图 + 后期替换)
- 统一输出 `app/src/<flavor>/res/drawable/cover.png`,1080x1920,文件名固定。