OddTTS更新:集成ZipVoice,纯CPU跑中英混合语音合成!

引言

上周把 Audio8/MOSS-TTS-Nano 集成到 OddTTS 了,测试一下 Audio8/MOSS-TTS-Nano 效果确实好,但 CPU 跑 RTF 3.26(55字合成耗时 27.85 秒),实在没法用于实时场景。

除了这两个模型个,kid 大佬推荐的模型里还差了一个 ZipVoice,123M 参数,Flow Matching 架构,号称”Small and Fast”。

这个周末在家就搞了一下这个模型。

然而,这个 zipvoice 的集成过程并不顺利,甚至可以说一波三折——踩了好多个大坑,从 PyTorch 推理跑不动,到 vocoder 选型错误合成出噪音,再到模型目录验证 bug 导致 voices 为空。这篇文章把这些坑都记录下来,希望能帮到同样想集成 ZipVoice 的同学。

最终方案:sherpa-onnx 推理后端 + ModelScope 模型下载 + 自动 vocoder 下载,pip 安装后一键启动,纯 CPU 运行,RTF 约 1.3-3.0。

快速开始使用 OddTTS

OddTTS 是一个功能强大的多引擎语音合成服务,提供统一的API接口和友好的Web界面,一套接口搞定多种主流TTS引擎,包括 EdgeTTSKokoro-82M-v1.1-zhZipVoiceMOSS-TTSAudio8ChatTTSBert-VITS2GptSovits v2等,同时也支持OpenAI TTS API的调用。

  • 仓库: https://github.com/oddmeta/oddtts
  • Python版本: 3.12
  • 版本: v2.1.15
  • 安装: pip install oddtts
  • 运行: oddtts
  • 演示: http://localhost:9001

Python测试

import openai
client = openai.OpenAI(base_url="http://localhost:9001/v1", api_key="dummy")
response = client.audio.speech.create(
    model="oddtts-zipvoice",
    voice="news-female",
    input="你好,欢迎使用OddTTS小奥语音合成服务。"
)

curl测试

curl http://127.0.0.1:9001/v1/audio/speech -H "Content-Type: application/json" -d "{\"input\": \"你好,欢迎使用OddTTS小奥语音合成服务。\", \"voice\": \"news-female\"}" --output speech.wav

直接听合成的效果

合成文本:关注我的公众号:奥德元,一起学习 AI,一起追赶时代。Good good study, day day up!

zipvoice-cctv-voice1.wav(news-female 音色,RTF=1.551)

zipvoice-cctv-voice2.wav(news-female-2 音色,RTF=1.410)

zipvoice-leijun-voice.wav(leijun-1 音色,RTF=1.307)

一、ZipVoice 模型概览

1.1 基本信息

特性
参数量~123M
架构Flow Matching (4步迭代)
量化方式INT8 (sherpa-onnx 预编译)
采样率24000 Hz
支持语言中文 + 英文(中英混合)
音色克隆零样本,仅需 3 秒参考音频
推理后端sherpa-onnx (ONNX Runtime)
模型大小~186 MB (encoder + decoder + vocoder)
内存占用~500-800 MB

1.2 架构

ZipVoice 采用 Flow Matching 架构,核心流程:

文本 → Tokenization (espeak-ng) → Encoder → Decoder (Flow Matching, 4步) → Vocoder (Vocos) → 音频
                                          ↑
                                    参考音频 (音色克隆)

相比传统扩散模型,Flow Matching 仅需 4 步迭代即可生成频谱,推理速度大幅提升。

1.3 模型文件

sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/
├── encoder.int8.onnx     # 文本编码器 (INT8)
├── decoder.int8.onnx     # Flow Matching 解码器 (INT8)
├── tokens.txt            # Token 词汇表
├── lexicon.txt           # 发音词典
├── espeak-ng-data/       # espeak-ng 音素数据
├── test_wavs/            # 内置参考音频
│   ├── prompt.txt        # 音色列表
│   ├── news-female.wav
│   ├── news-female-2.wav
│   └── leijun-1.wav
└── vocos_24khz.onnx      # Vocos 声码器 (100-mel, 24kHz)

1.4 内置音色

音色名称描述
news-female新闻联播女声,正式风格
news-female-2新闻联播女声2,另一播音风格
leijun-1雷军演讲风格

二、踩坑过程

这是本文的重点。集成 ZipVoice 的过程并不顺利,遇到了几个比较典型的问题。

2.1 坑1:PyTorch 官方仓库 CPU 跑不动

时间:2026-09-12

最初集成时,直接用 ZipVoice 官方 GitHub 仓库的推理代码。需要的依赖:

pip install torch torchaudio vocos pypinyin jieba safetensors huggingface_hub

模型从 HuggingFace 自动下载,推理用官方 infer_zipvoice 脚本。跑是跑通了,但速度完全无法接受:

