# 创造全流程 —— 这套配音系统是怎么被一步步调出来的

> 一句话版本：从"裸 TTS + 人耳全量重roll"出发，每次翻车都被你变成一条机制，
> 最后机器自己质检、自己重roll、自己按音色+节奏+重音挑版，你只听边缘案例和成品。
> 主体发生在 2026-06-10 ~ 06-11 两天、约 20 轮人机往返；06-12 起进入边产边修阶段（声调哨兵等）。

---

## 第 0 步 · 起点：裸流水线与真正的病根

最初形态只有一个 `配音.py`：文案切句 → 逐句调 Fish Audio 合成 → ffmpeg 拼接。
问题是"不稳定"，每次都要人耳全量听一遍、手动重roll。

**关键的第一刀是你下的诊断**：病根不是错字，而是
1. 有些地方**不像本人**（音色漂移）
2. **说话方式形变**（腔调怪）

这个定义决定了之后的一切——主质检手段选了**声纹相似度**而不是 ASR 对字，
因为要量化的是"像不像你"，不是"读没读对"。

## 第 1 步 · 把"耳朵"装进机器：声纹质检（质检.py）

- 本地跑 CAM++ 声纹模型（`models\campplus_zh-cn_16k.onnx`，零费用），
  算每段与"锚点质心"（你认可过的音色集合）的余弦相似度 = **像本人分数**
- 红/黄/绿判级：绿=机器替你听过，黄=拿不准建议人工，红=当场自动重roll
- 短段（<2.5s）声纹天然测不准 → 最差只判黄、永不自动 roll（防误杀）

**你的校准方法**：要求听重roll前的旧版（"这样我能知道你的判断是不是准确的"）。
结果你曾手动 roll 过的 002 段被机器独立判进低分区——人机判断第一次对齐。

## 第 2 步 · 自迭代闭环：反馈.py

你提出"能不能加入一个自迭代系统，随着每次配音加上我的反馈能越来越稳定"。落地为一条命令的反馈：

- `最像` → 3 倍权重滚入锚点（基准向你的耳朵靠拢）
- `不像` → 入负样本库（候选若更像负样本直接降级）
- `读错` → 只记台账（读音问题≠音色问题，不污染声纹库）

第一条反馈就有信息量：你耳选了机器排名第 3 的版本——人机排序不完全一致是正常校准信号。

## 第 3 步 · 质量地板与天花板定律

你定了地板："为了质量考量多roll几次，尽量 0.95 以上"，后来升级为铁律：
**一切修改以音色为基础标准，至少 0.95，金句允许 0.93**
（06-11 再加码为最终版：**0.95 无例外**，0.93 档删除、金句也不例外——见大事记）。
你还做了一次元验证：0.95 和 0.9 的听感差距真实可辨——分数体系和人耳是一致的。

机器侧随之发现**天花板定律**：分数上限与段长强相关，
<25 有效字的短段怎么 roll 都到不了 0.95（章标题 15 版 max 0.860）。由此落地三件套：

1. **短句并段**（min_unit_chars=25，按发音字数计）——治本
2. **分层目标分**（40+字→0.95 / 25~39→0.93 / 更短→0.91）——不为够不着的分白烧额度
   （06-11 被"0.95 无例外"取代：够不着就并段加长 `--minchars`，不降目标）
3. **roll 全留档**（roll留档_NNN\ 带分数文件名）——机器的每次取舍可审计

## 第 4 步 · 请进真人：原声入锚与参考接入

你给了树林语音包真实录音（"地球就是一个巨大的陌生人社交游戏"，195s/803字）。两个用途：

- **节奏画像**：每 11 字停一次、停顿中位 0.31s（95% 在 0.25~0.4s）、纯发声 4.67 字/s
  ——高频小停顿风格，文案行结构就是停顿地图
- **真人参考接入**：A/B/C 对照实验后，B 配置胜出（voice_id + 每次请求附带 25s 原声参考），
  像本人 0.939→0.954 且方差更小，从此默认开启

随后你下了**锚点冻结令**："声纹识别永远只拿现阶段给你的音频当标准，
之后给你的音频只能拿来训练说话的节奏。"——音色基准从此恒定（`anchor_frozen=true`，
冻结备份 `models\锚点_冻结基准_20260610.npz`）。

## 第 5 步 · 节奏形态定型：一次大翻车换来的

