dots.tts#
dots.tts is a text-to-speech model from rednote-hilab. It outputs 48 kHz speech and clones a speaker from a short reference clip plus its transcript.
dots.tts is a continuous-latent model, not a codec model. The backbone emits no audio tokens. Each AR step gives a hidden state; a MeanFlow DiT uses it to sample one latent patch (4 frames × 128 dims); a semantic encoder turns the patch back into the next backbone input; an AudioVAE decodes latents to waveform. No codebook, no token sampler. So temperature and top_k do nothing here — use the solver knobs (num_steps, guidance_scale) instead.
Component |
Spec |
|---|---|
Backbone |
Qwen2 1.5B decoder (28 L, hidden=1536, GQA 12/2) |
Acoustic tail |
MeanFlow DiT (18 L, hidden=1024, 16 heads) + VAE semantic encoder |
Latent patch |
4 frames × 128 dims (one patch ≈ 160 ms of audio) |
Context length |
2,048 tokens |
Sample rate |
48 kHz |
Solver |
MeanFlow + Euler, engine-wide |
Supported checkpoints#
Checkpoint |
Status |
|---|---|
MeanFlow. Continuous batching, |
|
Flow matching. Single request at a time ( |
|
Flow matching, same as SOAR. Serve it with |
Prerequisites#
Install sglang-omni by following Installation, then download and launch the server:
hf download dots-studio/dots.tts-mf
sgl-omni serve \
--model-path dots-studio/dots.tts-mf \
--config examples/configs/dots_tts.yaml \
--allowed-local-media-path docs/_static/audio \
--port 8000
To serve SOAR instead, swap both the checkpoint and the config:
hf download dots-studio/dots.tts-soar
sgl-omni serve \
--model-path dots-studio/dots.tts-soar \
--config examples/configs/dots_tts_soar.yaml \
--allowed-local-media-path docs/_static/audio \
--port 8000
SOAR is a flow-matching checkpoint. It runs the single-request solver with classifier-free guidance, so its config pins max_running_requests: 1 and num_steps: 10; continuous batching is MeanFlow-only. Every request example below works on either checkpoint — only the model field changes.
examples/configs/dots_tts.yaml is the canonical MeanFlow deployment. It is already tuned; compiled acoustic tail and vocoder (optimize: true, on by default); continuous batching at max_running_requests=16; and the backbone decode CUDA graph. --model-path alone keeps the compiled tail and batching but leaves backbone decode eager, which is slower per request (see Performance). Use the config file.
The examples below read local clips from docs/_static/audio. To fetch reference audio over HTTP instead, allow the domains you need, e.g. --allowed-media-domain huggingface.co.
Synthesizing Speech#
dots.tts needs a reference clip and its transcript. The speaker comes entirely from the reference (x-vector plus prompt latents), so there is no zero-shot voice preset. Under the default continuous-batching deployment, a request without references is rejected.
Voice Cloning#
The reference transcript matters. It is prefixed to your input text so the model can align prompt audio with prompt text. A wrong transcript hurts cloning quality.
Use curl:
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "dots-studio/dots.tts-mf",
"input": "Have a nice day and enjoy south california sunshine.",
"references": [{
"audio_path": "docs/_static/audio/male-voice.wav",
"text": "Hey, Adam here. Let'\''s create something that feels real, sounds human, and connects every time."
}],
"seed": 42
}' \
--output output.wav
Use Python
import requests
resp = requests.post(
"http://localhost:8000/v1/audio/speech",
json={
"model": "dots-studio/dots.tts-mf",
"input": "Have a nice day and enjoy south california sunshine.",
"references": [{
"audio_path": "docs/_static/audio/male-voice.wav",
"text": "Hey, Adam here. Let's create something that feels real, sounds human, and connects every time.",
}],
"seed": 42,
},
)
resp.raise_for_status()
with open("output.wav", "wb") as f:
f.write(resp.content)
ref_audio / ref_text are accepted as a shorthand for references[0].audio_path / references[0].text.
Streaming#
Streaming lets you play audio while generation is still running, which cuts time-to-first-audio. dots.tts streams raw 48 kHz PCM: the AudioVAE decoder emits a waveform chunk every few latent patches instead of waiting for the whole utterance.
Set "stream": true and "response_format": "pcm":
curl -N -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "dots-studio/dots.tts-mf",
"input": "Get the trust fund to the bank early.",
"references": [{
"audio_path": "docs/_static/audio/female-voice.wav",
"text": "By repeating what students say, teachers can demonstrate that they are listening. By extending what students say."
}],
"stream": true,
"response_format": "pcm",
"seed": 42
}' \
--output output.pcm
The -N flag disables curl’s output buffering so chunks are written as they arrive. The response is 16-bit mono PCM at 48 kHz with no in-band JSON framing; convert it with:
ffmpeg -f s16le -ar 48000 -ac 1 -i output.pcm output.wav
Request parameters#
Top-level fields:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
string |
served model |
Served dots.tts model identifier |
|
string |
(required) |
Text to synthesize |
|
list |
(required) |
Reference audio for cloning. Each item has |
|
string |
|
Shorthand for |
|
string |
|
Output audio format ( |
|
bool |
|
Enable raw PCM streaming |
|
int |
|
Seed for the flow sampler. Fixes the output for a given request |
|
string |
|
Language tag for the prompt text; |
|
string |
|
Style instructions; switches the prompt template to |
Solver knobs go under stage_params.latent_engine. They are not top-level fields:
CreateSpeechRequest drops unknown top-level keys, so sending num_steps next to
input changes nothing and reports no error.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
float |
|
Scales the speaker x-vector before conditioning. Higher values push harder toward the reference timbre |
|
float |
|
Classifier-free guidance strength for the flow sampler |
|
float |
|
Probability above which the EOS head ends generation. Lower values cut utterances earlier |
|
int |
|
Flow solver steps. MeanFlow fixes this engine-wide: another value fails the request. SOAR and base run one request at a time and honour it |
|
string |
|
Flow solver. MeanFlow accepts only |
|
int |
|
Cap on generated latent patches (≈ 160 ms each), bounded by the engine’s |
|
bool |
|
Run the upstream text normalizer (numbers, symbols) before tokenizing |
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "dots-studio/dots.tts-mf",
"input": "Have a nice day and enjoy south california sunshine.",
"references": [{
"audio_path": "docs/_static/audio/male-voice.wav",
"text": "Hey, Adam here. Let'\''s create something that feels real, sounds human, and connects every time."
}],
"seed": 42,
"stage_params": {"latent_engine": {"speaker_scale": 2.0, "eos_threshold": 0.6}}
}' \
--output output.wav
A rejected solver value comes back as HTTP 500 with the engine’s message, for example dots.tts num_steps is fixed for continuous batching. It is a validation failure, not a server fault.
temperature, top_p, and top_k do not apply. The backbone token logits are unused; the acoustic tail is a deterministic flow solve once the seed is fixed.
Performance#
We report throughput on Seed-TTS EN. Client --max-concurrency sweep against a single dots.tts server started from examples/configs/dots_tts.yaml (max_running_requests=16, bf16, num_steps=4, backbone decode CUDA graph and graph-captured acoustic tail on). Each row is the mean of two runs, seed 42. Hardware: 1x H100.
Concurrency |
Throughput (req/s) |
Mean latency |
RTF (per-req) |
audio_s/s |
WER |
|---|---|---|---|---|---|
1 |
0.90 |
1.11 s |
0.284 |
3.58 |
1.06% |
2 |
1.48 |
1.35 s |
0.329 |
6.16 |
1.25% |
4 |
2.43 |
1.64 s |
0.399 |
10.14 |
1.36% |
8 |
3.99 |
2.00 s |
0.486 |
16.65 |
1.31% |
16 |
4.64 |
3.43 s |
0.830 |
19.36 |
1.31% |
32 |
4.43 |
7.15 s |
1.797 |
18.47 |
1.27% |
Zero failed requests in every run, and no sample above 50% WER. c=1 is a 50-sample latency probe; the other rows use the full 1,088-sample set. WER is measured on the first run of each row.
Concurrency — Maximum number of in-flight client requests (
--max-concurrency).Throughput (req/s) — Completed requests divided by total benchmark wall-clock time.
Mean latency — Average end-to-end time per request (send to full response received).
RTF (per-req) — Average ratio of processing time to generated audio duration per request.
<1is faster than real time.audio_s/s — Total seconds of audio produced divided by total benchmark wall-clock time.
WER — Corpus word error rate of the generated speech, transcribed with
Qwen/Qwen3-ASR-1.7B.
To reproduce, start the server as in Prerequisites and run the benchmark against it:
python -m benchmarks.eval.benchmark_tts_seedtts \
--meta zhaochenyang20/seed-tts-eval-arrow \
--model dots-studio/dots.tts-mf \
--ref-format references \
--base-url http://127.0.0.1:8000 --port 8000 \
--lang en --max-concurrency 16 --warmup 8 --seed 42 \
--generate-only --use-existing-server \
--output-dir results/dots-seedtts-en-c16
python -m benchmarks.eval.benchmark_tts_seedtts \
--meta zhaochenyang20/seed-tts-eval-arrow \
--model dots-studio/dots.tts-mf \
--ref-format references --lang en --seed 42 \
--transcribe-only --port 8000 \
--output-dir results/dots-seedtts-en-c16