第25章 TTS 语音生成
开发者 | TTS 生成
Voicebox 多引擎后端中的文本转语音生成机制
概览
Voicebox 内置八种 TTS 引擎——Qwen3-TTS、Qwen CustomVoice、Qwen VoiceDesign、LuxTTS、Chatterbox Multilingual、Chatterbox Turbo、TADA 和 Kokoro——均通过统一的 TTSBackend Protocol 提供服务。它们暴露相同的异步接口,使路由和服务层无需针对特定引擎进行分支判断。
本页介绍生成流程如何穿过这层抽象。关于添加新引擎的分步指南,请参阅 TTS Engines。
TTSBackend Protocol
每个引擎都实现相同的契约(定义在 backend/backends/__init__.py 中):
@runtime_checkable
class TTSBackend(Protocol):
async def load_model(self, model_size: str) -> None: ...
async def create_voice_prompt(
self, audio_path: str, reference_text: str, use_cache: bool = True
) -> Tuple[dict, bool]: ...
async def combine_voice_prompts(
self, audio_paths: List[str], reference_texts: List[str]
) -> Tuple[np.ndarray, str]: ...
async def generate(
self,
text: str,
voice_prompt: dict,
language: str = "en",
seed: Optional[int] = None,
instruct: Optional[str] = None,
) -> Tuple[np.ndarray, int]: ...
def unload_model(self) -> None: ...
def is_loaded(self) -> bool: ...
ModelConfig 注册表
每种可下载的模型变体都由 ModelConfig dataclass 描述:
@dataclass
class ModelConfig:
model_name: str # "luxtts", "qwen-tts-1.7B", "kokoro"
display_name: str # "LuxTTS (Fast, CPU-friendly)"
engine: str # "luxtts", "qwen", "kokoro"
hf_repo_id: str # "YatharthS/LuxTTS"
model_size: str = "default"
size_mb: int = 0
needs_trim: bool = False
retries_runaway: bool = False
supports_instruct: bool = False
languages: list[str] = field(default_factory=lambda: ["en"])
backends/__init__.py 中的注册表辅助函数取代了以往针对单个引擎的 if/elif 链:
get_all_model_configs()— 获取所有 TTS + STT 变体get_tts_model_configs()— 仅获取 TTS 变体get_model_config(model_name)— 按名称查找engine_needs_trim(engine)— 判断输出是否需经过trim_tts_output()engine_retries_runaway(engine)— 判断不稳定输出是否需以更小块重试load_engine_model(engine, model_size)— 下载并加载模型,支持多尺寸引擎get_tts_backend_for_engine(engine)— 线程安全的后端工厂,采用双重检查锁定
TTS_ENGINES 字典是内置引擎名称的规范列表:
TTS_ENGINES = {
"qwen": "Qwen TTS",
"qwen_custom_voice": "Qwen CustomVoice",
"luxtts": "LuxTTS",
"chatterbox": "Chatterbox TTS",
"chatterbox_turbo": "Chatterbox Turbo",
"tada": "TADA",
"kokoro": "Kokoro",
}
语音提示模式
各引擎通过 create_voice_prompt() 返回的 prompt dict 决定如何表示语音。目前使用三种模式:
模式 A — 预计算张量(Qwen3-TTS, LuxTTS)
encoded = model.encode_prompt(audio_path)
return encoded, False # (prompt_dict, was_cached)
模式 B — 延迟文件路径(Chatterbox, Chatterbox Turbo, TADA)
return {"ref_audio": audio_path, "ref_text": reference_text}, False
模式 C — 预设语音指针(Kokoro, Qwen CustomVoice)
return {
"voice_type": "preset",
"preset_engine": "kokoro",
"preset_voice_id": "am_adam",
}, False
模式 C 用于 voice_type 为 "preset" 的配置文件——无需克隆步骤;引擎直接通过 ID 查找内置语音。
缓存语音提示的引擎会为其缓存键添加前缀以避免冲突:
cache_key = f"{engine}_{hash(audio_path, reference_text)}"
设备选择
引擎通过 backends/base.py 中的 get_torch_device() 选择设备,其优先级分层如下:
- VOICEBOX_FORCE_CPU 环境变量覆盖
- CUDA(若已编译且可用)
- XPU(Intel Arc 通过 IPEX)
- MPS(Apple Silicon)— 仅限支持该特性的引擎;部分引擎(如 Chatterbox 和旧版 Qwen 路径)因上游算子缺失而跳过 MPS,回退至 CPU
- CPU
Qwen TTS 在 Apple Silicon 上直接使用 MLX,而非通过 PyTorch——详见 mlx_backend.py。
生成流程
从前端到音频文件的请求路径:
- 请求 — 携带 GenerationRequest 的 POST /generate:
引擎字段需通过正则{ "profile_id": "uuid", "text": "...", "language": "en", "seed": 42, "model_size": "1.7B", "instruct": "warm, slightly amused", "engine": "qwen", "max_chunk_chars": 800 }^(qwen|qwen_custom_voice|luxtts|chatterbox|chatterbox_turbo|tada|kokoro)$验证。 - 路由 — routes/generate.py 验证输入并委托处理。
- 服务 — services/generation.py 获取配置文件,通过
get_tts_backend_for_engine(engine)解析引擎后端,并确保模型已加载(首次使用时下载并提供实时进度)。 - 语音提示 — 服务调用
create_voice_prompt()(或预设等价物)。对于包含多个样本的克隆配置文件,先调用combine_voice_prompts()合并参考音频。 - 队列 — 请求通过 services/task_queue.py 串行化,避免多个生成任务争抢 GPU。
- 推理 — 引擎的
generate()返回(audio_array, sample_rate)。 - 验证与后处理 — 带有
retries_runaway=True的引擎将以更小块重试不稳定输出。若engine_needs_trim(engine)为 True,trim_tts_output()将去除尾部静音。特效链(如有)应用于生成版本,而非干净版本。 - 持久化 — 音频写入生成目录,在 generations 表中插入一行,响应中包含生成元数据。
长文本分块
超过 max_chunk_chars 的文本(默认 800,范围 100–5000)将在句子边界处拆分,依次生成并交叉淡化拼接。分块行为与引擎无关——它位于服务层,而非单个后端中。
Instruct 模式
两种引擎支持通过 instruct 关键字参数进行自然语言语速控制:
- Qwen CustomVoice —
supports_instruct=True,完全接入模型的 instruct 头。 - Qwen Base — 静默丢弃 instruct 文本(
supports_instruct=False)。前端对 Base 配置文件隐藏 instruct 输入。
# 优质的 instruct 提示示例:
"warm and conversational, slight smile"
"whisper, intimate and close"
"authoritative, broadcast quality"
其他引擎完全忽略 instruct。
内存管理
模型在首次使用时惰性加载并驻留内存。在模型尺寸之间切换(如 Qwen 1.7B ↔ 0.6B)时,会先卸载旧模型再加载新模型,以避免 OOM:
def unload_model(self):
if self.model is not None:
del self.model
self.model = None
if torch.cuda.is_available():
torch.cuda.empty_cache()
模型管理 API(/models/load, /models/unload)允许用户手动释放 VRAM——详见 Model Management。
API 端点
| 方法 | 端点 | 描述 |
|---|---|---|
| POST | /generate | 从文本生成语音 |
| GET | /audio/{generation_id} | 提供生成的音频文件 |
响应模式
{
"id": "generation_uuid",
"profile_id": "profile_uuid",
"text": "...",
"language": "en",
"audio_path": "/path/to/audio.wav",
"duration": 3.5,
"seed": 42,
"engine": "qwen",
"model_size": "1.7B",
"instruct": "...",
"created_at": "2026-04-18T10:30:00Z"
}
性能考量
- CUDA 是所有基于 PyTorch 引擎的最快后端。对于 Qwen TTS,Apple Silicon MLX 可与 CUDA 竞争。
- 串行队列 — 每个进程同时仅运行一个生成任务;并发请求将被排队。
- 语音提示缓存可为同一配置文件的重复生成节省约 1-2 秒。
- 模型固定 — 首次加载较慢(下载 + 加载),后续生成复用内存中缓存的模型。
各引擎 VRAM 占用(CUDA 下近似值)
| 引擎 | VRAM |
|---|---|
| Kokoro | ~150 MB |
| LuxTTS | ~1 GB |
| Chatterbox Turbo | ~1.5 GB |
| Qwen 0.6B / Qwen CustomVoice 0.6B | ~2 GB |
| Chatterbox Multilingual | ~3 GB |
| Qwen 1.7B / Qwen CustomVoice 1.7B | ~6 GB |
| TADA 1B | ~4 GB |
| TADA 3B | ~8 GB |
后续步骤
- TTS Engines 添加新引擎 — 完整的分阶段工作流
- Model Management 模型下载、加载和卸载
- Voice Profiles 克隆与预设配置文件模式
Edit on GitHub | Voice Profiles — Voicebox 中语音配置文件管理的工作原理 | TTS Engines — 如何向 Voicebox 添加新的文本转语音引擎