jcode/skills/speech-synthesis/SKILL.md

85 lines
3.9 KiB
Markdown

---
name: speech-synthesis
description: >
Text-to-speech with Kokoro-82M (open weights, runs on CPU): synthesize WAV
files with a female or male voice, choose British/American English and other
languages, blend voices, set the speed, batch long texts, and play or embed
the result. Use for any TTS, voice-over, audiobook, speaking assistant or
"read this aloud" task.
---
# Speech synthesis with Kokoro
Kokoro-82M is a small, fast TTS model (Apache 2.0; ~330 MB download once).
Use the ready-made script [tts.py](./tts.py) - copy it into the project
(`cp <skill-dir>/tts.py .`) or import `synthesize()` from it.
## Setup
```
pip install kokoro soundfile # torch comes with it; first run downloads the weights
apt install espeak-ng / brew install espeak-ng # only for non-English languages
```
Python 3.10-3.12 recommended. CPU is fine: ~1 s of audio per 0.1-0.3 s compute.
A GPU is used automatically if torch sees one.
## Female and male voice - the two calls that matter
```python
from kokoro import KPipeline
import soundfile as sf, numpy as np
pipeline = KPipeline(lang_code='a') # 'a' American English, 'b' British English
# feminine voice
audio = np.concatenate([a for _, _, a in pipeline("Good morning! The report is ready.", voice='af_heart')])
sf.write('female.wav', audio, 24000)
# masculine voice
audio = np.concatenate([a for _, _, a in pipeline("Good morning! The report is ready.", voice='am_michael')])
sf.write('male.wav', audio, 24000)
```
`pipeline(text, voice=..., speed=1.0)` is a generator: one `(graphemes,
phonemes, audio)` item per chunk (chunks split at newlines by default). Audio
is a float32 array at **24 kHz**. Voices: `af_*`/`am_*` American female/male,
`bf_*`/`bm_*` British. Good defaults: `af_heart` (F), `am_michael` (M),
`bf_emma` (F, British), `bm_george` (M, British). `python tts.py --list-voices`
prints all.
## Script
```
python tts.py "Hello there." --out hello.wav # af_heart (female)
python tts.py "Hello there." --voice am_michael --out hello_m.wav
python tts.py --file chapter.txt --voice bf_emma --lang b --speed 0.9 --out chapter.wav
python tts.py "Blend." --voice af_heart,am_michael --out blend.wav # average of two voices
```
`synthesize(text, voice, lang, speed) -> (samples, 24000)` splits at blank
lines and sentence ends, inserts 250 ms pauses and concatenates - no length limit.
## Languages
Kokoro-82M v1.0: `a` American English, `b` British English, `e` Spanish,
`f` French, `h` Hindi, `i` Italian, `j` Japanese, `p` Brazilian Portuguese,
`z` Mandarin (`pip install misaki[ja]` / `misaki[zh]` for j/z). **No German.**
For German use Piper (`pip install piper-tts`, voice `de_DE-thorsten-medium`)
and say so in the summary instead of feeding German text to an English voice.
## Using the audio
- Play: `pip install sounddevice` then `sd.play(samples, 24000); sd.wait()`,
or `python tts.py ... --play`.
- MP3/OGG: write WAV, convert with `ffmpeg -i out.wav -b:a 96k out.mp3`.
- Web page: serve the WAV and use `<audio controls src="out.wav">`; for an
in-browser TTS without Python see kokoro-js (`kokoro-js` npm, WebGPU/WASM).
- Verify: check the duration (`len(samples)/24000`) is plausible for the text
(~2.5 words/s) and that `np.abs(samples).max() > 0.05` (not silence); for a
listening check write a short sample and tell the user the file name.
## Pitfalls
- First run is slow (weight download + model load); keep ONE `KPipeline` per
language and reuse it (`tts.py` caches it).
- Text with no sentence punctuation is spoken as one breathless chunk - add
periods or newlines.
- Numbers, dates and abbreviations are read as written; expand them in the
text ("3 km" -> "three kilometres") for clean output.
- Empty audio / `None` in the generator: the chunk was empty or unpronounceable
(only symbols). Skip it, don't crash.
- `numpy` must be < 2.0 for current kokoro releases (`pip install "numpy<2"` if
the import fails).