音色档案与声线管理(一个角色,一个声音)
适用场景
- 为一个数字员工/角色建立稳定的声线(如小织的播报声);
- 需要同一角色在多次任务里"听起来是同一个人";
- 需要在不同引擎之间切换(本机 MLX / OpenAI 兼容端点)而不换声。
一、档案结构(工位事实源)
~/.workloom/voice-station/profiles/<profile-id>/
├── reference.wav # 参考音频(24kHz 单声道 PCM,来自本机麦克风或授权素材)
├── profile.json # 逐字转写、时长、质量指标、引擎音色 id、参考 sha256
└── consent.json # 授权声明(谁、什么范围、谁声明、证据、到期)
profile-id只允许[a-zA-Z0-9._-],≤64 字符;命名建议<语言>-<角色>-<版本>,如zh-xiaozhi-v1;- reference.wav 只增不改:要换参考音频就新建版本 id(
zh-xiaozhi-v2)而不是原地覆盖——否则历史产物的声音无法复现; - 档案目录与授权回执同生命周期:撤回授权 = 停用档案并留痕,不删证据。
二、一个角色一个档案
系统的语音契约要求"同一角色第一次选中音色后必须锁定,后续段落不得重新猜测"(见 docs/voice-and-avatar-delivery-contract.md)。
配音师的对应做法:
- 每个角色只绑定一个 profile-id;
- 同一次任务的多段合成全部走同一档案;
- 需要"情绪变化"时改的是
instruct/ 语速,不是换人。
三、情绪、语速与韵律:能控什么
| 维度 | 手段 | 边界 |
|---|---|---|
| 语速 | speed / atempo | 播报 0.95–1.05×;配音 0.9–1.15×,超过 1.25× 会被时窗工具判 segment_overflow |
| 情绪/风格 | instruct(引擎支持时,如 [excited]、[whisper] 一类标签或自然语言指令) | 参考音频优先于指令:二者冲突时以参考音频为准 |
| 停顿 | 分句 + 段间 gap_ms(默认 180ms) | 播报段间 150–250ms;配音跟随时间窗,不额外加气口 |
| 音高 | 不做全局变调 | 变调会破坏音色一致性;需要更亮的声线应该重采参考音频 |
四、跨语言与方言
- 参考音频用原语言、逐字稿也用原语言;输出语言单独指定("跨语言克隆");
- 中文普通话是第一优先;需要粤语/日语/韩语等,先确认引擎的语种覆盖(本仓默认引擎 OmniVoice 覆盖 646+ 语种,中文实测 RTF ≈ 1.2 @ M3);
- 方言音色(如川普、东北腔)用同方言参考音频 + 匹配的
instruct,不要指望"普通话参考 + 指令"自动出方言。
五、多引擎口径(同一档案,不同后端)
| 引擎 | 克隆方式 | 档案要求 |
|---|---|---|
mlx(Apple Silicon 本机,默认) | 请求里直传 ref_audio + ref_text | 档案自包含,换机需重新登记(路径不出工位) |
openai(VoiceStudio :3900 / GPT-SoVITS :9880 等) | 音色在引擎侧登记,请求用 engine_voice_id | 档案里必须记 engine_voice_id 与引擎版本;引擎重建后要复核音色是否仍一致 |
mock | 无 | 仅 CI/干跑,不产出可用交付 |
换引擎 = 重新验收:同一档案在另一后端上要抽听复核(音色一致性 + 中文数字读法),确认后再切换。
输出契约
档案说明必须包含:profile-id、参考音频来源与授权范围、逐字转写、时长与质量指标、
绑定的引擎与 engine_voice_id、参考音频 sha256,以及"这个声音能用在哪些场景"的边界。
失败模式(症状 → 检测器 → 处置)
| 症状 | 检测器 | 处置 |
|---|---|---|
| 同一角色在不同段落"换人"了 | 任务内出现多个 profile-id / 音色未锁定 | 锁定单一档案重合成;情绪变化只改 instruct/语速,不换档案 |
| 历史产物声音无法复现 | 参考音频 sha256 与档案记录不一致(被原地覆盖) | 恢复版本化档案(-v2 新建而非覆盖);重采后回归抽听 |
| 换引擎后音色变了 | 换后端未抽听复核 / engine_voice_id 未登记 | 抽听复核(音色一致性 + 中文数字读法)后再切;不一致则回退原引擎 |
| 为了"更亮"做了全局变调 | 变调(pitch shift)记录 | 禁止全局变调;重采更亮的参考音频 |
| 语速被拉到听感失真 | speed/atempo >1.25× 或 segment_overflow | 改文案(删冗余字)而不是加倍速;播报 0.95–1.05× 为常规带 |
| 方言没出来(普通话参考 + 指令) | 参考音频语种与目标方言不匹配 | 用同方言参考音频重采;指令不能替代语料 |
| 授权缺失/到期仍在使用 | consent.json 缺失、范围不含该用途或已过期 | fail-closed:停用档案并留痕,不删证据;补授权前不得出新片 |