ChatTTS is a generative text-to-speech (TTS) model built for conversational, dialogue-style speech — the kind of natural, expressive narration you want from an LLM voice assistant. It speaks English and Chinese (including mixed-language input), supports multiple speakers, and can place fine-grained prosodic events like laughter, pauses, and intonation breaks directly from markers in your text.
This guide shows you how to install ChatTTS, generate your first audio, control how the voice sounds, and use every interface the project ships: the Python API, the command line, the web UI, and two HTTP API servers.
Important
The released model is published for academic and research purposes only. The code is licensed under
AGPLv3+; the pretrained model is licensed underCC BY-NC 4.0(non-commercial). To discourage misuse, the public 40,000-hour model was trained with added high-frequency noise and compressed audio quality. Use ChatTTS responsibly.
1. Quick Start
The fastest path from zero to audio is three lines of Python. The first call to load() downloads the pretrained model automatically.
import ChatTTS
import torch
import torchaudio
chat = ChatTTS.Chat()
chat.load(compile=False) # set compile=True for faster inference (slower startup)
texts = ["Hello, this is my first ChatTTS sentence."]
wavs = chat.infer(texts)
torchaudio.save("output.wav", torch.from_numpy(wavs[0]).unsqueeze(0), 24000)
[+0800 20260411 12:00:00] [INFO] ChatTTS | core | vocos loaded.
[+0800 20260411 12:00:01] [INFO] ChatTTS | core | dvae loaded.
[+0800 20260411 12:00:03] [INFO] ChatTTS | core | gpt loaded.
[+0800 20260411 12:00:03] [INFO] ChatTTS | core | speaker loaded.
[+0800 20260411 12:00:04] [INFO] ChatTTS | core | decoder loaded.
[+0800 20260411 12:00:04] [INFO] ChatTTS | core | tokenizer loaded.
code: 100%|██████████| 512/512(max) [00:07, 68.5it/s]
Note
All ChatTTS audio is generated at a 24,000 Hz sample rate, mono. Always pass
24000as the sample rate when saving.
Tip
wavs[i]is a NumPyfloat32array. Some versions oftorchaudioexpect a 2-D tensor (.unsqueeze(0)) and some expect a 1-D tensor. Iftorchaudio.saveraises a dimension error, try saving without.unsqueeze(0):try: torchaudio.save("output.wav", torch.from_numpy(wavs[0]).unsqueeze(0), 24000) except Exception: torchaudio.save("output.wav", torch.from_numpy(wavs[0]), 24000)
Prefer not to write Python? Two one-line commands give you the same result from the project root:
# Launch the browser-based web UI
python examples/web/webui.py
# Generate audio directly from the command line (saves output_audio_0.mp3)
python examples/cmd/run.py "Your first sentence." "Your second sentence."
2. Requirements
| Requirement | Detail |
|---|---|
| Python | 3.10 or 3.11 recommended (conda create -n chattts python=3.11) |
| PyTorch | torch>=2.1.0 plus torchaudio |
| GPU memory | At least 4 GB of VRAM for a ~30-second clip. CPU works but is slow. |
| Disk | Room for the pretrained model assets (downloaded on first run) |
| FFmpeg | Optional — needed for MP3/OGG output. Without it, the web UI falls back to WAV. |
Hardware acceleration is auto-detected in this order: CUDA GPU → Ascend NPU → Apple MPS (experimental) → DirectML (experimental) → CPU. You do not need to configure anything; ChatTTS picks the best available device and the GPU with the most free memory.
Note
On the 4090 GPU, ChatTTS generates roughly 7 semantic tokens per second, giving a Real-Time Factor (RTF) of about 0.3 (audio is produced ~3× faster than real time). Apple MPS is currently slower than CPU and is only used when you explicitly opt in.
3. Installation
ChatTTS can be installed as a published package or run directly from a clone of the repository. Run-from-clone is required if you want the CLI, web UI, or API examples, because those live in the examples/ folder and depend on the tools/ folder.
From PyPI (library use)
pip install ChatTTS
Latest from GitHub
pip install git+https://github.com/2noise/ChatTTS
Clone + requirements (full project)
git clone https://github.com/2noise/ChatTTS
cd ChatTTS
pip install --upgrade -r requirements.txt
Conda environment
conda create -n chattts python=3.11
conda activate chattts
pip install -r requirements.txt
Editable / dev mode
pip install -e .
Optional: text normalization packages
ChatTTS can normalize numbers, dates, and symbols into spoken words ("$123" → "one hundred and twenty three dollars") before synthesis. This requires extra packages that are not installed by default. They are Linux-friendly and registered manually at runtime (see Section 7).
# Chinese normalization (WeTextProcessing)
conda install -c conda-forge pynini=2.1.5
pip install WeTextProcessing
# English normalization (NeMo)
conda install -c conda-forge pynini=2.1.5
pip install nemo_text_processing
Note
pynini,WeTextProcessing, andnemo_text_processingare declared as Linux-only dependencies inrequirements.txt. On Windows/macOS, ChatTTS still runs — it simply skips text normalization and logs a warning telling you which package is missing.
Optional: vLLM acceleration (Linux only)
pip install safetensors vllm==0.2.7 torchaudio
Then pass use_vllm=True to chat.load(...). See Section 9.
Warning
Do NOT install TransformerEngine or FlashAttention-2 for normal use. The TransformerEngine integration is still under development and cannot run properly. FlashAttention-2 currently slows down generation. Both are gated behind experimental flags and are for development only.
4. Everyday Usage (Python API)
The entire Python interface is the ChatTTS.Chat class. The lifecycle is always the same: create → load → infer.
4.1 Load the model
import ChatTTS
chat = ChatTTS.Chat()
chat.load()
load() accepts several useful options:
| Parameter | Type | Default | What it does |
|---|---|---|---|
source |
"local" / "huggingface" / "custom" |
"local" |
Where to get the model. local downloads into the current directory's asset/; huggingface pulls from the HF Hub; custom uses your own custom_path. |
force_redownload |
bool |
False |
Re-download even if assets already exist. |
compile |
bool |
False |
Compile the model with torch.compile for faster inference. Slower first run; CUDA only. |
custom_path |
str |
None |
Path to a directory containing the asset/ model files (used with source="custom", or as a cache dir for HF). |
device |
torch.device |
None |
Force a device. If None, ChatTTS auto-selects (see Section 2). |
coef |
str |
None |
A custom DVAE coefficient string to tweak the vocoder. Leave unset normally. |
use_flash_attn |
bool |
False |
Enable FlashAttention-2. Not recommended (see warning above). |
use_vllm |
bool |
False |
Use the vLLM backend for the GPT stage (Linux). |
experimental |
bool |
False |
Allow experimental accelerators (MPS, DirectML, TransformerEngine). |
enable_cache |
bool |
True |
Use the model's KV cache during generation. |
load() returns True on success and False if the model failed to download or load — always check it:
if not chat.load(source="huggingface"):
raise RuntimeError("ChatTTS model failed to load")
Tip
The first
load()downloads several hundred megabytes of model assets. To control where they go, passcustom_path="/path/to/models". To reuse an already-downloaded model without any network access, usesource="custom"withcustom_pathpointing at the folder that containsasset/.
4.2 Generate audio with infer()
infer() is the one method you call to turn text into audio. It accepts a single string or a list of strings and returns a list of NumPy audio arrays.
texts = [
"Conversational speech, generated locally.",
"ChatTTS handles a list of sentences in one call.",
]
wavs = chat.infer(texts)
for i, wav in enumerate(wavs):
torchaudio.save(f"out_{i}.wav", torch.from_numpy(wav).unsqueeze(0), 24000)
The most useful infer() arguments:
| Argument | Default | Purpose |
|---|---|---|
text |
(required) | A string or list of strings to synthesize. |
stream |
False |
Stream audio chunks as they are generated instead of returning all at once (see Section 6). |
lang |
None |
Force language for normalization: "zh" or "en". None = auto-detect. |
skip_refine_text |
False |
Skip the text-refinement pass (see below). Set True when you control prosody yourself with inline tokens. |
refine_text_only |
False |
Return the refined text without generating audio. Useful for previewing how the model will read your input. |
use_decoder |
True |
Use the high-quality decoder path (recommended). |
do_text_normalization |
True |
Normalize numbers/symbols into words (needs the optional packages). |
do_homophone_replacement |
True |
Fix commonly mispronounced Chinese characters (see Section 7). |
split_text |
True |
Automatically split long input into sentence-sized batches. |
max_split_batch |
4 |
Maximum number of sentences processed per batch when splitting. |
params_refine_text |
RefineTextParams() |
Controls the text-refinement stage (see Section 5). |
params_infer_code |
InferCodeParams() |
Controls the audio-generation stage (see Section 5). |
Note
What is "refine text"? ChatTTS has two stages. The refine stage rewrites your raw text into a token stream sprinkled with prosody markers (pauses, breaks) so the speech sounds natural. The infer code stage turns that into audio. If you already wrote your own prosody markers into the text, pass
skip_refine_text=Trueso the model uses your markers verbatim.
4.3 Preview refined text without making audio
refined = chat.infer("hello how are you today", refine_text_only=True)
print(refined)
# e.g. ['hello [uv_break] how are you today [uv_break]']
4.4 Unload and interrupt
chat.unload() # free all model memory; chat can be reloaded later
chat.interrupt() # stop an in-progress streaming generation
5. Controlling the Voice
There are two layers of control: inline tokens you put inside the text, and parameter objects you pass to infer().
5.1 Inline prosody tokens
ChatTTS understands special bracketed markers inside your text. They tell the model to laugh, pause, or change speaking style at exactly that point.
| Token | Meaning | Where to use it |
|---|---|---|
[laugh] |
Insert laughter | Word level, inside the text |
[uv_break] |
A short, natural pause / breath | Word level, inside the text |
[lbreak] |
A longer break (line break) | Word level, end of a clause |
[oral_N] |
Oral/casual speaking style, N = 0–9 |
In the refine prompt |
[laugh_N] |
Laughter intensity, N = 0–2 |
In the refine prompt |
[break_N] |
Pause length, N = 0–7 |
In the refine prompt |
[speed_N] |
Speaking speed, N = 0–9 |
In the infer-code prompt (default [speed_5]) |
Important
In the current released model, the only token-level control units that work inside the body of your text are
[laugh],[uv_break], and[lbreak]. The[oral_N],[laugh_N], and[break_N]tokens belong in the refine-text prompt (see below), and[speed_N]belongs in the infer-code prompt. Additional emotion controls may be released in future model versions.
Word-level example (you supply the prosody, so skip refinement):
text = "What is [uv_break] your favorite english food?[laugh][lbreak]"
wavs = chat.infer(text, skip_refine_text=True)
5.2 RefineTextParams — shaping the text-refinement stage
Pass this as params_refine_text. Its prompt field is where [oral_N], [laugh_N], and [break_N] go.
params_refine_text = ChatTTS.Chat.RefineTextParams(
prompt="[oral_2][laugh_0][break_6]",
)
wavs = chat.infer(texts, params_refine_text=params_refine_text)
| Field | Default | Meaning |
|---|---|---|
prompt |
"" |
Prosody control tokens for refinement (e.g. [oral_2][laugh_0][break_6]). |
top_P |
0.7 |
Nucleus sampling probability. |
top_K |
20 |
Top-K sampling cutoff. |
temperature |
0.7 |
Sampling randomness. |
repetition_penalty |
1.0 |
Penalty for repeated tokens. |
max_new_token |
384 |
Maximum tokens of refined text. |
min_new_token |
0 |
Minimum tokens before the model may stop. |
show_tqdm |
True |
Show a progress bar. |
ensure_non_empty |
True |
Retry if the model produces empty output. |
manual_seed |
None |
Fix the random seed for reproducible refinement. |
5.3 InferCodeParams — shaping the audio stage
Pass this as params_infer_code. This is where you attach a speaker and set the speed. It inherits every field from RefineTextParams and adds speaker/streaming fields.
params_infer_code = ChatTTS.Chat.InferCodeParams(
spk_emb=rand_spk, # a fixed speaker (see Section 8)
temperature=0.3, # default for the audio stage
top_P=0.7,
top_K=20,
prompt="[speed_5]", # speaking speed
)
wavs = chat.infer(texts, params_infer_code=params_infer_code)
| Field | Default | Meaning |
|---|---|---|
prompt |
"[speed_5]" |
Speed token, [speed_0]–[speed_9]. |
spk_emb |
None |
A speaker-embedding string (fixed timbre). None = random each run. |
spk_smp |
None |
A speaker sample code derived from a reference audio clip (voice cloning). |
txt_smp |
None |
The transcript of the reference clip used with spk_smp. |
temperature |
0.3 |
Sampling randomness for audio (lower = more stable). |
repetition_penalty |
1.05 |
Penalty against repetitive audio tokens. |
max_new_token |
2048 |
Maximum audio tokens (caps clip length). |
stream_batch |
24 |
Tokens per streamed chunk. |
stream_speed |
12000 |
Samples emitted per streamed chunk. |
pass_first_n_batches |
2 |
Warm-up batches skipped before streaming starts. |
Tip
For the most natural results, the README's reference settings are
temperature=0.3,top_P=0.7,top_K=20for the audio stage. Lower the temperature toward0.1if the voice wanders or produces artifacts; raise it slightly for more expressive delivery.
6. Streaming Audio
Set stream=True to receive audio chunks as they are generated — ideal for low-latency playback while the rest of the sentence is still synthesizing.
chat = ChatTTS.Chat()
chat.load(compile=False)
rand_spk = chat.sample_random_speaker()
params = ChatTTS.Chat.InferCodeParams(spk_emb=rand_spk, temperature=0.3, top_P=0.7, top_K=20)
stream_gen = chat.infer(
["This sentence is produced and played back chunk by chunk."],
skip_refine_text=True,
stream=True,
params_infer_code=params,
)
for chunk in stream_gen: # chunk is a NumPy array of audio samples
... # send to your audio sink / websocket
The project also ships a ready-made streaming helper, ChatStreamer, in examples/cmd/stream.py. It buffers small chunks until there is enough audio, formats them as 16-bit PCM, and can play them straight to your speakers:
from examples.cmd.stream import ChatStreamer
stream_gen = chat.infer(texts, skip_refine_text=True, stream=True, params_infer_code=params)
ChatStreamer().play(stream_gen, wait=5) # buffers ~5 s, then plays in real time
Note
ChatStreamer.play()requires thepyaudiopackage, which you install manually. It is meant for CPU users where generation is slower than playback — thewaitargument is how many seconds to pre-buffer before audio starts.
7. Text Normalization and Homophones
ChatTTS cleans your input text before synthesis. Two independent steps are involved, both controllable from infer().
7.1 Text normalization (do_text_normalization=True)
Converts numbers, dates, currency, and symbols into spoken words, and detects whether the sentence is Chinese or English. It only runs if you have registered a normalizer for that language — otherwise the step is silently skipped.
Register normalizers after loading the model:
from tools.normalizer.en import normalizer_en_nemo_text
from tools.normalizer.zh import normalizer_zh_tn
chat.normalizer.register("en", normalizer_en_nemo_text())
chat.normalizer.register("zh", normalizer_zh_tn())
Warning
The English normalizer needs
nemo_text_processing; the Chinese normalizer needsWeTextProcessing. If the package is missing,register()raises and you'll see a log line telling you exactly whichpip installto run (see Section 3). The model still synthesizes — it just won't expand "$123" into words.
7.2 Homophone replacement (do_homophone_replacement=True)
For Chinese, ChatTTS ships a built-in map (homophones_map.json) of ~180,000 commonly mispronounced characters, each mapped to a correctly pronounced homophone. When enabled, mispronounced characters are swapped before synthesis and the substitutions are logged.
wavs = chat.infer(zh_texts, do_homophone_replacement=True)
[INFO] ChatTTS | norm | replace homophones: 喲->唷
Note
Homophone replacement defaults to
Truein the Python API but is set toFalsein some of the bundled example clients — check the example's body if your output differs from what you expect.
8. Speakers and Voice Cloning
By default, every infer() call samples a fresh random speaker, so the voice changes run to run. To keep a consistent voice, generate a speaker embedding once and reuse it.
8.1 Sample and reuse a fixed speaker
rand_spk = chat.sample_random_speaker()
print(rand_spk) # a long encoded string — SAVE THIS to recover the timbre later
params = ChatTTS.Chat.InferCodeParams(spk_emb=rand_spk)
wavs = chat.infer(texts, params_infer_code=params)
The returned rand_spk is a compact encoded string. Store it (in a file, database, or config) and pass it back as spk_emb any time you want that exact voice again.
Tip
To make speakers reproducible, fix the torch seed before sampling. The web UI uses this trick to map a numeric "Audio Seed" to a stable timbre:
import torch torch.manual_seed(1111) spk = chat.sample_random_speaker()
8.2 Zero-shot voice cloning from a reference clip
Provide a reference audio clip plus its transcript and ChatTTS will imitate that voice:
from tools.audio import load_audio
sample_audio = load_audio("reference.wav", 24000)
spk_smp = chat.sample_audio_speaker(sample_audio)
params = ChatTTS.Chat.InferCodeParams(
spk_smp=spk_smp,
txt_smp="the exact transcript of reference.wav",
)
params.spk_emb = None # cloning and fixed-embedding are mutually exclusive
wavs = chat.infer(texts, params_infer_code=params)
Important
When you supply
spk_smp(a cloned voice), setspk_emb=None. The two speaker mechanisms cannot be combined — providing a reference sample disables the speaker embedding.
Caution
Voice cloning quality varies. Community testing reports low speaker similarity in some zero-shot cases. Use a clean, single-speaker reference clip at 24 kHz for best results.
9. Performance and Acceleration
| Lever | How | Effect |
|---|---|---|
compile=True |
chat.load(compile=True) |
Compiles the model (CUDA only). Slower first run, faster steady-state inference. |
| vLLM backend | chat.load(use_vllm=True) (Linux) |
Faster GPT-stage generation. Install vllm==0.2.7 first. |
| Batch sentences | Pass a list to infer() |
Sentences are batched (max_split_batch=4) for throughput. |
Lower max_new_token |
InferCodeParams(max_new_token=...) |
Caps clip length and generation time. |
| Streaming | stream=True |
Reduces time-to-first-audio, not total compute. |
Caution
The vLLM-accelerated path has known timbre quirks: community reports note it may bias toward male voices and slightly degrade audio quality compared to the default path. If voice fidelity matters more than speed, use the default (non-vLLM) backend.
10. Using ChatTTS Without Python Code
10.1 Command line (examples/cmd/run.py)
Run from the project root. Positional arguments are the sentences to speak; output is saved as output_audio_0.mp3, output_audio_1.mp3, …
python examples/cmd/run.py "Your text 1." "Your text 2."
| Flag | Default | Description |
|---|---|---|
--spk |
random | A saved speaker-embedding string to fix the voice. |
--stream |
off | Generate in streaming mode. |
--source |
local |
Model source: huggingface, local, or custom. |
--custom_path |
"" |
Path to a custom model directory (with source custom). |
texts |
— | One or more sentences (positional, quote each one). |
# Use a custom local model and Chinese input
python -m examples.cmd.run --source custom --custom_path ../../models/2Noise/ChatTTS "你好喲" ":)"
10.2 Web UI (examples/web/webui.py)
A Gradio app that opens in your browser with sliders for temperature, top_P, top_K, seeds, speaker embedding, streaming, and audio/text-seed dice buttons.
python examples/web/webui.py
| Flag | Default | Description |
|---|---|---|
--server_name |
0.0.0.0 |
Host/interface to bind. |
--server_port |
8080 |
Port to serve on. |
--root_path |
— | Mount path if behind a reverse proxy. |
--custom_path |
— | Path to a custom model. |
--coef |
— | Custom DVAE coefficient string. |
--disable_cache |
off | Disable the model KV cache. |
--experimental |
off | Enable experimental accelerators. |
The UI lets you type text, pick a timbre, paste a speaker embedding, upload a reference clip for cloning ("Sample Audio" tab), and toggle Stream Mode and Auto Play. The 🎲 buttons randomize the audio/text seeds; the Reload button reloads the model with a new DVAE coefficient.
Note
A valid speaker-embedding string in the UI must start with a specific prefix; if you paste an invalid one, the Generate button quietly produces no audio. Use the 🎲 (audio seed) button to generate a valid embedding.
10.3 HTTP API — native server (examples/api/main.py)
A FastAPI service that accepts a JSON body and returns a ZIP of MP3 files (one per input sentence).
# Start the server (loads the model from Hugging Face on startup)
fastapi run examples/api/main.py
Send a request with the bundled client or your own:
python examples/api/client.py
# The request body shape (examples/api/client.py)
body = {
"text": ["四川美食确实以辣闻名,但也有不辣的选择。"],
"stream": False,
"lang": None,
"skip_refine_text": True,
"use_decoder": True,
"do_text_normalization": True,
"do_homophone_replacement": False,
"params_refine_text": {"prompt": "", "top_P": 0.7, "top_K": 20, "temperature": 0.7},
"params_infer_code": {"prompt": "[speed_5]", "top_P": 0.1, "temperature": 0.3, "spk_emb": None},
}
The endpoint is POST /generate_voice. The client reads the service location from environment variables:
| Variable | Default (client.py) | Purpose |
|---|---|---|
CHATTTS_SERVICE_HOST |
localhost |
Host of the API server. |
CHATTTS_SERVICE_PORT |
8000 |
Port of the API server. |
A second command-line client, examples/api/postScript.py, exposes every inference parameter as a flag (--text, --audio_seed, --infer_temperature, --refine_top_P, …) for scripted batch generation.
10.4 HTTP API — OpenAI-compatible server (examples/api/openai_api.py)
A drop-in replacement for OpenAI's text-to-speech endpoint, so existing OpenAI TTS clients work against ChatTTS.
fastapi run examples/api/openai_api.py
| Endpoint | Method | Purpose |
|---|---|---|
/v1/audio/speech |
POST |
Synthesize speech (OpenAI TTS schema). |
/health |
GET |
Health check; returns {"status": "healthy", "model_loaded": true}. |
Request fields follow the OpenAI spec:
| Field | Default | Notes |
|---|---|---|
model |
tts-1 |
Forced to tts-1. |
input |
(required) | Text to synthesize, max 2048 characters. |
voice |
default |
One of default, alloy, echo. |
response_format |
mp3 |
mp3, wav, or ogg. |
speed |
1.0 |
Range 0.5–2.0. |
stream |
False |
Stream the audio response. |
curl http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"Hello from ChatTTS","voice":"alloy","response_format":"mp3"}' \
--output speech.mp3
Note
The three named voices (
default,alloy,echo) load from speaker files (1528.pt,1384.pt,2443.pt). If a voice file is not present on disk, the server logs a warning and falls back to the default speaker. Download stable speaker files from the community ChatTTS Speaker spaces on ModelScope or Hugging Face.
11. Common Recipes
Reproducible voice + prosody
import torch, ChatTTS, torchaudio
chat = ChatTTS.Chat(); chat.load()
torch.manual_seed(1111)
spk = chat.sample_random_speaker()
wavs = chat.infer(
"chat T T S is a text to speech model. [uv_break] it sounds natural [laugh].",
skip_refine_text=True,
params_infer_code=ChatTTS.Chat.InferCodeParams(spk_emb=spk, temperature=0.3, top_P=0.7, top_K=20),
)
torchaudio.save("repro.wav", torch.from_numpy(wavs[0]).unsqueeze(0), 24000)
Casual, expressive delivery (refine prompt)
wavs = chat.infer(
"Let me tell you a quick story.",
params_refine_text=ChatTTS.Chat.RefineTextParams(prompt="[oral_2][laugh_0][break_4]"),
)
English self-introduction (from the README)
inputs_en = (
"chat T T S is a text to speech model designed for dialogue applications. "
"[uv_break]it supports mixed language input [uv_break]and offers multi speaker "
"capabilities with precise control over prosodic elements like "
"[uv_break]laughter[uv_break][laugh], [uv_break]pauses, [uv_break]and intonation. "
"[uv_break]it delivers natural and expressive speech,[uv_break]so please"
"[uv_break] use the project responsibly at your own risk.[uv_break]"
)
wavs = chat.infer(inputs_en, params_refine_text=ChatTTS.Chat.RefineTextParams(prompt="[oral_2][laugh_0][break_4]"))
torchaudio.save("intro.wav", torch.from_numpy(wavs[0]), 24000)
12. Troubleshooting
Error: RuntimeError: narrow(): length must be non-negative.
Cause: The input text was empty, whitespace-only, or too short after normalization/refinement, so the model produced zero tokens. This is one of the most common community-reported errors (issues #955, #978).
Solution: Ensure each sentence contains real, non-trivial text. Avoid passing empty strings in a list. If you used skip_refine_text=True, confirm your text still has speakable content after stripping tokens.
Prevention: Filter out empty/short entries before calling infer(); keep ensure_non_empty=True (the default).
Error: AttributeError: BertTokenizer has no attribute encode_plus
Cause: A transformers version mismatch — the installed Transformers changed the tokenizer's method surface (issue #987).
Solution: Match the required version: pip install "transformers>=4.41.1". ChatTTS already falls back from encode_plus to _encode_plus internally, so upgrading Transformers resolves it.
Prevention: Install from requirements.txt rather than pinning Transformers yourself.
Error: lzma.LZMAError: Corrupt input data
Cause: A malformed or truncated speaker-embedding / DVAE-coefficient string was passed to spk_emb/coef (issue #800). These strings are LZMA-compressed and base16384-encoded; any corruption breaks decoding.
Solution: Re-generate the speaker string with chat.sample_random_speaker() and copy it whole. Don't hand-edit it or truncate it.
Prevention: Store speaker strings verbatim; the web UI validates that valid embeddings start with a fixed prefix.
Error: FileNotFoundError: File not found: <file>
Cause: A reference audio path passed to load_audio() / voice cloning does not exist (from tools/audio/av.py).
Solution: Verify the path to your reference clip and that the file is a readable audio format.
Prevention: Use absolute paths for reference clips.
Error: ValueError: Padding must be 'center' or 'same'.
Cause: An invalid padding value reached the mel-spectrogram feature extractor (from ChatTTS/model/dvae.py).
Solution: Don't override internal feature-extractor config; use the shipped defaults.
Error: Unable to load model. (web UI) / Models load failed. (CLI)
Cause: The model assets failed to download or the integrity (SHA-256) check failed.
Solution: Re-run with force_redownload=True (Python) or delete the asset/ directory and let it re-download. Check your network access to Hugging Face / the asset host.
Prevention: Ensure enough free disk and a stable connection on first run.
No audio, no error (web UI Generate does nothing)
Cause: The Speaker Embedding field holds an invalid string (it must start with the expected prefix), or generation was interrupted. Solution: Click the 🎲 audio-seed button to regenerate a valid embedding, then Generate again.
Crash on Python 3.12 with CUDA 12.6 (no message)
Cause: An environment-specific incompatibility (issue #876).
Solution: Use Python 3.10/3.11 in a clean conda env (conda create -n chattts python=3.11).
no ffmpeg installed, use wav file output
Cause: FFmpeg/ffprobe not on PATH, so MP3/OGG encoding is unavailable.
Solution: Install FFmpeg, or accept WAV output. This is a warning, not a failure.
Generation runs but stops early: incomplete result. hit max_new_token
Cause: The clip reached the max_new_token cap before the model finished the sentence.
Solution: Increase InferCodeParams(max_new_token=...) (default 2048) for longer text.
CPU synthesis is extremely slow
Cause: No GPU/NPU detected, so generation runs on CPU. Solution: Use a CUDA GPU with ≥4 GB VRAM. For CPU, keep sentences short and consider streaming with a pre-buffer.
13. FAQ
Q: How much VRAM do I need, and how fast is it? For a 30-second clip, at least 4 GB of GPU memory. On a 4090, ChatTTS produces about 7 semantic tokens/second with a Real-Time Factor around 0.3.
Q: Why does the voice sound different every time?
Because infer() samples a random speaker by default. Generate one with sample_random_speaker() and reuse it via spk_emb for a consistent voice (see Section 8).
Q: Which languages are supported?
English and Chinese, including mixed-language input in a single sentence. Language for normalization is auto-detected or set via lang="zh"/lang="en".
Q: Can I control emotions beyond laughter?
In the current released model, the working token-level controls are [laugh], [uv_break], and [lbreak], plus the prompt-level [oral_N], [laugh_N], [break_N], and [speed_N]. Broader emotion control is on the roadmap for future model versions.
Q: The output is unstable — multiple speakers in one clip, or poor quality. Why?
This is a known characteristic of autoregressive TTS models. Generate several samples and pick the best, or fix a speaker embedding and lower temperature toward 0.1.
Q: How do I get the same voice across sessions?
Print and save the string from sample_random_speaker(), then pass it as spk_emb later. The string fully encodes the timbre.
Q: Do I need the normalization packages?
Only if you want numbers/symbols spoken correctly. Without nemo_text_processing (English) or WeTextProcessing (Chinese), ChatTTS still works; it just skips normalization and warns you.
Q: Can I clone a specific person's voice?
You can do zero-shot cloning from a reference clip via sample_audio_speaker() + spk_smp/txt_smp. Similarity varies; use a clean 24 kHz reference. Always have the right to use the voice you clone.
Q: Is ChatTTS free for commercial use?
No. The code is AGPLv3+ and the model is CC BY-NC 4.0 — non-commercial, academic/research use only.
Q: Should I install FlashAttention-2 or TransformerEngine to go faster?
No. FlashAttention-2 currently slows ChatTTS down, and TransformerEngine support is incomplete and cannot run properly. Use compile=True or vLLM instead.
Q: Where do I get higher-quality / additional voices?
Community speaker collections are hosted on ModelScope and Hugging Face (the "ChatTTS_Speaker" spaces). Drop the .pt files where the OpenAI-compatible server expects them.
Q: Can I use ChatTTS as an OpenAI TTS drop-in?
Yes — run examples/api/openai_api.py and point your OpenAI TTS client at /v1/audio/speech (see Section 10.4).