Inputs:
input_ids: int64 [1, n_phonemes] — phoneme token IDs (per config.json vocab).
MUST be wrapped with BOS=0 and EOS=0:
[0, *content_ids, 0]
style: float32 [1, 256] — voicepack slice at position [content_n_phonemes].
(Naming follows kokoro-js + thewh1teagle/kokoro-onnx
ecosystem convention.)
speed: float32 [1] — pacing multiplier (1.0 = neutral; <1.0 slows, >1.0 fastens).
Divides the predictor's per-phoneme duration BEFORE
rounding, so it scales actual frame allocation —
not just playback rate.
Outputs:
audio: float32 [1, n_samples] — 24 kHz waveform. Includes BOS+EOS audio at start/end —
strip `bos_frames * 600` samples from the front and
`eos_frames * 600` from the back if you want
content-only audio (Rasa-trained voicepacks generate
a soft breathy pre-roll for BOS that surfaces as
"umm" if not stripped).
pred_dur: int64 [1, n_phonemes] — per-phoneme durations in predictor frames.
1 frame = 600 audio samples at 24 kHz.
pred_dur[0] = BOS duration; pred_dur[-1] = EOS.
pred_dur is exposed so downstream apps can build phoneme/word-level timestamps.
Usage — onnxruntime (Python)
python
1import numpy as np
2import onnxruntime as ort
3import torch
4import soundfile as sf
5import json
6from misaki import espeak
78sess = ort.InferenceSession("onnx/model.onnx", providers=["CPUExecutionProvider"])9vocab = json.load(open("config.json"))["vocab"]10voice = torch.load("voices/mf_asha.pt", map_location="cpu", weights_only=True)1112g2p = espeak.EspeakG2P(language="mr")13text ="नमस्कार, मी मराठी बोलतो."14phonemes, _ = g2p(text)15content_ids =[vocab[p]for p in phonemes if p in vocab]1617# Wrap with BOS=0, EOS=018input_ids = np.array([[0,*content_ids,0]], dtype=np.int64)19# Voicepack indexed by CONTENT length (not wrapped length): [510, 1, 256] -> slot20style = voice[len(content_ids)].numpy().astype(np.float32)21speed = np.array([1.0], dtype=np.float32)2223audio, pred_dur = sess.run(None,{24"input_ids": input_ids,25"style": style,26"speed": speed,27})2829# Strip BOS+EOS audio (optional but recommended; see I/O notes above)30HOP =60031bos_frames =int(pred_dur.flatten()[0])32eos_frames =int(pred_dur.flatten()[-1])33audio = audio[bos_frames * HOP :len(audio)- eos_frames * HOP]3435sf.write("out.wav", audio,24000)
Usage — WebGPU / transformers.js
The live demo at shreyask/bol-tts-marathi uses this exact ONNX file via @huggingface/transformers. The TS client calls await model({ input_ids, style, speed }) and applies the BOS/EOS strip + per-utterance silence injection at punctuation boundaries client-side. Source: Space's src/model.ts.
For Marathi support in upstream Kokoro-JS pipelines, you'll need to monkey-patch 'm' as a Marathi lang_code (espeak 'mr').
Voicepacks (25)
This repo ships all 25 voicepacks deployed in the live demo as .pt files (use them as style input):
4 trained on Marathi corpora:mf_asha, mm_vivek (Rasa), mf_mukta, mm_dnyanesh (SPRINGLab)
19 stock-Kokoro crossovers:af_heart (Svara), af_nova (Tara), am_liam (Atharv), bf_emma-style (Ira), hm_omega (Vihaan), zf_xiaoxiao (Pari, kid), zf_xiaoyi (Vir, kid), … etc. See the demo's voicepacks.json for the full ID → display-name mapping.
2 synthetic:syn_sama (centroid mean of 5 voicepacks), syn_navya (centroid + Gaussian noise) — generated arithmetically with no reference audio.
⚠️ torch ≤ 2.8 required for export. torch ≥ 2.9 silently emits a static-output ONNX with the legacy tracer (dynamo=False) on Kokoro's InstanceNorm-under-spectral-norm + LSTM + CustomSTFT combo. The exported file loads + runs in onnxruntime but produces silence. We pin torch==2.6 in our export venv. See bol-tts-marathi pyproject.toml for the constraint.
disable_complex=True is mandatory — Kokoro's default TorchSTFT uses complex tensors that ONNX doesn't support.