VoxCPM.cpp

June 10, 2026 · View on GitHub

License

基于 ggml 构建的 VoxCPM 模型独立 C++ 推理项目。

English

状态

此目录现作为 VoxCPM.cpp 独立仓库的根目录。

  • third_party/ggml 作为供应商子树维护。
  • third_party/jsonthird_party/llama.cppthird_party/whisper.cppthird_party/SenseVoice.cpp 仅作为本地参考,被仓库忽略。
  • CMakeLists.txt 已支持在 third_party/json 缺失时通过 FetchContent 下载 nlohmann_json
  • VoxCPM2 现已进入初步支持状态。当前 C++ runtime 已经可以加载导出的 VoxCPM2 GGUF 权重、完成端到端推理、打通新的 reference 模式链路,并通过 AudioVAE V2 路径输出 48kHz 音频,但质量和数值对齐仍在持续验证中。

重构预告

仓库接下来会推进一轮更完整的 Torch-to-GGML runtime 重构,设计方向见以下两份文档:

这次重构的原因主要有几点:

  • 当前代码已经可以运行,但整体实现仍然带有较强的“把 PyTorch 模块直接拆成若干 C++ 片段”的迁移痕迹。
  • 这种写法适合快速跑通模型,但会让 shape 契约、对象所有权、持久状态、graph 生命周期以及 backend 放置策略逐渐缠在一起,后续维护和排错成本越来越高。
  • 热路径里如果频繁出现 tensor_get -> std::vector -> tensor_set 这类 host/backend 往返,随着模型变复杂、后端变多,性能和结构问题都会被进一步放大。

这轮重构不是表层整理,而是准备把 VoxCPM.cpp 推向更成熟的 ggml runtime 形态,包括:

  • 先固定 GGUF 契约和模块级输入输出契约
  • 用共享 WeightStore 和 backend-aware 的 loader/runtime 骨架统一权重与执行路径
  • 明确拆分权重、持久 state、compute 内存和 output buffer
  • 用真实重建条件驱动 graph cache,而不是只靠 shape 猜测
  • 让模块热路径尽量直接传递 backend-resident tensor 或 state handle,减少无意义的数据回传

一句话说,这次重构的目标不是继续堆补丁,而是把项目从“host 侧模块翻译式实现”推进到“契约先行、后端感知、可验证可扩展”的 runtime 架构。

为了加快这项工作,我也计划使用 ClaudeCode Opus 4.6 发起一轮更大规模的代码重写,重点改善代码可维护性、梳理 runtime 与模块边界,并减少模型初期跑通阶段遗留下来的胶水层代码。

构建

CPU 构建

cmake -B build
cmake --build build

CUDA 构建

只有当你打算使用 --backend cuda 时,才需要在配置阶段启用 ggml 的 CUDA backend:

cmake -B build-cuda \
  -DVOXCPM_CUDA=ON \
  -DVOXCPM_BUILD_BENCHMARK=OFF \
  -DVOXCPM_BUILD_TESTS=OFF \
  -DCMAKE_CUDA_ARCHITECTURES=89
cmake --build build-cuda

如果你希望同时保留 CPU 和 CUDA 两套构建,建议使用不同的构建目录,例如 buildbuild-cuda

重要提醒:

  • -DVOXCPM_CUDA=ON 只是在你要使用 --backend cuda 时才需要。
  • 如果你跑的是 --backend cpu 或其他非 CUDA 后端,不需要开启 CUDA 构建。
  • -DCMAKE_CUDA_ARCHITECTURES=89 只是 RTX 40 系列常见示例,不应盲目照抄。
  • 你应该根据自己的显卡架构设置 -DCMAKE_CUDA_ARCHITECTURES
  • 常见取值:
    • 86:很多 RTX 30 系列
    • 89:很多 RTX 40 系列

如果不确定,先确认自己的显卡型号,再决定这个值。

推理用法

基础 CPU 推理

./build/examples/voxcpm_tts \
  --model-path ./models/quantized/voxcpm1.5-q8_0-audiovae-f16.gguf \
  --prompt-audio ./examples/tai_yi_xian_ren.wav \
  --prompt-text "对,这就是我,万人敬仰的太乙真人。" \
  --text "大家好,我现在正在大可奇奇体验AI科技。" \
  --output ./out.wav \
  --backend cpu \
  --threads 8

带 Prompt 的推理