合成耗时: 74.70s, 音频时长: ~10s, RTF > 7
合成耗时: 89.04s, 音频时长: ~10s, RTF > 8
合成耗时: 98.31s, 音频时长: ~10s, RTF > 9

RTF > 7 意味着合成 1 秒音频需要 7 秒以上,完全不可用于实时场景。而且 PyTorch 依赖太重了,pip 安装后光 torch 就好几个 GB。

结论:PyTorch 推理方案在纯 CPU 场景下不可行。

2.2 坑2:sherpa-onnx 方案搞定了速度,但 vocoder 选型错误

时间:2026-09-13 上午

发现 k2-fsa 官方在 sherpa-onnx 中提供了 ZipVoice 的预编译 INT8 模型,仓库名 sherpa-onnx-zipvoice-distill-int8-zh-en-emilia。这意味着不需要 PyTorch,直接用 sherpa-onnx(底层是 ONNX Runtime)就能跑。

马上切换方案,用 sherpa-onnx 重写了 tts_zipvoice.py。模型从 ModelScope 下载(国内速度快),依赖只剩 sherpa-onnxsoundfile

但合成出来的音频是噪音

查了半天原因,最后发现是 vocoder 选型错误

  • 模型仓库里自带了一个 vocos-22khz-univ.onnx,80 个 mel 频段
  • 但 ZipVoice decoder 输出的是 100 个 mel 频段
  • 80 mel 的 vocoder 去解码 100 mel 的频谱,结果就是噪音

正确的 vocodervocos_24khz.onnx(100 mel 频段,24kHz),需要单独下载。

2.3 坑3:vocoder 下载源踩坑

找到正确的 vocoder 后,需要自动下载。 给了一个 ModelScope URL:https://www.modelscope.cn/models/roarson/vocos_24khz.onnx

第一次用 roarson/vocos_24khz 作为 repo ID 下载,ModelScope 返回 404。

# 错误的 repo ID
snapshot_download("roarson/vocos_24khz", local_dir=target_dir)
# 结果: 404 Not Found

试了好几个变体都不行。后来仔细看 URL,发现文件名就是 repo ID 的一部分

# 正确的 repo ID(带 .onnx 后缀)
snapshot_download("roarson/vocos_24khz.onnx", local_dir=target_dir)
# 成功!vocos_24khz.onnx (51.6 MB)

教训:ModelScope 的 repo ID 不一定和文件名一致,需要看实际 URL。

2.4 坑4:_validate_model_dir 用错了 isfile vs isdir

时间:2026-09-13 下午

vocoder 问题解决后,pip 安装的用户反馈:voices 列表为空

查日志发现 get_voices() 返回 [],但没有报错。因为原来的代码是:

async def get_voices(self):
    if self._builtin_voices is None:
        try:
            self._init_runtime()
        except Exception:
            pass  # ← 吞掉了所有异常!
    return self._builtin_voices or []

_init_runtime() 失败了但异常被静默吞掉,导致 voices 为空却看不到任何错误信息。

加上日志后发现问题根源:_validate_model_dir() 中检查 espeak-ng-data 时用了 os.path.isfile()

# 错误:espeak-ng-data 是目录,不是文件
return os.path.isfile(os.path.join(path, ESPEAK_DIR))  # 永远返回 False
# 正确:
return os.path.isdir(os.path.join(path, ESPEAK_DIR))

这导致模型目录验证永远失败,_get_model_dir() 返回 None_init_runtime() 抛异常,get_voices() 静默返回空列表。

修复os.path.isfile()os.path.isdir(),同时在 get_voices() 中加上 logger.warning() 输出异常信息。

三、性能对比

3.1 测试环境

  • 测试文本:55字中文文本(含中英混合)
  • 测试条件:纯CPU,WAV格式
  • 硬件环境:普通PC(无GPU)

3.2 性能数据

引擎采样率合成耗时音频时长RTF(实时率)
Kokoro v1.124000 Hz2.68秒8.18秒0.33
ZipVoice INT824000 Hz13.26秒8.55秒1.551
Audio8 0.1B ONNX INT844100 Hz27.85秒8.54秒3.26
Audio8 0.6B ONNX INT444100 Hz42.34秒8.59秒4.93

3.3 ZipVoice 各音色实测

音色音频时长合成耗时RTF
news-female8.55s13.26s1.551
news-female-27.95s11.21s1.410
leijun-15.91s7.72s1.307

3.4 性能分析

  • Kokoro v1.1 仍是最快的,RTF=0.33,但不支持音色克隆
  • ZipVoice INT8 RTF≈1.3-3.0,比 Audio8 快 2 倍以上,且支持零样本音色克隆
  • Audio8 音质最好但最慢,适合对质量要求高的离线场景

ZipVoice 在速度和功能之间取得了不错的平衡:比 Kokoro 稍慢但支持音色克隆,比 Audio8 快得多且依赖更轻。

四、代码实现细节

4.1 引擎注册

