--- license: apache-2.0 language: - en - fr - es - ja - zh - ko - hi - pt tags: - text-to-speech - onnx - android - on-device - kokoro base_model: hexgrad/Kokoro-82M library_name: onnxruntime pipeline_tag: text-to-speech --- # Kokoro 82M — ONNX [Kokoro-82M](https://huggingface.co/hexgrad/Kokoro-82M) converted to end-to-end ONNX graphs for on-device TTS inference on Android. Each graph fuses BERT → duration prediction → alignment → prosody → iSTFTNet decoder with fixed-shape I/O. The short-turn graph shares the same external weights as the full graph, so it adds only a small graph file rather than another 155 MB download. ## Model | Property | Value | |----------|-------| | Parameters | 82M | | Architecture | Non-autoregressive, single-pass | | Format | ONNX (opset 18) | | Weights | 155 MB (FP16 storage → FP32 compute, shared external data) | | Sample rate | 24 kHz | | Max phonemes | 128 | | Max audio | 5s (120,000 samples) | | Voices | 50+ (256-dim embeddings) | | Languages | 8 (en, fr, es, ja, zh, ko, hi, pt) | ### Graph variants | Graph | Audio tensor | Intended use | |-------|--------------|--------------| | `kokoro-e2e.onnx` | `[1, 1, 120000]` (5.0 s) | Full-capacity synthesis | | `kokoro-e2e-realtime.onnx` | `[1, 1, 72000]` (3.0 s) | Default short voice-agent replies | `kokoro-e2e-realtime.onnx` references the same `kokoro-e2e.onnx.data` file as the full graph. It avoids decoder work over the unused padded tail on short replies. For text that would exceed its safe output window, split at sentence or word boundaries and synthesize the pieces; speech-core performs that retry automatically. ## Files | File | Size | Description | |------|------|-------------| | `kokoro-e2e.onnx` | 3.5 MB | Full-capacity model graph | | `kokoro-e2e-realtime.onnx` | 2.9 MB | 3.0 s short-turn graph; shares the weights file below | | `kokoro-e2e.onnx.data` | 155 MB | Model weights (external data, FP16 storage) | | `vocab_index.json` | 2 KB | IPA phoneme → token ID mapping | | `us_gold.json` | 1.5 MB | Primary pronunciation dictionary | | `us_silver.json` | 3.2 MB | Fallback pronunciation dictionary | | `voices/*.bin` | 1 KB each | Voice embeddings (256 × float32) | ## Tensors ### Input | Name | Shape | Type | Description | |------|-------|------|-------------| | `input_ids` | `[1, 128]` | int64 | Phoneme token IDs (zero-padded) | | `attention_mask` | `[1, 128]` | int64 | 1 for real tokens, 0 for padding | | `ref_s` | `[1, 256]` | float32 | Voice style embedding | | `speed` | `[1]` | float32 | Speed factor (1.0 = normal) | | `random_phases` | `[1, 9]` | float32 | Initial harmonic phases (uniform [0,1)) | ### Output (`kokoro-e2e.onnx`) | Name | Shape | Type | Description | |------|-------|------|-------------| | `audio` | `[1, 1, 120000]` | float32 | Raw PCM waveform (24 kHz) | | `audio_length_samples` | `[1]` | int64 | Valid sample count (trim audio to this) | | `pred_dur` | `[1, 128]` | float32 | Predicted phoneme durations | For `kokoro-e2e-realtime.onnx`, the `audio` output is `[1, 1, 72000]`; the other input and output tensors are unchanged. ## Usage ```python import numpy as np import onnxruntime as ort sess = ort.InferenceSession("kokoro-e2e.onnx") # Prepare inputs (phoneme IDs from vocab_index.json) input_ids = np.zeros((1, 128), dtype=np.int64) input_ids[0, :5] = [0, 60, 46, 79, 0] # example phonemes attention_mask = np.zeros((1, 128), dtype=np.int64) attention_mask[0, :5] = 1 # Load voice embedding (256 floats from .bin file) voice = np.fromfile("voices/af_heart.bin", dtype=np.float32).reshape(1, 256) output = sess.run(None, { "input_ids": input_ids, "attention_mask": attention_mask, "ref_s": voice, "speed": np.ones(1, dtype=np.float32), "random_phases": np.random.rand(1, 9).astype(np.float32), }) audio = output[0].flatten()[:int(output[1][0])] # trim to valid length ``` ## Precision Weights are stored as **FP16** and cast to FP32 at use, so all computation runs in FP32 and the audio is bit-for-bit equivalent to the FP32 reference — verified by a TTS→ASR round-trip (identical transcriptions across full-length replies) with no length-dependent drift. This halves the download (310 → 155 MB) at no quality cost. Runtime memory is unchanged: ONNX Runtime materializes FP32 weights at session load. Full-precision weights remain available on the [`fp32`](https://huggingface.co/soniqo/Kokoro-82M-ONNX/tree/fp32) branch (`kokoro-e2e.onnx.data`, 310 MB). ## Source Converted from [hexgrad/Kokoro-82M](https://huggingface.co/hexgrad/Kokoro-82M) using the E2E pipeline with FluidInference-aligned SineGen (segmented cumsum + phase wrapping). ## Links - [speech-android](https://github.com/soniqo/speech-android) — Android SDK - [soniqo.audio](https://soniqo.audio) — website - [blog](https://soniqo.audio/blog) — blog --- - **Guide**: [soniqo.audio/guides/kokoro/android](https://soniqo.audio/guides/kokoro/android) - **Docs**: [soniqo.audio](https://soniqo.audio) - **GitHub**: [soniqo/speech-swift](https://github.com/soniqo/speech-swift)