./build/examples/voxcpm_tts \
  --model-path ./models/quantized/voxcpm1.5-q8_0-audiovae-f16.gguf \
  --prompt-audio ./examples/tai_yi_xian_ren.wav \
  --prompt-text "对,这就是我,万人敬仰的太乙真人。" \
  --text "大家好,我现在正在大可奇奇体验AI科技。" \
  --output ./out.wav \
  --backend cpu \
  --threads 8 \
  --inference-timesteps 10 \
  --cfg-value 2.0

CUDA 推理

./build-cuda/examples/voxcpm_tts \
  --model-path ./models/quantized/voxcpm1.5-q8_0-audiovae-f16.gguf \
  --prompt-audio ./examples/tai_yi_xian_ren.wav \
  --prompt-text "对,这就是我,万人敬仰的太乙真人。" \
  --text "大家好,我现在正在大可奇奇体验AI科技。" \
  --output ./out.wav \
  --backend cuda \
  --threads 8 \
  --inference-timesteps 10 \
  --cfg-value 2.0

voxcpm_tts 当前支持 --backend {cpu|cuda|vulkan|auto}

OpenAI 兼容 TTS 服务

voxcpm-server 现在提供单端口 HTTP 服务,同时支持:

  • POST /v1/voices
  • GET /v1/voices/{id}
  • DELETE /v1/voices/{id}
  • POST /v1/audio/speech

完整接口列表

GET /healthz

健康检查接口。

示例返回:

{
  "status": "ok"
}

POST /v1/voices

注册可复用的 voice,上传内容为:

  • multipart 字段 id:必填,唯一 voice id
  • multipart 字段 text:必填,参考音频对应文本
  • multipart 文件 audio:必填,参考音频文件

成功返回:201 Created

返回 JSON 字段:

  • id
  • prompt_text
  • prompt_audio_length
  • sample_rate
  • patch_size
  • feat_dim
  • created_at
  • updated_at

GET /v1/voices/{id}

查询已注册 voice 的元数据。

成功返回:200 OK

返回 JSON 字段:

  • id
  • prompt_text
  • prompt_audio_length
  • sample_rate
  • patch_size
  • feat_dim
  • created_at
  • updated_at

DELETE /v1/voices/{id}

删除已注册的 voice。

成功返回:200 OK

示例返回:

{
  "id": "taiyi",
  "deleted": true
}

POST /v1/audio/speech

使用已注册的 voice id 从文本合成语音。

JSON 请求字段:

  • model:必填字符串,必须和启动参数 --model-name 一致
  • input:必填字符串,长度范围 14096
  • voice:必填
    • 可以直接传字符串 voice id,例如 "taiyi"
    • 也可以传对象形式 { "id": "taiyi" }
  • response_format:可选,默认 mp3
    • 支持 mp3opusflacwavpcm
  • speed:可选浮点数,范围 0.254.0
  • stream_format:可选,支持 audiosse
  • instructions:为兼容接口而保留,但只要传非空值目前就会报错

返回行为:

  • stream_format=audio 或省略:
    • 直接返回音频二进制
    • Content-Typeresponse_format 对应
  • stream_format=sse
    • 返回 text/event-stream
    • 每个 audio.delta 事件都包含一个按请求格式编码好的独立音频片段
    • 发送:
      • event: audio.delta
      • event: audio.completed

服务端输出采样率:

  • 启动 voxcpm-server 时可以加 --output-sample-rate HZ,在编码前先把合成音频重采样到指定采样率
  • 如果省略该参数,服务器会使用模型 AudioVAE 的输出采样率
  • 对 OpenAI-compatible 的 pcm 返回,若客户端按 24 kHz 解释播放,请显式设置 --output-sample-rate 24000
  • 这个覆盖同样适用于 wavmp3opus

队列行为:

  • 单个 server 进程同一时间只处理一个合成请求
  • 额外请求进入等待队列,长度由 --max-queue 控制
  • 队列满时返回 503

输出格式:

  • mp3audio/mpeg
  • opusaudio/ogg; codecs=opus
  • flacaudio/flac
  • wavaudio/wav
  • pcmapplication/octet-stream

构建选项:

  • VOXCPM_ENABLE_MP3=ON|OFF
    • 控制 MP3 支持
    • 运行时会优先尝试原生编码器,初始化失败时再回退到 ffmpeg
  • VOXCPM_ENABLE_OPUS=ON|OFF
    • 控制 Opus 路径,启用后会使用 Ogg Opus 的 ffmpeg fallback
    • 如果配置 CMake 时找不到 ffmpeg,会自动关闭该支持,response_format=opus 请求返回 501

输出示例:

  • speech.mp3
  • speech.opus

构建

CUDA 部署推荐这样构建:

