Text-to-speech · 8 languages · 0.75B

OGG TTS

Expressive multilingual text-to-speech with block diffusion over Fish Audio codec tokens. Zero-shot voice cloning, optional emotion tags, context-aware delivery and streaming.

Preview: checkpoint 120k of a 400k-step pre-training run, still in progress. Built with Fish Audio.
Expressive by default

Reads with emotion that fits the text and its context, no tags needed.

24 inline tags

[angry] [whispering] [laughing] [sighing] … placed where they should take effect.

Zero-shot cloning

From a few seconds of reference audio and its transcript, or with the model's own voice.

Streaming

Blocks of 0.74 s are final once decoded and can be played immediately.

Architecture

A text-and-audio transformer (OmniVoice, initialised from Qwen3-0.6B) generates the semantic codebook of the Fish Audio codec block by block. An axial multi-token-prediction decoder fills in the 9 acoustic codebooks, and the codec renders 44.1 kHz audio.

Text target text, inline tags, neighbouring lines as context Voice prompt a few seconds of audio → Fish codec, 10 codes / frame Backbone · block diffusion OmniVoice / Qwen3-0.6B, 28 layers bidirectional inside a block, block-causal across blocks block of 16 frames (0.74 s) 6 unmasking iterations, most confident first, classifier-free guidance length: the model emits END itself Axial MTP · 110M codebooks 1–9 of the block, one per step, all frames at once Fish codec 10 codes → waveform 44.1 kHz, 21.5 fps states finished block (all 10 codes) joins the KV cache as context of the next block
Figure 1. One block of 0.74 s: the backbone unmasks the semantic codes, the axial MTP fills the 9 acoustic codebooks, the codec decodes audio. The block is then fixed and streamed.
Input sequence lang · style context lines prompt text + text prompt frames block 1 ✓ block 2 ✓ block 3 · masked Text tokens and audio frames share one sequence. Prompt frames and finished blocks carry all 10 codebooks; the block being generated starts fully masked. Attention contextblock 1block 2block 3 ● Context (text + prompt) sees only context. ● A finished block sees the context and every finished block up to itself (with all 10 codes). ● The block being generated sees everything before it and itself, bidirectionally (orange): its 16 frames are unmasked together, so prosody inside a block is planned as a whole. ● Finished blocks never change: KV cache, streaming, no look-back edits.
Figure 2. Block diffusion: autoregressive across blocks, diffusion (iterative unmasking) inside a block.
Axial MTP: a (frame × codebook) grid code 0 code 9 frames of the prompt, earlier blocks and the current block → Codebook attention (vertical, causal): token (t, j) sees codes 0…j of its own frame and predicts code j+1. Frame attention (horizontal): the same codebook across the prompt, earlier blocks and the current block. 6 layers, each with both attentions and a SwiGLU. Decoding: 9 steps per block, all 16 frames in parallel. The input of every frame is the backbone state.
Figure 3. The acoustic decoder. Vertical: the residual codebooks of one frame, coarse to fine. Horizontal: the same codebook over time, so the timbre and channel of the prompt carry over.
ComponentDetails
BackboneOmniVoice / Qwen3-0.6B, 28 layers, d = 1024, block attention (Figure 2)
Acoustic decoderAxial MTP, 6 layers, d = 768, 110M parameters
Parameters~0.75B (codec excluded)
CodecFish Audio Drama 3 / S2: 44.1 kHz, 21.5 frames/s, 1 semantic codebook (4096) + 9 residual (1024)
Block16 frames = 0.74 s
Decoding6 unmasking iterations with classifier-free guidance, then 9 MTP steps, per block; KV cache
Trainingabout 8,200 h of studio-quality speech in 8 languages (a private collection), 1× RTX PRO 6000, 400k steps planned

Samples

Checkpoint 120k, cfg=1.5, no voice prompt: the voices are the model's own. Every language has its own texts. Each item was generated 3 times; the take shown first was selected with Whisper (language identification and transcript), the other takes are under each sample.

Lines

Context sets the delivery

The same line, voice and seed. Only the preceding line, given as text context and not spoken, differs.

Longer text in one pass

Evaluation

Checkpoint 120k, cfg=1.5, one sample per item. WER with Whisper large-v3-turbo, speaker similarity with ECAPA2 (cosine), pitch variation as F0 standard deviation in semitones.

Held-out recordings

136 validation items in English, Spanish and German. Recordings and texts were never trained on. Each item clones a voice from a 6–10 s prompt and speaks two sentences that also exist as a real recording (passed through the same codec as the reference).

OGG TTS 120kReal recording
WER1.4 %2.6 %
Speaker similarity to the prompt0.7730.737
Duration vs. the real recording (median)0.991.00
Pitch variation (F0 std, semitones)3.584.25

All 8 languages

New texts, a neutral and an angry line per voice (32 items).

ENPLESDERUFRPTIT
WER0.0 %0.8 %0.0 %0.0 %0.0 %0.0 %3.3 %6.7 %
Similarity0.7800.7670.8280.8240.7630.7080.7710.841

The pace matches real speech. Intonation is still about 15 % flatter than in the real recordings; this is the main open point for the rest of the run. Public benchmarks (Seed-TTS, CV3) follow with the final checkpoint.

Usage

git clone https://github.com/FashionFlora/ogg-tts && cd ogg-tts
git clone https://github.com/fishaudio/fish-speech ogg_tts/fish-speech
export OMNIVOICE_PATH=$(huggingface-cli download k2-fsa/OmniVoice)
export FISH_CODEC_PATH=$(huggingface-cli download fishaudio/drama-3 codec.pth)
import sys; sys.path.insert(0, "ogg_tts")
from huggingface_hub import hf_hub_download
import infer_omnifish as inf, fish_codec as fc

model, tok = inf.load_model(hf_hub_download("FashionFlora/ogg-tts", "model.safetensors"), "cuda")
codec = fc.load_codec("cuda")
codes = inf.generate_cached(model, tok, None, "", "[sighing] Hello! I am OGG.", "en", cfg=1.5)
audio = fc.decode(codec, codes[None].cuda())[0].float().cpu().numpy()   # 44.1 kHz

Voice cloning: pass the reference codes (fc.encode) and its exact transcript. Context: semantic_left=(…) and after=(…). Details in the model card.