Metadata-Version: 2.4
Name: poto-tts
Version: 0.3.0
Summary: Ghanaian English speech synthesis on sherpa-onnx: several engines, one Ghanaian espeak-ng dictionary, cross-platform voice files
Author: GhanaNLP
License: MIT License
        
        Copyright (c) 2026 GhanaNLP
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/GhanaNLP/poto-tts
Project-URL: Source, https://github.com/GhanaNLP/poto-tts
Project-URL: Issues, https://github.com/GhanaNLP/poto-tts/issues
Project-URL: Models, https://huggingface.co/ghananlpcommunity/poto-tts-kokoro-gh
Project-URL: Trainer, https://github.com/OHF-voice/piper1-gpl
Keywords: ghana,english,tts,speech-synthesis,piper,vits,sherpa-onnx,onnx,ipa,espeak-ng,accent
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Natural Language :: English
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sherpa-onnx>=1.13.3
Requires-Dist: huggingface_hub>=0.24
Requires-Dist: numpy>=1.21
Provides-Extra: lexicon
Requires-Dist: ghana-english-g2p>=0.1.0; extra == "lexicon"
Requires-Dist: espeak-english>=0.1.1; extra == "lexicon"
Provides-Extra: tts
Provides-Extra: api
Requires-Dist: fastapi>=0.110; extra == "api"
Requires-Dist: uvicorn>=0.27; extra == "api"
Requires-Dist: pydantic>=2; extra == "api"
Provides-Extra: dev
Requires-Dist: poto-tts[api,lexicon]; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: onnx>=1.15; extra == "dev"
Provides-Extra: all
Requires-Dist: poto-tts[api,lexicon]; extra == "all"
Dynamic: license-file

# poto-tts

Ghanaian English speech synthesis on [sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx).
A 104,623-word Ghanaian lexicon is **compiled into an espeak-ng dictionary**, so
names, places and titles come out right from plain text.

```
                stock espeak                  poto-tts
Kwabena         kwˈeɪbnə                      kwɑːbˈɪnɑː
Achimota        ɐtʃɪmˈoʊɾə                    ɑːtʃimˈoɾɑː
Okuapenhene     ˈoʊkjuːˌeɪpənhˌiːn            okwɑːpɛnhˈɛnɛ
Nyankpani       nˌaɪɐŋkpˈɑːni                 njɑːŋkpˈɑːni
Gyasi           dʒaɪʲˈɑːsi                    dʒˈɑːsi
```

Ordinary English is untouched: `yesterday` keeps its own stress. Compiling the
lexicon in, rather than looking it up at runtime, means callers ship no lexicon file
and words the lexicon never had are still spoken — espeak's own rules handle them.

```bash
pip install poto-tts
poto-tts "Kwabena went to Achimota" -o out.wav
```

```python
from poto_tts import load

tts = load("kokoro")            # downloads the voice on first use
tts.save("The Okuapenhene met Nana Bawumia", "out.wav")
```

## Backends