cmake -B build-cuda \
  -DVOXCPM_CUDA=ON \
  -DVOXCPM_BUILD_BENCHMARK=OFF \
  -DVOXCPM_BUILD_TESTS=OFF \
  -DCMAKE_CUDA_ARCHITECTURES=89

cmake --build build-cuda -j8

这套 CUDA 构建只在你准备以 --backend cuda 启动服务时才需要。 如果你只想用 --backend cpu,普通 CPU 构建就够了:

cmake -B build -DVOXCPM_BUILD_BENCHMARK=OFF -DVOXCPM_BUILD_TESTS=OFF
cmake --build build -j8

如果想显式控制音频编码支持,可以再加上:

cmake -B build -DVOXCPM_BUILD_BENCHMARK=OFF -DVOXCPM_BUILD_TESTS=OFF \
  -DVOXCPM_ENABLE_MP3=ON \
  -DVOXCPM_ENABLE_OPUS=ON

这两个选项默认都是 ON

启动服务

服务会在启动时自动创建 --voice-dir,目录不存在也没关系。

CUDA 示例:

./build-cuda/examples/voxcpm-server \
  --host 127.0.0.1 \
  --port 8080 \
  --model-path ./models/quantized/voxcpm1.5-q8_0-audiovae-f16.gguf \
  --model-name voxcpm-1.5 \
  --threads 8 \
  --backend cuda \
  --voice-dir ./runtime/voices \
  --max-queue 8 \
  --max-decode-steps 512 \
  --output-sample-rate 24000 \
  --disable-auth

服务长文本时可以设置 --max-decode-steps。不设置或设为 0 时,服务端会继续使用保守的后端默认 decode 预算。

CPU 示例:

./build/examples/voxcpm-server \
  --host 127.0.0.1 \
  --port 8080 \
  --model-path ./models/quantized/voxcpm1.5-q8_0-audiovae-f16.gguf \
  --model-name voxcpm-1.5 \
  --threads 8 \
  --backend cpu \
  --voice-dir ./runtime/voices \
  --max-queue 8 \
  --max-decode-steps 512 \
  --output-sample-rate 24000 \
  --disable-auth

注册 voice

curl -X POST http://127.0.0.1:8080/v1/voices \
  -F "id=taiyi" \
  -F "text=对,这就是我,万人敬仰的太乙真人。" \
  -F "audio=@./examples/tai_yi_xian_ren.wav"

示例返回:

{
  "created_at": "2026-03-18T11:32:51Z",
  "feat_dim": 64,
  "id": "taiyi",
  "patch_size": 4,
  "prompt_audio_length": 43,
  "prompt_text": "对,这就是我,万人敬仰的太乙真人。",
  "sample_rate": 44100,
  "updated_at": "2026-03-18T11:32:51Z"
}

合成语音

curl -X POST http://127.0.0.1:8080/v1/audio/speech \
  -H "Content-Type: application/json" \
  -d '{
    "model": "voxcpm-1.5",
    "input": "大家好,我现在正在大可奇奇体验AI科技。",
    "voice": "taiyi",
    "response_format": "wav",
    "speed": 1.0,
    "stream_format": "audio"
  }' \
  --output ./voxcpm_taiyi.wav

MP3 示例:

curl -X POST http://127.0.0.1:8080/v1/audio/speech \
  -H "Content-Type: application/json" \
  -d '{
    "model": "voxcpm-1.5",
    "input": "大家好,我现在正在大可奇奇体验AI科技。",
    "voice": "taiyi",
    "response_format": "mp3",
    "stream_format": "audio"
  }' \
  --output ./voxcpm_taiyi.mp3

Opus 示例:

curl -X POST http://127.0.0.1:8080/v1/audio/speech \
  -H "Content-Type: application/json" \
  -d '{
    "model": "voxcpm-1.5",
    "input": "大家好,我现在正在大可奇奇体验AI科技。",
    "voice": "taiyi",
    "response_format": "opus",
    "stream_format": "sse"
  }' \
  --output ./voxcpm_taiyi.opus

说明

  • voice 字段当前直接传已注册的 voice id,例如 "taiyi"
  • instructions 为兼容 OpenAI 请求体而保留,但 VoxCPM v1 还未实现。
  • stream_format 支持 audiosseaudio.deltaformat 字段和实际字节内容会与非流式路径一致。
  • opus 输出是 Ogg Opus 容器,不是裸 Opus packet。
  • 如果你只需要本地离线推理,examples/voxcpm_tts 仍然是最简单的入口。
  • 如果启用了鉴权,上面所有接口都需要带 Authorization: Bearer <api-key>
  • 如果请求了构建时禁用的编码器,服务器会返回 501,例如 this build does not include opus encoder support
  • 错误返回格式如下:
{
  "error": {
    "message": "可读错误信息",
    "type": "invalid_request_error",
    "code": "bad_request"
  }
}

