Extensions/IndexTTS2-PauseControl
ComfyUI Extension

IndexTTS2-PauseControl

IndexTTS2 精确停顿控制([pause:N])ComfyUI 节点包:句中与句号前后停顿精确调整,平均偏差 13ms;支持批量生成、候选试听验收、SRT 字幕与 seed 复现。Precise pause control ([pause:N]) ComfyUI nodes for IndexTTS2 — waveform-domain pause editing in-sentence and around periods, ±13ms, with batch generation, candidate acceptance and SRT subtitles.

By lynx-gt·Created 18 days ago·Updated 17 days ago· 4
lynx-gt/IndexTTS2-PauseControl
Nodes7
On cloudLocal install
CategoryIndexTTS
Stars4
Updated17 days ago
Readme

IndexTTS2-PauseControl

English: README_EN.md

这是什么

IndexTTS2 的语音合成中,停顿长短由模型自行决定且不可控——同一个逗号,不同次生成可能停顿 200ms 或 500ms,无法按需求指定。

本工具(ComfyUI 节点包)提供精确停顿控制:在文本中直接指定停顿要求:

他停下脚步[pause:800ms]深吸一口气[pause:200ms]然后推开了那扇门[pause:600ms]。

生成后,"停下脚步"后停顿 800ms,"深吸一口气"后 200ms,句号前("那扇门"后)600ms——实测平均偏差 13ms。句中、句号前后均可用同一方式指定。

模型在个别位置可能断句不稳定——应停顿处未停顿、或在无标点处出现停顿。标记不受标点限制:可在任意位置(包括无标点处)指定停顿,生成时由模型在该处产生停顿并精确调整为目标时长。

官方句间静音参数(interval_silence)默认 200ms,短于句中逗号的模型自然停顿(约 300ms)——会出现句号停顿反而比逗号短的倒挂。本项目节点默认400ms(与句号的自然停顿接近),并可对句号处写标记精确覆盖。

无需修改模型、无需重新训练,安装节点即可使用。

安装

