Metadata-Version: 2.4
Name: espeak-english
Version: 0.1.1
Summary: English text to IPA with libespeak-ng 1.51 bundled — no system espeak-ng needed
Author: GhanaNLP
License: GPL-3.0-or-later
Project-URL: Homepage, https://github.com/GhanaNLP/espeak-english
Project-URL: Issues, https://github.com/GhanaNLP/espeak-english/issues
Project-URL: espeak-ng upstream, https://github.com/espeak-ng/espeak-ng
Keywords: espeak,espeak-ng,ipa,phonemes,g2p,tts,english
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: COPYING
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# espeak-english

English text to IPA, with **libespeak-ng bundled**. No `apt install espeak-ng`, no
`brew install`, no system dependency of any kind.

```bash
pip install espeak-english
```

```python
import espeak_english

espeak_english.phonemes("Good morning and welcome to the news.")
# 'ɡˈʊd mˈɔːɹnɪŋ ænd wˈɛlkʌm tə ðə nˈuːz'
```

```bash
espeak-english "The Bank of Ghana raised the policy rate."
# ðə bˈæŋk ʌv ɡˈɑːnɚ ɹˈeɪzd ðə pˈɑːlɪsi ɹˈeɪt
```

660 KB installed: a stripped `libespeak-ng` plus the eight data files English needs, out of the
~31 MB a full espeak-ng install carries for 130 languages.

## Why this exists

TTS and ASR models trained on espeak's phonemes need *exactly* the phonemes they were trained on
at inference time. A front-end that disagrees with training does not raise an error — it produces
fluent, confident, wrong output. The project this was built for measured that once: a front-end
differing from its training targets on 51% of units scored **68.6% phoneme error** where the
matching one scored 25.6%.

That makes espeak a reproducibility problem, and `apt install espeak-ng` an unreliable answer to
it, because which espeak you get depends on your distribution and when you installed it.

## Two things this gets right, both measured

**The version is pinned to espeak-ng 1.51.** espeak's English output is not stable across
releases. Over 1,505 words and sentences from a real corpus:

| against 1.51 | differs on | example |
|---|---|---|
| espeak-ng 1.52 | **7.1%** | `biara` → `biʲˈɑːɹə` (a palatalisation mark 1.51 does not emit) |
| espeak-ng 1.53 | **2.4%** | `before` → `bᵻfˈɔːɹ` instead of `bᵻfˈoːɹ` |

Neither is a bug upstream. Both are wrong *here*. If your model was trained against a different
version, build from that tag instead — `ESPEAK_VERSION=1.52 bash tools/build_espeak.sh` — and
regenerate the golden file. Do not mix.

**The output matches the CLI exactly, which the obvious API call does not.** `espeak_TextToPhonemes`
looks like the right function and agrees with `espeak-ng -q --ipa` on every multi-word input, but
disagrees on isolated function words — the CLI gives `the` → `ðˈə` where it gives `ðə`, differing
on 3.6% of 2,000 single words. The CLI does not use it; it calls `espeak_SetPhonemeTrace` then
`espeak_Synth`, so that is what this does. Measured at **5,023/5,023 identical** to the CLI,
including punctuation, numbers, and empty input.

## Verified per platform, not per release

`tests/golden_en_us_1.51.tsv` is the output of `espeak-ng -q --ipa -v en-us` at 1.51 on the machine
that produced a real model's training targets. It is external ground truth, not a snapshot of this
package — if a test fails, the build is wrong, never the fixture.

Every platform compiles espeak separately, so CI installs each wheel and runs the suite against
that file from outside the source tree. Nothing else would catch a build whose optimisation or
locale handling shifted a vowel.

Wheels: Linux x86_64/aarch64, macOS x86_64/arm64, Windows x86_64. Python 3.9+, one wheel per
platform — there is no extension module here, only `ctypes`.

## Two traps this avoids

**Only `en` and not `en-US`.** They are separate data files, and shipping just `en` makes
`SetVoiceByName("en-us")` fail. The tempting recovery — fall back to `en` — is British English:
measured **52.4% different** from `en-us` across 2,000 words (`nˈəʊ`/`nˈoʊ`, `ˈand`/`ˈænd`,
`fˈɔː`/`fˈɔːɹ`). Both files ship, and a test asserts the two voices still disagree, because if they
ever agree the en-US data was not loaded.

**Assuming the data path is discoverable.** espeak has a path compiled in at build time and also
reads `ESPEAK_DATA_PATH`. A relocated bundle can honour neither: the library reports the *build
machine's* path and exits, killing the host process with no traceback. (This is exactly how
`espeakng-loader` fails.) The path is passed explicitly to `espeak_Initialize`.

**Assuming a long install path works.** espeak copies `<path>/espeak-ng-data` into a fixed
160-byte buffer without checking whether it fit, so a path over ~144 characters is truncated —
measured: 135 works, 145 does not. The truncated path then fails espeak's own existence check, so
it falls back to the build-time path and reports *that* as missing. You get
`Error processing file '/tmp/tmp.XXXX/install/share/espeak-ng-data/phontab'` — a directory on a
machine you have never used — with nothing suggesting the real cause is the length of your own
install path. A virtualenv inside a project inside a home directory hits 168 characters easily;
the one this was found in did. When the path is too long, the data is exposed through a short
symlink; short paths are used directly and create nothing.

## API

```python
espeak_english.phonemes(text, voice="en-us")   # -> str, IPA
espeak_english.library_path()                  # -> Path to the bundled .so/.dylib/.dll
espeak_english.data_path()                     # -> Path to the bundled espeak-ng-data
espeak_english.ESPEAK_VERSION                  # "1.51"
```

`voice` accepts `en-us` and `en`. Calls are serialised behind a lock: espeak keeps voice and
translator state in file-scope globals, so concurrent callers would interleave into each other's
output rather than fail. Phonemising is microseconds, so this costs nothing.

## Licence

**GPL-3.0-or-later**, because the wheel contains espeak-ng, which is GPL-3.0.

That is the entire reason this is a separate package rather than code inside the library that
needs it: a project can depend on it *optionally* and decide the licensing question for itself,
instead of having the obligation arrive attached to something MIT.

espeak-ng is © Reece Dunn and contributors — https://github.com/espeak-ng/espeak-ng