Benchmark 脚本

导出量化权重

./scripts/export_quantized_weights.sh

这个脚本会导出:

  • Q4_K
  • Q8_0
  • F16
  • 对应的 +AudioVAE-F16 变体
  • F32 baseline 拷贝

并生成类似 logs/quantized_weights_manifest_*.tsv 的 manifest 文件。

对导出权重做 Benchmark

CPU:

./scripts/benchmark_exported_weights.sh \
  --weights-file ./logs/quantized_weights_manifest_*.tsv \
  --backend cpu

CUDA:

./scripts/benchmark_exported_weights.sh \
  --weights-file ./logs/quantized_weights_manifest_*.tsv \
  --backend cuda

如果不传 --weights-file,脚本会自动选取 logs/ 下最新的 manifest。

测试

cd build
ctest --output-on-failure

测试模型/trace 路径配置和开源协作说明请见 docs/TEST_SETUP.md

ggml 维护

项目保持当前 ggml 导入和补丁流程的本地溯源:

  • 上游:https://github.com/ggerganov/ggml.git
  • 仓库拆分前的本地基础提交:4773cde162a55f0d10a6a6d7c2ea4378e30e0b01
  • 当前本地补丁:src/ggml-vulkan/ggml-vulkan.cpp 中的 Vulkan 头文件兼容性调整

详见 docs/ggml_subtree_maintenance_strategy.md

TODO

  1. 准备添加一个 WASM 用例,让用户可以直接在网页上试用 VoxCPM 模型。
  2. 继续优化推理性能。根据 https://github.com/DakeQQ/Text-to-Speech-TTS-ONNX 的报告,我们和它们当前展示的性能表现相比仍然有一段差距。
  3. 继续补全 OpenAI 兼容 TTS 服务和 voice 管理链路的测试覆盖。
  4. 继续收敛 VoxCPM2 初步支持中的质量问题和数值对齐差距。
  5. 使用 ClaudeCode Opus 4.6 推进一轮以可维护性为目标的大重写。

预告

接下来我也计划为 https://huggingface.co/fishaudio/s2-pro 单独创建一个 GGML 推理仓库。

基准测试

模型大小与压缩比

ModelQuantSize (MB)Compression
voxcpm1.5F3233921.00x (基准)
voxcpm1.5F1617001.99x
voxcpm1.5Q8_09423.60x
voxcpm1.5Q4_K5825.82x
voxcpm-0.5bF3227791.00x (基准)
voxcpm-0.5bF1613941.99x
voxcpm-0.5bQ8_07663.62x
voxcpm-0.5bQ4_K4775.82x

CPU 推理性能 (RTF - 越低越好)

ModelQuantModel OnlyWithout EncodeFull Pipeline
voxcpm1.5Q4_K2.3953.3955.598
voxcpm1.5Q4_K+AudioVAE-F161.8732.8484.433
voxcpm1.5Q8_02.0862.9824.291
voxcpm1.5Q8_0+AudioVAE-F162.2853.3215.248
voxcpm1.5F163.2574.3666.263
voxcpm1.5F16+AudioVAE-F162.9803.9155.374
voxcpm1.5F324.8205.7377.494
voxcpm-0.5bQ4_K1.8262.2193.609
voxcpm-0.5bQ4_K+AudioVAE-F161.8952.2953.915
voxcpm-0.5bQ8_02.1552.5463.873
voxcpm-0.5bQ8_0+AudioVAE-F161.9132.2843.638
voxcpm-0.5bF162.5582.9314.086
voxcpm-0.5bF16+AudioVAE-F162.6853.0574.409
voxcpm-0.5bF323.6914.0555.260

CUDA 推理性能 (RTF - 越低越好)

ModelVariantAudioVAEModel OnlyWithout EncodeFull PipelineTotal Time (s)
voxcpm1.5Q4_Kmixed0.3420.4320.6222.189
voxcpm1.5Q4_K+AudioVAE-F16f160.3360.4260.5962.192
voxcpm1.5Q8_0mixed0.3200.4110.5962.002
voxcpm1.5Q8_0+AudioVAE-F16f160.3080.3970.5592.148
voxcpm1.5F16mixed0.3520.4420.6481.970
voxcpm1.5F16+AudioVAE-F16f160.3470.4380.6551.885
voxcpm1.5F32 (baseline)original0.4140.5030.6862.305
voxcpm-0.5bQ4_Kmixed0.4010.4420.5502.067
voxcpm-0.5bQ4_K+AudioVAE-F16f160.3960.4370.5551.953
voxcpm-0.5bQ8_0mixed0.4300.4700.6231.644
voxcpm-0.5bQ8_0+AudioVAE-F16f160.4170.4560.5951.809
voxcpm-0.5bF16mixed0.3900.4280.5671.678
voxcpm-0.5bF16+AudioVAE-F16f160.3920.4300.5651.718
voxcpm-0.5bF32 (baseline)original0.5000.5390.6801.903

