长文本配音流水线:从分段到批量到压缩的一条完整流程
给绘本配了一百多本之后,我把流程固定下来了。这篇写的是现在实际在跑的那条线,从 txt 进、mp3 出,中间尽量少人工干预。
一、为什么必须先切句再合成
很多人图省事,把整章文本塞进去一次生成。会出问题:
- 单次生成太长,容易在中间某个地方开始发糊、漏字,而且没法只重录那一句。
- 断句和停顿完全不可控,长句一口气读完,听着累。
- 出错之后重来的成本太高。
切成小段之后,好处很直接:哪句读错了就重生成哪句,改一个字不用重录全文。
二、分段策略
我的规则:
- 优先按句号、问号、感叹号、分号切。
- 单段控制在 80 到 150 字,超过 200 字一定要切。
- 逗号多的长句,如果读起来会喘不上气,就在逗号处再切一刀。
- 每段首尾留干净,别带引号和空行。
- 每段单独存一个文件,文件名带序号,方便后面重录和拼接。
切完之后做两遍文本清洗,这两步能省掉大量返工:
- 数字和英文转中文写法。“2025年"写成"二零二五年”,“3.5米"写成"三点五米”,“iPhone"写成"苹果手机”。各家 TTS 对数字的处理规则不一样,统一转写是最稳的。
- 多音字用词表兜底。建一个自己的替换表,比如"重(chóng)新"“银行(háng)",合成前先跑一遍字符串替换。
三、批量队列
用 API 模式跑,写个简单的 Python 脚本,逻辑就三层:
import os, time
from concurrent.futures import ThreadPoolExecutor
def tts(text, out_path, retry=2):
for i in range(retry + 1):
try:
r = call_api(text) # 换成你自己的引擎调用
if r.ok:
save(r.audio, out_path)
return True
except Exception as e:
print(text[:20], e)
time.sleep(1)
return False # 失败的写进 failed.txt
files = sorted(os.listdir("chunks"))
with ThreadPoolExecutor(max_workers=2) as ex: # 并发别开太高,显存会炸
for f, ok in zip(files, ex.map(lambda f: tts(read(f), out(f)), files)):
log(f, ok)
几个要点:
- 并发数控制在 2 到 4。显存有限,开太高不会更快,只会 OOM。
- 失败要落盘。把失败的文本单独写进
failed.txt,跑完统一重跑,别让脚本中途崩掉前功尽弃。 - 日志记录每一段的耗时和状态,跑几百条的时候,没有日志你根本不知道卡在哪。
- 先跑 5 条试水,确认音色、语速、参数都对,再全量跑。这条能救你几小时。
四、音频压缩:码率和体积
本地生成出来一般是 wav,体积大得离谱,一分钟大约 10MB 上下,必须压。
我用 ffmpeg,常用的两套参数:
# 语音旁白,64k 单声道够用
ffmpeg -i in.wav -codec:a libmp3lame -b:a 64k -ac 1 -ar 44100 out.mp3
# 有背景音乐,或者想要更干净一点,用 96k
ffmpeg -i in.wav -codec:a libmp3lame -b:a 96k -ac 1 -ar 44100 out.mp3
经验值:
- 纯人声旁白 64k 单声道基本听不出损失,体积大概是原 wav 的十分之一左右。
- 带配乐或者要保留一点空间感,用 96k 到 128k。
- 采样率 44100 和 24000 对语音来说区别不大,儿童故事用 24000 能再省一截。
- 批量转换写成一行循环就行,几百个文件也就几分钟。
顺手做一下响度归一化,多段音频接在一起时音量不会忽大忽小:
ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11 out_norm.mp3
五、成品整理
- 目录结构:一本书一个文件夹,章节做子目录,文件名用
第01章_001.mp3这种零填充格式,排序不会乱。 - 拼接:按序号拼成整章或整本,ffmpeg 用 concat 文件列表最省事。
- 导出 m4b:要做有声书就导出 m4b,能带章节标记,播放器里可以直接跳章。
- manifest 表:写一个 CSV,记录每段的文件名、对应原文、生成时间、参数。哪天要改一句,直接查表定位,不用重新翻。
六、质检
这一步不能省,也不能全做。我的做法是抽 10%,重点听三类:
- 错读漏读(多音字、数字最容易出问题)
- 爆音和削顶(音量突然变大)
- 句尾崩掉(尾音拉长或突然断掉)
再扫一遍文件时长,异常短的文件基本都是生成失败。
实际账:2000 字的文本,纯生成大概几分钟到十几分钟,加上清洗和质检,半小时能出成品。比人工录快一个数量级,而且声音永远稳定。