为了贴近你的断句习惯，先试了**行级独立短请求**（每行单独合成）——大翻车：
乱码、莫名其妙的语音、拖尾怪音（你的原话"它甚至都会有乱码的那种"）。
教训：**短文本请求是乱码之源**。

定型方案 `rhythm_line_end`：**段级长请求 + 行尾逗号句号化**。
TTS 对句号自然停 ~0.31s、约每 11 字一停——恰好等于你的真人画像，
实测 0.95~0.97 全绿零乱码。段间停顿 0.45s（真人实测值）。

你顺手给了一条耳测标准："不是。"被单独拎出来读是加分项——强调短句要拎出来。

## 第 6 步 · 两条血泪铁律

**铁律一：永不调速。** 一次提速尝试出了电音，你的原话：
"记住了，永远不能调速，调快、调慢都不行。" `prosody.speed` 从此焊死 1.0，
节奏只能动文本形态（标点、行尾句号、段间 gap），没有别的办法。
（附：真人 82s vs 合成 74s 的差距，95% 来自模型咬字基础速率，约束内无解，已接受为模型属性。）

**铁律二：永不在输出端去呼吸。** 对合成音频做呼吸衰减手术（42 处）被你一票否决
（"完全不行……一坨……很奇怪"）。呼吸的正解是三层：

1. **源头**：参考音频里的呼吸剪成纯静音（参考是提示不是波形复制，无违和）→ 合成呼吸减半，音色不掉
2. **检测**：连喘模式（相邻呼吸间隔 <0.45s，实案"呵、呵"）→ 韵律⚠ → 触发 roll 换版
3. **成品**：BGM 标准浓度 0.18 掩盖残余

呼吸成因也是这一步查清的：voice 训练数据自带倾向 + references 参考每次强化（官网无 references 所以少）。

## 第 7 步 · 读音系统化："不要只当作一次性的经验"

错读案例攒了一批（校验读 xiào、重装读 chóng、一本线读 yì、考得差读 chā、
"转换为/什么"切分错、全盘读不出），你提了系统化要求：
**修完一类问题要落"预检 + 决策树 + 案例库"三件套，不许一次性经验。** 落地为 `发音规则.md` 六分支决策树：

1. **分词歧义型 → 标点手术**（确定性最高优先用）："认知转换，为什么重要"根治 wéi 错读
2. **词典型 → 同音字替换**：声调必须一致、按词组防误伤、**假词必须人耳验证连读**
   （"较验"被拆开读的教训）；定案：校验→教验、全盘→全磐、差chà系→岔
3. 修复后 `重roll.py --force` 覆盖（跳过防降级保护）

配套机制：**风险库预检**（合成前自动标出"听点"）、**数字口语化**
（六百八不说六百八十、8%→百分之八、2→两分钟）、字幕/预览永远显示原文。

## 第 8 步 · 高阶韵律：从你的演讲里偷师

三项专题研究，数据全部来自你的真实录音：

- **悬停规律**（162 次悬停）：戏剧性悬停全部来自即兴演讲（念稿场景零悬停、停顿上限 0.49s）；
  可偷的技巧是"**说完金句再悬停**"（悬停前 78% 是短句）。结论：悬停要克制——
  文案用 `!` 标记金句，每批至多 1~2 处，防疲劳（你的原话"不是所有的金句都一定要选的"）
- **语速起伏**（691 块统计）：你的语速起伏 1.55 倍，分工明确——
  **设问快抛（7~9字/s，钩子）→ 解释推进 → 金句慢砸（3~4字/s）+ 悬停（钉子）**。
  落地为质检节奏分的内容自适应区间（宣告段该慢/设问段该快），消除误报
- **重音签名**（12397 字字级分析）：句首砸 +1.01 / 句尾滑 -0.83（纯能量口径），
  枢纽词（但/而/却/最/更）必须砸、虚词（的/了/吗/呢）必须滑。
  关键校准：TTS 天然会句首尾起伏（比你还夸张），所以判别力放在**句中类别重音**

最终合流为 **roll 三维挑版**：音色 + 节奏 + 重音 → 综合分（韵律问题每个罚 0.01，
量级保证音色永远优先），红段必roll、韵律⚠在配额内换版。

## 第 9 步 · 结构手术：音色与节奏解耦

两个"硬约束打架"的问题（短段要独立才有停顿感，但独立合成音色必崩），都用**音频层手术**解开：

