万字长稿转有声读物,整篇扔给 TTS 接口,第一分钟就会碰壁:请求体超限被拒,或者接口有单段长度上限;侥幸全吞进去的,后半段韵律崩坏,语速越读越快像赶场。正解是分片合成再拼接,听着简单,做好靠三个工程细节。
切分:切在句界,不按字数硬切
半句中间拼接,一听就穿帮。切分逻辑按句末标点分句,再攒到目标长度:
import re
def chunk_text(text, max_chars=200):
sents = [s.strip() for s in re.split(r'(?<=[。!?;])', text) if s.strip()]
chunks, buf = [], ""
for s in sents:
if len(buf) + len(s) > max_chars and buf:
chunks.append(buf)
buf = s
else:
buf += s
if buf:
chunks.append(buf)
return chunks
单片 150~300 字是稳定区间:太短调用次数暴涨、韵律碎片化;太长单片重试代价大。段落空行处强制断开,给成片留"章节感"的换气点。
一致性:参数快照防音色漂移
分片合成最大的隐性坑是各片参数不一致——拼接处音色漂移。temperature、语速、音色版本必须全片统一且显式传值,不依赖接口默认值(默认值随服务版本变)。真实教训:月初月底各合成一批,接口默认语速悄悄改过一版,同一本书两个月的章节语速出现可闻差异。对策是把"合成参数快照"写进每本书的元数据,重合成按快照回填——版本漂移永远会发生,唯一可靠的是自己留档。
衔接:静音按标点分级
片间直接首尾相接太赶,插长静音又断气。实践口径:句号 350ms、问号叹号 400ms、段落界 800ms,静音用零值 PCM 拼装,注意采样率对齐。拼接完成后统一做响度归一:
ffmpeg -i joined.wav -af loudnorm=I=-16:TP=-1.5:LRA=11 final.wav
这步处理的是分片间微小能量差异——不做的话,长听感像音量在缓慢随机游走,听着容易疲劳。
验收与流水线
四步验收:抽听每章开头结尾确认无半句拼接;同一本书随机抽两章对比音色语速一致;整篇响度无突变;断网重试只重跑失败片。流水线定形后就是:切分 → 逐片提交(失败单片重试)→ 按序拼接 → 归一化。文本源、参数快照、成片、分片缓存四个目录一摆,重合成任意一本书只需换文本。没有哪一步难,但拼起来少一步都不行——这正是工程和调 API 的区别。

评论0