Files
blind/audio_2/项目状态说明.md
T
xsl 12eb1eab44 VoxCPM2 v5 音色克隆:脚本、参考音频、游戏对白与状态文档
通过 Voice Design 试听选定各角色参考音色,Style Control 全量克隆并部署到 audio/mp3/tts;清理未引用音频与 audio_1 旧产物。
2026-05-24 00:34:50 +08:00

237 lines
8.2 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.
# 《林夏》音频制作 — 当前状态说明
> 更新日期:2026-05-23
> 本文档记录 TTS 音色选型、全量生成、游戏部署的**现行状态**,便于下次继续迭代。
---
## 1. 一句话总结
**游戏当前使用的是 v5 克隆音色**:从 Voice Design 试听样本中选定参考音频 → VoxCPM2 Style Control 克隆 → 部署到 `audio/mp3/tts/*_wav_v1.mp3`
`audio/mp3/` 已清理,仅保留游戏引用的 **184** 个文件。
---
## 2. 游戏实际加载哪些音频
游戏代码在 `game/js/story.js``game/js/engine.js`,根路径:
```
../audio/mp3/
├── tts/ 对白(177 条)
├── music/ BGM、铃声
├── sfx/ 音效
└── ambience/ 环境音
```
### 命名规则(重要)
| 文件名 | 游戏是否使用 |
|--------|-------------|
| `*_wav_v1.mp3` | **是** — 全部对白、系统语音都用这种 |
| `*_mp3_v1.mp3` | **否** — 旧流水线遗留,已全部删除 |
后缀里的 `wav` 表示「用 WAV 参考音色生成的 v1 版」,文件格式仍是 MP3。
### 示例
```
game/js/story.js → npc('lv11_0015_mom_call_01_wav_v1.mp3')
实际文件 → audio/mp3/tts/lv11_0015_mom_call_01_wav_v1.mp3
```
**不需要改 `story.js`**,只要更新 `audio/mp3/tts/` 里对应的 `*_wav_v1.mp3` 即可。
---
## 3. TTS 服务
| 项目 | 路径 / 值 |
|------|-----------|
| 服务目录 | `/home/xsl/tts-server/` |
| 启动脚本 | `VOXCPM_PORT=8002 VOXCPM_ENABLE_DENOISER=0 bash /home/xsl/tts-server/start-voxcpm.sh` |
| 端口 | **8002**8000 被 head3d 占用) |
| 健康检查 | `curl http://127.0.0.1:8002/health` |
| 主要接口 | `POST /v1/voices` 注册参考音频 |
| | `POST /v1/speech/styled` 合成(`voice_id` + `style` = 克隆;无 `voice_id` = Voice Design |
---
## 4. 音色演进(v1 → v5
| 版本 | 目录 | 方式 | 状态 |
|------|------|------|------|
| v1 | `audio_1/tts/v1_voxcpm2/` | 旧参考音频直接克隆,无 style | 已被 v5 替代 |
| v2 | `audio_2/tts/v2_styled/`(已不在磁盘) | 旧参考 + Style Control 表演 | 已废弃 |
| v3 | `audio_2/tts/v3_voice_design/`(已不在磁盘) | 纯 Voice Design,无参考 | 已废弃 |
| v4 | `audio_2/tts/v4_voice_samples/` | 每角色 5 条试听,用户挑选 | **保留选定样本** |
| **v5** | `audio_2/tts/v5_cloned/` | 选定样本克隆 + 表演修饰 | **当前正式版** |
| 游戏 | `audio/mp3/tts/*_wav_v1.mp3` | v5 自动部署副本 | **游戏正在使用** |
---
## 5. 各角色选定的参考音色(v4 试听挑选)
参考文件位于 `audio_2/tts/v4_voice_samples/{角色}/`,每角色仅保留 1 条 `.wav`
| 角色 | 选定文件 | 性别 |
|------|----------|------|
| linxia | `linxia_v05.wav` | 女 |
| mom | `mom_v01.wav` | 女 |
| azhe | `azhe_v03.wav` | 男 |
| xiaomei | `xiaomei_v05.wav` | 女 |
| xiaomei_drunk | `xiaomei_drunk_v05.wav` | 女 |
| zhounan | `zhounan_v05.wav` | 男 |
| hr | `hr_v05.wav` | 女 |
| anan | `anan_v04.wav` | 女 |
| dorm_a | `dorm_a_v03.wav` | 女 |
| dorm_b | `dorm_b_v02.wav` | 女 |
| dorm_c | `dorm_c_v03.wav` | 女 |
| delivery | `delivery_v04.wav` | **男** |
| self_3y | `self_3y_v03.wav` | 女 |
副本已同步到 `voice/{角色}/{角色}_ref.wav``xiaomei_drunk``voice/xiaomei/xiaomei_drunk_ref.wav``self_3y``voice/linxia/self_3y_ref.wav`)。
---
## 6. v5 全量生成结果
- **177 条** MP3`audio_2/tts/v5_cloned/*_wav_v5.mp3`
- **177 条**已部署:`audio/mp3/tts/*_wav_v1.mp3`
- 生成清单:`audio_2/tts/v5_cloned/clone_manifest.tsv`
来源台词表:`audio/batch_tts_voxcpm.py``LINES`173 条)+ 脚本内 `EXTRA_LINES`4 条):
| 额外文件 | 角色 | 说明 |
|----------|------|------|
| `lv11_0030_self3y_voice_01/02/03` | self_3y | 三年前录音三版 |
| `lv11_sys_memo_01` | linxia | 系统提示「三年前今天…」 |
克隆时**只用表演修饰**(warm/cold/分段等),音色完全由参考 wav 决定。配置见 `audio_2/voice_style_prompts.py``get_clone_style()`
---
## 7. 目录结构速查
```
blind/
├── game/js/story.js 游戏剧情 + 音频引用
├── game/js/engine.js 系统语音、结局、BGM
├── audio/
│ ├── batch_tts_voxcpm.py 台词表 LINES + 角色语速 ROLES
│ └── mp3/ 游戏运行时音频(184 文件,已清理)
│ ├── tts/ 177 条对白
│ ├── music/
│ ├── sfx/
│ └── ambience/
├── voice/ 各角色参考音频(含 v5 选定副本)
└── audio_2/
├── 项目状态说明.md ← 本文档
├── 角色语音风格提示词.md Voice Design 说明(历史参考)
├── voice_style_prompts.py 表演修饰 + Voice Design 提示词
├── voice_sample_prompts.py v4 试听:5 变体/角色 + 统一试听文本
├── scripts/
│ ├── 01_gen_tts_styled.py v2 Style Control(旧)
│ ├── 02_gen_tts_voice_design.py v3 Voice Design(旧)
│ ├── 03_gen_voice_samples.py v4 生成试听样本
│ └── 04_gen_tts_clone.py v5 克隆 + 部署游戏 ★
└── tts/
├── v4_voice_samples/ 选定参考音色(每角色 1 wav + mp3)
└── v5_cloned/ 正式产物 + clone_manifest.tsv
```
---
## 8. 常用命令
### 启动 TTS
```bash
VOXCPM_PORT=8002 VOXCPM_ENABLE_DENOISER=0 bash /home/xsl/tts-server/start-voxcpm.sh
curl http://127.0.0.1:8002/health
```
### 重跑全量克隆(改参考音频或提示词后)
```bash
cd /home/xsl/blind
VOXCPM_PORT=8002 python audio_2/scripts/04_gen_tts_clone.py --force
```
- 输出:`audio_2/tts/v5_cloned/*_wav_v5.mp3`
- 自动覆盖:`audio/mp3/tts/*_wav_v1.mp3`
### 只重跑某角色
```bash
VOXCPM_PORT=8002 python audio_2/scripts/04_gen_tts_clone.py --role mom --force
```
### 重新生成某角色试听(换提示词挑音色)
```bash
VOXCPM_PORT=8002 python audio_2/scripts/03_gen_voice_samples.py --role delivery --force
```
### 预览任务不合成
```bash
python audio_2/scripts/04_gen_tts_clone.py --dry-run
```
---
## 9. 其他音频服务(非 TTS)
| 用途 | 位置 |
|------|------|
| 音乐 / 音效生成 | `/home/xsl/tools/ACE-Step-1.5` |
| Stable Audio | `/home/xsl/tools/stable-audio-tools` |
| UI 短音效 | Web Audio(浏览器内合成) |
VoxCPM2 **不能**用提示词生成敲门、心跳等 SFX,只能做人声 TTS。
---
## 10. 已知注意点
1. **Voice Design 音色不稳定**:同角色各条可能漂移,因此最终方案是「试听选定 → 克隆锚定」。
2. **外卖小哥语速**`delivery` 提示词已调慢,`ROLES["delivery"]["speed"]` 已从 1.20 改为 **1.00**
3. **男声角色**`azhe``zhounan``delivery`
4. **重新清理未使用音频**:扫描 `game/js/*.js``audio/mp3/` 对比删除;当前已对齐(184 = 184)。
5. **改游戏引用**:若将来改用 `_wav_v5.mp3` 等新后缀,需批量改 `story.js` / `engine.js`;目前无需改动。
---
## 11. 下次可继续的事
- [ ] 实机/playtest 听一遍各角色,标记需重跑的台词或角色
- [ ] 若某角色不满意:改 `voice_sample_prompts.py` → 重跑 `03` 挑选 → 替换 `v4_voice_samples/{role}/` → 重跑 `04`
- [ ] 微调单条表演:改 `voice_style_prompts.py``_performance` 规则 → `--role xxx --force`
- [ ] 设计文档:`林夏.md``执行文档_音频素材制作.md`
---
## 12. 快速验证游戏音频是否正常
```bash
# 游戏引用的文件是否都存在
python3 - <<'PY'
import re
from pathlib import Path
root = Path("/home/xsl/blind")
text = "".join((root/"game/js/story.js").read_text() + (root/"game/js/engine.js").read_text())
refs = set()
for m in re.finditer(r'\$\{(MUS|SFX|AMB|T)\}/([^`\']+\.mp3)', text):
sub = {'MUS':'music','SFX':'sfx','AMB':'ambience','T':'tts'}[m.group(1)]
refs.add(root/f"audio/mp3/{sub}/{m.group(2)}")
for m in re.finditer(r"(?:npc|lx|rx)\('([^']+\.mp3)'\)", text):
refs.add(root/f"audio/mp3/tts/{m.group(1)}")
missing = [r for r in refs if not r.exists()]
print(f"引用 {len(refs)} 缺失 {len(missing)}")
for p in sorted(missing): print(" MISSING:", p.relative_to(root))
PY
```
预期输出:`缺失 0`