背景痛点:语音合成“三座大山”
第一次做语音合成项目时,我踩过的坑可以总结成三句话:
- 模型太多,不会选。Tacotron2、FastSpeech2、Glow-TTS……名字一个比一个酷,跑起来一个比一个吃显存。
- API 太绕,文档跳级。官方示例直接甩出 100 多行训练脚本,我只想“输入文字→拿到 WAV”,结果要翻五六层源码才找到 inference 入口。
- 性能太悬,上线就炸。本地 2080Ti 生成 10 秒语音只要 0.8 s,搬到 2 核 4 G 的云容器直接 12 s 起步,用户以为程序卡死。
如果你也卡在任意一环,Coqui TTS 的 Python 版可以一站式解决“选、跑、上线”全流程,下面把我亲测的入门路线拆开给你。
技术选型:为什么先试试 Coqui?
| 维度 | Coqui TTS | Google TTS | Amazon Polly |
|---|---|---|---|
| 离线部署 | 完全开源 | 仅云端 | 仅云端 |
| 定制音色 | 15 min 音频即可微调 | 仅官方音色 | 仅官方音色 |
| 费用 | 0 美元(自建电费和显卡) | 按字符 | 按字符 |
| 延迟 | 本地 <1 s | 网络 RTT + 0.3 s | 网络 RTT + 0.5 s |
| 开发语言 | Python 优先 | REST / gRPC | REST |
| 中文效果 | 需自己找模型,但支持 | 优秀 | 优秀 |
一句话总结:要“免费 + 可离网 + 可魔改”,先上 Coqui;要“一把梭上线、不想管机器”,再考虑云厂商。
核心实现:10 行代码跑通“Hello TTS”
安装
# CPU 版先跑通 pip install TTS # 想提速再换 GPU 版 pip install TTS[torch-gpu]选模型。Coqui 把“预训练模型”当插件管理,命令行列出可用模型:
tts --list_models中文首推
tts_models/zh-CN/baker/tacotron2-DDC-GST,英文玩jenny/tacotron2-DDC。最小脚本:
from TTS.api import TTS import os # 1. 自动下载模型,只下第一次 tts = TTS(model_name="tts_models/zh-CN/baker/tacotron2-DDC-GST", progress_bar=False, gpu=False) # 2. 合成 text = "今天天气真不错,让我们一起跑代码。" tts.tts_to_file(text=text, file_path="out.wav") print("已生成 out.wav,时长:", os.popen("soxi -D out.wav").read().strip(), "秒")跑通后目录里出现
out.wav,浏览器就能播放,全程无手工下载权重、无写配置。
完整示例:带异常处理 + 批量生产
下面这段脚本是我内部小工具的裁剪版,支持:
- 批量读 txt
- 自动建输出目录
- 捕获 GPU 爆显存自动降级 CPU
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 批量文本转语音,Coqui TTS 版 python batch_tts.py --in_dir texts/ --out_dir wavs/ --model_name tts_models/zh-CN/baker/tacotron2-DDC-GST """ import argparse, os, sys, logging, torch from TTS.api import TTS from pathlib import Path logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") def parse_args(): p = argparse.ArgumentParser() p.add_argument("--in_dir", required=True, help="存 txt 的文件夹") p.add_argument("--out_dir", required=True, help="输出 wav 的文件夹") p.add_argument("--model_name", default="tts_models/zh-CN/baker/tacotron2-DDC-GST") p.add_argument("--gpu", action="store_true", help="强制使用 GPU") return p.parse_args() def main(): args = parse_args() device = "cuda" if args.gpu and torch.cuda.is_available() else "cpu" logging.info("选用设备:%s", device.upper()) try: tts = TTS(model_name=args.model_name, progress_bar=True, gpu=device=="cuda") except Exception as e: logging.exception("模型加载失败,检查网络或模型名:%s", e) sys.exit(2) os.makedirs(args.out_dir, exist_ok=True) for txt_file in Path(args.in_dir).glob("*.txt"): out_wav = Path(args.out_dir)/(txt_file.stem + ".wav") if out_wav.exists(): logging.info("已存在,跳过:%s", out_wav) continue text = txt_file.read_text(encoding="utf-8").strip() if not text: logging.warning("空文件,跳过:%s", txt_file) continue try: tts.tts_to_file(text=text, file_path=str(out_wav)) logging.info("合成完成:%s", out_wav) except RuntimeError as e: if "out of memory" in str(e): logging.error("GPU 显存溢出,尝试重启脚本并加 --gpu False") else: logging.exception("合成失败:%s", e) if __name__ == "__main__": main()保存后直接python batch_tts.py --in_dir texts/ --out_dir wavs/,几十条语音一次性生成,比手动点网页快得多。
性能实测与优化
测试环境:
- CPU:i7-12700H 12 核
- GPU:RTX 3060 Laptop 6 G
- 文本:60 字中文
- 采样率:22050 Hz
| 设备 | 首次加载 | 合成延迟 | 峰值内存 | 优化技巧 |
|---|---|---|---|---|
| CPU | 8 s | 2.3 s | 1.2 G | 开torch.set_num_threads(4)控制线程 |
| GPU | 6 s | 0.4 s | 1.1 G + 1.3 G 显存 | 用fp16半精度,显存降 25 % |
生产环境如果想再压时间,可以:
- 把模型提前
tts.to(device)后常驻内存,避免每次重新加载。 - 对高频句子做缓存,Key 为文本哈希,Value 直接存 WAV。
- 用 ONNX 导出(Coqui 官方实验分支),CPU 推理可再快 30 %。
生产环境避坑清单
- 模型第一次下载被墙:提前在本地
~/.local/share/tts/打包 Docker 镜像,或给容器挂代理。 - 云函数冷启动慢:把
/root/.local/share/tts打进层(Lambda Layer),或者换 ECS 常驻进程。 - 音色忽大忽小:在
tts.tts_to_file()前加tts.synthesizer.tts_model.ap.set_attribute("gain", 0.9)统一增益。 - 并发高时显存炸:用
torch.cuda.empty_cache()每 20 次推理后清一次,或干脆 CPU Fallback。 - 中文多音字读错:准备 200 句领域语料,用 Coqui 自带的
TTS.recipe.ljspeech.finetune微调 1/4 个 epoch 就能改善。
动手时间
- 把上面脚本跑通后,尝试替换英文模型
jenny/tacotron2-DDC,听一下中英文混合效果。 - 用
sox给生成的 WAV 加 0.5 dB 淡入淡出,写进脚本自动后处理。 - 把服务用 FastAPI 封装成
/tts?text=你好,测一下并发 10 请求时的平均延迟,贴出你的数据。
欢迎在评论区甩 GitHub 链接或性能截图,一起把 Coqui 玩出花。