先切短句再合成长文——dots.tts 长文本处理的节奏控制

文章导读
长文合成出现中途断掉、两句之间咬字粘连或段落衔接生硬,通常不是模型能力问题,而是单次输入太长、切分点选在了不该断的位置。可行的处理顺序是:先把原文按标点和语义拆成 1-3 句的小段,给每段编号并记录其在原文中的位置,再处理拼接处的静音与语速,最后用试听回查定位需要重合成的片段,只重跑问题段而不是整篇重来。
📋 目录
  1. A 按标点和语义把长文切成 1-3 句的小段
  2. B 给每段编号并保留原文顺序
  3. C 检查段落拼接处的静音和语速
  4. D 用试听回查定位需要重合成的片段
A A

长文合成出现中途断掉、两句之间咬字粘连或段落衔接生硬,通常不是模型能力问题,而是单次输入太长、切分点选在了不该断的位置。可行的处理顺序是:先把原文按标点和语义拆成 1-3 句的小段,给每段编号并记录其在原文中的位置,再处理拼接处的静音与语速,最后用试听回查定位需要重合成的片段,只重跑问题段而不是整篇重来。

长文合成的节奏问题,多数出在切分粒度和拼接缝上,而不是模型本身。适用场景是分钟级旁白、有声书、课程稿这类连续文本:先按句末标点切成 1-3 句的小段,用 001_ 前缀编号并记录每段在原文中的起止位置,拼接时按环境试加一小段静音,再逐段回听语速是否一致。验证只针对段落边界和问题段,需要改的片段单独重跑,不整篇重来;静音过长会拖慢节奏,语速参数改动后要重新回查。

按标点和语义把长文切成 1-3 句的小段

一次输入几百字,停顿位置就交给模型自己决定了,同一段文字两次合成可能断在不同地方。把每个片段的长度压到 1-3 句、几十个字,停顿点基本落在你选定的标点上,节奏才可控。切分时按优先级往下走,能在一级切开就不要用二级。

优先级切分依据处理动作需要注意
1句末标点:。!?直接在此处断开,作为段落边界引号内、括号内的句末标点不算边界
2分号:;一段按句末标点切完仍偏长时,在分号处再切分号切开的往往是对举句,两段语气要能各自收住
3逗号:,只在单段超过字数上限时作为兜底使用从逗号断开容易造成语气悬空,切完必须试听
4连词:但是、因此、于是、然后在连词前断开,连词留在后一段开头不要让连词单独成段,也不要以连词结尾收段

不建议切断的位置包括:引号内、括号内、书名号内、数字与单位之间(例如 “120 毫秒” 这类写法)、人名与职务之间,以及连词与其后分句之间。如果把 “但是” 和后面的分句切开,前一段会以一个悬空连接词收尾,听起来就像被掐断。

# 通用切分骨架,不依赖具体 TTS 接口
import re

SENT  = re.compile(r'(?<=[。!?!?])')    # 一级:句末标点
SEMI  = re.compile(r'(?<=[;;])')         # 二级:分号
COMMA = re.compile(r'(?<=[,,])')         # 三级:逗号
CONJ  = ('但是', '因此', '于是', '然后', '所以')  # 兜底:连词前断开

def split_long(text, max_chars=120, max_sent=3):
    # 1) 先按句末标点切句
    # 2) 累积到 max_sent 句或超过 max_chars 就成段
    # 3) 单句仍超长时,依次尝试分号、逗号、连词位置
    ...

切完之后先看每段的字数分布。如果某一段明显比其它段长,多半是里面含有多层引号或括号,需要手动再拆一次。

给每段编号并保留原文顺序

切分脚本最容易出的问题是顺序错乱和漏段,尤其是并发合成时返回顺序和提交顺序对不上。命名规则建议用三位序号加简短标识,例如 001_开头四个字,序号按原文出现顺序分配,标识只用于人工核对,不参与排序。

先切短句再合成长文——dots.tts 长文本处理的节奏控制

同时把每段在原文中的起止字符位置写进清单文件,合成结果也按同样的编号落盘,最后按序号排序拼接,而不是按返回时间排序:

# segments.jsonl,每行一段
{"id": "001", "name": "001_开头四个字", "start": 0,  "end": 78,  "text": "..."}
{"id": "002", "name": "002_下一段",     "start": 78, "end": 150, "text": "..."}

有了 start/end,就能反查某一段对应原文的哪一句。发现漏字或多字时,直接按位置回原文比对,不用通读全文;切分脚本改动后重新生成清单,也比对前后两次的 start/end 是否连续。

检查段落拼接处的静音和语速

直接首尾相接有两个常见听感问题:前一段尾音被后一段起音吃掉,或者两段之间完全没有呼吸感。通常的做法是在拼接处插入一小段静音,具体时长需要结合环境确认,可以先从很短的量级试起,用同一段文字做两次拼接对比:一次直接相接,一次在段落之间加静音,只听接缝那几秒。

拼接静音与语速检查项:

先切短句再合成长文——dots.tts 长文本处理的节奏控制
  • 段尾是否被截断:单独播放每一段,听最后一个字的韵母是否完整;
  • 段首是否吃字:后一段的第一个字是否被静音或淡入削掉;
  • 静音是否过长:连续多段之后整体节奏是否被拖慢,独白稿尤其明显;
  • 句末语气是否收住:疑问句、感叹句的调子有没有被下一段打断;
  • 语速是否一致:段落之间有没有忽快忽慢,切分点落在逗号上的段落更容易出现;
  • 音量是否有台阶:不同段落的响度是否突然跳变。

语速参数通常按整篇设置。如果发现某一段明显偏快,先检查该段文本是否过短——短句在同样语速下听起来更快,这种情况适合合并相邻短句,而不是先去调全局语速。

用试听回查定位需要重合成的片段

整篇重跑既慢又难比较。回查时按编号顺序只听段落边界:每段末尾和下一段开头各听一遍,标出有问题的编号。确认问题只影响个别段落时,只重跑这些段落,其余段落沿用已有结果。

问题段记录建议用一张表,字段至少包含段落编号、问题类型、修改后的文本、重跑结果:

segment_id,problem_type,text_revised,rerun_result
003,尾音截断,调整句末标点后的文本,重新合成后待回听
007,衔接生硬,拆分后的两段文本,加入段间静音后待回听

重跑后回到同一位置复听,确认问题消失再把该段替换进拼接列表。如果问题依旧,先改文本(调整切分点或补一个句末标点)再合成,不要反复用同样的文本试参数。整篇拼接完成后再完整听一遍,重点听之前标记过的边界位置。