- **章标题根治**：标题自动并入下文凑足 40+ 字合成（音色 0.868→0.9776），
  1.0s 章节停顿由 `悬停.py` 在音频层对标题尾拉伸补回——全自动，无需标记
- **金句悬停（停顿拉伸术）**：金句并在长段里合成保音色（0.95±），
  再定位金句尾句号的静音、正中切开插入差额拉到 0.65s——零 API 成本、零音色损伤

副产物：**SRT 一键随行**（段级精确打点 + 段内按字数比例分句，行尾不留标点），每次合并自动产出。

## 第 10 步 · 现在的成品工作流

```
1. 文案贴进 文案.txt（金句段开头标 !，每批至多1~2处）
2. python 配音.py 训练\批NN\文案.txt --out 训练\批NN
3. python 悬停.py && python 合并.py        （有金句/章标题时）
4. 带BGM成品：合并_带BGM.mp3（标准浓度 0.18）
```

跑一次配音，机器在暗中做完的事：读音风险预检 → 短句并段/标题并段 → 数字口语化 →
行尾句号化 → 附带真人参考合成 → 声纹+节奏+重音三维质检 → 不达标自动 roll（分层目标+预算熔断）→
连喘检测 → 全留档 → SRT。你只需要听成品和它标出来的"听点"。

---

## 大事记

| 时间 | 事件 |
|---|---|
| 06-10 | 诊断病根=音色漂移非错字；P1 声纹质检上线（CAM++ + 锚点 + 红黄绿 + 自动重roll） |
| 06-10 | 反馈.py 自迭代闭环；90s/3分钟稳定性测试；SRT 成为标配 |
| 06-10 | 真人原声入锚 + 节奏画像（11字一停/0.31s）；真人参考 A/B/C 实验，B 胜出默认接入 |
| 06-10 | 行级请求乱码大翻车 → rhythm_line_end 定型（段级长请求+行尾句号化） |
| 06-10 | **调速禁令**（电音）；锚点冻结令（现阶段音频=永久音色基准） |
| 06-10 | 悬停规律研究（162次）→ 金句后悬停 + `!` 标记克制使用 |
| 06-10 | 语速起伏画像（1.55x，设问快/金句慢）→ 节奏分内容自适应 |
| 06-10~11 | 读音系统化：发音规则.md 决策树 + 风险库预检 + 数字口语化 |
| 06-11 | **0.95 音色铁律**；分层目标分；天花板定律 → 短句并段 |
| 06-11 | 重音签名研究（12397字）→ roll 三维挑版上线 |
| 06-11 | 标题并段根治（0.868→0.9776）+ 悬停拉伸术 + 章节停顿自动补回 |
| 06-11 | **输出端去呼吸被否**（"一坨"）→ 呼吸三层方案（净化参考/连喘检测/BGM） |
| 06-11 | 批01~批05 训练完成；首个正式成品《知道与相信》全绿 0.956~0.977 |
| 06-11 | 复利四件套上线（错读闸门/听点回流/批次台账/语料清单）；《注意力的战争》全绿 0.950~0.968 |
| 06-11 | 并行施工上线：3工人队列+段内2路roll+限流闸，干跑同成本 131s→48s（2.7×）；产物与串行逐字节一致 |
| 06-11 | 《黑粉》全绿 0.953~0.980（均值 0.965 新高）；听点回流首战：电音尾=真警报、便宜=误报，风险库自收敛开始转 |
| 06-11 | 数字口语化两根因修复：小数/百分比小数/千分位支持 +「百X十」略读收窄（《黑粉》「一百五」真凶=自家规则）；预算饿死警报上线 |
| 06-11 | 《命运的排除法》耳测掉档破案：主因=我的碎排版（短句逐行句号化）+重roll扫尾韵律盲排序，均已修；"原文标点=停顿地图"入规范；祛魅=qù 四声用户读法定案（读音表→去魅） |
| 06-11 | **0.95 无例外定案**（用户："0.93 绝对不行，不要为金句强行分行"）：target_tiers→[[0,0.95]]、`--minchars` 并段开关上线；《命运的排除法》终版 3 大段 **0.960~0.971 全过线**（0.918 顶死的格言段并入 134 字大段→0.960，"文体天花板"证伪=段长效应）；段中悬停手术首战成功；力竭→力杰读音案（jié 被读 jiě） |
| 06-12 | **声调哨兵上线**（"不一定"读 bú、roll 不愈、ASR 全盲案催生）：风险库 `tone_sentinels` 列管字按字级时间戳截音频测基频验收四声，判据=降幅≥10%（对照实验：耳朵听成二声的是 -5%/-7% 浅降）；首战识破自动挑版选中的 +1% 平调版，改派 -28% 版，用户耳测验收通过 |
| 06-12 | 《高质量的失败》8 段全过 0.95（0.9642~，均值 0.9715 / 复听负担 0.37 分钟）——声调哨兵首战批 |

