2026-05-04 16:41:29 -04:00
# Text-To-Speech (Online Serving)
Source <https://github.com/vllm-project/vllm-omni/tree/main/examples/online_serving/text_to_speech>.
vLLM-Omni exposes TTS models through the OpenAI-compatible
[`POST /v1/audio/speech` ](https://github.com/vllm-project/vllm-omni/tree/main/docs/serving/speech_api.md ) endpoint,
launched with `vllm serve <model> --omni` . Each TTS model has its own
subdirectory containing client snippets, gradio demos, and helper
scripts; this README is the single doc entry point for all of them.
For offline inference, see [`examples/offline_inference/text_to_speech` ](https://github.com/vllm-project/vllm-omni/tree/main/examples/offline_inference/text_to_speech/README.md ).
For the full list of supported architectures across all modalities, see
[Supported Models ](https://github.com/vllm-project/vllm-omni/tree/main/docs/models/supported_models.md ).
## Supported Models
| Model | HuggingFace repo | Voice cloning | Streaming | Voice presets / upload | Gradio demo |
2026-09-03 02:16:55 +08:00
| --- | --- | --- | --- | --- | --- |
2026-05-20 03:24:06 -04:00
| CosyVoice3 | `FunAudioLLM/Fun-CosyVoice3-0.5B-2512` | ✓ (`ref_audio` +`ref_text` ) | ✓ (PCM stream) | — | — |
2026-05-04 16:41:29 -04:00
| Fish Speech S2 Pro | `fishaudio/s2-pro` | ✓ (`ref_audio` +`ref_text` ) | ✓ (PCM stream) | — | ✓ |
2026-09-15 14:56:51 -07:00
| Gepard-1.0 | `nineninesix/gepard-1.0` | — (zero-shot default voice) | ✓ (PCM / WAV stream) | `default` only | — |
2026-05-25 23:16:07 +08:00
| higgs-audio v2 | `bosonai/higgs-audio-v2-generation-3B-base` | ✓ (`ref_audio` +`ref_text` ) | ✓ (codec_streaming) | — | — |
2026-05-24 05:42:26 +08:00
| GLM-TTS | `zai-org/GLM-TTS` | ✓ (`ref_audio` +`ref_text` , required) | ✓ (PCM stream) | — | ✓ |
2026-05-04 16:41:29 -04:00
| OmniVoice | `k2-fsa/OmniVoice` | (offline only) | — | — | — |
| Qwen3-TTS | `Qwen/Qwen3-TTS-12Hz-1.7B-{CustomVoice,VoiceDesign,Base}` | ✓ (Base) | ✓ (PCM + WebSocket) | ✓ (presets + `/v1/audio/voices` upload) | ✓ (standard + FastRTC) |
| VoxCPM2 | `openbmb/VoxCPM2` | ✓ | ✓ (AudioWorklet via gradio) | — | ✓ |
| Voxtral TTS | `mistralai/Voxtral-4B-TTS-2603` | ✓ (gated upstream) | ✓ | ✓ (presets) | ✓ |
2026-05-20 03:24:06 -04:00
For offline inference of any of these models, see the [offline TTS hub ](https://github.com/vllm-project/vllm-omni/tree/main/examples/offline_inference/text_to_speech/README.md ).
2026-05-04 16:41:29 -04:00
## Common Quick Start
Launch the server (defaults shown — adjust `--port` , `--gpu-memory-utilization` , etc. as needed):
```bash
vllm serve <hf-repo-or-local-path> --omni --port 8091
```
Send a TTS request via curl:
```bash
curl -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"input": "Hello, how are you?",
"voice": "default",
"response_format": "wav"
}' --output output.wav
```
Or via Python httpx:
```python
import httpx
response = httpx . post (
"http://localhost:8091/v1/audio/speech" ,
json = {
"input" : "Hello, how are you?" ,
"voice" : "default" ,
"response_format" : "wav" ,
},
timeout = 300.0 ,
)
open ( "output.wav" , "wb" ) . write ( response . content )
```
Or via the OpenAI SDK:
```python
from openai import OpenAI
client = OpenAI ( base_url = "http://localhost:8091/v1" , api_key = "none" )
response = client . audio . speech . create (
model = "<hf-repo>" ,
voice = "default" ,
input = "Hello, how are you?" ,
)
response . stream_to_file ( "output.wav" )
```
2026-06-30 10:17:24 +08:00
Streaming PCM output (where supported) — set `stream=true` , `stream_format="audio"` , and `response_format="pcm"` :
2026-05-04 16:41:29 -04:00
```bash
curl -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"input": "Hello, how are you?",
"voice": "default",
"stream": true,
2026-06-30 10:17:24 +08:00
"stream_format": "audio",
2026-05-04 16:41:29 -04:00
"response_format": "pcm"
}' --no-buffer | play -t raw -r 24000 -e signed -b 16 -c 1 -
```
2026-09-15 14:56:51 -07:00
Adjust the player's sample rate to match the model (44.1 kHz for Fish Speech, 48 kHz for VoxCPM2, 22.05 kHz for Gepard, 24 kHz for the others).
2026-05-04 16:41:29 -04:00
For full request-shape documentation (all parameters, response formats, error codes), see the [Speech API reference ](https://github.com/vllm-project/vllm-omni/tree/main/docs/serving/speech_api.md ).
---
2026-05-20 03:24:06 -04:00
## CosyVoice3
2-stage TTS (`talker` + flow-matching `code2wav` ) at 24 kHz. Voice cloning only — every request needs `ref_audio` + `ref_text` ; there are no built-in voice presets.
### Prerequisites
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
```bash
huggingface-cli download FunAudioLLM/Fun-CosyVoice3-0.5B-2512
```
If your downloaded checkpoint lacks `config.json` , add one with `{"model_type": "cosyvoice3", "architectures": ["CosyVoice3Model"]}` (the loader reads `model_type` to select the class).
### Launch
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
```bash
vllm serve FunAudioLLM/Fun-CosyVoice3-0.5B-2512 --omni --port 8091 --trust-remote-code
# or:
./cosyvoice3/run_server.sh
```
Streaming is on by default via `async_chunk: true` in `vllm_omni/deploy/cosyvoice3.yaml` . Pass `--no-async-chunk` (or `NO_ASYNC_CHUNK=1 ./cosyvoice3/run_server.sh` ) for the legacy synchronous path.
2026-09-12 20:31:49 +08:00
Cross-request Stage-1 flow batching is opt-in. Enable it in the server environment when concurrent requests should share a flow-estimator call:
```bash
export COSYVOICE3_BATCH_FLOW = 1
vllm serve FunAudioLLM/Fun-CosyVoice3-0.5B-2512 --omni --port 8091 --trust-remote-code
```
Batching preserves output lengths and streaming cache alignment, but the different GEMM shapes can produce small waveform differences compared with processing each request separately. Leave `COSYVOICE3_BATCH_FLOW` unset (or set it to `0` ) when request-independent numerical behavior is required. Set `COSYVOICE3_BATCH_FLOW_DEBUG=1` to log the observed group-size distribution and enable detailed Stage-1 profiler scopes; diagnostics are disabled by default to avoid per-step profiling overhead.
2026-05-20 03:24:06 -04:00
### CLI client
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
The client defaults to the official upstream zero-shot prompt, so it runs without extra flags:
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
```bash
cd examples/online_serving/text_to_speech/cosyvoice3
python speech_client.py --text "收到好友从远方寄来的生日礼物。"
```
Pass your own reference clip and transcript for a different voice:
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
```bash
python speech_client.py --text "Hello, this is a cloned voice." \
--ref-audio /path/to/reference.wav \
--ref-text "Transcript of the reference audio."
```
Stream PCM instead of WAV:
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
```bash
python speech_client.py --text "Hello world" --stream --output output.pcm
```
The client supports `--api-base` , `--model` , `--text` , `--ref-audio` , `--ref-text` , `--response-format` , `--stream` , `--output` .
### Notes
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
- Stage 0 (`talker` ) emits speech tokens; stage 1 (`code2wav` ) runs flow matching + HiFiGAN to synthesize waveform.
- Deploy config auto-loads from `vllm_omni/deploy/cosyvoice3.yaml` based on HF `model_type` . Pass `--deploy-config <path>` to override.
- For offline inference and the end-to-end script, see the [offline CosyVoice3 section ](https://github.com/vllm-project/vllm-omni/tree/main/examples/offline_inference/text_to_speech/README.md#cosyvoice3 ).
---
2026-05-04 16:41:29 -04:00
## Fish Speech S2 Pro
2026-09-07 18:22:36 -07:00
4B dual-AR TTS at 44.1 kHz. Server uses the DAC codec, which is vendored in
vLLM-Omni (no extra packages required).
2026-05-04 16:41:29 -04:00
### Launch
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
vllm serve fishaudio/s2-pro --omni --port 8091
# or:
./fish_speech/run_server.sh
```
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
The deploy config auto-loads from `vllm_omni/deploy/fish_qwen3_omni.yaml` (the HF `model_type` on the fishaudio checkpoint is `fish_qwen3_omni` ).
### Voice cloning
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
curl -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"input": "Hello, this is a cloned voice.",
"voice": "default",
"ref_audio": "https://example.com/reference.wav",
"ref_text": "Transcript of the reference audio."
}' --output cloned.wav
```
### CLI client
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
cd examples/online_serving/text_to_speech/fish_speech
python speech_client.py --text "Hello, how are you?"
python speech_client.py --text "Hello world" --stream --output output.pcm
```
### Gradio demo
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
./fish_speech/run_gradio_demo.sh # launches server + Gradio
python fish_speech/gradio_demo.py --api-base http://localhost:8091 # if server already running
```
### Notes
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
- Output: 44.1 kHz mono.
- Streaming PCM player command must use `-r 44100` .
---
2026-09-15 14:56:51 -07:00
## Gepard-1.0
Single-stage native AR TTS at 22.05 kHz mono. Zero-shot only: omit `voice` or pass `"default"` . Voice cloning from reference audio is not available yet.
### Prerequisites
Same NeMo NanoCodec install as the [offline Gepard section ](https://github.com/vllm-project/vllm-omni/tree/main/examples/offline_inference/text_to_speech/README.md#gepard-10 ). On a host whose CUDA toolkit cannot build kernels — no `nvcc` /`ninja` , or a consumer Blackwell (`sm_120` ) card — also `export VLLM_USE_FLASHINFER_SAMPLER=0` before launch. That env is not a deploy-YAML field.
### Launch
```bash
vllm-omni serve nineninesix/gepard-1.0 --omni --port 8091 --trust-remote-code \
--stage-init-timeout 900 \
--deploy-config vllm_omni/deploy/gepard.yaml
# or:
./gepard/run_server.sh
```
`--stage-init-timeout 900` matches the online e2e fixture and the offline example. Serve defaults to 300s, which is often too short for a cold download of the talker plus NanoCodec.
The packaged `vllm_omni/deploy/gepard.yaml` must be passed with `--deploy-config` . The checkpoint self-identifies as `qwen3_5_text` , so omitting the YAML launches a diffusion fallback instead of the Gepard pipeline. The YAML sets `async_chunk: false` , `max_num_seqs: 4` , and currently pins `seed: 42` , so serving is deterministic by default until that YAML seed is removed. Pass an explicit per-request `seed` in tests and clients rather than depending on either default.
### Sending requests
```bash
python examples/online_serving/text_to_speech/gepard/speech_client.py \
--text "Hello, this is Gepard speaking."
python examples/online_serving/text_to_speech/gepard/speech_client.py \
--text "Hello, this is Gepard speaking." --seed 7 --stream --output output.pcm
```
Gepard accepts `input` (required), `voice` (`"default"` ), `response_format` , `stream` / `stream_format` , `max_new_tokens` , and `seed` . Unsupported fields include `speed` , `language` , `instructions` , `task_type` , `ref_audio` , `ref_text` , `extra_params` , and `word_timestamps` .
```bash
curl -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"input": "Hello, this is Gepard speaking.",
"voice": "default",
"seed": 7
}' --output output.wav
```
### Notes
- Output: 22.05 kHz mono. `max_new_tokens` is a **frame** budget (1 token = 1 frame = 1024 samples ≈ 46.4 ms at 21.5 fps; adapter bounds 1..4096).
- Supported request fields: `input` (required), `voice` (`default` only), `response_format` (`wav` default; `wav/pcm/flac/mp3` non-streaming; `opus` 400 because 22.05 kHz is not an Opus sample rate; streaming `pcm/wav` only), `stream` / `stream_format` , `max_new_tokens` , `seed` .
- Unsupported: `speed` , `extra_params` (including `temperature` /`top_p` /`top_k` ), `ref_audio` , `ref_text` , `speaker_embedding` , `task_type` , `instructions` , `language` , and `word_timestamps` .
- Concurrent requests at `max_num_seqs: 4` are supported. Native-AR recompute preemption is a known limitation of this architecture (a request that is preempted mid-generation can resume incorrectly); keep concurrency at or below `max_num_seqs` and treat preemption as out of scope until the platform fix lands.
- Optional comparison against the upstream Gepard reference server needs Blackwell/Hopper + CUDA 13 + Postgres and is not part of CI.
- For the request-field table, see the [Speech API Gepard section ](https://github.com/vllm-project/vllm-omni/blob/main/docs/serving/speech_api.md#gepard-10 ).
---
2026-05-25 23:16:07 +08:00
## higgs-audio v2
2-stage TTS at 24 kHz: a vLLM-native Llama-3.2-3B talker with a DualFFN audio expert (Stage 0) feeding a HiggsAudio codec decoder (Stage 1) that streams chunks back to the client.
### Prerequisites
Voice clone uses HF's `HiggsAudioV2TokenizerModel` loaded from `k2-fsa/OmniVoice/audio_tokenizer/` (~806 MB subdir; the boson-ai standalone tokenizer Hub repo's `model.safetensors` is the 3B talker LM, not the codec):
```bash
pip install -U "transformers>=5.3.0"
```
### Launch
2026-09-03 02:16:55 +08:00
2026-05-25 23:16:07 +08:00
```bash
GPUS = 6,7 PORT = 8094 bash examples/online_serving/text_to_speech/higgs_audio_v2/run_server.sh
```
2026-09-03 02:16:55 +08:00
2026-05-25 23:16:07 +08:00
Deploy config auto-loads from `vllm_omni/deploy/higgs_audio_v2.yaml` .
### Sending requests
2026-09-03 02:16:55 +08:00
2026-05-25 23:16:07 +08:00
```bash
# Plain TTS
python higgs_audio_v2/batch_speech_client.py \
--base-url http://localhost:8094 \
--output-dir /tmp/higgs_out \
--prompts "Hello world." "The quick brown fox jumps over the lazy dog."
# Voice cloning — pass a reference clip and its transcript together
python higgs_audio_v2/batch_speech_client.py \
--base-url http://localhost:8094 \
--output-dir /tmp/higgs_clone \
--ref-audio /path/to/reference.wav \
--ref-text "Exact transcript spoken in reference.wav." \
--prompts "Hello, this is a cloned voice."
```
### Notes
2026-09-03 02:16:55 +08:00
2026-05-25 23:16:07 +08:00
- Output: 24 kHz mono.
- `--ref-text` must be the real transcript of `--ref-audio` ; mismatched text degrades cloned-voice quality.
- Out of scope (rejected with explicit 4xx): multi-speaker `[SPEAKERn]` tags inside `input` , `profile:` text-only speaker descriptions, the `ref_audio_in_system_message` system-block variant, chunked long-form generation, and per-request `voice` / `instructions` / `task_type` / `language` / `speed != 1.0` / `x_vector_only_mode` / `speaker_embedding` .
2026-09-03 02:16:55 +08:00
2026-05-24 05:42:26 +08:00
## GLM-TTS
2-stage TTS (AR + DiT flow-matching) at 24 kHz. Every request requires `ref_audio` + `ref_text` .
### Launch
2026-09-03 02:16:55 +08:00
2026-05-24 05:42:26 +08:00
```bash
vllm serve zai-org/GLM-TTS --omni --trust-remote-code --port 8091
# or:
bash examples/online_serving/text_to_speech/glm_tts/run_server.sh /path/to/GLM-TTS
```
### Sending requests
2026-09-03 02:16:55 +08:00
2026-05-24 05:42:26 +08:00
```bash
# Voice cloning (required)
python examples/online_serving/text_to_speech/glm_tts/openai_speech_client.py \
--text "你好,这是语音克隆测试。" \
--ref-audio file:///path/to/ref.wav \
--ref-text "这是参考音频的文本内容。"
# Custom format
python examples/online_serving/text_to_speech/glm_tts/openai_speech_client.py \
--text "Hello, this is a voice cloning test." \
--ref-audio file:///path/to/ref.wav \
--ref-text "Transcript of the reference audio." \
--response-format mp3 -o output.mp3
```
### Gradio demo
2026-09-03 02:16:55 +08:00
2026-05-24 05:42:26 +08:00
```bash
bash examples/online_serving/text_to_speech/glm_tts/run_gradio_demo.sh
```
### Notes
2026-09-03 02:16:55 +08:00
2026-05-24 05:42:26 +08:00
- Output: 24 kHz mono WAV via HiFT vocoder.
- `ref_audio` + `ref_text` are **required** together on every request. Reference audio should be 3-10 seconds.
- Voice cloning feature extraction (WhisperVQ, CampPlus, mel) runs on the model side — no external dependency on the serving layer.
---
2026-05-04 16:41:29 -04:00
## OmniVoice
Zero-shot multilingual TTS (600+ languages). Online serving currently exposes **auto voice** only; voice cloning and voice design are available offline.
### Prerequisites
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
huggingface-cli download k2-fsa/OmniVoice
```
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
Voice cloning (offline) needs `transformers>=5.3.0` ; auto voice works with `transformers>=4.57.0` .
### Launch
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
vllm serve k2-fsa/OmniVoice --omni --port 8091 --trust-remote-code
# or:
./omnivoice/run_server.sh
```
### CLI client
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
cd examples/online_serving/text_to_speech/omnivoice
python speech_client.py --text "Hello, how are you?"
python speech_client.py --text "Bonjour, comment allez-vous?" --language French
```
The client supports `--api-base` , `--model` , `--text` , `--response-format` , `--language` , `--output` .
### Notes
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
- Voice cloning and voice design require offline inference; see the [offline OmniVoice section ](https://github.com/vllm-project/vllm-omni/tree/main/examples/offline_inference/text_to_speech/README.md#omnivoice ).
---
## Qwen3-TTS
Three model variants exposed via separate checkpoints:
| Variant | HF repo | Use |
2026-09-03 02:16:55 +08:00
| --- | --- | --- |
2026-05-04 16:41:29 -04:00
| CustomVoice | `Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice` | Predefined speakers (`vivian` , `ryan` , …) with optional style instructions |
| VoiceDesign | `Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign` | Natural-language voice style description |
| Base | `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | Voice cloning from a reference audio |
Each variant ships smaller `0.6B` companions where available.
### Launch
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice --omni --port 8091
# or:
./qwen3_tts/run_server.sh # default: CustomVoice
./qwen3_tts/run_server.sh VoiceDesign
./qwen3_tts/run_server.sh Base
```
2026-09-16 22:29:31 -07:00
For a local deployment with multiple API frontend processes sharing one set
of TTS stage engines, add `--api-server-count` :
```bash
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
--omni --api-server-count 2 --port 8091
```
This mode supports local EngineCore stages with one local process group per
replica, including stages configured with multiple `num_replicas` . Headless,
remote, intra-replica data-parallel, and Ray stage deployments are not
supported. Runtime voice upload and deletion are disabled with multiple
API frontends because their registries are process-local. Built-in voices,
inline `ref_audio` , and voices restored from `custom_voice_dir` at startup are
supported.
2026-05-20 03:24:06 -04:00
### Executor backend
2026-09-03 02:16:55 +08:00
2026-05-20 03:24:06 -04:00
Single-GPU serves now default to the uniproc executor (lower IPC overhead, the Base cloning use case from [#2603 ](https://github.com/vllm-project/vllm-omni/issues/2603 ) / [#2604 ](https://github.com/vllm-project/vllm-omni/pull/2604 )). `vllm_omni/deploy/qwen3_tts.yaml` is the only Qwen3-TTS deploy config; pass `--deploy-config <path>` to override.
2026-05-04 16:41:29 -04:00
2026-05-20 03:24:06 -04:00
To opt out of chunked streaming, pass `--no-async-chunk` — the pipeline auto-dispatches to the end-to-end codec processor.
2026-05-04 16:41:29 -04:00
2026-05-29 07:48:58 -07:00
### Tuning stage 1 `max_num_seqs` per task type
2026-09-03 02:16:55 +08:00
2026-05-29 07:48:58 -07:00
The bundled `qwen3_tts.yaml` ships stage 1 (Code2Wav) at `max_num_seqs: 10` , tuned for Base voice cloning: stage-1 lifetimes are long (~3 s/req), so admitting up to 10 concurrent codec sequences lets requests progress in parallel in the scheduler — ~2× TTFA p95 at c=4 / c=8 (1× H100, 1.7B-Base, seed-tts) at an 8– 12 % audio-throughput cost.
CustomVoice / VoiceDesign have much shorter stage-1 lifetimes (~50– 200 ms) and are TTFA-optimal at `max_num_seqs: 1` . Override the default when serving those task types:
```bash
2026-09-03 02:16:55 +08:00
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice --omni \
2026-05-29 07:48:58 -07:00
--stage-overrides '{"1": {"max_num_seqs": 1}}'
```
2026-05-04 16:41:29 -04:00
### Sending requests
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
# CustomVoice with a predefined speaker
python qwen3_tts/openai_speech_client.py \
--model Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
--text "今天天气真好" \
2026-09-03 02:16:55 +08:00
--speaker ryan \
2026-05-04 16:41:29 -04:00
--instructions "用开心的语气说"
# VoiceDesign with a style description
python qwen3_tts/openai_speech_client.py \
--model Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign \
--task-type VoiceDesign \
--text "哥哥,你回来啦" \
--instructions "体现撒娇稚嫩的萝莉女声,音调偏高"
# Base voice cloning
python qwen3_tts/openai_speech_client.py \
--model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
--task-type Base \
--text "Hello, this is a cloned voice" \
--ref-audio /path/to/reference.wav \
--ref-text "Original transcript of the reference audio"
```
### Voices endpoint
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
List available voices, or upload a custom one for Base cloning:
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
# List
curl http://localhost:8091/v1/audio/voices
# Upload
curl -X POST http://localhost:8091/v1/audio/voices \
-F "audio_sample=@/path/to/voice_sample.wav" \
-F "consent=user_consent_id" \
-F "name=custom_voice_1" \
-F "ref_text=The exact transcript of the audio sample." \
-F "speaker_description=warm narrator"
```
2026-09-03 02:16:55 +08:00
For Qwen3-TTS, uploaded voices are Base voice-cloning inputs and require a Base
checkpoint. When a request names an uploaded voice, the server infers
`task_type="Base"` . Built-in presets such as `vivian` and `ryan` remain
CustomVoice speakers and require a CustomVoice checkpoint.
2026-05-04 16:41:29 -04:00
2026-09-16 22:29:31 -07:00
The runtime upload and delete routes require a single API frontend; with
`--api-server-count > 1` , use inline `ref_audio` or restore precomputed voices
from `custom_voice_dir` at startup.
2026-06-01 01:51:24 +08:00
### Precomputed custom voices
2026-09-03 02:16:55 +08:00
For reused Base voice-cloning speakers, precompute the reference artifacts once
and load them at server startup. Precomputed voices use the same Base task and
checkpoint-matching rules as uploaded voices:
2026-06-01 01:51:24 +08:00
```bash
python qwen3_tts/precompute_custom_voice.py \
--model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
--voice-name alice \
--ref-audio /path/to/reference.wav \
--ref-text "Original transcript of the reference audio" \
--mode icl \
--output-dir /path/to/custom_voices
```
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
`--mode icl` stores both `speaker_embedding` and `ref_code` ; `--mode xvec` stores only the speaker embedding. Add the output directory to a deploy config:
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
```yaml
custom_voice_dir : /path/to/custom_voices
```
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
Then start the server with that config and call the Speech API with only the voice name:
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
```bash
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-Base --omni --deploy-config /path/to/qwen3_tts_custom_voice.yaml
curl -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input":"Hello from a precomputed voice.","voice":"alice","task_type":"Base"}' \
--output alice.wav
```
2026-05-04 16:41:29 -04:00
### Streaming PCM
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
curl -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"input": "Hello, how are you?",
"voice": "vivian",
"language": "English",
"stream": true,
2026-06-30 10:17:24 +08:00
"stream_format": "audio",
2026-05-04 16:41:29 -04:00
"response_format": "pcm"
}' --no-buffer | play -t raw -r 24000 -e signed -b 16 -c 1 -
```
2026-09-03 02:16:55 +08:00
2026-08-20 07:09:12 +08:00
Raw PCM streaming requires `stream_format="audio"` , `response_format="pcm"` , and `async_chunk: true` in the deploy config (default in `qwen3_tts.yaml` ). `speed` is not supported when streaming.
2026-05-04 16:41:29 -04:00
### Streaming WebSocket
2026-09-03 02:16:55 +08:00
2026-07-28 16:46:19 -07:00
The `/v1/audio/speech/stream` endpoint accepts text incrementally and synthesizes the buffered text as one continuous request on `input.done` :
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
python qwen3_tts/streaming_speech_client.py --text "Hello world. How are you? I am fine."
python qwen3_tts/streaming_speech_client.py --text "..." --simulate-stt --stt-delay 0.1
```
2026-07-28 16:46:19 -07:00
`input.done` flushes without closing, so repeating `--text` synthesizes several utterances over one connection:
2026-09-03 02:16:55 +08:00
2026-07-28 16:46:19 -07:00
```bash
python qwen3_tts/streaming_speech_client.py \
--text "First utterance." \
--text "Second utterance, same connection."
```
2026-05-04 16:41:29 -04:00
### Gradio demos
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
./qwen3_tts/run_gradio_demo.sh # CustomVoice (default)
./qwen3_tts/run_gradio_demo.sh --task-type VoiceDesign
./qwen3_tts/run_gradio_demo.sh --task-type Base
# FastRTC variant (gapless WebRTC streaming):
pip install fastrtc
python qwen3_tts/gradio_fastrtc_demo.py --api-base http://localhost:8000
```
### Speaker embedding interpolation
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
`qwen3_tts/speaker_embedding_interpolation.py` blends two predefined speakers' embeddings to produce intermediate voices. See the script for usage.
### Batch client
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
`qwen3_tts/batch_speech_client.py` issues many concurrent requests for throughput measurement.
### Notes
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
- Base voice cloning has uniproc-vs-mp tradeoffs depending on per-request reference audio cost; see the executor-backend section above.
- With async chunking, Qwen3-TTS Base voice cloning sends the full reference context in the first Code2Wav packet, then caches that prefix on the Code2Wav stage for follow-up chunks in the same request.
2026-05-04 16:41:29 -04:00
- `vllm_omni/deploy/qwen3_tts.yaml` is the default deploy config (loaded by HF `model_type` ); per-stage runtime overrides are available via `--stage-N-<field> <value>` .
2026-06-01 06:59:10 -07:00
- Under vocoder-bound overload (single-stream `rtf_p99 ≥ 1` at the target concurrency), set `active_stream_window: 2` at the top of the deploy yaml to cap simultaneously active Stage 1 streams. Off by default; trades TTFP for streaming continuity. See [#3592 ](https://github.com/vllm-project/vllm-omni/pull/3592 ) for the mechanism and tradeoff numbers.
2026-05-04 16:41:29 -04:00
---
---
## VoxCPM2
Single-stage native AR TTS at 48 kHz.
### Launch
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
vllm serve openbmb/VoxCPM2 --omni --host 0.0.0.0 --port 8000
```
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
Deploy config auto-loads from `vllm_omni/deploy/voxcpm2.yaml` . Pass `--deploy-config <path>` to override or `--stage-N-<field> <value>` for per-stage runtime tweaks.
### Sending requests
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
# Zero-shot synthesis
python voxcpm2/openai_speech_client.py --text "Hello, this is VoxCPM2."
# Voice cloning
python voxcpm2/openai_speech_client.py \
--text "This should sound like the reference speaker." \
--ref-audio /path/to/reference.wav
```
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
The `ref_audio` field accepts local file paths (auto-base64), HTTP URLs, or `data:audio/wav;base64,...` data URIs.
2026-06-01 01:51:24 +08:00
### Precomputed custom voices
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
For repeated VoxCPM2 speakers, precompute the prompt cache and load it through `custom_voice_dir` :
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
```bash
python voxcpm2/precompute_custom_voice.py \
--model openbmb/VoxCPM2 \
--voice-name alice \
--ref-audio /path/to/reference.wav \
--mode ref_continuation \
--prompt-text "Original transcript of the reference audio" \
--output-dir /path/to/custom_voices
```
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
Add the output directory to the deploy config:
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
```yaml
custom_voice_dir : /path/to/custom_voices
```
2026-09-03 02:16:55 +08:00
2026-06-01 01:51:24 +08:00
After startup, `/v1/audio/voices` lists `alice` , and `/v1/audio/speech` can use `voice="alice"` without sending `ref_audio` .
2026-05-04 16:41:29 -04:00
### Gradio demo (gapless streaming via AudioWorklet)
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
python voxcpm2/gradio_demo.py
```
2026-09-03 02:16:55 +08:00
2026-06-30 10:17:24 +08:00
Uses an AudioWorklet-based player adapted from the Qwen3-TTS demo for gap-free playback. Raw PCM audio is streamed from the OpenAI Speech endpoint with `stream=true` and `stream_format="audio"` .
2026-05-04 16:41:29 -04:00
---
## Voxtral TTS
Voxtral-4B-TTS (Mistral). Uses the `mistral_common` `SpeechRequest` protocol; voice presets are model-specific.
### Prerequisites
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
Latest `mistral_common` with `SpeechRequest` support:
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
pip install -e /path/to/mistral-common # or upgrade from PyPI when available
```
### Launch
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
vllm serve mistralai/Voxtral-4B-TTS-2603 --omni --port 8091
```
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
Deploy config auto-loads from `vllm_omni/deploy/voxtral_tts.yaml` .
### Gradio demo
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
```bash
python voxtral_tts/gradio_demo.py
```
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
The demo handles voice-preset selection and reference-audio upload. `voxtral_tts/text_preprocess.py` provides the text-normalization helpers used by the demo (also available for other clients).
### Notes
2026-09-03 02:16:55 +08:00
2026-05-04 16:41:29 -04:00
- Voice presets are listed on the HF model card (`mistralai/Voxtral-4B-TTS-2603` ).
- Voice cloning is gated upstream and may require a recent `mistral_common` .
- A standalone CLI client is not yet shipped; the gradio demo is the canonical reference for now.
## Example materials
2026-05-20 03:24:06 -04:00
??? abstract "cosyvoice3/run_server.sh"
--8<-- "examples/online_serving/text_to_speech/cosyvoice3/run_server.sh"
??? abstract "cosyvoice3/speech_client.py"
--8<-- "examples/online_serving/text_to_speech/cosyvoice3/speech_client.py"
2026-05-04 16:41:29 -04:00
??? abstract "fish_speech/gradio_demo.py"
``````py
--8<-- "examples/online_serving/text_to_speech/fish_speech/gradio_demo.py"
` `````
??? abstract "fish_speech/run_gradio_demo.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/fish_speech/run_gradio_demo.sh"
` `````
??? abstract "fish_speech/run_server.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/fish_speech/run_server.sh"
` `````
??? abstract "fish_speech/speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/fish_speech/speech_client.py"
` `````
2026-05-25 23:16:07 +08:00
??? abstract "higgs_audio_v2/batch_speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/higgs_audio_v2/batch_speech_client.py"
` `````
??? abstract "higgs_audio_v2/run_server.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/higgs_audio_v2/run_server.sh"
2026-05-24 05:42:26 +08:00
??? abstract "glm_tts/gradio_demo.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/glm_tts/gradio_demo.py"
` `````
??? abstract "glm_tts/openai_speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/glm_tts/openai_speech_client.py"
` `````
??? abstract "glm_tts/run_gradio_demo.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/glm_tts/run_gradio_demo.sh"
` `````
??? abstract "glm_tts/run_server.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/glm_tts/run_server.sh"
` `````
2026-05-04 16:41:29 -04:00
??? abstract "omnivoice/run_server.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/omnivoice/run_server.sh"
` `````
??? abstract "omnivoice/speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/omnivoice/speech_client.py"
` `````
??? abstract "qwen3_tts/batch_speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/batch_speech_client.py"
` `````
??? abstract "qwen3_tts/gradio_demo.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/gradio_demo.py"
` `````
??? abstract "qwen3_tts/openai_speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/openai_speech_client.py"
` `````
??? abstract "qwen3_tts/run_gradio_demo.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/run_gradio_demo.sh"
` `````
??? abstract "qwen3_tts/run_server.sh"
` `````sh
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/run_server.sh"
` `````
??? abstract "qwen3_tts/speaker_embedding_interpolation.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/speaker_embedding_interpolation.py"
` `````
??? abstract "qwen3_tts/streaming_speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/streaming_speech_client.py"
` `````
??? abstract "qwen3_tts/tts_common.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/qwen3_tts/tts_common.py"
` `````
??? abstract "voxcpm2/gradio_demo.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/voxcpm2/gradio_demo.py"
` `````
??? abstract "voxcpm2/openai_speech_client.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/voxcpm2/openai_speech_client.py"
` `````
??? abstract "voxtral_tts/gradio_demo.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/voxtral_tts/gradio_demo.py"
` `````
??? abstract "voxtral_tts/text_preprocess.py"
` `````py
--8<-- "examples/online_serving/text_to_speech/voxtral_tts/text_preprocess.py"
` `````