oddtts_params.py 中新增枚举值:

class ODDTTS_TYPE(Enum):
    ODDTTS_ZIPVOICE = 11

base_tts_driver.py 中注册策略:

elif tts_type == ODDTTS_TYPE.ODDTTS_ZIPVOICE:
    tts.client = ZipVoiceAPI()
    return tts

4.2 sherpa-onnx 配置

import sherpa_onnx

config = sherpa_onnx.OfflineTtsConfig(
    model=sherpa_onnx.OfflineTtsModelConfig(
        zipvoice=sherpa_onnx.OfflineTtsZipvoiceModelConfig(
            tokens=os.path.join(model_dir, "tokens.txt"),
            encoder=os.path.join(model_dir, "encoder.int8.onnx"),
            decoder=os.path.join(model_dir, "decoder.int8.onnx"),
            data_dir=os.path.join(model_dir, "espeak-ng-data"),
            lexicon=os.path.join(model_dir, "lexicon.txt"),
            vocoder=vocoder_path,  # vocos_24khz.onnx
        ),
        num_threads=2,
    ),
)
tts = sherpa_onnx.OfflineTts(config)

4.3 模型自动下载

优先从 ModelScope 下载(国内友好),GitHub 作为 fallback:

ZIPVOICE_REPO_ID = "roarson/sherpa-onnx-zipvoice-distill-int8-zh-en-emilia"
# ModelScope → 自动下载到 models/zipvoice/

VOCODER_REPO_ID = "roarson/vocos_24khz.onnx"
# 单独下载 vocos_24khz.onnx 到模型目录

4.4 内置音色发现

test_wavs/prompt.txt 自动解析内置音色:

news-female.wav 各位村民, 大家新年好! 近期, 湖北省武汉市等多个地区
news-female-2.wav 本台消息, 中共中央国务院, 近日印发关于构建数据基础制度,
leijun-1.wav 那还是36年前, 1987年. 我呢考上了武汉大学的计算机系

每行格式:文件名.wav 参考文本,自动解析为音色列表。

4.5 合成调用

audio = self.tts.generate(
    text=text,
    prompt_text=ref_text,
    prompt_samples=samples.tolist(),  # 参考音频采样点
    sample_rate=sr,
    speed=1.0,
    num_steps=8,  # Flow Matching 迭代步数
)

五、实际应用指南

5.1 安装与配置

# 安装OddTTS(sherpa-onnx 会自动安装)
pip install oddtts

# 启动服务(首次启动会自动下载模型 + vocoder)
oddtts

5.2 切换引擎

在 Web 界面中选择引擎:Zipvoice

或在配置文件中修改:

# oddtts_config.py
"tts_type": ODDTTS_TYPE.ODDTTS_ZIPVOICE

5.3 API调用示例

import requests

# 生成语音文件
response = requests.post("http://localhost:9001/v1/audio/speech", json={
    "input": "关注我的公众号:奥德元,一起学习 AI,一起追赶时代。",
    "voice": "news-female",
    "response_format": "wav",
    "model": "oddtts-zipvoice"
})

# 保存音频文件
with open("output.wav", "wb") as f:
    f.write(response.content)

5.4 OpenAI 兼容 API

from openai import OpenAI

client = OpenAI(base_url="http://localhost:9001/v1", api_key="dummy")

response = client.audio.speech.create(
    model="oddtts-zipvoice",
    voice="news-female",
    input="Hello, this is a ZipVoice TTS test with Chinese and English mixed text. Good good study, day day up!"
)
response.stream_to_file("output.wav")

六、总结与展望

6.1 本次更新的意义

  1. 新引擎:为 OddTTS 增加了 ZipVoice 引擎,填补了”中等速度 + 音色克隆”的空白
  2. 轻量部署:sherpa-onnx 方案无需 PyTorch,pip 安装后一键启动
  3. 国内友好:模型从 ModelScope 下载,无需翻墙
  4. 自动配置:vocoder 自动下载,模型目录自动检测,开箱即用

6.2 踩坑经验总结

问题原因解决方案
PyTorch CPU 太慢官方仓库用 PyTorch 推理切换 sherpa-onnx
合成噪音vocoder mel 频段不匹配(80 vs 100)使用正确的 vocos_24khz.onnx
vocoder 下载 404ModelScope repo ID 带 .onnx 后缀roarson/vocos_24khz.onnx
voices 为空os.path.isfile() 检查目录改为 os.path.isdir()
异常被吞掉get_voices() 静默 catch加 logger.warning()

6.3 后续优化方向

  1. 语音克隆:当前仅使用内置 preset voices,后续可开放零样本语音克隆功能
  2. 流式生成:实现真正的流式输出,减少首字延迟
  3. 性能优化:调整 num_steps 参数(4步 vs 8步)权衡速度和质量
  4. 更多音色:支持用户上传自定义参考音频

相关资源

Leave a comment

Your email address will not be published. Required fields are marked *