| backend | model | commercial use | accent from |
|---|---|---|---|
| `kokoro` (default) | [Kokoro v1.0](https://huggingface.co/hexgrad/Kokoro-82M), Apache-2.0 | **yes** | the dictionary |
| `piper` | trained on Ghanaian broadcast speech | **no** | the weights and the dictionary |

Both read the same `espeak-ng-data`, so either pronounces a Ghanaian name the same
way; what differs is the voice and the licence. The `piper` corpus is broadcast
recordings whose speakers did not consent to being modelled — research and
non-commercial use only. For a commercial Ghanaian voice use `kokoro`, or train
`piper` on recordings you hold rights to (`tools/` takes any dataset).

```python
tts = load("piper", voice="gh_00")
print(tts.licence, tts.commercial_use)
```

## Models

Voices live on the Hub and are fetched through `huggingface_hub`, so downloads are
counted:

| backend | repo |
|---|---|
| `kokoro` | [ghananlpcommunity/poto-tts-kokoro-gh](https://huggingface.co/ghananlpcommunity/poto-tts-kokoro-gh) |
| `piper` | `ghananlpcommunity/poto-tts-piper-gh-16k` *(training)* |

Each repo's `config.json` names its own files, speakers and licence, so a new voice
needs no release of this library:

```python
tts = load("kokoro", repo_id="your-org/your-voice")
```

`POTO_TTS_CACHE` moves model files off `~/.cache`.

## Cross-platform

sherpa-onnx runs on **Android, iOS, WebAssembly, C++, C, Go, C#, Java, Kotlin, Rust,
Dart and Swift**, across x86, arm64, arm32 and RISC-V. Pronunciation lives in a data
directory, not in Python, so a voice is a file set that any of those runtimes loads
directly — offline, no server:

| file | |
|---|---|
| `onnx/model.onnx` | the generator |
| `tokens.txt` | phoneme → id |
| `voices.bin` | speaker embeddings (Kokoro only) |
| `espeak-ng-data/` | **the Ghanaian part. Ship this one.** |

> A stock `espeak-ng-data` leaves a working voice that mispronounces every Ghanaian
> name, with no error. That directory is the deliverable.

Point your platform's sherpa-onnx TTS API at those files — Kotlin `OfflineTts`,
Swift `SherpaOnnxOfflineTts`, the WASM build, or `sherpa-onnx-offline-tts`.
[Upstream docs](https://k2-fsa.github.io/sherpa/onnx/).

## REST API

```bash
pip install 'poto-tts[api]'
poto-tts serve --host 0.0.0.0 --port 8080
curl "localhost:8080/speak?text=Kwabena+went+to+Achimota" -o out.wav
```

| endpoint | |
|---|---|
| `GET /health` | liveness, loaded voices |
| `GET /backends` | engines, licences, commercial-use flags |
| `GET /voices?backend=kokoro` | speaker names, recommended first |
| `GET /platforms` | how to run the same voice off-server |
| `POST /speak` | `{"text", "backend", "voice", "speed"}` → WAV |
| `GET /speak?text=…` | the same, for a browser or curl |

Requests over 2,000 characters are refused rather than truncated
(`POTO_TTS_MAX_CHARS`). For phone apps, ship the files instead — no network, no
latency, no server.

## Changing how a word is said

The dictionary will still miss your grandmother's name. Rebuilding is the supported
fix: it changes the data every runtime loads, so Android and iOS get it too. Needs
the `espeak-ng` binary and the lexicon extra:

```bash
pip install 'poto-tts[lexicon]'
```

```tsv
# my_words.tsv — word<TAB>pronunciation
Darkoa	d a r k o a
Kufuor	=kufu'or
```

```bash
poto-tts dict --out my-espeak-data --extra my_words.tsv
POTO_TTS_ESPEAK_DATA=./my-espeak-data poto-tts "Darkoa met Kufuor" -o out.wav
```

Values are Ghanaian IPA, mapped by the same tables as the packaged lexicon. A
leading `=` passes raw espeak mnemonics through, and is checked: an invalid mnemonic
makes espeak silently discard the rest of an entry, so the build rejects it rather
than shipping half a word. **Your entries override the packaged lexicon**, so the
file corrects entries as well as adding them.

For a quick check without rebuilding:

```bash
poto-tts --phonemes "Nana Addo met Kwame Nkrumah"
```

```python
from poto_tts import GhanaInjector
GhanaInjector(lexicon={"Darkoa": "d a r k o a"})("Darkoa came home")
# "[[dA:rk'oA:]] came home"
```

That is a debugging tool, not a deployment route: Python-only, and Kokoro's frontend
rewrites `:` in its input, which corrupts length marks.

## How the dictionary is built

`ghana-english-g2p`'s Ghanaian IPA is mapped to espeak mnemonics
(`poto_tts/mnemonics.py`) and compiled into espeak's `en_extra`. Entries are written
**only for words that are not ordinary English**, checked against a 370k-word list:
espeak already handles English, including its stress. An earlier version that
entered every word whose pronunciation differed took 93,526 of them and moved the
stress in *yesterday*, *January* and *Wednesday*.

The build verifies its own output — no truncated entries, and espeak's English
unchanged byte for byte. The vowel decisions are in the module's comments: `/a/` uses
`A:` rather than `0`, which espeak reads as a different vowel before `/r/`; `æ`
merges into `/a/`; FACE and GOAT map to the monophthongs espeak's English rules
never emit but its tables define.

## Training a voice

`tools/` holds the pipeline behind the `piper` backend, and takes any dataset:

| stage | |
|---|---|
| `prepare_dataset.py filter` | duration, character set, disfluency, transcript/audio mismatch |
| `prepare_dataset.py embed` | ECAPA speaker embeddings on GPU |
| `prepare_dataset.py cluster` | pseudo-speakers, with per-speaker and total caps |
| `prepare_dataset.py align` | forced alignment, storing word timings |
| `prepare_dataset.py segment` | cut at real pauses, punctuate from the silences |
| `check_alignable.py` | drop utterances that crash `monotonic_align` |
| `train.sh` | Piper VITS at the corpus's native sample rate |
| `export_sherpa.py` | package a voice for sherpa-onnx |

Thresholds there are measured, and the comments record the distributions they came
from — including the ones that were wrong first.

## Credits

[sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx) (k2-fsa) ·
[Kokoro-82M](https://huggingface.co/hexgrad/Kokoro-82M) (hexgrad, Apache-2.0) ·
[piper1-gpl](https://github.com/OHF-voice/piper1-gpl) (OHF Voice) ·
[ghana-english-g2p](https://github.com/GhanaNLP/ghana-english-g2p) (GhanaNLP) ·
[espeak-ng](https://github.com/espeak-ng/espeak-ng)

MIT, except the bundled `espeak-ng-data` (GPL-3.0) and the `piper` voice
(non-commercial).
