Simple script for TTS in Chinese https://spacecruft.org/Chinese/TTS
  • Python 96.9%
  • Shell 3.1%
Find a file
2026-10-23 12:41:01 -06:00
docs Review various default voices 2026-10-19 17:07:11 -06:00
servers Example server configs, scripts 2026-10-23 12:41:01 -06:00
src/tts Add more remotes 2026-10-23 12:37:07 -06:00
.env.example Add more remotes 2026-10-23 12:37:07 -06:00
.gitignore Implement cosyvoice 3 2026-10-19 21:15:04 -06:00
.python-version Python 3.13 2026-06-09 12:04:21 -06:00
LICENSE-apache.txt Apache 2.0 2026-06-09 12:03:22 -06:00
pyproject.toml Add more remotes 2026-10-23 12:37:07 -06:00
README.md Add more remotes 2026-10-23 12:37:07 -06:00

Chinese TTS

Chinese TTS scripts using Alibaba's Qwen and inclusionAI's Ming.

Upstream

Install

Dependencies:

sudo apt install sox

Install the package (editable) into the venv; this puts tts-remote-qwen3 on venv/bin/:

./venv/bin/pip install -e .

Local model backends are extras with mutually incompatible pinned dependency stacks, so each goes into its own venv:

python -m venv venv-qwen3 && venv-qwen3/bin/pip install -e '.[qwen3]'     # tts-qwen3
python -m venv venv-ming3 && venv-ming3/bin/pip install -e '.[ming3]'     # tts-ming3
python -m venv venv-voxcpm2 && venv-voxcpm2/bin/pip install -e '.[voxcpm2]'  # tts-voxcpm2
python -m venv venv-auk && venv-auk/bin/pip install -e '.[auk]'           # tts-auk
# tts-cosyvoice3 pins torch==2.3.1, which needs Python <= 3.12 (e.g. pyenv 3.10):
python3.10 -m venv venv-cosyvoice3 && venv-cosyvoice3/bin/pip install -e '.[cosyvoice3]'  # tts-cosyvoice3

For CPU-only hosts, prefer PyTorch's CPU wheels to avoid downloading the CUDA stack:

venv-ming3/bin/pip install -e '.[ming3]' --extra-index-url https://download.pytorch.org/whl/cpu

For development (ruff):

./venv/bin/pip install -e '.[dev]'

Usage

tts-qwen3 "你好,世界。"
tts-qwen3 --slow --speaker Vivian "今天天气很好。"
tts-qwen3 --speaker Eric --instruct "用开心的语气说" "巴适得很。"

The first time it is run it will download a ~4 GB model from huggingface.

--speaker is free-form: besides the presets shown by --list-speakers, any name the model/server supports is accepted. --instruct passes an arbitrary style/emotion instruction to the model (mutually exclusive with --slow).

