Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ad2baa7
[WIP] Add VoxCPM v1 — lightweight VoxCPM TTS support (0.5B / 1.5B)
jasonchen31 Aug 16, 2026
11f37a7
fix(voxcpm1): resolve partialy pure noise output by fixing synthesize…
jasonchen31 Aug 17, 2026
59d6185
# Commit: Fix VoxCPM1 Voice Quality (Pure Noise → Intelligible Speech)
jasonchen31 Aug 17, 2026
fdb06c2
Merge branch '0xShug0:main' into main
jasonchen31 Aug 17, 2026
43b12a9
Merge branch '0xShug0:main' into main
jasonchen31 Aug 17, 2026
36567b7
# VoxCPM1 GGUF Self-Contained Loading
jasonchen31 Aug 17, 2026
bc80b8b
Fix VoxCPM1 issues: sample rate (V1.5) and early stopping (V1 0.5B)
jasonchen31 Aug 17, 2026
0ec6230
Merge branch '0xShug0:main' into main
jasonchen31 Aug 18, 2026
85e5859
**feat(voxcpm1): enable voice clone & streaming support (parity with …
jasonchen31 Aug 18, 2026
a8fef3d
fix(voxcpm1): load real RoPE longrope factors and align min_tokens floor
jasonchen31 Aug 18, 2026
7f224ab
Merge branch '0xShug0:main' into main
jasonchen31 Aug 18, 2026
65ea6a1
fix(voxcpm1): align voice-clone conditioning and VAE encoder with gol…
jasonchen31 Aug 19, 2026
0801a84
Merge branch '0xShug0:main' into main
jasonchen31 Aug 19, 2026
00ea405
fix(voxcpm1): route --voice-ref through prompt path so V1 cloning works
jasonchen31 Aug 19, 2026
51c4452
Merge branch 'main' of https://github.com/jasonchen31/audio.cpp into …
jasonchen31 Aug 19, 2026
57b6498
refactor: release tensor_source framework changes back to upstream
jasonchen31 Aug 19, 2026
2b65a0d
fix(voxcpm1): Add Webui support for VoxCPM v1 (0.5B)
jasonchen31 Aug 19, 2026
1d9c59b
perf(voxcpm1): release text-length-scaled VRAM after each request
jasonchen31 Aug 19, 2026
6e4f09d
Merge branch '0xShug0:main' into main
jasonchen31 Aug 19, 2026
1d6576c
remove local
jasonchen31 Aug 19, 2026
f125922
Merge branch 'main' of https://github.com/jasonchen31/audio.cpp into …
jasonchen31 Aug 19, 2026
063cbe3
Merge remote-tracking branch 'upstream/main'
jasonchen31 Aug 19, 2026
5b949e7
generate new index.html based on merged code
jasonchen31 Aug 19, 2026
064bd91
Merge remote-tracking branch 'upstream/main'
jasonchen31 Aug 20, 2026
47a7e7e
fix(vpxcpm1): release the last framework change. udpate index.html fo…
jasonchen31 Aug 20, 2026
730fb37
remove loacal file
jasonchen31 Aug 20, 2026
03e18ca
add missing file
jasonchen31 Aug 20, 2026
a252b6d
fix(voxcpm1/2): remove strange stream blocking setting.
jasonchen31 Aug 20, 2026
44d38be
feat(webui): expose VoxCPM1 1.5B variants in catalog
jasonchen31 Aug 20, 2026
0d23e6c
fix(voxcpm1): read CFM config keys from GGUF with golden defaults
jasonchen31 Aug 20, 2026
28915eb
feat(tools): add VoxCPM1 1.5B q8_0+q4_k cli path-test cases
jasonchen31 Aug 20, 2026
75152ff
feat(voxcpm1): add VoxCPM1 community model family and fix V1 weight l…
jasonchen31 Aug 21, 2026
f173672
Merge remote-tracking branch 'upstream/main'
jasonchen31 Aug 21, 2026
5ac840d
update webuil for voxcpm1
jasonchen31 Aug 21, 2026
264e5df
add model description
jasonchen31 Aug 21, 2026
19323a5
Fix VoxCPM1 streaming TTS chunk boundary glitches
jasonchen31 Aug 22, 2026
e29c090
Clean up VoxCPM1 debug code
jasonchen31 Aug 22, 2026
6f680ee
fix the issue voxcpm1 does not read reference_text
jasonchen31 Aug 22, 2026
956c3cd
voxcpm1: deduplicate tokenizer logic into shared tokenizer_common
jasonchen31 Aug 22, 2026
4706cef
merge and update webui
jasonchen31 Aug 22, 2026
9363611
merge upstream/main: resolve index.html conflict using ours
jasonchen31 Aug 22, 2026
5715b6c
add missing tokenizer_common.cpp
jasonchen31 Aug 22, 2026
10d4eb4
add tokenizer_common.h
jasonchen31 Aug 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,24 @@ audiocpp_add_model(voxcpm2
engine::models::voxcpm2::make_voxcpm2_loader
)

audiocpp_add_model(voxcpm1
SOURCES
src/community_models/voxcpm1/assets.cpp
src/community_models/voxcpm1/audiovae.cpp
src/community_models/voxcpm1/config_gguf.cpp
src/community_models/voxcpm1/generator.cpp
src/community_models/voxcpm1/gguf_metadata.cpp
src/community_models/voxcpm1/minicpm.cpp
src/community_models/voxcpm1/session.cpp
src/community_models/voxcpm1/tokenizer_common.cpp
src/community_models/voxcpm1/tokenizer_gguf.cpp
src/community_models/voxcpm1/tokenizer_text.cpp
INCLUDES
engine/community_models/voxcpm1/session.h
LOADERS
engine::community_models::voxcpm1::make_voxcpm1_loader
)

audiocpp_add_model(vibevoice
SOURCES
src/models/vibevoice/assets.cpp
Expand Down
1 change: 1 addition & 0 deletions docs/community_models/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Practical expectations:
| **minimax_music3** | Music | auto | [@0xShug0](https://github.com/0xShug0) | [MiniMax Music 3](minimax_music3.md) text-to-music generation with lyrics conditioning |
| **moss_tts_local** | TTS, voice cloning | auto, optional language hint | [@justinjohn0306](https://github.com/justinjohn0306) | [MOSS-TTS-Local Transformer v1.5](../models/moss_tts.md) support in the core model tree |
| **outetts** | TTS, voice cloning | en, ar, zh, nl, fr, de, it, ja, ko, lt, ru, es, pt, be, bn, ka, hu, lv, fa, pl, sw, ta, uk | Mirek [@mirek190](https://github.com/mirek190) | [Llama-OuteTTS-1.0-1B](outetts.md) TTS and voice cloning support |
| **voxcpm1** | TTS, voice cloning | zh, en, ja, ko | Community | [VoxCPM1](voxcpm1.md) tokenizer-free 0.5B TTS with 16 kHz output, streaming, and continuation-mode voice cloning |
| **parakeet_tdt** | ASR | auto, bg, cs, da, de, el, en, es, et, fi, fr, hr, hu, it, lt, lv, mt, nl, pl, pt, ro, ru, sk, sl, sv, uk | [@dleiferives](https://github.com/dleiferives) | [Parakeet-TDT 0.6B v3](parakeet_tdt.md) offline, long-form, and buffered-streaming ASR support |
| **sense_asr** | ASR | auto, zh, en, yue, ja, ko, pt, ru, es, it, fr, de, nl, pl, tr, ar, hi, vi, th, id, ms, fa, nospeech | Jason Chen [@jasonchen31](https://github.com/jasonchen31), [@LauraGPT](https://github.com/LauraGPT) / FunASR | [SenseVoice-Small](sense_asr.md) offline/streaming SAN-M + CTC transcription with event/emotion/language tags and ITN |
| **vietneu_tts** | TTS, voice cloning | vi, en | Phuoc [@phuocnguyen90](https://github.com/phuocnguyen90) | [VieNeu-TTS-v3-Turbo](vietneu_tts.md) TTS and voice cloning support |
Expand Down
169 changes: 169 additions & 0 deletions docs/community_models/voxcpm1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# VoxCPM1

VoxCPM1 is a **tokenizer-free TTS model** from [OpenBMB](https://github.com/OpenBMB/VoxCPM) that generates 16 kHz mono speech. The audio.cpp port is based on [VoxCPM.cpp](https://github.com/bluryar/VoxCPM.cpp) and reuses the existing VoxCPM2 runtime tree with a GGUF tensor-adaptation layer that handles the OpenBMB-specific conventions (folded AudioVAE weights, no `weight_v`/`weight_g` split, no SR-conditioning tensors).

| Field | Value |
|---|---|
| **Family** | `voxcpm1` |
| **Model directory** | `models/VoxCPM1-GGUF` (0.5B) |
| **Task** | `tts`, `clon` |
| **Modes** | `offline`, `streaming` |
| **Languages** | Model auto-handles supported languages (zh, en, ja, ko validated) |
| **Voice input** | Optional reference WAV; optional transcript via `--reference-text` |
| **Built-in voices** | Not exposed |
| **Output** | mono 16 kHz WAV |

---

## 🚀 Installation & Quick Start

### 1. Install GGUF Model Weights

```bash
# Via model manager (recommended)
python3 tools/model_manager_v2.py install voxcpm1_0.5b_q8_0 --models-root models
```

This downloads the `voxcpm-0.5b-q8_0-audiovae-f16.gguf` package (~690 MB) to `models/VoxCPM1-GGUF/`.

### 2. Compile the CLI Target

```bash
chmod +x scripts/build_linux.sh
./scripts/build_linux.sh --backend cpu --target audiocpp_cli
```

For CUDA (recommended for production):

```bash
./scripts/build_linux.sh --backend cuda --target audiocpp_cli
```

### 3. Run Inference

**Text-to-speech (offline):**

```bash
./build/linux-cpu-release/bin/audiocpp_cli \
--task tts \
--family voxcpm1 \
--model models/VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf \
--backend cpu \
--text "Hello from VoxCPM1." \
--out out.wav
```

**Voice clone (continuation-mode):**

```bash
./build/linux-cpu-release/bin/audiocpp_cli \
--task tts \
--family voxcpm1 \
--model models/VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf \
--backend cpu \
--text "Hello from VoxCPM1." \
--voice-ref assets/resources/b.wav \
--reference-text "Some call me nature. Others call me Mother Nature." \
--out out.wav
```

**Streaming output:**

```bash
./build/linux-cpu-release/bin/audiocpp_cli \
--task tts \
--family voxcpm1 \
--model models/VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf \
--backend cpu \
--mode streaming \
--text "Hello from VoxCPM1 streaming." \
--request-option retry_badcase=false \
--out out.wav
```

---

## 📊 Performance Benchmark

Measured on Ubuntu 24.04 with OpenMP optimization (CPU backend):

- **GGUF package size**: **~690 MB** (Q8_0 LLM + F16 AudioVAE)
- **Startup latency**: **Instant (<0.01s)** via `mmap` lazy loading
- **Inference speed** (short sentence): **~0.25 RTF** on CPU (~4x faster than real-time)
- **Idle VRAM** (with `mem_saver`): **~1.4 GB**; long text up to **~3.5 GB**

---

## 🛡️ Options & Customizations

| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--voice-ref` | WAV path | not set | Reference speaker audio for voice cloning. |
| `--reference-text` | text | empty | Transcript of the reference audio (for clone prompting). |
| `--mode` | `offline`, `streaming` | `offline` | Full-output or streaming run mode; streaming requires `retry_badcase=false`. |
| `--session-option voxcpm1.mem_saver` | `true`, `false` | `false` | Use tighter graph workspaces and release request runtime graphs to reduce resident VRAM. |
| `--session-option voxcpm1.prompt_cache_slots` | integer | `1` | Prompt and prompt-audio embedding cache slots. Set `0` to disable prompt caching. |
| `--max-tokens` | integer | `4096` | Maximum generated AR tokens. |
| `--num-inference-steps` | integer | `10` | CFM flow-matching diffusion steps. |
| `--guidance-scale` | float | `2.0` | CFG strength. |
| `--request-option retry_badcase` | `true`, `false` | `true` | Auto-retry when generation is detected as a bad case. |
| `--request-option retry_badcase_max_times` | integer | `2` | Maximum retry count. |
| `--text-chunk-mode` | `default`, `tag_aware`, `japanese`, `endline` | `tag_aware` | Long-form text chunking strategy. |

---

## 🔧 Architecture Notes

- **Base LM**: 24-layer MiniCPM transformer (1024 hidden, 73,448 vocab)
- **Residual LM**: 6-layer autoregressive refinement
- **Local Encoder**: 4-layer feature encoder (FSQ quantization, 8 codebooks)
- **Local DiT**: 4-layer diffusion transformer for CFM sampling
- **AudioVAE**: Encoder (128 dim, rates `[2,5,8,8]`) + Decoder (1536 dim, rates `[8,8,5,2]`)
- **Output**: 16 kHz mono

The port adapts V1 GGUF conventions to the V2 loader:
- **Folded AudioVAE weights**: `weight_v` receives folded data; `weight_g` synthesized as per-row L2 norms (identity fold)
- **No SR-conditioning tensors**: Synthesized as identity (scale=1, bias=0)
- **Embedding transpose**: V1 stores `[hidden, vocab]` → transposed to `[vocab, hidden]` for ggml
- **Synthesized-weight guard**: `is_synthesized()` distinguishes fabricated tensors (e.g., `fusion_concat_proj`) from loaded ones
- **Config & tokenizer from GGUF metadata**: Fully self-contained; no external sidecars needed

---

## ✅ Validation Status

| Mode | Status | Notes |
|---|---|---|
| **Offline TTS** | ✅ Works | "This is a test run for the fix" → transcribes as **"This is a test."** (SenseVoice) |
| **Voice Clone** | ✅ Works | 6/6 target sentences transcribe exactly via SenseVoice; continuation-mode with reference audio + transcript |
| **Streaming** | ✅ Works | SSE PCM chunks at native 16 kHz; requires `retry_badcase=false` |
| **Reference-only clone** | ⚠️ Limited | `ref_start`/`ref_end` fails identically in the golden `VoxCPM.cpp` — model-level limitation |

**Regression guard**: VoxCPM2 path is untouched (`config.v1` default `false`); still generates 48 kHz speech with byte-identical output.

---

## 📦 Model Package

The default package is the standalone GGUF:

| Package ID | Display Name | Format | Precision | Files |
|---|---|---|---|---|
| `voxcpm1_0.5b_q8_0` | VoxCPM 0.5B Q8_0 GGUF | gguf | q8_0 (LLM) + f16 (AudioVAE) | `VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf` |

The GGUF embeds:
- Hybrid-quantized model (LLM Q8_0, AudioVAE F16)
- Full config (`voxcpm.*` metadata keys)
- BPE tokenizer (`audiocpp.vocab_*` metadata)

No separate tokenizer.json or config.json files are needed.

---

## 🔗 References

- **Upstream model**: [OpenBMB/VoxCPM](https://github.com/OpenBMB/VoxCPM)
- **Reference port**: [VoxCPM.cpp](https://github.com/bluryar/VoxCPM.cpp)
- **audio.cpp porting doc**: [VOXCPM1_Porting.md](../VOXCPM1_Porting.md)
- **Model spec**: [model_specs/voxcpm1.json](../../model_specs/voxcpm1.json)
- **Main docs**: [docs/tts.md#voxcpm1](../../docs/tts.md#voxcpm1)
44 changes: 44 additions & 0 deletions docs/tts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
| NeuTTS | `neutts` | `tts` | [NeuTTS](#neutts) |
| OmniVoice | `omnivoice` | `tts` | [OmniVoice](#omnivoice), [full guide](models/omnivoice.md) |
| PocketTTS | `pocket_tts` | `tts` | [PocketTTS](#pockettts) |
| VoxCPM1 | `voxcpm1` | `tts` | [VoxCPM1](#voxcpm1) |
| VoxCPM2 | `voxcpm2` | `tts`, `vdes` | [VoxCPM2](#voxcpm2) |
| Higgs Audio v3 TTS | `higgs_audio_tts` | `tts` | [Higgs Audio v3 TTS](#higgs-audio-v3-tts) |
| Fish Audio S2 Pro | `fish_audio` | `tts` | [Fish Audio S2 Pro](#fish-audio-s2-pro) |
Expand Down Expand Up @@ -422,6 +423,49 @@ audiocpp_cli --task tts --family pocket_tts --model models/pocket-tts --backend
| `--text-chunk-size` | integer chars | `256` | Long-form chunk size. |
| `--session-option pocket_tts.voice_state_cache_slots=<n>` | integer slots | `4` | Prepared voice-state cache slots; set `0` to disable reuse. |

## VoxCPM1

VoxCPM1 supports offline and streaming TTS plus short-reference voice cloning. It reuses the VoxCPM2 runtime tree with a GGUF tensor-adaptation layer that understands the OpenBMB folded AudioVAE weights. The registered package is the 16 kHz 0.5B model; the runtime is size-agnostic, so a different VoxCPM1 GGUF can still be loaded via an explicit `--model <path>`.

| Field | Value |
|---|---|
| Family | `voxcpm1` |
| Model directory | `models/VoxCPM1-GGUF` (0.5B) |
| Task | `tts` |
| Modes | `offline`, `streaming` |
| Languages | Model auto-handles supported languages |
| Voice input | Optional reference WAV; optional transcript through `--reference-text` |
| Built-in voices | Not exposed |

Text to speech:

```bash
audiocpp_cli --task tts --family voxcpm1 --model models/VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf --backend cpu --text "Hello from VoxCPM1." --out out.wav
```

Voice clone:

```bash
audiocpp_cli --task tts --family voxcpm1 --model models/VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf --backend cpu --text "Hello from VoxCPM1." --voice-ref assets/resources/b.wav --out out.wav
```

Streaming output:

```bash
audiocpp_cli --task tts --family voxcpm1 --model models/VoxCPM1-GGUF/voxcpm-0.5b-q8_0-audiovae-f16.gguf --backend cpu --mode streaming --text "Hello from VoxCPM1." --request-option retry_badcase=false --out out.wav
```

| Option | Values | Default | Meaning |
|---|---:|---:|---|
| `--voice-ref` | WAV path | not set | Reference speaker audio. |
| `--reference-text` | text | empty string | Transcript for the reference audio (clone prompting). |
| `--mode` | `offline`, `streaming` | `offline` | Full-output or streaming run mode; streaming requires `retry_badcase=false`. |
| `--session-option voxcpm1.mem_saver=true\|false` | bool | `false` | Use tighter graph workspaces and release MiniCPM/AudioVAE request graphs after completion to reduce resident VRAM. |
| `--session-option voxcpm1.prompt_cache_slots=<n>` | integer | `1` | Prompt and prompt-audio embedding cache slots. Set to `0` to disable prompt caching. |
| `--max-tokens` | integer | `4096` | Maximum generated AR tokens. |
| `--num-inference-steps` | integer | `10` | Flow matching steps. |
| `--guidance-scale` | float | `2.0` | CFG strength. |

## VoxCPM2

VoxCPM2 supports plain TTS, voice design, controllable voice cloning, and an ultimate-clone style that uses both prompt audio and transcript. The CLI expresses voice design with the same text convention as the upstream examples: put the voice/style description in parentheses at the start of `--text`.
Expand Down
102 changes: 102 additions & 0 deletions include/engine/community_models/voxcpm1/assets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#pragma once

#include "engine/framework/assets/resource_bundle.h"
#include "engine/framework/assets/tensor_source.h"
#include "engine/community_models/voxcpm1/tokenizer_gguf.h"

#include <cstdint>
#include <filesystem>
#include <memory>
#include <string>
#include <vector>

namespace engine::community_models::voxcpm1 {

struct VoxCPM1RopeScalingConfig {
std::string type;
std::vector<float> long_factor;
std::vector<float> short_factor;
int64_t original_max_position_embeddings = 0;
};

struct VoxCPM1MiniCPMConfig {
int64_t bos_token_id = 1;
int64_t eos_token_id = 2;
int64_t hidden_size = 0;
int64_t intermediate_size = 0;
int64_t max_position_embeddings = 0;
int64_t num_attention_heads = 0;
int64_t num_hidden_layers = 0;
int64_t num_key_value_heads = 0;
int64_t kv_channels = 0;
int64_t vocab_size = 0;
int64_t scale_emb = 1;
int64_t dim_model_base = 0;
float rms_norm_eps = 1.0e-5F;
float rope_theta = 10000.0F;
float scale_depth = 1.0F;
bool use_mup = false;
bool no_rope = false;
VoxCPM1RopeScalingConfig rope_scaling;
};

struct VoxCPM1LocalTransformerConfig {
int64_t hidden_dim = 0;
int64_t ffn_dim = 0;
int64_t num_heads = 0;
int64_t num_layers = 0;
int64_t kv_channels = 0;
};

struct VoxCPM1CFMConfig {
float sigma_min = 1.0e-6F;
std::string solver = "euler";
std::string t_scheduler = "log-norm";
float inference_cfg_rate = 2.0F;
};

struct VoxCPM1DiTConfig : VoxCPM1LocalTransformerConfig {
bool mean_mode = false;
VoxCPM1CFMConfig cfm;
};

struct VoxCPM1AudioVAEConfig {
int64_t encoder_dim = 0;
std::vector<int64_t> encoder_rates;
int64_t latent_dim = 0;
int64_t decoder_dim = 0;
std::vector<int64_t> decoder_rates;
std::vector<int64_t> sample_rate_bin_boundaries;
int sample_rate = 0;
int output_sample_rate = 0;
};

struct VoxCPM1Config {
std::string architecture;
VoxCPM1MiniCPMConfig lm;
int64_t patch_size = 4;
int64_t feat_dim = 64;
int64_t residual_lm_num_layers = 8;
bool residual_lm_no_rope = false;
int64_t scalar_quantization_latent_dim = 512;
int64_t scalar_quantization_scale = 9;
VoxCPM1LocalTransformerConfig encoder;
VoxCPM1DiTConfig dit;
VoxCPM1AudioVAEConfig audio_vae;
int64_t max_length = 8192;
std::string device = "cuda";
std::string dtype = "bfloat16";
bool v1 = false;
};

struct VoxCPM1Assets {
assets::ResourceBundle resources;
VoxCPM1Config config;
std::shared_ptr<const assets::TensorSource> model_weights;
std::shared_ptr<const assets::TensorSource> audiovae_weights;
std::shared_ptr<const VoxCPM1GgufTokenizer> gguf_tokenizer;
};

std::shared_ptr<const VoxCPM1Assets> load_voxcpm1_assets(const std::filesystem::path & model_path);

} // namespace engine::community_models::voxcpm1
Loading
Loading