voice_audio_multi/docs/models/qwen3_tts.md
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

224 lines
15 KiB
Markdown

# Qwen3 TTS
Qwen3 TTS includes Base voice cloning, VoiceDesign, and CustomVoice variants.
For shared conversion guidance, see [Qwen3 GGUF checkpoints](qwen3.md#gguf-checkpoints).
## Qwen3 TTS Base
Qwen3 TTS Base is the voice-clone TTS path. It requires reference audio and,
by default, its transcript. To use reference audio without a transcript, pass
`--request-option x_vector_only_mode=true`.
| Field | Value |
|---|---|
| Family | `qwen3_tts` |
| Model directory | `models/Qwen3-TTS-12Hz-1.7B-Base` |
| Tasks | `tts`, `clon` |
| Modes | `offline`, `streaming` |
| Voice input | Reference WAV through `--voice-ref` |
| Transcript | `--reference-text` required unless `x_vector_only_mode=true` |
```bash
audiocpp_cli --task tts --family qwen3_tts --model models/Qwen3-TTS-12Hz-1.7B-Base --backend cuda --text "Hello from Qwen3 TTS." --voice-ref assets/resources/b.wav --reference-text "Some call me nature. Others call me Mother Nature. I've been here for over 4.5 billion years. 22,500 times longer than you." --out out.wav
```
`tts` and `clon` enter the same Base synthesis implementation.
### Common Options (use directly)
| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--text` | text | required | Text to synthesize. |
| `--voice-ref` | WAV path | required | Voice clone reference audio. |
| `--reference-text` | text | required by default | Transcript for reference audio; optional with `x_vector_only_mode=true`. |
| `--language` | language code | empty string | Text language hint. |
| `--text-chunk-size` | integer chars | `8192` | Long-form text chunk size. Each chunk is voiced separately; with the session option `qwen3_tts.stitch_decode=true` the talker still runs per chunk but the codes of all chunks are voiced in one decoder pass. |
| `--max-tokens` | positive integer | checkpoint generation config (`2048` if absent) | Maximum generated speech tokens per chunk. |
## Qwen3 TTS CustomVoice
Qwen3 CustomVoice uses speaker ids packaged with the model. Use `--request-option voice_id=Vivian` to select one; legacy `--speaker Vivian` remains accepted. Examples use `Vivian` and `Ryan`.
| Field | Value |
|---|---|
| Family | `qwen3_tts` |
| Model directory | `models/Qwen3-TTS-12Hz-1.7B-CustomVoice` |
| Task | `tts` |
| Modes | `offline`, `streaming` |
| Voice input | Built-in speaker id through `--speaker` |
| Style control | Optional instruction through `--instruct` |
| External voice WAV | Not used by this path |
```bash
audiocpp_cli --task tts --family qwen3_tts --model models/Qwen3-TTS-12Hz-1.7B-CustomVoice --backend cuda --text "Hello from a custom voice." --speaker Vivian --instruct "Very happy." --out out.wav
```
### Common Options (use directly)
| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--text` | text | required | Text to synthesize. |
| `--speaker` | packaged speaker name | required | Built-in speaker id, such as `Vivian` or `Ryan`. |
| `--instruct` | text | empty string | Style or emotion instruction. |
| `--language` | language code | empty string | Text language hint. |
| `--text-chunk-size` | integer chars | `8192` | Long-form text chunk size. Each chunk is voiced separately; with the session option `qwen3_tts.stitch_decode=true` the talker still runs per chunk but the codes of all chunks are voiced in one decoder pass. |
| `--max-tokens` | positive integer | checkpoint generation config (`2048` if absent) | Maximum generated speech tokens per chunk. |
## Qwen3 TTS VoiceDesign
Qwen3 VoiceDesign creates a voice from an instruction. It does not require a speaker reference WAV.
| Field | Value |
|---|---|
| Family | `qwen3_tts` |
| Model directory | `models/Qwen3-TTS-12Hz-1.7B-VoiceDesign` |
| Tasks | `tts`, `vdes` |
| Modes | `offline`, `streaming` |
| Voice input | Instruction text through `--instruct` |
| Reference audio | Not required |
```bash
audiocpp_cli --task vdes --family qwen3_tts --model models/Qwen3-TTS-12Hz-1.7B-VoiceDesign --backend cuda --text "Hello from a designed voice." --instruct "A warm adult narrator" --out out.wav
```
`tts` and `vdes` enter the same VoiceDesign synthesis implementation.
### Common Options (use directly)
| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--text` | text | required | Text to synthesize. |
| `--instruct` | text | empty string | Optional voice design instruction. |
| `--language` | language code | empty string | Text language hint. |
| `--text-chunk-size` | integer chars | `8192` | Long-form text chunk size. Each chunk is voiced separately; with the session option `qwen3_tts.stitch_decode=true` the talker still runs per chunk but the codes of all chunks are voiced in one decoder pass. |
| `--max-tokens` | positive integer | checkpoint generation config (`2048` if absent) | Maximum generated speech tokens per chunk. |
## Qwen3 TTS Streaming
All three TTS variants accept `--mode streaming`. Audio is decoded and emitted
while speech tokens are still being generated, rather than after the complete
utterance. The first audio chunk contains one codec frame (80 milliseconds at
24 kHz). Subsequent chunks double up to the configured `stream_frames_per_event`
maximum: with the default 12, the sequence is 1, 2, 4, 8, 12, 12, and so on.
These are audio durations, not latency guarantees.
Set `stream_chunk_policy=fixed` to use `stream_frames_per_event` from the first
event instead; the last event may contain fewer frames. A fixed size of 1 emits
one frame at a time. The default policy is `grow`.
Streaming retains 25 codec frames of left context and reuses a fixed-capacity
decoder graph, with a separate cached graph for the first frame. The final
partial chunk is flushed at end of generation. The
final audio is exactly the concatenation of the emitted chunks. Streaming uses
smaller codec windows than offline decoding, so waveform differences are expected;
the offline decoding path is unchanged.
For the server, configure the model with `"mode": "streaming"` and request
`"stream_format": "sse"` from `/v1/audio/speech` (streaming defaults to PCM).
Audio arrives through the existing `speech.audio.delta`
events. Base models require reference audio; the transcript can be omitted only
with `x_vector_only_mode=true`.
VoiceDesign accepts an empty instruction; CustomVoice requires a packaged speaker.
## Qwen3 TTS Sampling
These sampling controls are shared by the Qwen3 TTS Base, VoiceDesign, and CustomVoice paths.
### Common Options (use directly)
| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--do-sample` | `true`, `false` | `true` | Enable sampling. |
| `--temperature` | float | `0.9` | Main talker temperature; must be > 0 when `--do-sample=true`, ignored when false. |
| `--top-k` | integer | `50` | Main talker top-k. |
| `--top-p` | float | `1.0` | Main talker top-p. |
| `--repetition-penalty` | float > 0 | `1.05` | Main talker repetition penalty. |
| `--seed` | integer | random if omitted | Sampling seed. |
### Request Options (use with `--request-option`)
| Option | Values | Default | Meaning |
|---|---|---:|---|
| `subtalker_do_sample` | bool | `true` | Enable subtalker sampling for the remaining audio codebooks. |
| `voice_id` | packaged speaker name | unset | CustomVoice preset selection. Legacy `speaker` remains accepted. An explicit request option takes precedence over the structured voice's cached id. |
| `subtalker_temperature` | float | `0.9` | Must be > 0 when `subtalker_do_sample=true`; ignored when false. |
| `subtalker_top_k` | integer | `50` | Subtalker top-k. |
| `subtalker_top_p` | float | `1.0` | Subtalker top-p. |
| `x_vector_only_mode` | bool | `false` | Base voice cloning using only the reference speaker embedding, without ICL reference codec prompting. |
| `stream_frames_per_event` | positive integer | `12` | Codec frames per fixed event, or the maximum for growing events. Smaller values reduce chunk latency but increase decoding overhead. Ignored offline. |
| `stream_chunk_policy` | `grow` or `fixed` | `grow` | Grow from one frame to the configured maximum, or emit fixed-size chunks. Ignored offline. |
### Session Options (use with `--session-option`)
These options apply to Base, VoiceDesign, and CustomVoice sessions. Arena and
constant-context sizes are in MiB.
`cuda_perf` is a preset. Explicit session options override its settings:
```bash
--session-option qwen3_tts.perf_mode=cuda_perf \
--session-option qwen3_tts.attention=eager \
--session-option qwen3_tts.packed_projections=false
```
In server configs, put the same keys and values in the model's `session_options`.
| Option | Values | Default | Meaning |
|---|---|---:|---|
| `qwen3_tts.mem_saver` | bool | `false` | Release the talker cached-step graph after each request. Voice-prompt, prefill, code-predictor, and speech-decoder caches remain reusable. |
| `qwen3_tts.lora` | adapter directory or checkpoint file | not set | Load a PEFT Talker/Subtalker adapter at session creation. Keep `adapter_config.json` beside its weights. A trained custom voice may also provide `speaker_embedding` and `tts_config.json` sidecars; select its name with `voice_id`. |
| `qwen3_tts.lora_scale` | finite float | `1` | Strength multiplier applied after the adapter's alpha/r scaling. Speaker embedding replacements are not scaled. |
| `qwen3_tts.perf_mode` | `off`, `flash_attention`, `cuda_perf` | `off` | Preset. `off` keeps normal defaults; `flash_attention` selects flash attention. `cuda_perf` also selects packing, optimized cache writes, F16 KV and convolution weights, stateful streaming decoding, and KV alignment 256. Explicit options override the preset. |
| `qwen3_tts.attention` | `mixed`, `auto`, `eager`, `flash` | `mixed` | `mixed` uses manual prefill and contiguous-K/V flash cached decoding for the talker and code predictor, with eager speech tokenization. `auto` uses the shared backend policy; `eager` disables flash; `flash` uses flash with K/V views. Explicit values override the preset; `cuda_perf` selects `flash`. |
| `qwen3_tts.packed_projections` | bool | `false`; `true` with `cuda_perf` | Pack QKV and gate/up projections in the talker and code predictor without changing weight dtype. |
| `qwen3_tts.kv_cache_write` | `standard`, `optimized` | `standard`; `optimized` with `cuda_perf` | Optimized cache views and I64 indices allow backend fusion where supported; no CUDA-version requirement. |
| `qwen3_tts.kv_cache_type` | `f32`, `f16` | `f32`; `f16` with `cuda_perf` | Talker and code-predictor KV storage. F16 reduces cache memory and may change numerical results. Explicit values override the preset. |
| `qwen3_tts.talker_kv_cache_alignment` | int ≥ 1 | `1`; `256` with `cuda_perf` | Round talker KV capacity to this many rows. Alignment 1 disables padding. Explicit values override the preset. Larger alignments may use more memory and change numerical results. |
| `qwen3_tts.stream_decoder` | `windowed`, `stateful` | `windowed`; `stateful` with `cuda_perf` | GGML streaming decoder. `stateful` carries convolution and attention history and caches graphs for exact chunk lengths. Uses more graph memory and can change audio. |
| `qwen3_tts.stitch_decode` | bool | `false` | Voice the concatenated speech codes of all text chunks in one decoder pass instead of voicing each chunk separately. Per-chunk voicing voices the voice-clone reference once per chunk and zeroes the stateful vocoder cache at every chunk boundary (measured on RK3588, 300 characters, 22.5 s reference: 7 vocoder runs for 159.9 s against 1 run for 57.5 s); the stitched pass costs device memory proportional to the utterance, since one decode call builds a vocoder graph sized by its frame count. |
| `qwen3_tts.voice_prompt_cache_slots` | nonnegative integer | `1` | Cached voice-clone prompts; `0` disables prompt caching. |
| `qwen3_tts.weight_type` | `native`, `f32`, `f16`, `bf16`, `q8_0` | `native` | Talker weight storage fallback. |
| `qwen3_tts.talker_weight_type` | `native`, `f32`, `f16`, `bf16`, `q8_0` | inherits `weight_type` | Explicit talker weight storage override. |
| `qwen3_tts.speech_encoder_weight_type` | `native`, `f32`, `f16`, `bf16`, `q8_0` | `native` | Reference speech encoder matmul weight storage. |
| `qwen3_tts.speech_decoder_weight_type` | `native`, `f32`, `f16`, `bf16`, `q8_0` | `native` | Speech decoder matmul weight storage. |
| `qwen3_tts.conv_weight_type` | `native`, `f32`, `f16` | `f32`; `f16` with `cuda_perf` | Speech tokenizer and speaker encoder convolution weight storage. |
| `qwen3_tts.talker_graph_arena_mb` | positive integer | `32` | Talker graph arena. |
| `qwen3_tts.speech_encoder_graph_arena_mb` | positive integer | `32` | Reference speech encoder graph arena. |
| `qwen3_tts.speech_decoder_graph_arena_mb` | positive integer | `32` | Speech decoder graph arena. |
| `qwen3_tts.speaker_encoder_graph_arena_mb` | positive integer | `32` | Speaker encoder graph arena. |
| `qwen3_tts.talker_constant_context_mb` | positive integer | `4` | Talker constant tensor context. |
| `qwen3_tts.code_predictor_constant_context_mb` | positive integer | `4` | Code predictor constant tensor context. |
| `qwen3_tts.speech_decoder_constant_context_mb` | positive integer | `4` | Speech decoder constant tensor context. |
| `qwen3_tts.exec_plan` | JSON object of component plans, encoded as a string | `{}` | `{"speech_decoder":{"impl":"rknn","artifacts":{"main":"/models/decoder.rknn","first":"/models/decoder-first.rknn","window":"/models/decoder-window.rknn"}}}` selects the NPU decoder and skips GGML decoder weights. `main` is required; `first` optionally uses a smaller engine for the first streaming group; `window` optionally streams with window semantics. No decoder-specific params. See [component execution plans](../build/rockchip-npu.md#component-execution-plans). |
## Rockchip NPU decoder
The speech decoder stage can run on a Rockchip NPU through `librknnrt` instead
of ggml. The path is opt-in at build time:
```bash
cmake -S . -B build -DENGINE_ENABLE_RKNN=ON \
-DENGINE_RKNN_INCLUDE_DIR=/usr/local/include \
-DENGINE_RKNN_LIBRARY=/usr/lib/librknnrt.so
```
The `.rknn` must be the stateful fp16 vocoder export for the same checkpoint
and target SoC: inputs `audio_codes`, `freqs_in`, `cache_valid`, `cache_in`;
outputs `audio_values` and `cache_out`; 1920 samples per token at 24 kHz, with
the convolution and KV cache carried across steps in the graph I/O. The
optional first-group model runs the first token group at a smaller compiled
batch; the cache layout is batch-independent, so its cache feeds the main
model directly. The optional window model (`artifacts.window`) streams with the
ggml path's window semantics instead: every event runs a full decode of its
whole `[left context + new]` window from a zeroed cache and drops the context
frames' audio, so each event reproduces the CPU streaming function exactly
(25 frames re-voiced per event, at one NPU step of `25 + frames_per_event`
tokens). Naming the same file as `main` aliases it: the resident engine covers
the default 25 + 12 window; a dedicated export compiled at `--codes-length
25 + stream_frames_per_event` is cheaper per event. Offline decoding keeps
using `main`. A `.rknn` is compiled per SoC and is not portable between
chips. Build the file with
[`tools/rknn/prepare_qwen3_tts_rknn_vocoder.py`](../../tools/rknn/prepare_qwen3_tts_rknn_vocoder.py)
and see [the Rockchip NPU build guide](../build/rockchip-npu.md) for the toolkit
venv, the measured numbers, and the limits of this path. This path is opt-in
and validated only on RK3588.