## 翻车博物馆（每次翻车 → 一条机制）

| 翻车 | 教训 → 机制 |
|---|---|
| 行级短请求出乱码/拖尾怪音 | 短文本请求是乱码之源 → 段级长请求 + 行尾句号化（verbatim 模式行尾必须补句号） |
| 调速出电音 | 永不调速，speed 焊死 1.0，节奏只动文本形态 |
| 输出端去呼吸"一坨" | 永不后期动人声波形 → 输入端净化参考 + BGM 掩盖 |
| 弯引号"X"替换直引号静默失败（两次） | 文本替换后必须程序验证生效，关键替换用正则 |
| "较验"假词被拆开读 | 同音字替换必须按词、声调一致、人耳验证连读 |
| sherpa-onnx ASR 报 invalid unordered_map key | Windows 中文路径打不开 ASR 模型 → 模型放纯 ASCII 路径（C:\Users\Public\asr-paraformer*） |
| 重音时长口径全是伪影 | 停顿污染时长 → 重音只用纯能量口径 |
| PowerShell $d 覆盖 $D / UTF8 BOM 读坏指针文件 | PS5.1 变量大小写不敏感；文本读取用 utf-8-sig |
| 连喘检测 0.6s 阈值误报你认可的音频 | 阈值收紧到 0.45s——检测器要对齐你的耳朵，不是想当然 |
| 错读闸门把助词「地」标成读错（5/6 听点是它，白烧 roll） | ASR 按书面语法把 de 归一成「的」→ 的/地/得替换互相宽恕（同儿化音先例：ASR 的转写习惯≠错读） |
| 《黑粉》「一百五十」读成「一百五」，先冤枉了 TTS 缩读 | 真凶=自家口语略读规则误吞「十」（680 案例的负向断言没排除量词「个」）；错读闸门对照的是被污染后的发音文本，全程沉默 → 略读收窄到句尾/撞 shì 音 + normalize 过 14 项用例 battery；闸门只能守「送出去的文本」，送出前的变换要自带测试 |
| 0.901 的段一次 roll 没拿到却报「全绿」 | 全局 roll 预算被先完成的段烧光，LPT 调度下最短段必饿死 → ⚠预算尽 警报（段尾标记+收尾汇总+补法），「分数凑合」≠「天花板」 |
| 「祛魅」读成「趣味」，roll 4 次不愈 | 生僻术语是系统性错读，roll 治不了 → 读音表一击修复；新术语首遇先入风险库盯听 |
| 「祛魅」我修成"驱"(一声)，用户耳测：是四声 | 读音修复的"正确读音"以**用户读法**为准（同巴甫洛夫 pǔ 先例），不是字典 → 改读音表前先按用户读法定声调，定不了就留听点请耳裁决（终版：祛魅→去魅 qù） |
| 《命运的排除法》整批耳测掉档，我先归因"文体天花板" | 主因=我把用户原文切碎成 2~6 字行（句号密度翻倍，TTS 碎读）——违反了系统自己发现的天花板定律（用户点名：只有长段保质量）；恢复"原文标点=停顿地图"后 5/7 段回 0.95+ → 长段原则=段长×段内纹理双维度，--preview 版式预检自动拦截（段<40 字 / 句号间均<8 字），不靠排版人的记性 |
| 重roll.py 扫尾换上的版本节奏没把关（整批听感再伤一刀） | 旧排序只罚错读不罚韵律，与批内挑版口径不一致 → 综合分对齐（音色−0.01×韵律−0.02×错读），现版对比也按综合分 |
| "不一定"的"不"读成二声，roll 不愈，闸门全程沉默 | 声调错读是 ASR 盲区（LM 把转写纠正回正字）→ **声调哨兵**：列管字按字级时间戳截音频测基频，对照实验定标出反直觉判据——**耳朵听成二声的其实是"浅降"（-5%/-7%）而非上升**，故合格线=降幅≥10%（置信四声实测 -20%~-28%）；读音表同步 不一定→部一定（死四声字）。首战即纠错：自动挑版选中的 +1% 平调版被哨兵识破，换成 -28% 版。声调类错读从"耳朵反复抓"升级为"机器逐候选声学验收" |
| 用户报「在这」有电音，五维硬伤全阴性 | 真凶=去声「在」逐版渲染拔高、该降不降（~250Hz 平台 vs 好版 195 下降），整句均值正常→色准/HNR 质检与频谱探针都测不到；重roll 换版即愈、非文本因（疑"三十秒"重复=红鲱鱼，证伪）。试建**跨版离群检测**抓同类→**证伪**：坏版在连续分布(1.41~1.92)高端、非离散离群抓不住 → 这类只能 重roll+耳裁，诊断用同字 F0 叠轴（详见记忆 peiyin-tone-contour-glitch） |
| 「语速快」韵律⚠ 误报 56%，一次过率被压到 0~9%（翻 102 段档案才揪出） | 平叙线 5.8 正好卡在嗓子发声中位 5.87→半数段被误判超速、空触发节奏 roll、烧预算，而含停顿真实语速 4.97 很稳 → 阈值重标 6.2（≈发声 p80，只逮真快尾部 12%）；复利表「一次过率」难看的真凶是它、不是质量。阈值要按嗓子实际分布定，别拍脑袋 |
| 「地坐→的做」仍报错读（06-11 的 的/地/得宽恕没全生效） | 旧宽恕只在「整段差异都是 de 三兄弟」时放过，跨字组合（地坐→的做 = 地→的 ＋ 坐→做）漏网，且 pypinyin 把「地」读 dì≠de 令拼音宽恕也失败 → 在 `_norm_for_cer` 把 地/得→的 **比对前归一**（更彻底）；单元测：地坐→的做放过、真漏读/错读仍抓 |

