mlx-audio 中的 Chatterbox TTS:基于 MLX 的 0.5B 表达力语音合成与声音克隆实战指南
mlx-audio 中的 Chatterbox TTS:基于 MLX 的 0.5B 表达力语音合成与声音克隆实战指南
Chatterbox 是 ResembleAI 开源的 0.5B 参数表达力语音合成(TTS)模型,支持声音克隆(Voice Cloning)与细粒度情感控制。本文以 mlx-audio 仓库中 Chatterbox 文档 为骨架,结合 chatterbox.py 等源码实现,系统讲解模型变体、架构组成、权重转换、CLI/Python 调用方式、情感夸张参数、全部生成参数以及多语言支持细节,帮助你在 Apple Silicon 上直接运行并二次定制 Chatterbox。
模型概览与核心能力
Chatterbox 是一个约 0.5B 参数的 TTS 模型,它的核心卖点是:
- 声音克隆:通过一段参考音频(reference audio)提取说话人特征,无需额外微调即可克隆音色;
- 细粒度情感控制:通过
exaggeration参数(0~1)在"平淡"与"高度夸张"之间连续调节表达力; - 多语言:除英文单语 checkpoint 外,还提供覆盖 23 种语言的多语言 v2 与 v3 checkpoint。
mlx-audio 对 Chatterbox 的 MLX 移植支持英文模型以及 23 语言多语言 v2/v3 权重。其中 v3 在保留 v2 架构的前提下,提升了说话人相似度、生成稳定性与对话式语音的表现,这也是官方 MLX 社区权重主要推荐 v3 的原因。
从源码结构看,仓库中与 Chatterbox 相关的实现位于 mlx_audio/tts/models/chatterbox/,包含:
| 组件 | 目录/文件 | 作用 |
|---|---|---|
主模型 Model |
chatterbox.py | 组装各组件、条件化处理、推理主流程 |
配置 ModelConfig/T3Config |
config.py | 模型超参、多语言开关、文本预处理策略 |
| T3 文本→语音 token | t3/t3.py | 以 LLaMA-520M 为骨干的 token 生成器 |
| S3Gen token→波形 | s3gen/s3gen.py | Flow Matching 解码器 + HiFi-GAN 声码器 |
| 说话人编码器 | voice_encoder/ | 从参考音频提取 speaker embedding |
| S3Tokenizer | s3tokenizer/ | 将参考音频转为 S3 语音 token |
| 文本分词器 | tokenizer.py | 英文与多语言文本→token,含各语言预处理 |
| 权重转换 | scripts/convert.py | PyTorch/ONNX→MLX 转换与量化 |
模型变体与获取方式
仓库支持以下 MLX 社区权重:
| 模型 | 变体 | 说明 |
|---|---|---|
mlx-community/chatterbox-multilingual-v3 |
多语言 v3 | 推荐的多语言版本,文本预处理为 "NFKD,fullcase" |
mlx-community/chatterbox-fp16 |
多语言 v2 | 早期多语言版本,文本预处理为 "legacy" |
!!! note
Chatterbox 依赖 S3Tokenizer 权重(仓库为 mlx-community/S3TokenizerV2),首次使用时会在 Model.from_pretrained 与 post_load_hook 中通过 snapshot_download 自动下载,无需手动处理。该 tokenizer 是多个 TTS 模型(Chatterbox、CosyVoice2 等)共享的组件,因此单独存放。
在 chatterbox.py 中定义了 SUPPORTED_LANGUAGES 字典(23 个语言代码);而在 config.py 中,英文版使用 704-token 文本词表,多语言 v2/v3 使用 2454-token 文本词表,语音 token 词表统一为 8194(其中 6561 为 SOT、6562 为 EOT)。
本地转换官方 checkpoint
如果你想从官方原始权重自行转换 MLX 权重(而不是直接使用 mlx-community 成品),可运行仓库自带的转换脚本。转换多语言 v3 官方 checkpoint:
python -m mlx_audio.tts.models.chatterbox.scripts.convert \
--variant v3 \
--output-dir Chatterbox-Multilingual-v3-MLX
从 scripts/convert.py 的源码可以看到,该脚本支持更丰富的选项:
# 转换英文版
python -m mlx_audio.tts.models.chatterbox.scripts.convert
# 转换多语言 v2(或使用别名 multilingual)
python -m mlx_audio.tts.models.chatterbox.scripts.convert --variant v2
# 对 T3 骨干做选择性 4-bit 量化(体积约减少 53%)
python -m mlx_audio.tts.models.chatterbox.scripts.convert --variant v3 --quantize
# 自定义量化位宽与分组大小
python -m mlx_audio.tts.models.chatterbox.scripts.convert --variant v3 --quantize --q-bits 4 --q-group-size 64
# 单独转换共享组件 S3Tokenizer(供 Chatterbox/CosyVoice2 等共用)
python -m mlx_audio.tts.models.chatterbox.scripts.convert --s3-tokenizer-only
# 转换后上传到 Hugging Face
python -m mlx_audio.tts.models.chatterbox.scripts.convert --variant v3 --upload-repo my-org/my-chatterbox
转换过程的几个实现细节(来自 scripts/convert.py):
- 源 checkpoint 由 CHATTERBOX_VARIANTS 定义,v2/v3 共用
grapheme_mtl_merged_expanded_v1.json分词器,区别在于 T3 权重文件名与文本预处理策略; - 转换后权重合并为单个
model.safetensors,并按组件前缀组织:ve.*(VoiceEncoder)、t3.*(T3)、s3gen.*(S3Gen),tokenizer.*与 S3Tokenizer 权重被排除在外(后者单独成仓); - 量化是"选择性"的:
quantize_t3_backbone只量化tfmr.model.layers.*路径下的 Linear 层,其余组件(说话人编码器、条件编码器等)保持全精度,因为它们对量化更敏感; - 转换要求
torch safetensors huggingface_hub onnx s3tokenizer等依赖,转换完成后推理只需要mlx。
架构与推理工作流:T3 + S3Gen 两阶段生成
Chatterbox 采用"先文本→语音 token,再 token→波形"的两阶段架构。主模型 Model 的类注释(chatterbox.py)明确了它的四个组成部分:
- T3(Token-To-Token):以 LLaMA-520M 为骨干的文本→语音 token 生成器。从 t3/t3.py 看,它的输入是
[条件嵌入 | 文本 token | 语音 token]拼接序列,通过text_head与speech_head两个投影头分别输出文本与语音 logits;推理时使用 KV Cache 加速,并集成了 classifier-free guidance(CFG)、repetition penalty、min_p 与 top_p 采样(见T3.inference)。 - S3Gen:Flow Matching 解码器 + HiFi-GAN 声码器,把 S3 语音 token 还原为 24kHz 波形。从 s3gen/s3gen.py 看,内部包含 Conformer 编码器(
UpsampleConformerEncoder)、因果条件 flow matching 解码器(CausalConditionalCFM)、x-vector 说话人编码器(CAMPPlus)与 HiFi-GAN(HiFTGenerator)。 - VoiceEncoder:说话人 embedding 提取器。
- S3Tokenizer:参考音频→语音 token 的编码器。
generate() 主流程(chatterbox.py)可概括为:
参考音频 → prepare_conditionals() → 条件嵌入(speaker_emb + 语音 prompt tokens + emotion_adv)
文本 → _tokenize_text() → 文本 tokens(首尾补 SOT/EOT)
→ T3.inference() 生成语音 tokens(含 CFG/采样控制)
→ drop_invalid_tokens() 去掉 SOT(6561)/EOT(6562)
→ 过滤 ≥ 6561 的非法 token
→ S3Gen() 生成 24kHz 波形
→ 产出 GenerationResult(含时长、RTF、峰值内存等指标)
值得注意的实现细节(可追溯源码):
- 条件化参考音频分两条路径(
prepare_conditionals):S3Gen 使用最多 10 秒的音频(DEC_COND_LEN = 10 * 24000),T3 编码器使用最多 6 秒的音频(ENC_COND_LEN = 6 * 16000);语音 token 从 24kHz 重采样到 16kHz 提取,说话人 embedding 则使用完整 16kHz 音频,不截断; - 情感条件
emotion_adv是一个(1,1,1)的张量,值即 exaggeration,通过mx.ones(...) * exaggeration注入 T3 条件编码器; - CFG 实现:当
cfg_weight > 0时文本 token 被复制成两份,第二份在prepare_input_embeds中被置零(mx.zeros_like),解码时对条件/无条件 logits 做cond + cfg_weight * (cond - uncond)线性外推; - 缓存管理:T3 推理前后显式调用
mx.clear_cache(),避免大模型切换时显存峰值。
快速开始:基于参考音频的声音克隆
Chatterbox 的推理必须提供参考音频用于声音克隆(除非模型目录下已存在预计算的 conds.safetensors,此时可以省略参考音频,见 post_load_hook)。
CLI 方式
mlx_audio.tts.generate \
--model mlx-community/chatterbox-multilingual-v3 \
--text "Hello, this is Chatterbox on MLX!" \
--ref_audio reference.wav
多语言场景下追加 --lang_code(例如法语):
mlx_audio.tts.generate \
--model mlx-community/chatterbox-multilingual-v3 \
--text "Bonjour, voici Chatterbox sur MLX !" \
--ref_audio reference.wav \
--lang_code fr
mlx_audio.tts.generate 是仓库 tts/generate.py 的 CLI 入口,generate_audio() 会调用 load_model() 加载模型,然后执行 model.generate(**gen_kwargs) 并把每个 GenerationResult 写为音频文件(默认 audio_000.wav,可用 --file_prefix、--audio_format、--output_path 调整)。
Python 方式
from mlx_audio.tts.utils import load_model
model = load_model("mlx-community/chatterbox-multilingual-v3")
for result in model.generate(
text="Hello, this is Chatterbox on MLX!",
ref_audio="reference.wav",
):
audio = result.audio # mx.array 波形,采样率 24kHz
load_model 位于 tts/utils.py,它会根据 config.json 中的 model_type: "chatterbox" 自动路由到 Chatterbox 的 Model 类;ref_audio 是 audio_prompt 的兼容别名,若未显式给出 audio_prompt_sr,则默认按 24kHz 处理。
result 是 GenerationResult 对象,除 audio 外还包含:
sample_rate:24kHz;audio_duration:HH:MM:SS.mmm格式的音频时长;real_time_factor:实时率(处理时间/音频时长);token_count与tokens-per-sec:文本 token 数与生成速率;peak_memory_usage:推理峰值显存(GB)。
这些字段在 tts/generate.py 的 verbose 输出中也会打印。
情感夸张控制:exaggeration 参数
Chatterbox 最独特的控制项是 exaggeration(取值 0~1),它直接控制情感的夸张程度。源码中该值被写入 T3 的条件张量 emotion_adv(chatterbox.py),是模型条件编码器的显式输入,因此可以在不改变文本、不换参考音频的情况下连续调节表达力。
from mlx_audio.tts.utils import load_model
model = load_model("mlx-community/chatterbox-multilingual-v3")
# 微妙的表达(subtle)
for result in model.generate(
text="That's really interesting.",
ref_audio="reference.wav",
exaggeration=0.1,
):
audio = result.audio
# 高度夸张的表达
for result in model.generate(
text="That's really interesting!",
ref_audio="reference.wav",
exaggeration=0.9,
):
audio = result.audio
注意:model.generate() 内部默认 exaggeration=0.1;CLI 的 --exaggeration 默认值为 0.5(见 tts/generate.py),两者默认值不同,建议显式指定以获得可预期的效果。
生成参数详解
Chatterbox 的 generate() 方法(chatterbox.py)支持的参数如下,表中原文档所列默认值与源码完全一致:
| 参数 | 默认值 | 说明 |
|---|---|---|
exaggeration |
0.1 |
情感夸张因子(0~1),注入 T3 的 emotion_adv 条件 |
cfg_weight |
0.5 |
Classifier-free guidance 权重,越大越"忠于条件",0 表示关闭 CFG |
temperature |
0.8 |
采样温度,越高越发散 |
repetition_penalty |
1.2 |
重复 token 惩罚系数,抑制复读 |
min_p |
0.05 |
最小概率阈值(min-p 采样) |
top_p |
1.0 |
Top-p(nucleus)采样阈值 |
max_new_tokens |
1000 |
生成的语音 token 数量上限 |
除上述模型专属参数外,为兼容 mlx-audio 统一的 generate_audio() 接口,generate() 还接收以下别名/兼容参数:
| 参数 | 说明 |
|---|---|
ref_audio |
audio_prompt 的别名,声音克隆参考音频路径/数组 |
lang_code |
多语言 v2/v3 的语言代码,默认 "en";英文版只接受 "en" |
max_tokens |
max_new_tokens 的别名 |
voice / speed |
被忽略(Chatterbox 使用参考音频克隆音色,不支持变速) |
stream / streaming_interval |
被忽略(Chatterbox 当前不支持流式生成) |
各采样参数的底层实现在 t3/t3.py 的 T3.inference:make_sampler 负责 temperature/top_p/min_p,make_logits_processors 负责 repetition penalty,CFG 在每步采样前对条件与无条件 logits 做外推。
多语言支持与文本预处理
多语言 v2/v3 checkpoint 支持以下 23 种语言(源码中的 SUPPORTED_LANGUAGES 与文档一致):
阿拉伯语(ar)、丹麦语(da)、德语(de)、希腊语(el)、英语(en)、西班牙语(es)、芬兰语(fi)、法语(fr)、希伯来语(he)、印地语(hi)、意大利语(it)、日语(ja)、韩语(ko)、马来语(ms)、荷兰语(nl)、挪威语(no)、波兰语(pl)、葡萄牙语(pt)、俄语(ru)、瑞典语(sv)、斯瓦希里语(sw)、土耳其语(tr)、中文(zh)。
语言代码与检查
_tokenize_text(chatterbox.py)会把 lang_code 转小写后与 SUPPORTED_LANGUAGES 比对,未收录的语言会抛出 ValueError;若加载的是英文单语 checkpoint,传入非 "en" 语言同样会报错并提示改用多语言 v2/v3。文本 token 化前会为每个 token 序列加上语言前缀 token(如 [fr])与 SOT/EOT 边界 token。
语言相关文本预处理
tokenizer.py 实现了高度语言特化的文本规范化,v2(legacy)与 v3(NFKD,fullcase)的处理管线不同:
- 中文(zh):使用仓颉码(Cangjie)转换,依赖
Cangjie5_TC.json映射与spacy_pkuseg分词,输出[cj_...]token; - 日语(ja,仅 v2):通过
pykakasi把汉字转平假名(hiragana_normalize); - 希伯来语(he,仅 v2):通过
dicta_onnx添加元音符号(add_hebrew_diacritics); - 韩语(ko,仅 v2):把音节分解为 Jamo 字母(
korean_normalize); - 俄语(ru):添加重音标记(
russian_text_stresser)。
v3 的差异在于:保留大小写(fullcase)+ NFKD 归一化,中文与俄语变换发生在 NFKD 之前,且不再应用 v2 时代的日语/希伯来语/韩语变换(见 MTLTokenizer.preprocess_text 的注释)。这也是 v3 文本预处理标记为 "NFKD,fullcase" 的原因,对应 config.py 中 v3 默认 text_preprocessing 的取值逻辑。
此外,英文与多语言模型还会先经过 punc_norm(chatterbox.py)做标点清洗:把 ...、…、:、-、; 等替换为逗号/连字符、合并多余空格、必要时补句号,避免 LLM 生成文本中的少见字符干扰合成。
量化与资源占用
从 config.py 的 LLAMA_520M_CONFIG 可以确认 T3 骨干为 LLaMA 架构:hidden size 1024、30 层、16 头注意力、intermediate 4096、RoPE theta 500000、llama3 型 RoPE scaling(factor 8.0)。整个模型约 0.5B 参数。
若显存紧张,可转换时使用 --quantize 生成 4-bit 量化权重(默认 --q-bits 4 --q-group-size 64),from_pretrained 在检测到 config.json 中的 quantization 字段时会自动对 T3 骨干应用 nn.quantize(chatterbox.py)。需要说明的是:量化是选择性的,只覆盖 T3 的 transformer 层,说话人编码器与 S3Gen 保持全精度,以保住对量化敏感组件的声音质量。
深入阅读指引
- 完整使用文档:docs/models/tts/chatterbox.md
- 主模型实现:mlx_audio/tts/models/chatterbox/chatterbox.py
- 模型配置:mlx_audio/tts/models/chatterbox/config.py
- 权重转换脚本:mlx_audio/tts/models/chatterbox/scripts/convert.py
- 文本分词与多语言预处理:mlx_audio/tts/models/chatterbox/tokenizer.py
- T3 文本→语音 token 生成器:mlx_audio/tts/models/chatterbox/t3/t3.py
- S3Gen token→波形解码器:mlx_audio/tts/models/chatterbox/s3gen/s3gen.py
- 统一 CLI 入口:mlx_audio/tts/generate.py
- 模型加载工具:mlx_audio/tts/utils.py
本文所有命令与参数均以当前仓库源码为准:推理前请确保已安装 mlx-audio 及其依赖,首次加载会自动从 Hugging Face 拉取模型权重与 S3Tokenizer;如无特殊说明,所有示例均针对 Apple Silicon 环境运行。