RTF 定义:

  • Model Only:纯模型推理(prefill + decode loop),不含 AudioVAE
  • Without Encode:模型 + AudioVAE decode(离线预计算 prompt 特征的部署场景)
  • Full Pipeline:端到端完整流程,包含 AudioVAE encode + 模型 + decode

关键发现

CPU

  1. CPU 最优配置现在取决于模型和指标voxcpm1.5 Q4_K+AudioVAE-F16 在 model-only 和 without-encode 指标上最好,voxcpm1.5 Q8_0 在完整流水线指标上最好,而 voxcpm-0.5b Q4_K 仍然是整体最稳妥的 CPU 选择。
  2. 1.5B 在 CPU 上明显受益于 AudioVAE-F16Q4_K+AudioVAE-F16voxcpm1.5 上拿到了最好的 Model OnlyWithout Encode RTF,而 Q8_0 拿到了最好的完整流水线 RTF。
  3. 0.5B 的 CPU 最优仍然是 Q4_Kvoxcpm-0.5b Q4_K 的整体 CPU RTF 最好,Q8_0+AudioVAE-F16 在完整流水线指标上非常接近。
  4. 这台 CPU 上 F32 最慢:无论是 voxcpm1.5 还是 voxcpm-0.5b,F32 baseline 都是最慢的 CPU 配置。

CUDA

  1. CUDA 明显快于 CPU:在本轮测试中,完整流水线 RTF 从 CPU 的 3.83-15.02 下降到 CUDA 的 0.55-0.69
  2. CUDA 下最佳配置取决于评价指标:对 voxcpm1.5Q8_0+AudioVAE-F16 的 RTF 最好,而 F16+AudioVAE-F16 的总耗时最短;对 voxcpm-0.5bQ4_K 的完整流水线 RTF 最好,而 Q8_0 的总耗时最短。
  3. CUDA 不再明显偏爱 Q4_K:和 CPU 不同,Q4_K 在 CUDA 上并不总是最快,Q8_0F16 经常同样有竞争力,甚至更好。
  4. AudioVAE F16 在 CUDA 上有帮助:把 AudioVAE 强制导出为 F16 后,多组 CUDA 测试结果变好,尤其是 voxcpm1.5 Q8_0voxcpm-0.5b Q8_0

部署建议

场景推荐配置
生产部署voxcpm-0.5b Q4_K (477 MB, RTF 3.609)
平衡精度voxcpm1.5 Q8_0 (942 MB, RTF 4.291)
1.5B 离线 prompt 场景voxcpm1.5 Q4_K+AudioVAE-F16 (647 MB, Without Encode RTF 2.848)
最高精度基线voxcpm1.5 F32 (3392 MB, RTF 7.494)

CUDA 部署建议

场景推荐配置
最低完整流水线 RTFvoxcpm-0.5b Q4_K (477 MB, RTF 0.550)
1.5B 最佳延迟/RTF 平衡voxcpm1.5 Q8_0+AudioVAE-F16 (984 MB, RTF 0.559)
1.5B 较小且适合 CUDA 的模型voxcpm1.5 Q4_K+AudioVAE-F16 (647 MB, RTF 0.596)
最高精度基线voxcpm1.5 F32 (3392 MB, RTF 0.686)

CPU 测试环境:

  • CPU:12th Gen Intel(R) Core(TM) i5-12600K
  • 线程:8
  • 后端:CPU
  • 基准结果来源:logs/benchmark_summary_cpu_20260318_092142.txt

CUDA 测试环境:

  • 后端:CUDA
  • GPU:NVIDIA GeForce RTX 4060 Ti
  • CUDA 设备:CUDA0
  • Compute capability:8.9
  • CUDA VMM:yes
  • 主机 CPU:12th Gen Intel(R) Core(TM) i5-12600K
  • 线程:8
  • Inference timesteps:10
  • CFG value:2.0
  • 基准结果来源:logs/benchmark_summary_cuda_20260318_092028.txt