ChatTTS 对话场景语音合成实战:安装部署、两阶段推理与细粒度韵律控制
ChatTTS 是 2noise 团队开源的面向日常对话(LLM 助手等对话场景)的生成式语音合成模型。本文基于仓库 README 的完整脉络展开,并结合 ChatTTS/core.py、ChatTTS/config/config.py、examples/cmd/ 下的真实实现,讲清它的安装部署方式、基础与进阶 API 用法、流式推理机制,以及口语化控制 token(笑声、停顿、插话)背后的两阶段推理原理,帮助读者可复制地跑通并完成音色与韵律的精细控制。
一、项目定位:为对话而生,而非通用朗读
README 对 ChatTTS 的核心定位是:"a text-to-speech model designed specifically for dialogue scenarios such as LLM assistant",即专为 LLM 助手这类对话场景优化的 TTS 模型。它当前支持的语种为英语和中文(README 中其他语种标注为 "Coming Soon")。
三个核心亮点(对应 README 的 Highlights 一节):
- 对话式 TTS(Conversational TTS):面向对话任务优化,支持多说话人(multi speakers),可进行交互式对话合成;
- 细粒度控制(Fine-grained Control):模型可预测并控制细粒度韵律特征,包括笑声(laughter)、停顿(pauses)、语气词/插话(interjections);
- 更好的韵律(Better Prosody):官方表述其在韵律上超越了多数开源 TTS 模型,并提供预训练模型供后续研究。
数据与许可方面需要特别注意(README 的 Dataset & Model / Licenses / Disclaimer 三节):
- 主模型使用 100,000+ 小时的中英文音频数据训练;
- 开源的 HuggingFace 版本(2Noise/ChatTTS)是 40,000 小时的预训练模型,未经 SFT;
- 代码许可为 AGPLv3+,模型权重许可为 CC BY-NC 4.0,仅限教育与研究用途,不得用于商业或非法目的;
- 官方在 40k 小时模型的训练过程中加入了少量高频噪声,并用 MP3 格式尽可能压缩音频质量,以防止恶意滥用;同时作者计划开源一个内部训练的"检测模型"。
Roadmap 中已完成项包括:开源 40k 小时基础模型与 spk_stats 文件、流式音频生成、开源 DVAE 编码器与 zero-shot 推理代码;未完成项包括多情感控制和 ChatTTS.cpp(C++ 移植)。
二、环境准备与安装
2.1 依赖清单
仓库根目录的 requirements.txt 列出了核心依赖:
numpy<3.0.0
numba
torch>=2.1.0
torchaudio
tqdm
vector_quantize_pytorch
transformers>=4.41.1
vocos
IPython
gradio
pybase16384
pynini==2.1.5; sys_platform == 'linux'
WeTextProcessing; sys_platform == 'linux'
nemo_text_processing; sys_platform == 'linux'
av
pydub
requests
其中 pybase16384 用于音色嵌入字符串的编解码(见后文 ChatTTS/model/speaker.py);pynini/WeTextProcessing/nemo_text_processing 仅在 Linux 上安装,是文本正则化(text normalization)的可选组件,缺少时 examples/cmd/run.py 会给出警告但不影响基础功能。
2.2 安装方式
方式一:克隆仓库 + 安装依赖(开发/示例运行)
git clone https://github.com/2noise/ChatTTS
cd ChatTTS
pip install --upgrade -r requirements.txt
或者使用 conda 环境(官方示例使用 Python 3.11):
conda create -n chattts python=3.11
conda activate chattts
pip install -r requirements.txt
方式二:直接安装发布包(推荐生产使用)
# 1. PyPI 稳定版
pip install ChatTTS
# 2. GitHub 最新版
pip install git+https://github.com/2noise/ChatTTS
# 3. 本地目录开发模式
pip install -e .
可选组件(Linux):
# vLLM 加速(README 标注 Linux only)
pip install safetensors vllm==0.2.7 torchaudio
README 还给出两个不推荐安装的可选项,并明确警告 "DO NOT INSTALL":
- TransformerEngine:对 NVIDIA GPU 的适配仍在开发中,目前无法正常运行,安装过程很慢,仅限开发目的;
- FlashAttention-2:按官方引用的 transformers issue,当前 FA2 反而会拖慢生成速度,同样仅限开发目的。
三、模型组成:从 config.py 看清架构参数
chat.load() 之后,Chat 实例内部持有六个模块。从 ChatTTS/config/config.py 可以看出各资产的默认路径与关键超参:
资产文件(config.Path) |
作用 | 关键配置 |
|---|---|---|
asset/Vocos.safetensors |
预训练声码器 | 采样率 24000 Hz,n_mels=100,n_fft=1024,hop_length=256,8 层 VocosBackbone + ISTFTHead |
asset/DVAE.safetensors |
音频 tokenizer(VQ-VAE 变体) | 4 层 VQ,levels=(5,5,5,5),dim=1024,编/解码器各 12 层 |
asset/gpt |
自回归主模型 | 20 层、12 头、hidden_size=768,max_position_embeddings=4096,音频词表 626、文本词表 21178、num_vq=4,说话人嵌入维度 spk_emb_dim=192 |
asset/Decoder.safetensors |
高音质解码器(替代 DVAE 解码路径) | 12 层,idim/odim=384 |
asset/tokenizer |
BertTokenizerFast 词表 | 特殊 token 如 [spk_emb]、[break_0]、[Ebreak] |
asset/Embed.safetensors |
文本/音频统一嵌入层 | 768 维 |
从 ChatTTS/core.py 的 _load() 流程看,加载顺序是:Vocos → DVAE → Embed → GPT → Speaker → Decoder → Tokenizer,并在 has_loaded() 中校验模块是否齐全。加载参数还有几个值得注意的实现细节:
compile=True仅当设备为 CUDA 时生效(gpt.prepare(compile=compile and "cuda" in str(device)));- MPS 设备上 GPT 会被回退到 CPU(
device_gpt),Vocos 也运行在 CPU 以避免 MPS 崩溃;NPU 设备上 Vocos 与 DVAE 的 Mel 谱计算回退 CPU; use_vllm=True时 GPT 内部走 vLLM 引擎(对应ChatTTS/model/velocity/目录下的实现),use_flash_attn则启用 FA2(如前所述,README 不推荐)。
# Chat.load() 的完整签名(摘自 ChatTTS/core.py)
load(
source="local", # "huggingface" | "local" | "custom"
force_redownload=False,
compile=False,
custom_path=None, # source="custom" 时指向本地资产目录
device=None, # 默认由 select_device() 自动选择
coef=None, # DVAE 解码的量化系数,通常无需手动指定
use_flash_attn=False,
use_vllm=False,
experimental=False,
enable_cache=True,
)
source 三选一:local 默认在当前工作目录寻找/下载资产;huggingface 通过 snapshot_download(repo_id="2Noise/ChatTTS") 拉取;custom 直接加载指定目录。模型完整性由 ChatTTS/res/sha256_map.json 校验。
四、快速上手:WebUI 与命令行
以下命令需在项目根目录下执行(README 原话)。
4.1 启动 WebUI
python examples/web/webui.py
WebUI(examples/web/webui.py)基于 Gradio,提供文本输入、Sample Audio / Sample Audio Code(zero-shot 参考音频音色提取)、Refine text 开关、Audio Temperature / top_P / top_K 滑杆、Timbre 下拉框、Audio Seed 与 Text Seed 随机按钮,以及 Speaker Embedding 字符串编辑框等控件,适合交互式验证音色与韵律效果。
4.2 命令行推理
python examples/cmd/run.py "Your text 1." "Your text 2."
生成的音频会保存为 ./output_audio_n.mp3(n 从 0 递增)。examples/cmd/run.py 的完整 CLI 参数为:
| 参数 | 说明 | 默认值 |
|---|---|---|
texts(位置参数,可多个) |
待合成文本 | — |
--spk |
指定说话人嵌入字符串,留空则随机采样一个 | None |
--stream |
启用流式模式,逐段生成并保存 | 关 |
--source |
模型来源:huggingface / local / custom |
local |
--custom_path |
自定义模型资产目录(配合 --source custom) |
空 |
例如加载本地已有资产:python examples/cmd/run.py --source custom --custom_path ../../models/2Noise/ChatTTS 你好喲 ":)"。脚本内部会自动尝试注册中文(WeTextProcessing)与英文(nemo_text_processing)正则化器,注册失败仅记录警告。
五、API 基础用法
最小可用示例(README Basic Usage 原样保留):
import ChatTTS
import torch
import torchaudio
chat = ChatTTS.Chat()
chat.load(compile=False) # Set to True for better performance
texts = ["PUT YOUR 1st TEXT HERE", "PUT YOUR 2nd TEXT HERE"]
wavs = chat.infer(texts)
for i in range(len(wavs)):
"""
In some versions of torchaudio, the first line works but in other versions, so does the second line.
"""
try:
torchaudio.save(f"basic_output{i}.wav", torch.from_numpy(wavs[i]).unsqueeze(0), 24000)
except:
torchaudio.save(f"basic_output{i}.wav", torch.from_numpy(wavs[i]), 24000)
注意两点:采样率固定 24000 Hz(与 ChatTTS/config/config.py 中 FeatureExtractorInitArgs.sample_rate = 24000 一致);README 保留了双行 torchaudio.save 的兼容写法,以适配不同版本 torchaudio 对输入维度的差异要求。
六、进阶用法:音色采样与两级控制
6.1 说话人(音色)的采样与复用
# Sample a speaker from Gaussian.
rand_spk = chat.sample_random_speaker()
print(rand_spk) # save it for later timbre recovery
sample_random_speaker() 返回一个字符串形式的音色编码,保存下来即可在后续调用中精确复现同一音色(timbre recovery)。其实现见 ChatTTS/model/speaker.py:
Speaker在初始化时从配置中的spk_stat大字符串(base16384 编码)解码出两组 float16 张量std与mean(对应GPT配置中spk_emb_dim=192维);_sample_random()执行randn(dim).mul_(std).add_(mean),即在预训练得到的均值/方差上做高斯采样,每个样本都是一个新的音色;- 该向量经 LZMA 压缩 + base16384 编码后成为可打印、可持久化的字符串;
apply()时再把字符串解码回来,对嵌入张量做 L2 归一化后替换[spk_emb]位置。
zero-shot 场景下则用 chat.sample_audio_speaker(wav):传入一段参考音频(np.ndarray 或 torch.Tensor),经 DVAE 编码后得到音色 prompt 字符串,供 InferCodeParams.spk_smp 使用。
6.2 句子级控制:RefineTextParams
README Advanced Usage 原样示例:
params_infer_code = ChatTTS.Chat.InferCodeParams(
spk_emb = rand_spk, # add sampled speaker
temperature = .3, # using custom temperature
top_P = 0.7, # top P decode
top_K = 20, # top K decode
)
# For sentence level manual control.
# use oral_(0-9), laugh_(0-2), break_(0-7)
# to generate special token in text to synthesize.
params_refine_text = ChatTTS.Chat.RefineTextParams(
prompt='[oral_2][laugh_0][break_6]',
)
wavs = chat.infer(
texts,
params_refine_text=params_refine_text,
params_infer_code=params_infer_code,
)
这两个 dataclass 定义在 ChatTTS/core.py 中,默认值如下(README 示例中未覆盖的参数在此补齐):
Chat.RefineTextParams(第一阶段:文本润色)
| 参数 | 默认值 | 说明 |
|---|---|---|
prompt |
"" |
前置控制提示,可写 [oral_2][laugh_0][break_6] 这类 token 组合 |
top_P / top_K / temperature |
0.7 / 20 / 0.7 | 解码采样策略 |
repetition_penalty |
1.0 | 重复惩罚 |
max_new_token / min_new_token |
384 / 0 | 生成长度上下限 |
show_tqdm / ensure_non_empty |
True / True | 进度条;确保非空输出 |
manual_seed |
None | 随机种子,复现文本级随机性 |
oral_(0-9)、laugh_(0-2)、break_(0-7) 表示口语感、笑声强度、停顿强度三档控制,数值越大强度越高。这一阶段由 GPT 以 infer_text=True 的方式生成"润色后的文本"(可能插入 [uv_break]、[laugh] 等特殊 token),即 _refine_text()。
Chat.InferCodeParams(继承自 RefineTextParams,第二阶段:声学码本生成)
| 参数 | 默认值 | 说明 |
|---|---|---|
prompt |
"[speed_5]" |
生成音频 token 前的提示 |
spk_emb |
None | 音色嵌入字符串(高斯采样或外部指定) |
spk_smp |
None | zero-shot 参考音频得到的音色 prompt(代码中自动从第一段音频提取) |
txt_smp |
None | 参考文本(与 spk_smp 配套,代码中自动取首段文本) |
temperature |
0.3 | 音频 token 采样温度,可传长度为 4 的列表分别控制 4 层 VQ |
repetition_penalty |
1.05 | 重复惩罚 |
max_new_token |
2048 | 最大生成 token 数 |
stream_batch |
24 | 流式模式下每累积多少个 token 产出一次音频块 |
stream_speed |
12000 | 流式模式下每个音频块的目标采样点数(约 0.5 秒 @24kHz) |
pass_first_n_batches |
2 | 流式模式跳过的首批块数(用于预热/缓冲) |
6.3 词级控制:跳过 refine 直接写 token
# For word level manual control.
text = 'What is [uv_break]your favorite english food?[laugh][lbreak]'
wavs = chat.infer(text, skip_refine_text=True, params_refine_text=params_refine_text, params_infer_code=params_infer_code)
"""
In some versions of torchaudio, the first line works but in other versions, so does the second line.
"""
try:
torchaudio.save("word_level_output.wav", torch.from_numpy(wavs[0]).unsqueeze(0), 24000)
except:
torchaudio.save("word_level_output.wav", torch.from_numpy(wavs[0]), 24000)
关键在 skip_refine_text=True:此时不经过第一阶段的文本润色,由用户在文本中手工插入控制 token。README FAQ 确认:当前发布模型中 token 级可控单元仅有三个——[laugh](笑声)、[uv_break](微停顿/换气感)、[lbreak](较长停顿),更细的情感控制(multi-emotion)在 Roadmap 中尚未完成。
README 还给出一个英文自我介绍的完整例子,展示了多 token 混排:
inputs_en = """
chat T T S is a text to speech model designed for dialogue applications.
[uv_break]it supports mixed language input [uv_break]and offers multi speaker
capabilities with precise control over prosodic elements like
[uv_break]laughter[uv_break][laugh], [uv_break]pauses, [uv_break]and intonation.
[uv_break]it delivers natural and expressive speech,[uv_break]so please
[uv_break] use the project responsibly at your own risk.[uv_break]
""".replace('\n', '') # English is still experimental.
params_refine_text = ChatTTS.Chat.RefineTextParams(
prompt='[oral_2][laugh_0][break_4]',
)
audio_array_en = chat.infer(inputs_en, params_refine_text=params_refine_text)
torchaudio.save("self_introduction_output.wav", torch.from_numpy(audio_array_en[0]), 24000)
官方同时提醒:英文仍是实验性状态,效果以中文为主。
七、流式推理:低延迟音频输出
README 的 Roadmap 已勾选 "Streaming audio generation"。infer() 传入 stream=True 时返回的是生成器,而不是完整波形列表。ChatTTS/core.py 中 _infer_code() 把 stream_batch 传给 GPT 的 generate()(生成循环中每累积 stream_batch 个 token 触发一次中间产出),_infer() 再按 stream_speed 切片,并跳过前 pass_first_n_batches 个块,从而形成稳定的块状音频流。
examples/cmd/stream.py 给出了完整消费端实现——ChatStreamer 类:
- 以
base_block_size=8000为阈值缓冲小块数据,避免过短的音频块; - 输出格式支持
PCM16_byte/PCM16/ 原始浮点; play()方法通过 pyaudio 以 24000 Hz / 16 位 / 单声道打开输出流,并先预缓冲wait秒(示例中 5 秒,适配 CPU 慢速生成的场景)再开始播放:
chat = ChatTTS.Chat()
chat.load(compile=False)
rand_spk = chat.sample_random_speaker()
params_infer_code = ChatTTS.Chat.InferCodeParams(
spk_emb=rand_spk, temperature=0.3, top_P=0.7, top_K=20,
)
streamchat = chat.infer(
["总结一下,AI Agent是大模型功能的扩展……", "你太聪明啦。", "举个例子,……"],
skip_refine_text=True,
stream=True,
params_infer_code=params_infer_code,
)
ChatStreamer().play(streamchat, wait=5)
命令行等价用法是 python examples/cmd/run.py --stream "text 1" "text 2",逐块 mp3 会分别落盘。
八、两阶段推理与文本预处理:源码级原理
从 ChatTTS/core.py 的 infer() → _infer() → _refine_text() / _infer_code() 调用链看,ChatTTS 采用文本 LLM + 音频 LLM 的两阶段自回归架构:
阶段一:文本润色(refine_text)。_refine_text() 将原文包装为 [Sbreak]原文[Pbreak]prompt(见 Speaker.decorate_text_prompts),让 GPT 在文本词表上生成润色文本;随后 text_tokens = [i[i.less(break_0_ids)] ...] 会把生成的 token 截断到 [break_0] 之前,只保留"文本类"token 再解码。这一阶段负责把用户文本"口语化",并按 prompt 提示决定插入多少 [uv_break]/[laugh]。
阶段二:声学码本生成(infer_code)。_infer_code() 中,Speaker.decorate_code_prompts() 把每条文本包装成 [Stts][spk_emb]参考文本原文[Ptts](无指定音色时为 [empty_spk]);Tokenizer.encode()(基于 BertTokenizerFast,见 ChatTTS/model/tokenizer.py)完成左填充批处理,并把 spk_smp 解码出的 4 层 VQ prompt 拼接到序列末尾;若提供了 spk_emb 字符串,Speaker.apply() 会在每个序列的 [spk_emb] 位置写入归一化音色向量;最终 GPT 在 4 个 VQ 层(num_vq=4,对应 DVAE 的 4 级码本)上并行自回归采样,产出隐藏状态后交给 Decoder(或 DVAE)→ Vocos 还原成 24 kHz 波形。
长文本切分:infer() 默认 split_text=True,按 。\s 或 \n 切句(re.split(r"(?<=。)|(?<=\.\s)", text)),每批 max_split_batch=4 句批量推理;当输入多句且未指定 spk_smp 时,代码会先用首句推理一次,从中提取音色(sample_audio_speaker)回填到 params_infer_code.spk_smp,保证整段长文音色一致。
文本正则化与同音字替换:ChatTTS/norm.py 中的 Normalizer 在推理前做:语言检测(中文字符数 vs 英文单词数)、半角转全角(中文)、标点简化、非法字符剔除,以及基于 ChatTTS/res/homophones_map.json 的同音字替换——用 numba JIT 加速的逐字替换表,把模型容易读错的字换成同音正确字(注释中说明该映射来自 1.8 百万词条语料上约 18 万读错词的统计结果)。infer() 的 do_text_normalization / do_homophone_replacement 参数可分别关闭这两类处理。
零填充裁剪:非流式返回前,infer() 会对每段波形做 wav[np.abs(wav) > 1e-5] 的首尾静音裁剪,并在 split_text 时拼接成单段返回。
九、资源需求与 FAQ(README 原文)
1. 显存需求与推理速度? README 给出的官方数据:合成 30 秒音频至少需要 4 GB 显存;RTX 4090 上生成速度约为 7 个语义 token/秒,实时因子(RTF)约 0.3。注意这是官方 FAQ 的表述,实际取决于设备、batch 与是否启用 vLLM/compile。
2. 模型稳定性不足(多说话人串音、音质差)怎么办?
官方解释这是自回归模型(与 bark、valle 类似)的通病,较难完全避免,建议多次采样挑选满意的结果(调低 temperature、换 manual_seed 是常用手段)。
3. 除笑声外还能控制什么?能控制其他情绪吗?
当前发布模型中 token 级控制单元只有 [laugh]、[uv_break]、[lbreak];更多情绪控制能力官方表示"未来版本可能开源"(与 Roadmap 的 "Multi-emotion controlling" 未完成项对应)。
十、小结
ChatTTS 以 20 层、768 维的自回归 GPT 为核心,配合 4 层 VQ 音频 tokenizer 与 Vocos 声码器,通过"文本润色 → 声学码本生成"的两阶段流程实现口语化、可细粒度控制的对话语音合成。上手路径非常清晰:pip install ChatTTS 或克隆仓库安装依赖后,一条 python examples/web/webui.py / python examples/cmd/run.py 即可出音;进阶控制则集中在 RefineTextParams.prompt 的 oral_/laugh_/break_ 三组 token、InferCodeParams 的音色字符串与采样参数,以及 skip_refine_text 下的词级 [laugh]/[uv_break]/[lbreak] 手工标记。需要强调的是:模型权重为 CC BY-NC 4.0(非商用),代码为 AGPLv3+,且官方在 40k 小时开源权重中刻意加入了高频噪声与 MP3 压缩以限制滥用,使用时应遵循相应许可并负责任地应用该能力。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00