# MOSS-TTS-v1.5 Zero-shot voice cloning from a short reference recording, on the 8B delay-pattern member of the MOSS-TTS family. - Family: `moss_tts_v15` - Tasks: `tts`, `clon` (cloning), offline - Languages: English, Chinese - Weights: [OpenMOSS-Team/MOSS-TTS-v1.5](https://huggingface.co/OpenMOSS-Team/MOSS-TTS-v1.5), Apache-2.0 - Codec weights: [OpenMOSS-Team/MOSS-Audio-Tokenizer](https://huggingface.co/OpenMOSS-Team/MOSS-Audio-Tokenizer), Apache-2.0 - Architecture: `moss_tts_delay` — a Qwen3-8B backbone with 32 audio codebook embeddings and `1 + 32` output heads, decoded on a delay pattern - Codec: MOSS-Audio-Tokenizer v1, 24 kHz mono, hop 1920 (12.5 frames a second) It shares its architecture with `moss_voicegen`, which is the same design at Qwen3-1.7B and 16 codebooks. The backbone, heads, delay decoder and config parser are shared framework code (`engine/framework/decoders/moss_tts_delay/`); what is specific to this checkpoint is the prompt and the session. ## Usage Cloning, which is what this checkpoint is for: ```bash audiocpp_cli --family moss_tts_v15 --model --task clon \ --voice-ref reference.wav \ --text "This sentence should be spoken in the voice from the reference recording." \ --out out.wav ``` Without a reference, it is plain TTS and the voice is whatever the model picks: ```bash audiocpp_cli --family moss_tts_v15 --model --task tts \ --text "The quick brown fox jumps over the lazy dog." --out out.wav ``` The reference is resampled and downmixed to the codec's 24 kHz mono and trimmed to whole frames, so any sample rate and channel count will do. It is encoded once per request, not once per text chunk. ### Options | option | meaning | |---|---| | `--voice-ref ` | Reference recording to clone. | | `--request-option duration_frames=` | Duration budget in codec frames at 12.5 a second — the model's own `- Tokens:` field. 40 frames is about 3.2 s. Legacy `tokens` remains accepted. | | `--request-option instruct=` | Voice description. Legacy `instruction` remains accepted. **See the limitation below.** | | `--request-option text_temperature=` | Text-stream sampling temperature; default 1.5. Legacy `moss_tts_v15.text_temperature` remains accepted. | | `--language ` | Full language name; the model does not understand codes like `en`. | | `--seed`, `--temperature`, `--top-p`, `--top-k`, `--repetition-penalty` | Sampling. | | `--request-option min_frames=` | Minimum audio frames per text chunk; default 0 derives a text-length-based floor. Nonpositive values select this automatic behavior. Separate from `duration_frames` conditioning. | | `--request-option max_frames=` | Audio-frame ceiling per text chunk; default 0 derives a text-length-based ceiling. Nonpositive values select this automatic behavior. Runtime raises the ceiling to at least the effective minimum plus the number of codebooks. | | `moss_tts_v15.weight_type` | `native` (default), `f32`, `bf16` or `q8_0`; native preserves packaged quantization. | `f16` is rejected for the backbone: its attention-sink activations run far past f16's range and produce NaN from the first position. This is inherited from the family and is the same on `moss_voicegen`. ## ⚠ Limitation: voice-attribute instructions are not reliably followed `instruct` is accepted, but on this checkpoint a description of the *speaker* is followed only loosely. Measured on the reference implementation, four prompts differing only in the requested speaker, median F0 of the result: ``` "A high-pitched young woman's voice, clearly female." -> 216.3 Hz plausible "A woman speaking softly and warmly." -> 184.5 Hz borderline "A man speaking in a low register." -> 187.8 Hz miss "A very deep, low-pitched man's voice." -> 180.8 Hz clear miss ``` Five samples with median F0 as a proxy is not a rigorous evaluation, and one of the four was plausible — so this supports "unreliable", not "never works". But a "very deep, low-pitched man's voice" at 181 Hz is wrong by any measure. **Use a reference recording when the voice matters.** Cloning does work: a clone of a 189.4 Hz reference came back at 184.0 Hz, where the same model without a reference produced 117.3 Hz. If you want a voice from a written description rather than a recording, `moss_voicegen` is the model built for that. ## Performance and VRAM Measured on an RTX 3090 (24 GiB) and a 16-thread CPU. The times are `session.wall_ms` — the work itself, excluding model load, which for a 9 GB package is tens of seconds of disk I/O and swamps everything else if you time the whole process. | package | backend | RTF | peak VRAM (cloning) | |---|---|---|---| | q4_k | CUDA | 0.69–0.72 | 14.1 GiB | | q8_0 | CUDA | 0.80–0.84 | 17.9 GiB | | bf16 | CUDA | — | does not fit in 24 GiB when cloning | | q4_k | CPU, 16 threads | ~16 | — | The CUDA figures are the range over three runs on an otherwise-in-use desktop GPU; a single run of q8_0 also came back at 1.08, so treat these as a band rather than a number. CUDA is roughly 25-30x faster than the CPU path here, and comfortably faster than real time; the CPU path is not. **`weight_type` defaults to `native`, and you want to keep it there.** A GGUF package carries its own type; forcing `bf16` dequantises it on load, so a quantised package costs exactly as much memory as bf16 and the quantisation buys nothing. Measured: q4_k peaks at 21.2 GiB forced to `bf16` against 10.6 GiB left `native`. For safetensors it makes no difference — those are bf16 upstream anyway. **bf16 cannot clone on a 24 GiB card.** Cloning holds both halves of the codec resident, where plain TTS needs only the decoder, and the encoder's weights are the allocation that does not fit. Plain TTS works at every size. Use q8_0 on a 24 GiB card, or bf16 on something larger. ## What works Measured on the reference implementation and reproduced through audio.cpp: | | | |---|---| | voice cloning | tracks the reference closely | | Chinese | works, from an English instruction | | long form | 20.4 s from four sentences; RTF *improves* with length | | `tokens` budget | honoured — 40 tokens produced 41 frames | ## Packaging For safetensors, the model and the audio tokenizer are separate Hugging Face repositories. Place the tokenizer snapshot under `audio_tokenizer/` inside the model root — a real directory, not a symlink, which the GGUF converter does not follow: ```text MOSS-TTS-v1.5/ config.json tokenizer.json tokenizer_config.json merges.txt model.safetensors.index.json model-0000{1..4}-of-00004.safetensors audio_tokenizer/ config.json model.safetensors.index.json model-0000{1,2}-of-00002.safetensors ``` Building the GGUF package: ```bash audiocpp_gguf \ --input model_weights=/model.safetensors.index.json \ --input audio_tokenizer_weights=/audio_tokenizer/model.safetensors.index.json \ --output moss_tts_v15_bf16_codec_f16.gguf \ --type bf16 --keep-type "audio_tokenizer_weights*=f16" \ --family moss_tts_v15 --root ``` That produces about 20.5 GB with 2063 tensors, the model spec and 22 sidecars embedded. Unlike `moss_voicegen`, the codec encoder is included as well as the decoder, because cloning needs it.