# ChatTTS User Guide

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 under `CC 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.

```python
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)
```

```text
[+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 `24000` as the sample rate when saving.

> [!TIP]
> `wavs[i]` is a NumPy `float32` array. Some versions of `torchaudio` expect a 2-D tensor (`.unsqueeze(0)`) and some expect a 1-D tensor. If `torchaudio.save` raises a dimension error, try saving without `.unsqueeze(0)`:
> ```python
> 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:

```bash
# 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)"

    ```bash
    pip install ChatTTS
    ```

=== "Latest from GitHub"

    ```bash
    pip install git+https://github.com/2noise/ChatTTS
    ```

=== "Clone + requirements (full project)"

    ```bash
    git clone https://github.com/2noise/ChatTTS
    cd ChatTTS
    pip install --upgrade -r requirements.txt
    ```

=== "Conda environment"

    ```bash
    conda create -n chattts python=3.11
    conda activate chattts
    pip install -r requirements.txt
    ```

=== "Editable / dev mode"

    ```bash
    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](#7-text-normalization-and-homophones)).

```bash
# 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`, and `nemo_text_processing` are declared as **Linux-only** dependencies in `requirements.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)

```bash
pip install safetensors vllm==0.2.7 torchaudio
```

Then pass `use_vllm=True` to `chat.load(...)`. See [Section 9](#9-performance-and-acceleration).

> [!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

```python
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](#2-requirements)). |
| `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:

```python
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, pass `custom_path="/path/to/models"`. To reuse an already-downloaded model without any network access, use `source="custom"` with `custom_path` pointing at the folder that contains `asset/`.

### 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.

```python
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](#6-streaming-audio)). |
| `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](#7-text-normalization-and-homophones)). |
| `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](#5-controlling-the-voice)). |
| `params_infer_code` | `InferCodeParams()` | Controls the audio-generation stage (see [Section 5](#5-controlling-the-voice)). |

> [!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=True` so the model uses your markers verbatim.

### 4.3 Preview refined text without making audio

```python
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

```python
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):

```python
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.

```python
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.

```python
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=20` for the audio stage. Lower the temperature toward `0.1` if 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.

```python
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:

```python
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 the `pyaudio` package, which you install manually. It is meant for CPU users where generation is slower than playback — the `wait` argument 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:

```python
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 needs `WeTextProcessing`. If the package is missing, `register()` raises and you'll see a log line telling you exactly which `pip install` to run (see [Section 3](#optional-text-normalization-packages)). 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.

```python
wavs = chat.infer(zh_texts, do_homophone_replacement=True)
```

```text
[INFO] ChatTTS | norm | replace homophones: 喲->唷
```

> [!NOTE]
> Homophone replacement defaults to `True` in the Python API but is set to `False` in 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

```python
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:
> ```python
> 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:

```python
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), set `spk_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`, …

```bash
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). |

```bash
# 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.

```bash
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).

```bash
# 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:

```bash
python examples/api/client.py
```

```python
# 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.

```bash
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. |

```bash
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

```python
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)

```python
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)

```python
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](#8-speakers-and-voice-cloning)).

**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](#104-http-api--openai-compatible-server-examplesapiopenai_apipy)).

---

_Generated June 2026 from 2noise/ChatTTS at 77b89ee. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