## 如果有一天要从零复刻（换音色重建清单）

1. Fish Audio 建 voice，`config.json` 填 api_key/voice_id（密钥永不外传）
2. 录 20~30s 干净参考音频 + 逐字文本 → 呼吸剪成静音 → `models\参考音频.mp3` + `参考文本.txt`
3. 先合成一小批、人耳挑出认可段 → `python 质检.py --init` 建锚点
4. 给一段 3 分钟以上真实录音 → 跑节奏/语速/重音画像，校准质检区间
5. 批次训练（250~350字/批），每批反馈 `最像/不像/读错`，锚点进化到稳定后冻结
6. 全程守两条铁律：永不调速、永不后期动人声波形

## 文件地图（2026-06-12 更新）

```
自动化配音\
├─ 配音.py / 质检.py / 悬停.py / 合并.py / 重roll.py / 反馈.py   ← 六件套
├─ 加BGM.py / 语料.py                                          ← 成品BGM / 语料导出
├─ 质检校准.py / 对比试听.py / 对比长.py                          ← 调试期工具（备用）
├─ config.json（密钥+全部开关）  文案.txt（投稿箱）
├─ CLAUDE.md（AI 红线速查）/ 使用说明.md / 文案写作规范.md /
│  发音规则.md / 方案-人机协同配音流水线.md / 创造全流程.md（本文）
├─ models\        ← 系统的"脑子"：锚点+冻结备份 / 净化参考音频+原始备份 /
│                    声纹模型 / 读音风险库 / 各画像报告 / 反馈台账 / 批次指标 /
│                    语料清单 / ASR缓存（BGM曲目首次 --bgm 后收进此处）
├─ output_第六章成品\   ← 第六章（时间尺度）9分钟成品 + 文案
└─ 训练\
   ├─ 进度.md / 全文-原始.md / 全文-配音版.txt（高考全文，批06=第五章 待续）
   ├─ 批01v2 ~ 批05\         ← 各批成品（文案/逐段/合并/SRT/质检报告）
   ├─ 陌生人完整对比\         ← 真人 vs 合成 全文对照（含BGM版）
   └─ 试产-真实反馈\ + 正式成品五部：配音-知道与相信\ / 配音-注意力的战争\ /
      配音-黑粉\ / 配音-命运的排除法\ / 配音-高质量的失败\
```
（清理隔离区 `_待删归档\` 已于 2026-06-11 后删除。）

ASR 模型常驻位（不在本文件夹）：`C:\Users\Public\asr-paraformer`（03-28 标点版）、
`C:\Users\Public\asr-paraformer-ts`（09-14 时间戳版，质检.py 在用）——必须 ASCII 路径。