Every clip is post-processed by default: leading silence is trimmed (−42 dB relative to the clip's peak frame, 40 ms pad), the trailing edge is trimmed at a much lower threshold (−60 dB, floored at −60 dBFS, 150 ms pad) with a raised-cosine fade-out so the final syllable's natural decay is preserved and the clip ends smoothly at zero, and loudness is normalized (−20 dBFS RMS target, −1 dBFS peak ceiling), so a set of clips has uniform edges and loudness. Disable individually with --no-trim / --no-normalize, or skip all post-processing with --raw (writes the audio exactly as the model/server produced it).

Local: tts-ming3

tts-ming3 has the same CLI shape as tts-qwen3, but synthesizes locally with inclusionAI's Ming-3.0-tts-0.5b (0.5B dense, 44.1 kHz output). It runs on CPU (slow but usable) as well as CUDA; the first run downloads a ~3 GB model.

The upstream inference code is not pip-installable, so it is imported from a git clone: $MING_TTS_REPO if set, otherwise a shallow clone under ~/.cache/chinese-tts/Ming-3.0-tts created automatically on first use.

Usage:

tts-ming3 "你好,世界。"
tts-ming3 --speaker DB30 "今天天气很好。"
tts-ming3 --speaker 灵小安 "内置音色直接报名字。"
tts-ming3 --instruct "用开心的语气说" "巴适得很。"

Differences from tts-qwen3:

  • --speaker selects one of the local cloning presets shown by --list-speakers (default: lingguang), or any built-in premium voice name (100+ Chinese voices such as 灵小安; see cookbooks/instructions.md in the Ming-3.0-tts clone).
  • --instruct/--slow use the model's FREEFORM task, which takes no reference voice, so they cannot be combined with --speaker.
  • Only --temperature, --seed and --max-new-tokens affect sampling; the remaining sampling flags are Qwen-only.
  • Text normalization (numbers, dates, symbols) requires pynini, which is not in the ming3 extra (no universal wheel; conda install -c conda-forge pynini==2.1.6 if wanted). Without it, normalization is disabled automatically with a warning; --no-text-normalization silences the warning.

Local: tts-voxcpm2

tts-voxcpm2 has the same CLI shape as tts-qwen3, but synthesizes locally with OpenBMB's VoxCPM2 (2B, tokenizer-free diffusion AR, 48 kHz output), installed from PyPI as the voxcpm package. It runs on CPU (slow: minutes per clip) as well as CUDA; the first run downloads a ~5 GB model.

Usage:

tts-voxcpm2 "你好,世界。"
tts-voxcpm2 --speaker myvoice.wav "今天天气很好。"
tts-voxcpm2 --instruct "用开心的语气说" "巴适得很。"

Differences from tts-qwen3:

  • VoxCPM2 has no built-in voices — it always clones a reference clip. --speaker accepts a bundled preset name (--list-speakers: female (default), female-whisper, male-1, male-2; MIT-licensed AuK demo clips in src/tts/data/, selected per docs/VOICES.txt) or a path to any reference WAV file. The presets are shared with tts-auk.
  • --instruct/--slow prepend a parenthesized style description to the text (VoxCPM2's controllable-cloning syntax) and combine freely with --speaker.
  • Only --seed and --max-new-tokens affect generation; the remaining sampling flags are Qwen-only. Generation uses the upstream defaults cfg_value=2.0, inference_timesteps=10.

Local: tts-auk

tts-auk synthesizes locally with Tencent's AuK (1.5B DiT + Qwen2.5-Omni-3B text encoder, 24 kHz output) via zero-shot voice cloning. It runs on CPU (fp32; minutes per clip) as well as CUDA. First run downloads ~14 GB of models (AuK + the Qwen encoder).

The upstream inference code is not on PyPI, so it is imported from a git clone: $AUK_REPO if set, otherwise a shallow clone under ~/.cache/chinese-tts/AuK created automatically on first use.

Usage:

tts-auk "你好,世界。"
tts-auk --speaker male-1 "今天天气很好。"
tts-auk --speaker myvoice.wav --ref-text "参考音频的逐字稿。" "文本。"
tts-auk --instruct "用开心的语气说" "巴适得很。"

Notes:

  • --speaker uses the shared bundled presets (--list-speakers) or any reference WAV. Output length is estimated from the reference transcript (known for presets; pass --ref-text for custom clips) — without a transcript a fixed ~4.2 chars/second heuristic is used.
  • --instruct/--slow add a style clause to the cloning instruction and combine freely with --speaker.
  • Only --seed affects generation; the other sampling flags are Qwen-only. AuK is a diffusion model pinned to nfe=32 / cfg=2.0.

Local: tts-cosyvoice3

tts-cosyvoice3 synthesizes locally with FunAudioLLM's Fun-CosyVoice3-0.5B-2512 (0.5B LLM + flow matching + HiFT, 24 kHz output) via zero-shot voice cloning. It runs on CPU (RTF ~20 on 32 cores) as well as CUDA; the first run downloads a ~6.4 GB model.

The upstream inference code is not pip-installable, so it is imported from a git clone: $COSYVOICE_REPO if set, otherwise a shallow clone under ~/.cache/chinese-tts/CosyVoice (with the Matcha-TTS submodule) created automatically on first use.

Usage:

tts-cosyvoice3 "你好,世界。"
tts-cosyvoice3 --speaker male-1 "今天天气很好。"
tts-cosyvoice3 --speaker myvoice.wav --ref-text "参考音频的逐字稿。" "文本。"
tts-cosyvoice3 --instruct "请用四川话表达" "巴适得很。"

Notes:

  • --speaker uses the shared bundled presets (--list-speakers) or any reference WAV. Without --ref-text, custom clips fall back to speaker-embedding-only cloning; presets carry known transcripts.
  • --instruct/--slow use CosyVoice3's instruct mode (dialects/emotion/speed, e.g. "请用广东话表达") and combine freely with --speaker.
  • Only --seed affects generation; the sampling flags are Qwen-only.

Remote: tts-remote-*

The tts-remote-* commands have the same CLI as their local counterparts, plus a few remote-only options, but synthesize via a remote OpenAI-compatible TTS server (POST /v1/audio/speech) — vllm-omni, sglang-omni, or anything else speaking that API — instead of loading the model locally.

One command per served model; each reads its own prefixed variables from .env (see .env.example):

command served model env prefix voice
tts-remote-qwen3 qwen3-tts QWEN3_ named speakers
tts-remote-qwen3-base qwen3-tts-base QWEN3_BASE_ clones --speaker ref
tts-remote-qwen3-voice-design qwen3-tts-voice-design QWEN3_VOICE_DESIGN_ --instruct (required)
tts-remote-cosyvoice3 cosyvoice3 COSYVOICE3_ clones --speaker ref
tts-remote-voxcpm2 voxcpm2 VOXCPM2_ clones --speaker ref
tts-remote-moss-tts-local moss-tts-local MOSS_TTS_LOCAL_ clones --speaker ref
tts-remote-moss-voice-generator moss-voice-generator MOSS_VOICE_GENERATOR_ --instruct (required)
tts-remote-auk auk AUK_ clones --speaker ref
tts-remote-auk-flash auk-flash AUK_FLASH_ clones --speaker ref
# Full base URL (required for https:// servers); wins if set:
QWEN3_BASE_URL=https://tts.example.com
# or plain host/port (http:// assumed):
QWEN3_HOST=192.168.1.100
QWEN3_PORT=44001
QWEN3_MODEL=qwen3-tts

COSYVOICE3_HOST=192.168.1.100
COSYVOICE3_PORT=44002
COSYVOICE3_MODEL=cosyvoice3

Connection failures are retried with exponential backoff (3 attempts).

Sampling defaults are greedy (temperature=0.0 for both the talker and the subtalker/codebook tower) and pinned client-side, because the server does not reliably apply the checkpoint's own defaults. Sampled decoding (the model's official temperature=0.9) stochastically produces shouting, extreme slowness, or sustained-vowel tails on some inputs, and the server's --seed does not give reproducible audio. For expressive sampling, set both temperatures, e.g. --temperature 0.9 --subtalker-temperature 0.9.

Notes:

  • Feed the model hanzi, not pinyin (贾宝玉, not Jiǎ Bǎoyù).
  • --seed is accepted but the server does not produce bit-identical audio from it.

Usage:

tts-remote-qwen3 "你好,世界。"
tts-remote-qwen3 --slow --speaker Vivian "今天天气很好。"
tts-remote-qwen3 --instruct "用开心的语气说" "今天天气很好。"
tts-remote-qwen3 --stream --text-file story.txt -o story.wav
tts-remote-qwen3 --response-format mp3 --sample-rate 8000 -o story.mp3 --text-file story.txt

tts-remote-voxcpm2 "你好,世界。"
tts-remote-voxcpm2 --instruct "用开心的语气说" "今天天气很好。"
tts-remote-voxcpm2 --stream --text-file story.txt -o story.wav

tts-remote-cosyvoice3 --speaker male-1 "今天天气很好。"
tts-remote-qwen3-base --speaker myvoice.wav --ref-text "参考音频的逐字稿。" "文本。"
tts-remote-qwen3-voice-design --instruct "一位声音低沉的成年男性。" "你好,世界。"
tts-remote-moss-voice-generator --instruct "活泼的年轻女声。" "你好,世界。"
tts-remote-auk "你好,世界。"

Voice backends:

  • Named speakers (tts-remote-qwen3): --speaker picks a built-in voice (--list-speakers).
  • Cloning (tts-remote-qwen3-base, -cosyvoice3, -voxcpm2, -moss-tts-local, -auk, -auk-flash): --speaker defaults to a bundled preset clip sent per-request as base64 ref_audio (presets carry known transcripts), or a path to any reference WAV — add --ref-text with its transcript for in-context cloning.
  • Voice design (tts-remote-qwen3-voice-design, tts-remote-moss-voice-generator): no --speaker; --instruct is required and describes the voice to design, e.g. --instruct "一位声音低沉的成年男性,语速缓慢。".
  • tts-remote-auk* also send a duration_seconds estimate with every request — without it the server generates to a generous default length (~13 s for a one-second text).

Remote-only options:

  • --response-format {wav,mp3,flac,pcm,opus} — mp3/flac give smaller files.
  • --sample-rate {8000,24000} — 8000 Hz for smaller files. (tts-remote-qwen3* only; the other servers are fixed at their native rates and reject sample_rate, so those CLIs do not offer the flag.)
  • --stream — streams audio from the server (lower time-to-first-audio on long texts); the output file is always WAV.

tts-remote-voxcpm2 notes:

  • Voice handling differs from tts-remote-qwen3. With no reference the server invents a voice from the text, so the timbre changes with every input. To keep one voice across runs, --speaker defaults to cloning a bundled preset clip (female; see --list-speakers), sent per-request as base64 ref_audio; preset clips carry known transcripts, which the server uses for in-context cloning (ref_text).
  • --speaker also accepts a path to any reference WAV (add --ref-text with its transcript for higher-quality in-context cloning), the name of an uploaded/precomputed voice on the server (GET /v1/audio/voices, upload via POST /v1/audio/voices), or server-default for the context-inferred voice.
  • The remaining sampling flags (--temperature, --top-k, ...) are Qwen-only and are not sent to the VoxCPM2 server.

Help

$ tts-qwen3 --help

usage: tts-qwen3 [-h] [--version] [--text-file PATH] [-o PATH]
                 [--speaker NAME] [--list-speakers] [--slow | --instruct TEXT]
                 [--no-trim] [--no-normalize] [--seed SEED]
                 [--temperature TEMPERATURE] [--top-k TOP_K] [--top-p TOP_P]
                 [--repetition-penalty REPETITION_PENALTY]
                 [--subtalker-temperature SUBTALKER_TEMPERATURE]
                 [--subtalker-top-k SUBTALKER_TOP_K]
                 [--subtalker-top-p SUBTALKER_TOP_P]
                 [--max-new-tokens MAX_NEW_TOKENS]
                 [text]

Synthesize Standard Mandarin Chinese speech with Qwen3-TTS.

positional arguments:
  text                  Chinese text to synthesize (final positional argument).

options:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  --text-file PATH      Read text to synthesize from a file instead of the positional argument.
  -o, --output PATH     Output filename. Defaults to an auto-incrementing output-NNN.wav.
  --speaker NAME        Speaker voice (default: Dylan). Free-form: any name the model/server supports is accepted; --list-speakers shows the standard Chinese presets.
  --list-speakers       List the standard Chinese speaker presets and exit.
  --slow                Speak slowly.
  --instruct TEXT       Style/emotion instruction for the model, e.g. "用开心的语气说". Cannot be combined with --slow.
  --no-trim             Do not trim leading/trailing silence (trimming is on by default).
  --no-normalize        Do not loudness-normalize the clip (on by default).
  --seed SEED           Random seed for reproducible sampling (optional).
  --temperature TEMPERATURE
                        Talker sampling temperature (default: 0.0 = greedy; the model's recommended sampling value is 0.9).
  --top-k TOP_K         Top-k sampling (default: 50; applies when --temperature > 0).
  --top-p TOP_P         Top-p sampling (default: 1.0; applies when --temperature > 0).
  --repetition-penalty REPETITION_PENALTY
                        Repetition penalty (default: 1.05).
  --subtalker-temperature SUBTALKER_TEMPERATURE
                        Subtalker (codebook tower) sampling temperature (default: 0.0 = greedy; recommended sampling value is 0.9).
  --subtalker-top-k SUBTALKER_TOP_K
                        Subtalker top-k (default: 50; applies when --subtalker-temperature > 0).
  --subtalker-top-p SUBTALKER_TOP_P
                        Subtalker top-p (default: 1.0; applies when --subtalker-temperature > 0).
  --max-new-tokens MAX_NEW_TOKENS
                        Maximum number of new tokens to generate (optional).

examples:
  tts-qwen3 "你好,世界。"
  tts-qwen3 --slow --speaker Vivian "今天天气很好。"
  tts-qwen3 --speaker Eric --instruct "用开心的语气说" "巴适得很。"
  tts-qwen3 --text-file story.txt -o story.wav
  tts-qwen3 --list-speakers

License

Apache 2.0 License. See LICENSE-apache.txt for details.

Copyright © 2026 Jeff Moe