voxi-tts / README.md
Joshuant's picture
Add voxi_speak.py ([emotion] tag wrapper) + document tag synthesis
0e56fa9 verified
|
Raw
History Blame Contribute Delete
4.49 kB
---
license: apache-2.0
language:
- en
library_name: kokoro
pipeline_tag: text-to-speech
base_model: hexgrad/Kokoro-82M
tags:
- text-to-speech
- tts
- kokoro
- styletts2
- expressive
- emotional-tts
- british-english
---
# Voxi-TTS — Expressive Kokoro (British English, 8 emotions)
An expressive fine-tune of [**Kokoro-82M**](https://huggingface.co/hexgrad/Kokoro-82M) that speaks British English in **8 selectable emotional styles**, each exposed as its own voicepack.
| Emotion voicepacks |
|---|
| `bf_neutral` · `bf_happy` · `bf_sad` · `bf_angry` · `bf_excited` · `bf_disgust` · `bf_sarcastic` · `bf_surprised` |
(`bf_` = British female, matching the source speaker.)
## How it was trained
- **Base:** Kokoro-82M (StyleTTS2 + ISTFTNet decoder, 82M params)
- **Recipe:** two-stage StyleTTS2 fine-tune via [`semidark/kikiri-tts`](https://github.com/semidark/kikiri-tts) (patched StyleTTS2)
- **Data:** [EMNS — Emotive Narrative Storytelling Corpus](https://www.openslr.org/136/) (OpenSLR 136, Apache-2.0), single British-English speaker, ~1.9h, 8 balanced emotions
- **Approach:** each emotion treated as a distinct speaker (`multispeaker`), so the style space separates per emotion; one voicepack extracted per emotion
- **Hardware:** NVIDIA A100-40GB, fp32, batch 4. Stage 1: 12 epochs (Mel 0.47→0.35). Stage 2: 24 epochs, adversarial SLM from epoch 3 (F0 10.0→3.6, Dur 1.5→0.83).
## Files
| Path | What |
|---|---|
| `kokoro_voxi_v1.pth` | Converted Kokoro-format **inference** weights (use this) |
| `voices/bf_*.pt` | The 8 emotion voicepacks |
| `config.json` | Kokoro model config |
| `speaker_map.json` | emotion ↔ training speaker-id map |
| `checkpoints/epoch_2nd_00022.pth` | Full Stage-2 training checkpoint (to resume) |
| `checkpoints/first_stage.pth` | Stage-1 checkpoint (also used as style-encoder source for voicepack extraction) |
## Usage
```python
import torch, numpy as np, soundfile as sf
from huggingface_hub import hf_hub_download
from kokoro import KModel, KPipeline
repo = "Joshuant/voxi-tts"
model_path = hf_hub_download(repo, "kokoro_voxi_v1.pth")
config_path = hf_hub_download(repo, "config.json")
voice_path = hf_hub_download(repo, "voices/bf_angry.pt") # pick an emotion
km = KModel(repo_id="hexgrad/Kokoro-82M", config=config_path, model=model_path).eval()
pipe = KPipeline(lang_code="b", repo_id="hexgrad/Kokoro-82M", model=km) # b = British English
voice = torch.load(voice_path, map_location="cpu", weights_only=True)
audio = np.concatenate([a for _, _, a in pipe("I can't believe you actually did that.", voice=voice)])
sf.write("out.wav", audio, 24000)
```
Switch emotion by loading a different `voices/bf_<emotion>.pt`.
## Expressive multi-emotion synthesis — inline `[emotion]` tags
The model has no SSML/tag parsing on its own (emotion = which voicepack you load). The included **`voxi_speak.py`** wrapper adds inline `[emotion]` tags by switching voicepacks per span and stitching the audio:
```python
from voxi_speak import VoxiSpeaker
voxi = VoxiSpeaker() # auto-downloads this repo from the Hub
audio = voxi.speak("[happy] I'm so glad you came! [sad] But now you must go. [angry] And you didn't even tell me!")
voxi.save("out.wav", audio)
```
```bash
python voxi_speak.py "[angry] Get out! [neutral] ...please." -o out.wav
python voxi_speak.py --list # emotions + aliases
```
- Tags are **case-insensitive** and support aliases: `[joy]`→happy, `[anger]`→angry, `[surprise]`→surprised, `[disgusted]`→disgust, `[sarcasm]`→sarcastic, etc.
- Text before the first tag uses `default` (neutral). Unknown tags are ignored with a warning (the current emotion continues).
- A small configurable silence (`gap_ms`) is inserted between emotion spans.
Available emotions: `neutral · happy · sad · angry · excited · disgust · sarcastic · surprised`.
## Notes & limitations
- Trained on ~1.9h from a **single speaker** — it's one expressive British voice, not multi-speaker.
- Emotion intensity varies; lower-resource emotions (e.g. `sarcastic`, `disgust`) may be subtler.
- Inference needs `misaki` with `phonemizer-fork` + `espeakng_loader`, and `lang_code="b"`.
## Credits & licenses
- Base model: [Kokoro-82M](https://huggingface.co/hexgrad/Kokoro-82M) (Apache-2.0)
- Training recipe: [kikiri-tts](https://github.com/semidark/kikiri-tts)
- Dataset: EMNS (OpenSLR 136, Apache-2.0) — Kari Noriy, Xiaosong Yang, Jian Zhang (2023)
Released under **Apache-2.0**.