Multi-language pronunciation¶
FaceFX ships an Additional Language Framework: drop-in dictionaries declaring a phonetic alphabet and locale, rule-based pronouncers that see word context, and custom tokenizers so non-Latin scripts survive. OpenFaceFX was hard-wired to English; issue #8 formalizes the grapheme-to-phoneme seam into a protocol and adds that machinery — every hook is opt-in, so the default English path is byte-identical to before.
The seam is a protocol¶
Pronouncer is a typing.Protocol — a
tokenizer (tokenize(text) -> words) plus a word/phrase grapheme-to-phoneme
map. The existing G2P is the English implementation; a
locale can supply its own conformer.
IPA / SAMPA aliases¶
Every one of the 39 internal ARPAbet phonemes has an IPA and an X-SAMPA alias
(phonemes.IPA_ALIASES / SAMPA_ALIASES), exposed for display and used by the
loader. The maps are bijections, so internal -> alias -> internal
round-trips exactly (to_ipa/from_ipa, to_sampa/from_sampa,
from_alphabet).
Dictionaries¶
read_dictionary parses a .dict file
whose header declares the locale and phoneme alphabet (arpabet | ipa |
sampa); each entry's phonemes are mapped into the internal inventory, so a
loaded IPA or SAMPA dictionary drives the same downstream pipeline:
g = G2P()
g.load_dictionary("toy.dict") # adds mapped entries; first spelling wins
naive_segments("ka ki", 2.0, g2p=g) # a normal track, via the naive path
Pronouncer hook¶
Set G2P(pronouncer=fn) with a
PronouncerHook —
callable(word, prev, next) -> phonemes | None. It is consulted between
dictionary lookup and the rule fallback (FaceFX's lookup → pronouncer → rules
order) and receives the correct previous/next word context (None at the
utterance edges); returning None defers to the rules. This is how Czech/Polish
are done entirely in code in FaceFX.
Pluggable tokenizer¶
Set G2P(tokenizer=fn) with a callable(text) -> words. The English default is
re.findall(r"[A-Za-z']+", text), which drops non-Latin script; a per-language
tokenizer (e.g. str.split, or a kana/Hangul splitter) keeps every token.
Out-of-inventory phonemes¶
A phoneme with no internal equivalent passes through and falls to sil at the
viseme stage (the documented PHONEME_TO_VISEME behaviour) — an unmapped symbol
is silent, never a crash. Map it (extend the alias tables or the mapping) to give
it a mouth shape.
openfacefx.pronounce
¶
Multi-language pronunciation framework (issue #8).
FaceFX ships an Additional Language Framework: drop-in dictionaries that declare
a phonetic alphabet (arpabet / ipa / sampa) and a locale, rule-based pronouncers
that receive previous/current/next word context, and per-language tokenizers so
non-Latin scripts survive. OpenFaceFX was hard-wired to English. This module
formalizes the grapheme-to-phoneme seam into a small protocol and adds the
additive machinery around it — every hook is opt-in, so the default English path
(:class:openfacefx.g2p.G2P with no dictionary, pronouncer or custom tokenizer)
is byte-identical to before.
- :class:
Pronouncer— the protocol: a tokenizer (text -> words) plus a word/phrase grapheme-to-phoneme map.G2Pis the English implementation. - :func:
read_dictionary— load a.dictfile declaringlocaleandalphabet, mapping each entry into the internal ARPAbet inventory via the IPA/SAMPA alias tables in :mod:openfacefx.phonemes. - :data:
PronouncerHook— thecallable(word, prev, next) -> phonemes | Noneconsulted between dictionary lookup and the rule fallback, mirroring FaceFX's lookup → pronouncer → rules order.
A phoneme that maps to nothing in the internal inventory passes through and falls
to sil at the viseme stage (the documented PHONEME_TO_VISEME behaviour), so
an unmapped symbol is silent, never a crash.
PronouncerHook = Callable[[str, Optional[str], Optional[str]], Optional[List[str]]]
module-attribute
¶
Tokenizer = Callable[[str], List[str]]
module-attribute
¶
Pronouncer
¶
Bases: Protocol
The grapheme-to-phoneme seam: a tokenizer plus word/phrase resolution.
:class:openfacefx.g2p.G2P is the English implementation; a locale can supply
its own conformer.
Dictionary(locale: str = '', alphabet: str = 'arpabet', entries: Dict[str, List[str]] = dict())
dataclass
¶
A parsed pronunciation dictionary: a locale tag, the source phoneme
alphabet and entries already mapped into the internal inventory.
read_dictionary(path: str) -> Dictionary
¶
Parse a pronunciation .dict file into a :class:Dictionary.
Header lines (;;; locale = ja-JP / ;;; alphabet = ipa) declare the
locale and phoneme alphabet; each remaining line is word p1 p2 ... with
the phonemes written in that alphabet. Every phoneme is mapped into the
internal ARPAbet inventory via the alias tables, so a loaded IPA or SAMPA
dictionary drives the same downstream pipeline. The first spelling of a
repeated word wins (as CMUdict primary pronunciations do).