方式一:完整包(开箱即用,推荐)

  1. ComfyUI-Manager 搜索安装:节点已上架官方 Comfy Registry,在 Manager 中搜索 IndexTTS2-PauseControl 一键安装。⚠️ 新上架节点可能暂未进入 Manager 的搜索索引(偶尔会搜不到),搜不到时请直接采用下面的手动安装:将本仓库目录(即 IndexTTS2-PauseControl/)放到 ComfyUI 的 custom_nodes/ 下,目录名保持 IndexTTS2-PauseControl(⚠️ 如已存在同名目录请先备份——覆盖会替换旧内容)
  2. 安装依赖到 ComfyUI 的 Python 环境(见 requirements.txt
  3. 运行 python install.py(自动装依赖 + 检查模型目录),或手动按 requirements.txt 安装
  4. 下载 IndexTTS2 模型权重到 ComfyUI/models/index_tts/(模型来源见官方 index-tts/index-tts,权重不属于本仓库)
  5. 重启 ComfyUI

⚠️ 请以目录方式运行(放 custom_nodes/ 下),不要 pip install .——本项目 包名与官方 indextts 同名,pip 安装会覆盖官方推理包。

方式二:打补丁(已有官方 indextts 包的用户,非 ComfyUI 也可)

如果你已有官方 IndexTTS2 推理代码(官方整合包 / 官方 ComfyUI 节点 / 自建管线),只需应用本仓库 patch/ 目录下的修改——不限于 ComfyUI

  • ComfyUI 用户:打补丁后直接用本仓库节点(与方式一效果相同)
  • 非 ComfyUI 用户(Gradio WebUI 整合包、自建 API 等):打补丁后,在调用 IndexTTS2.infer(...) 时传入 pause_mode=True 即可获得 [pause:N] 能力 (详见 patch/PATCH_GUIDE.md;WebUI 接入方式未经实际部署验证)
  1. patch/modified/ 中列出的文件覆盖到你的 indextts 包对应位置
  2. patch/new_files/ 中列出的文件复制到对应目录
  3. 调用 IndexTTS2.infer(...) 时传入 pause_mode=True 即可(见 patch/PATCH_GUIDE.md

补丁采用「完整文件覆盖 + 补丁说明」而非 git diff——官方代码版本会漂移,覆盖文件更可靠。

快速开始

1. 写标记

在文本中想要停顿的位置,直接写 [pause:时长]

他停下脚步[pause:800ms]深吸一口气[pause:200ms]然后推开了那扇门。
他停下来[pause:1.5s]深吸一口气。

支持的写法:[pause:600ms] / [pause:600] / [pause:1.5s] / [pause:0.8s](也兼容 [wait:][stop:] 前缀)。生成时标记会被替换成普通逗号送入模型,不会读出"pause"之类的字。

2. 生成

ComfyUI 中连好工作流(示例见 workflows/):

IndexTTSLoader → IndexTTSSingle(打开 pause_mode) → PreviewAudio

导入示例工作流后,把 spk_ref 改成你的音色参考音频路径即可。

3. 试听对比

examples/audio/ 提供 4 条对比音频(同音色、同 seed,唯一差异是有没有标记):

| 文件 | 内容 | 实测停顿 | |------|------|---------| | zh_plain.wav | 他停下脚步,深吸一口气,然后推开了那扇门。 | 469/499/359ms(自然节奏,不规律) | | zh_pause.wav | 他停下脚步[pause:800ms]深吸一口气[pause:200ms]… | 798ms / 190ms(精确命中) | | en_plain.wav | He stopped and took a deep breath. | 80/309ms(自然) | | en_pause.wav | He stopped[pause:800ms]and took a deep breath. | 798ms(精确命中) |

波形对比(点击波形图下载音频试听):

| 中文 | 英文 | |------|------| | zh_plain 波形<br>zh_plain(无 pause) | en_plain 波形<br>en_plain(无 pause) | | zh_pause 波形<br>zh_pause(有 pause) | en_pause 波形<br>en_pause(有 pause) |

参考音色来自第三方收集音色(非本项目作者声音)。也可用examples/make_compare_demo.py 配合你自己的参考音频重新生成。

标记的更多细节

句号前后(最稳定,两种写法都精确):

他深吸一口气[pause:800ms]。然后推开了门。    ← 句号前
他深吸一口气。[pause:800ms]然后推开了门。    ← 句号后

时长范围:推荐 150ms ~ 5s(下限约 100ms;上限无硬限制,1s/2s/5s 都可以)。

中英文均可:英文句子同样精确(800ms 目标 → 实测 778ms)。

功能一览

核心能力——精确控制停顿

  • 句中停顿(逗号、顿号处):精确调整,±20ms
  • 句号前后停顿:句号前、句号后两种写法都支持;即使模型在该处没有自然停顿,也会自动用"句间静音"实现目标时长——模型停不停都能达到你要的时长
  • 引号场景句号+引号…。')处同样可控,不用刻意避开
  • 统一句号停顿:不写标记时,句号处停顿由 interval_silence 统一控制(默认 400ms)。官方默认 200ms 时,段间停顿短于句中逗号的模型自然停顿(约 300ms),会出现句号停顿反而比逗号短的倒挂;400ms 与句号的自然停顿更接近,听感自然。适合先定整体节奏,再对个别句号精确覆盖

配套的生产流程能力(做长篇内容时用得上):

  • 批量生成:分句稿(Markdown)或多行文本,一次跑全书;断点续跑
  • 候选挑选:每段生成多轮候选(rounds),试听节点里逐轮试听
  • 验收标记:听中意的候选,一键标记"第 N 轮通过"(写入 manifest,供后续拼接)
  • seed 复现:固定 seed 可精确复现同一条音频;随机时自动记录实际 seed
  • 字幕生成:按验收结果生成 SRT 字幕(读实际音频时长,自动对齐)
  • 停顿微调:已生成的音频,按"序号"或"时间点"调整/删除某个停顿
  • 显存释放:TTS 跑完、切到视频生成流程前,一键卸载模型腾显存

使用进阶

批量生成 + 候选试听 + 验收

工作流:

IndexTTSLoader → IndexTTSBatch(打开 pause_mode,rounds=3) → IndexTTSListen → PreviewAudio×3
  • IndexTTSBatch 按分句稿(segments_md 填文件路径)或 text 多行文本批量生成
  • 每段生成 rounds 轮候选(如 001_1.wav001_2.wav001_3.wav
  • IndexTTSListen 选片段号试听 3 个候选;accept_round 填 1/2/3 标记"第 N 轮通过验收"(写入 manifest.json,供后续拼接/字幕流程选用)

分句稿格式(Markdown)

segments_md 接受分句稿 Markdown 文件,格式:

---
story: 我的故事
voice_ref: references/我的音色/main.wav
emotion_base: references/译制片语气
emotions: [平静, 紧张]
speaking_speed: 1.0
max_text_tokens_per_segment: 120
---

# 片段 001 [叙述 / 平静]
<!-- 标题: 标准同样严苛 -->
标准同样严苛[pause:800ms]不多一分。

<!-- 处理后: 标准同样严苛[pause:800ms]不多一分。 -->
  • YAML 头(可选):story / voice_ref / emotion_base / emotions / speaking_speed / max_text_tokens_per_segment
  • 片段标题# 片段 N [角色 / 情绪]——情绪用于情感参考挑选("目录随机"策略)
  • 正文:生成文本,[pause:N] 原样保留;若提供 <!-- 处理后: ... -->,以其内容为准
  • 建议一句一个片段(句号停顿统一由句间静音控制,节奏规整)
  • 不填分句稿时,直接在 text 框按行写文本(每行 = 一个片段)

批量输出

运行后产生一个任务目录output/{tag}_{时间戳}/,或自定义 output_dir):

output/批量生成_20260807_120000/
├── 001_1.wav        片段 1 第 1 轮候选
├── 001_1.wav.seed   该轮种子(固定 seed 可复现)
├── 001_2.wav / 001_2.wav.seed
├── 001_3.wav / 001_3.wav.seed
├── 002_1.wav …      片段 2 的三轮候选
└── manifest.json    任务参数 + 每片段每轮的 seed/时长/验收标记
  • 命名:{片段序号:03d}_{轮次}.wav001_1 = 片段 1 第 1 轮)
  • 再次运行同一任务目录 = 断点续跑(已完成轮次跳过;参数变更则全量重跑)

节点参数

| 节点 | 参数 | 说明 | |------|------|------| | IndexTTSSingle | pause_mode | 开启 [pause:N] 精确停顿控制 | | IndexTTSSingle | detect_cfm_steps | 已弃用(无作用),保留兼容旧工作流 | | IndexTTSBatch | pause_mode | 批量开启;开启后自动跳过旧版后处理 | | IndexTTSBatch | rounds | 每片段生成轮次(候选数) | | IndexTTSBatch | interval_silence | 句号处停顿 ms(未写标记时,默认 400) | | IndexTTSListen | task_dir | 接收批量节点的 task_dir 输出(连线优先) | | IndexTTSListen | accept_round | 验收:0=仅试听;1/2/3=标记该轮通过(写入 manifest.json) | | IndexTTSUnload | model | 释放模型显存(TTS 跑完、进视频流程前调用) | | IndexTTSFix | marks | 停顿修复串:2:800, 3:0(序号:目标ms,0=删除)或 7.53:500(时间点:目标ms) | | IndexTTSSrt | chosen | 定版选择:1,2,1,3(每片段轮次);单值 3=全部第 3 轮;空=全第 1 轮 |

建议的文本组织方式

  • 分句稿一句一个片段;句号停顿默认统一(interval_silence),个别要精确的写标记覆盖
  • 句内停顿直接写 [pause:N]
  • 长句(>40 字)多标记时,个别标记可能因模型停顿波动未命中——换 seed 或从多轮候选中挑(rounds=3 通常有全命中的候选)

工作原理(简述)

想了解实现原理的看这里,日常使用不需要:

  1. 分句:文本按标点句切分(句号=分句边界;句内不切,保持语气连贯)
  2. 标记分类:句中标记 → 分句内处理;句号前/后标记 → 分句边界处理
  3. 分句内处理:标记转逗号 → 模型正常生成 → 用能量检测找出音频里的停顿(物理信号,不依赖识别模型)→ 把标记与停顿全局对齐(容忍模型多停/漏停)→ 只对停顿的"静音核心"做延长/缩短/插入(不碰语音,免重新解码)
  4. 分句边界处理:分句尾标记命中则跳过句间静音(防叠加),未命中则用句间静音补目标时长;最后一个分句未命中时在音频末尾追加静音
  5. 拼接:句间静音 = interval_silence(默认 400ms),被标记覆盖的间隙用标记时长

术语分句(segment)= 按标点切出的标点句(官方 split_sentences 输出);片段 = 分句稿中的 # 片段 N 条目(一个片段可能含多个分句)。

详细方案、参数标定与精度数据见 docs/PAUSE_CONTROL.md;术语与代码符号对照、数据流见 docs/CODE_WALKTHROUGH.md

已知限制

  • 长单句多标记:标记位置按字符比例估算,长句(>40 字)误差可能超过匹配上限,个别标记可能未命中——换 seed 或从多轮候选中挑选即可覆盖
  • 分句尾模型无停顿:会改用句间静音/末尾追加实现目标时长(时长仍达成),但停顿位置落在句号处而非标记字后——听感正确,位置略偏
  • 长停顿后的换气声:模型自然韵律(长停顿后可能吸气),工具不处理呼吸声,介意可对该段做降噪后处理
  • 插入需要真静音:语音连续处无法插入停顿(宁缺毋滥,不切字)

测试

test/ 提供三层测试:

python test/unit_test.py    # 核心函数单元测试(无需 GPU/模型,CI 自动跑)
python test/smoke_test.py --model-dir <模型目录> --spk-ref <参考音频>   # 中英双语冒烟
python test/regression_test.py --model-dir <模型目录> --spk-ref <参考音频>  # 5 片段回归

发布版实测:冒烟中英 PASS;回归 5 片段平均偏差 5ms。

致谢

致谢不代表上述团队对本项目的认可或背书;对原模型的修改与官方无关(见 THIRD_PARTY_NOTICES.md)。

License

  • 本项目原创部分(ComfyUI 节点、pause 方案、文档):MIT License(见 LICENSE
  • indextts/ 推理代码:源自 bilibili IndexTTS2,受 bilibili Model Use LicenseAgreement 约束(商用需官方授权等),完整条款见 LICENSE.bilibili.txtTHIRD_PARTY_NOTICES.md