Text-to-Speech
PEFT
Safetensors
tts
speech-synthesis
orpheus
snac
lora
unsloth
yoruba
hausa
igbo
nigerian-pidgin
nigeria
african-languages
multilingual
low-resource
Instructions to use Shinzmann/sorotts with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use Shinzmann/sorotts with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("/root/.cache/huggingface/hub/models--hypaai--hypaai_orpheus_v5/snapshots/a8786380f8f8c9b1215bc5b299ab740b3df1781d") model = PeftModel.from_pretrained(base_model, "Shinzmann/sorotts") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Unsloth Studio
How to use Shinzmann/sorotts with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Shinzmann/sorotts to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Shinzmann/sorotts to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for Shinzmann/sorotts to start chatting
Load model with FastModel
pip install unsloth from unsloth import FastModel model, tokenizer = FastModel.from_pretrained( model_name="Shinzmann/sorotts", max_seq_length=2048, )
| license: cc-by-nc-sa-4.0 | |
| language: | |
| - yo | |
| - ha | |
| - ig | |
| - pcm | |
| base_model: hypaai/hypaai_orpheus_v5 | |
| base_model_relation: adapter | |
| library_name: peft | |
| pipeline_tag: text-to-speech | |
| datasets: | |
| - naijavoices/naijavoices-dataset | |
| - google/fleurs | |
| - google/WaxalNLP | |
| - asr-nigerian-pidgin/nigerian-pidgin-1.0 | |
| tags: | |
| - text-to-speech | |
| - tts | |
| - speech-synthesis | |
| - orpheus | |
| - snac | |
| - lora | |
| - peft | |
| - unsloth | |
| - yoruba | |
| - hausa | |
| - igbo | |
| - nigerian-pidgin | |
| - nigeria | |
| - african-languages | |
| - multilingual | |
| - low-resource | |
| # SoroTTS: a natural voice for Yorùbá, Hausa, Igbo, and Nigerian Pidgin | |
| **SoroTTS** (from *sọ̀rọ̀*, "speak" in Yorùbá) is a LoRA fine-tune of **Orpheus-3B** that gives natural, expressive text-to-speech in **four Nigerian languages**, Yorùbá, Hausa, Igbo, and Nigerian Pidgin, from a single adapter under the **4B "Tiny Titan"** line. | |
| It powers [**Naija Solar**](https://huggingface.co/spaces/build-small-hackathon/naija-solar), a voice-first solar-sizing app that reads your plan aloud in your own language, and it was built for the **Build Small Hackathon** (Gradio × Hugging Face). | |
| 🔊 **[Listen](#-listen)** · 🟢 **[Try it in Naija Solar](https://huggingface.co/spaces/build-small-hackathon/naija-solar)** · 🎥 **[Demo video](https://youtu.be/PfQeRfNof8Y)** · 💻 **[Training code on GitHub](https://github.com/Mystique1337/naija-solar)** | |
| ## 🔊 Listen | |
| Hear each language (cleanest voice per language): | |
| | Language | Voice | Sample | | |
| |---|---|---| | |
| | Yorùbá | `Yor1` | [yor_Yor1.wav](https://huggingface.co/Shinzmann/sorotts/resolve/main/samples/yor_Yor1.wav) | | |
| | Hausa | `Hau1` | [hau_Hau1.wav](https://huggingface.co/Shinzmann/sorotts/resolve/main/samples/hau_Hau1.wav) | | |
| | Igbo | `Ibo1` | [ibo_Ibo1.wav](https://huggingface.co/Shinzmann/sorotts/resolve/main/samples/ibo_Ibo1.wav) | | |
| | Nigerian Pidgin | `NaijaA` | [pcm_NaijaA.wav](https://huggingface.co/Shinzmann/sorotts/resolve/main/samples/pcm_NaijaA.wav) | | |
| ## Why it exists | |
| Off-the-shelf TTS for Nigerian languages is either robotic (Meta MMS) or simply absent. Orpheus is one of the most natural open speech LLMs, but it is English-first and speaks no Nigerian language. SoroTTS closes that gap. It keeps Orpheus's natural prosody while speaking Yorùbá, Hausa, Igbo, and the language no public model spoke before, **Nigerian Pidgin**, the everyday tongue of perhaps a hundred million people. | |
| ## What it is | |
| - **Architecture:** Orpheus-3B, a Llama-3B backbone that predicts [SNAC](https://huggingface.co/hubertsiuzdak/snac_24khz) 24 kHz neural audio codes. About 3B parameters, under the 4B line. | |
| - **Base:** [`hypaai/hypaai_orpheus_v5`](https://huggingface.co/hypaai/hypaai_orpheus_v5) (Hypa-Orpheus), already adapted to Yorùbá, Hausa, and Igbo. SoroTTS **adds Nigerian Pidgin** and reinforces the other three. | |
| - **This repo:** a single **LoRA adapter** (r=64) covering all four languages, plus the tokenizer. Attach it to the base at inference time (see Quickstart). | |
| ## Voices | |
| Each clean data source becomes a named **voice tag** that you prepend to the text. An Orpheus speech LLM conditions on the voice tag, so more clean sources means more coverage. The cleanest source per language is voice `1`: | |
| | Language | Cleanest voice | Other voices | | |
| |---|---|---| | |
| | Yorùbá | `Yor1` | `Yor2`, `Yor3` | | |
| | Hausa | `Hau1` | `Hau2`, ... , `HauBible` (BibleTTS studio) | | |
| | Igbo | `Ibo1` | `Ibo2`, `Ibo3` | | |
| | Nigerian Pidgin | `NaijaA` | `NaijaB`, ... (one per speaker) | | |
| English falls back to a base Orpheus / Hypa voice (e.g. `Eniola`). | |
| > **Tip:** write Yorùbá and Igbo **with diacritics** (tone marks and dots) and Hausa **with hooked letters** (ɓ ɗ ƙ). The model learned diacritised text, so `Ẹ kú àbọ̀` sounds far better than `E ku abo`. | |
| ## 🚀 Quickstart | |
| ```bash | |
| pip install unsloth snac soundfile peft huggingface_hub | |
| ``` | |
| ```python | |
| import os, torch, soundfile as sf | |
| from huggingface_hub import snapshot_download | |
| from unsloth import FastLanguageModel | |
| from peft import PeftModel | |
| from snac import SNAC | |
| # 1) load the base WITHOUT its bundled adapter, then attach SoroTTS | |
| local = snapshot_download("hypaai/hypaai_orpheus_v5", ignore_patterns=["adapter_*"]) | |
| for f in ("adapter_config.json", "adapter_model.safetensors", "adapter_model.bin"): | |
| p = os.path.join(local, f) | |
| if os.path.exists(p): | |
| os.remove(p) | |
| model, tok = FastLanguageModel.from_pretrained(local, max_seq_length=2048, dtype=None, load_in_4bit=False) | |
| model = PeftModel.from_pretrained(model, "Shinzmann/sorotts") # <- the SoroTTS adapter | |
| FastLanguageModel.for_inference(model) | |
| snac = SNAC.from_pretrained("hubertsiuzdak/snac_24khz").to("cuda").eval() | |
| # 2) Orpheus token scheme (must match training) | |
| SOH, EOT, EOH, SOS, EOS, EOAI, OFF = 128259, 128009, 128260, 128257, 128258, 128262, 128266 | |
| DEV = next(snac.parameters()).device | |
| def _decode(codes): | |
| cl = lambda v: 0 if v < 0 else (4095 if v > 4095 else v) # clamp so a stray code can't crash SNAC | |
| l1, l2, l3 = [], [], [] | |
| for i in range((len(codes) + 1) // 7): | |
| b = 7 * i | |
| l1.append(cl(codes[b])); l2.append(cl(codes[b+1] - 4096)) | |
| l3.append(cl(codes[b+2] - 2*4096)); l3.append(cl(codes[b+3] - 3*4096)) | |
| l2.append(cl(codes[b+4] - 4*4096)) | |
| l3.append(cl(codes[b+5] - 5*4096)); l3.append(cl(codes[b+6] - 6*4096)) | |
| c = [torch.tensor(x).unsqueeze(0).to(DEV) for x in (l1, l2, l3)] | |
| with torch.inference_mode(): | |
| return snac.decode(c).squeeze().cpu().numpy() | |
| def speak(text, voice="Yor1", max_new_tokens=1024): | |
| ids = tok(f"{voice}: {text}", return_tensors="pt").input_ids | |
| ids = torch.cat([torch.tensor([[SOH]]), ids, torch.tensor([[EOT, EOH]])], dim=1).to(model.device) | |
| out = model.generate(input_ids=ids, attention_mask=torch.ones_like(ids), | |
| max_new_tokens=max_new_tokens, do_sample=True, temperature=0.55, | |
| top_p=0.95, repetition_penalty=1.1, | |
| eos_token_id=[EOS, EOAI], use_cache=True)[0] # stop on EOS or EOAI | |
| sos = (out == SOS).nonzero(as_tuple=True)[0] | |
| seq = out[sos[-1].item() + 1:] if len(sos) else out | |
| seq = seq[seq >= OFF] # keep only SNAC audio codes | |
| n = (seq.size(0) // 7) * 7 | |
| return _decode([t.item() - OFF for t in seq[:n]]) | |
| wave = speak("Ẹ kú àbọ̀ sí Nàìjíríà, orílẹ̀-èdè wa tó kún fún ìbùkún.", voice="Yor1") | |
| sf.write("out.wav", wave, 24000) | |
| ``` | |
| **Two gotchas worth knowing** (both handled above): | |
| 1. **Stop on `EOS` *or* `EOAI`.** The model often ends a clip with `EOAI` (128262); if you stop only on `EOS` it will run to the token cap and babble. | |
| 2. **Keep only audio codes, and clamp them.** Filter to tokens `>= 128266`, then clamp each SNAC value to `[0, 4095]`. A stray control token left in the stream corrupts the 7-token frame alignment and crashes the SNAC decoder. | |
| Generate **one sentence per call** (each call has a roughly 2048-token, about 15 second budget) and stitch sentences with a short silence for longer text. | |
| ## Training data | |
| **31,574 SNAC-tokenised clips** (0.7 to 15 seconds each), streamed and ranked cleanest-first, across: | |
| | Corpus | Languages | License | Role | | |
| |---|---|---|---| | |
| | [NaijaVoices](https://huggingface.co/datasets/naijavoices/naijavoices-dataset) | yor / hau / ibo | CC-BY-NC-SA | the bulk (about 600h per language available) | | |
| | [WAXAL TTS](https://huggingface.co/datasets/google/WaxalNLP) | yor / hau / ibo | CC-BY | clean single-speaker | | |
| | [FLEURS](https://huggingface.co/datasets/google/fleurs) | yor / hau / ibo | CC-BY | read speech | | |
| | [BibleTTS](https://huggingface.co/datasets/vpetukhov/bible_tts_hausa) | hau | CC-BY-SA | studio 48 kHz (`HauBible`) | | |
| | [Nigerian Pidgin v1.0](https://huggingface.co/datasets/asr-nigerian-pidgin/nigerian-pidgin-1.0) | pcm | CC-BY | the Pidgin anchor (the new language) | | |
| Because NaijaVoices (non-commercial) is included, the model is stamped **`cc-by-nc-sa-4.0`**. A permissive, commercial variant is possible by training on the CC-BY and CC-BY-SA sources only (the training script has a `--commercial-only` flag). | |
| ## How it was trained | |
| - **LoRA** r=64, α=64, dropout 0, on all attention and MLP projections (the Orpheus-TTS standard), via **[Unsloth](https://github.com/unslothai/unsloth)**. Only **97.3M parameters are trained, 2.86%** of the 3.40B-parameter model. | |
| - **2 epochs = 7,894 steps**, bf16, AdamW-8bit, lr 2e-4, 3% warmup, **total batch 8** (8 per device, 1 gradient-accumulation step), on a single **NVIDIA B200**. The run takes about **31 minutes** (1,894 s, roughly 33 samples per second) and converges to a train loss near 3.5. | |
| - Audio is encoded to Orpheus's **7-tokens-per-frame SNAC stream**; each training sequence is | |
| `[SOH] voice: text [EOT][EOH] [SOAI][SOS] <snac codes> [EOS][EOAI]`. | |
| - **Fully reproducible on Modal**: streaming and SNAC-encoding the data, the LoRA training, and the Hub push all run as one serverless job. See [`modal/finetune_orpheus.py`](https://github.com/Mystique1337/naija-solar/blob/main/modal/finetune_orpheus.py) (train), [`serving_tts.py`](https://github.com/Mystique1337/naija-solar/blob/main/modal/serving_tts.py) (serve), and [`test_sorotts.py`](https://github.com/Mystique1337/naija-solar/blob/main/modal/test_sorotts.py) (samples). | |
| ## Intended use and limitations | |
| - **Intended use:** accessibility and voice interfaces in Nigerian languages, reading text aloud, IVR, narration. Built for [Naija Solar](https://huggingface.co/spaces/build-small-hackathon/naija-solar). | |
| - **Speed:** Orpheus is autoregressive (roughly 10 to 25 seconds of compute per sentence on an A100 or H200). Great behind a "written-first" UI; for real time, serve via vLLM. | |
| - **English** is the base model's, not specifically fine-tuned here. | |
| - **Pidgin** has the thinnest data (one clean 4 to 5 hour corpus). It works because Pidgin is English-lexified and Orpheus has a strong English prior, but a dedicated single-speaker corpus would sharpen it. | |
| - **Numbers and units:** like most TTS, it reads digits and abbreviations literally. For natural speech, spell numbers as words (Naija Solar does this per language). | |
| - **Ethics:** this is a voice for accessibility. Do not use it to clone a real person's voice without consent. | |
| ## License | |
| **CC-BY-NC-SA-4.0**, inherited from the NaijaVoices data. The base model and the other corpora carry their own licenses; see each link above. Please attribute SoroTTS and share derivatives alike. | |
| ## Acknowledgements | |
| Built on [Hypa-Orpheus](https://huggingface.co/hypaai/hypaai_orpheus_v5), [Orpheus-TTS](https://github.com/canopyai/Orpheus-TTS), [SNAC](https://huggingface.co/hubertsiuzdak/snac_24khz), and [Unsloth](https://github.com/unslothai/unsloth), with data from NaijaVoices, Google WAXAL and FLEURS, BibleTTS, and the Nigerian Pidgin ASR Corpus. | |
| ## Citation | |
| ```bibtex | |
| @misc{ashinze2026sorotts, | |
| title = {SoroTTS: a natural voice for Yoruba, Hausa, Igbo, and Nigerian Pidgin}, | |
| author = {Ashinze, Emmanuel}, | |
| year = {2026}, | |
| howpublished = {\url{https://huggingface.co/Shinzmann/sorotts}}, | |
| note = {LoRA fine-tune of Orpheus-3B, built for the Build Small Hackathon} | |
| } | |
| ``` | |
| *By Ashinze Emmanuel, for the Build Small Hackathon.* | |