scriptconv 0.0.4a8__tar.gz → 0.0.4a10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scriptconv-0.0.4a8/scriptconv.egg-info → scriptconv-0.0.4a10}/PKG-INFO +3 -2
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/README.md +2 -1
- scriptconv-0.0.4a10/scriptconv/phonemizers/_thirdparty/vosk_g2p.py +135 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/en.py +32 -11
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/enums.py +2 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/ja.py +16 -2
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/ko.py +8 -1
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/mul.py +69 -34
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/pt.py +8 -1
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/registry.py +5 -0
- scriptconv-0.0.4a10/scriptconv/phonemizers/ru.py +114 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/vi.py +8 -1
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/zh.py +8 -1
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/version.py +1 -1
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10/scriptconv.egg-info}/PKG-INFO +3 -2
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv.egg-info/SOURCES.txt +4 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_phonemizers_base.py +4 -2
- scriptconv-0.0.4a10/tests/test_phonemizers_friendly_import_errors.py +129 -0
- scriptconv-0.0.4a10/tests/test_phonemizers_ru.py +218 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/LICENSE +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/pyproject.toml +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/requirements.txt +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/__main__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/cangjie.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/conventions.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/data/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/diacritics.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/graph.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/notation.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/bw2ipa.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/hangul2ipa.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/aspiration.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/assimilation.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/double_coda.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/neutralization.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/tensification.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/espeak.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/frontend.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/normalize.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/shami/phoneme_inventory.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_thirdparty/zh_num.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/kog2p/LICENSE.md +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/kog2p/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/kog2p/rulebook.txt +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/LICENSE.md +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/buck/__init__.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/buck/phonetise_buckwalter.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/buck/symbols.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/buck/tokenization.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/num2words.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/_vendored/mantoq/unicode_symbol2label.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/ar.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/base.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/eu.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/fa.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/gl.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/he.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/mwl.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/o2ipa.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/phonemizers/shami.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/py.typed +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/readings.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/scripts.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv/translit.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv.egg-info/dependency_links.txt +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv.egg-info/requires.txt +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/scriptconv.egg-info/top_level.txt +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/setup.cfg +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_arpa_stress.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_cangjie.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_cli.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_conventions.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_diacritics.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_diacritics_graph.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_errors_policy.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_examples.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_graph.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_notation.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_phonemizers_cjk_ar.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_readings.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_readings_zh.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_scripts.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_scripts_stressonnx_compat.py +0 -0
- {scriptconv-0.0.4a8 → scriptconv-0.0.4a10}/tests/test_translit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4a10
|
|
4
4
|
Summary: Zero-dependency script & phoneme-notation core — ISO-15924 detection & metadata, IPA↔ARPABET/X-SAMPA/Lexique/Kirshenbaum/Cotovía/RFE, Buckwalter↔Arabic, Hangul→jamo, kana
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
|
|
@@ -350,7 +350,8 @@ phonemize("hello", "en", override=Phonemizer.GRUUT)
|
|
|
350
350
|
|
|
351
351
|
Defaults resolve in-house engines first: an explicit per-language chain
|
|
352
352
|
(Arabic → arbtok, Basque → euskaphone, Mirandese, Portuguese → tugaphone,
|
|
353
|
-
Hebrew → phonikud, Galician → Cotovía for
|
|
353
|
+
Hebrew → phonikud, Galician → Cotovía and Russian → vosk for their own
|
|
354
|
+
notations), then
|
|
354
355
|
orthography2ipa wherever it has a language spec, then espeak as the last
|
|
355
356
|
resort. Arabic never falls back past arbtok — a missing engine raises rather
|
|
356
357
|
than silently degrading. Every backend resolves lazily; a missing package
|
|
@@ -223,7 +223,8 @@ phonemize("hello", "en", override=Phonemizer.GRUUT)
|
|
|
223
223
|
|
|
224
224
|
Defaults resolve in-house engines first: an explicit per-language chain
|
|
225
225
|
(Arabic → arbtok, Basque → euskaphone, Mirandese, Portuguese → tugaphone,
|
|
226
|
-
Hebrew → phonikud, Galician → Cotovía for
|
|
226
|
+
Hebrew → phonikud, Galician → Cotovía and Russian → vosk for their own
|
|
227
|
+
notations), then
|
|
227
228
|
orthography2ipa wherever it has a language spec, then espeak as the last
|
|
228
229
|
resort. Arabic never falls back past arbtok — a missing engine raises rather
|
|
229
230
|
than silently degrading. Every backend resolves lazily; a missing package
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Russian grapheme-to-phoneme rules and pronunciation-dictionary loader for the
|
|
3
|
+
Vosk-TTS voices.
|
|
4
|
+
|
|
5
|
+
Vendored from ``vosk_tts/g2p.py`` — https://github.com/alphacep/vosk-tts
|
|
6
|
+
(Apache-2.0) — so the Vosk Russian front-end is available without the
|
|
7
|
+
``vosk-tts`` package at runtime.
|
|
8
|
+
|
|
9
|
+
``convert`` is a faithful port of ``vosk_tts/g2p.py``: it turns an (optionally
|
|
10
|
+
stress-marked) Russian word into the Vosk phoneme inventory — palatalised
|
|
11
|
+
consonants get a trailing ``j`` (``bj``, ``tj`` …), vowels carry a stress digit
|
|
12
|
+
(``a0`` unstressed, ``a1`` stressed). A ``+`` immediately before a vowel marks
|
|
13
|
+
it as stressed; without any ``+`` every vowel is emitted unstressed.
|
|
14
|
+
|
|
15
|
+
The shipped ``dictionary`` file (word → phonemes) overrides the rules for known
|
|
16
|
+
words; ``load_dictionary`` reads both the historical ``word phon…`` layout and
|
|
17
|
+
the newer ``word prob phon…`` layout (keeping the highest-probability variant).
|
|
18
|
+
The rules alone already produce usable Russian, so the dictionary is optional.
|
|
19
|
+
"""
|
|
20
|
+
import os
|
|
21
|
+
from typing import Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
# Cyrillic letters that soften the preceding consonant.
|
|
24
|
+
softletters = set(u"яёюиье")
|
|
25
|
+
# Contexts after which я/ю/е/ё gains a leading glide /j/.
|
|
26
|
+
startsyl = set(u"#ъьаяоёуюэеиы-")
|
|
27
|
+
# Markers dropped from the final phoneme stream.
|
|
28
|
+
others = set(["#", "+", "-", u"ь", u"ъ"])
|
|
29
|
+
|
|
30
|
+
softhard_cons = {
|
|
31
|
+
u"б": u"b", u"в": u"v", u"г": u"g", u"Г": u"g", u"д": u"d",
|
|
32
|
+
u"з": u"z", u"к": u"k", u"л": u"l", u"м": u"m", u"н": u"n",
|
|
33
|
+
u"п": u"p", u"р": u"r", u"с": u"s", u"т": u"t", u"ф": u"f",
|
|
34
|
+
u"х": u"h",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
other_cons = {
|
|
38
|
+
u"ж": u"zh", u"ц": u"c", u"ч": u"ch", u"ш": u"sh",
|
|
39
|
+
u"щ": u"sch", u"й": u"j",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
vowels = {
|
|
43
|
+
u"а": u"a", u"я": u"a", u"у": u"u", u"ю": u"u", u"о": u"o",
|
|
44
|
+
u"ё": u"o", u"э": u"e", u"е": u"e", u"и": u"i", u"ы": u"y",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def pallatize(phones: List[tuple]) -> None:
|
|
49
|
+
"""In-place: map consonants to their (palatalised) phoneme, looking one
|
|
50
|
+
character ahead to decide whether a soft vowel follows."""
|
|
51
|
+
for i, phone in enumerate(phones[:-1]):
|
|
52
|
+
if phone[0] in softhard_cons:
|
|
53
|
+
if phones[i + 1][0] in softletters:
|
|
54
|
+
phones[i] = (softhard_cons[phone[0]] + "j", 0)
|
|
55
|
+
else:
|
|
56
|
+
phones[i] = (softhard_cons[phone[0]], 0)
|
|
57
|
+
if phone[0] in other_cons:
|
|
58
|
+
phones[i] = (other_cons[phone[0]], 0)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def convert_vowels(phones: List[tuple]) -> List[str]:
|
|
62
|
+
"""Emit vowels with their stress digit, inserting a glide /j/ before
|
|
63
|
+
iotated vowels at syllable starts."""
|
|
64
|
+
new_phones: List[str] = []
|
|
65
|
+
prev = ""
|
|
66
|
+
for phone in phones:
|
|
67
|
+
if prev in startsyl:
|
|
68
|
+
if phone[0] in set(u"яюеё"):
|
|
69
|
+
new_phones.append("j")
|
|
70
|
+
if phone[0] in vowels:
|
|
71
|
+
new_phones.append(vowels[phone[0]] + str(phone[1]))
|
|
72
|
+
else:
|
|
73
|
+
new_phones.append(phone[0])
|
|
74
|
+
prev = phone[0]
|
|
75
|
+
return new_phones
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def convert(stressword: str) -> str:
|
|
79
|
+
"""Convert a (possibly ``+``-stress-marked) Russian word to a
|
|
80
|
+
space-separated Vosk phoneme string."""
|
|
81
|
+
phones = ("#" + stressword + "#")
|
|
82
|
+
|
|
83
|
+
# Assign stress marks: a '+' sets the stress flag for the next character.
|
|
84
|
+
stress_phones = []
|
|
85
|
+
stress = 0
|
|
86
|
+
for phone in phones:
|
|
87
|
+
if phone == "+":
|
|
88
|
+
stress = 1
|
|
89
|
+
else:
|
|
90
|
+
stress_phones.append((phone, stress))
|
|
91
|
+
stress = 0
|
|
92
|
+
|
|
93
|
+
pallatize(stress_phones)
|
|
94
|
+
phones = convert_vowels(stress_phones)
|
|
95
|
+
phones = [x for x in phones if x not in others]
|
|
96
|
+
return " ".join(phones)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def load_dictionary(path: Optional[str]) -> Dict[str, List[str]]:
|
|
100
|
+
"""
|
|
101
|
+
Load a Vosk pronunciation dictionary into a ``word -> [phoneme, …]`` map.
|
|
102
|
+
|
|
103
|
+
Handles both file layouts:
|
|
104
|
+
- ``word phon1 phon2 …`` (older voices, e.g. 0.1)
|
|
105
|
+
- ``word prob phon1 phon2 …`` (newer voices; highest prob wins)
|
|
106
|
+
|
|
107
|
+
Returns an empty dict when ``path`` is falsy or missing — the rule-based
|
|
108
|
+
:func:`convert` fallback covers any out-of-dictionary word.
|
|
109
|
+
"""
|
|
110
|
+
dic: Dict[str, List[str]] = {}
|
|
111
|
+
if not path or not os.path.isfile(path):
|
|
112
|
+
return dic
|
|
113
|
+
probs: Dict[str, float] = {}
|
|
114
|
+
with open(path, encoding="utf-8") as f:
|
|
115
|
+
for line in f:
|
|
116
|
+
parts = line.split()
|
|
117
|
+
if len(parts) < 2:
|
|
118
|
+
continue
|
|
119
|
+
word, rest = parts[0], parts[1:]
|
|
120
|
+
# The second column is a probability only when it parses as a float;
|
|
121
|
+
# a real phoneme (a0, sch, …) never does.
|
|
122
|
+
try:
|
|
123
|
+
prob = float(rest[0])
|
|
124
|
+
phones = rest[1:]
|
|
125
|
+
except ValueError:
|
|
126
|
+
prob = None
|
|
127
|
+
phones = rest
|
|
128
|
+
if not phones:
|
|
129
|
+
continue
|
|
130
|
+
if prob is None:
|
|
131
|
+
dic.setdefault(word, phones)
|
|
132
|
+
elif probs.get(word, -1.0) < prob:
|
|
133
|
+
dic[word] = phones
|
|
134
|
+
probs[word] = prob
|
|
135
|
+
return dic
|
|
@@ -16,9 +16,16 @@ class DeepPhonemizer(BasePhonemizer):
|
|
|
16
16
|
def __init__(self, model="latin_ipa_forward.pt"):
|
|
17
17
|
import os
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
try:
|
|
20
|
+
import dp
|
|
21
|
+
from dp.phonemizer import Phonemizer
|
|
22
|
+
import torch
|
|
23
|
+
except ImportError as e:
|
|
24
|
+
raise ImportError(
|
|
25
|
+
"deep-phonemizer and torch are required for the DeepPhonemizer "
|
|
26
|
+
"phonemizer. Install them with 'pip install deep-phonemizer torch' "
|
|
27
|
+
"(or 'pip install scriptconv[en-phonemizers]')."
|
|
28
|
+
) from e
|
|
22
29
|
# needed for latest torch version
|
|
23
30
|
torch.serialization.add_safe_globals([dp.preprocessing.text.Preprocessor])
|
|
24
31
|
torch.serialization.add_safe_globals([dp.preprocessing.text.LanguageTokenizer])
|
|
@@ -90,10 +97,17 @@ class OpenPhonemizer(BasePhonemizer):
|
|
|
90
97
|
"""
|
|
91
98
|
|
|
92
99
|
def __init__(self):
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
try:
|
|
101
|
+
from openphonemizer import OpenPhonemizer
|
|
102
|
+
import torch
|
|
103
|
+
# needed for latest torch version
|
|
104
|
+
import dp
|
|
105
|
+
except ImportError as e:
|
|
106
|
+
raise ImportError(
|
|
107
|
+
"openphonemizer and torch are required for the OpenPhonemizer "
|
|
108
|
+
"phonemizer. Install them with 'pip install openphonemizer torch' "
|
|
109
|
+
"(or 'pip install scriptconv[en-phonemizers]')."
|
|
110
|
+
) from e
|
|
97
111
|
torch.serialization.add_safe_globals([dp.preprocessing.text.Preprocessor])
|
|
98
112
|
torch.serialization.add_safe_globals([dp.preprocessing.text.LanguageTokenizer])
|
|
99
113
|
torch.serialization.add_safe_globals([dp.preprocessing.text.SequenceTokenizer])
|
|
@@ -142,10 +156,17 @@ class G2PEnPhonemizer(BasePhonemizer):
|
|
|
142
156
|
|
|
143
157
|
def __init__(self, alphabet=Alphabet.IPA):
|
|
144
158
|
assert alphabet in [Alphabet.IPA, Alphabet.ARPA]
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
159
|
+
try:
|
|
160
|
+
import nltk
|
|
161
|
+
nltk.download('averaged_perceptron_tagger_eng')
|
|
162
|
+
nltk.download('cmudict')
|
|
163
|
+
from g2p_en import G2p
|
|
164
|
+
except ImportError as e:
|
|
165
|
+
raise ImportError(
|
|
166
|
+
"g2p_en is required for the G2PEn phonemizer. "
|
|
167
|
+
"Install it with 'pip install g2p_en' "
|
|
168
|
+
"(or 'pip install scriptconv[en-phonemizers]')."
|
|
169
|
+
) from e
|
|
149
170
|
self.g2p = G2p()
|
|
150
171
|
super().__init__(alphabet)
|
|
151
172
|
|
|
@@ -30,6 +30,7 @@ class Alphabet(str, Enum):
|
|
|
30
30
|
BUCKWALTER = "buckwalter"
|
|
31
31
|
MANTOQ = "mantoq" # ar — Halabi Arabic-Phonetiser inventory # ar
|
|
32
32
|
CANGJIE = "cangjie" # zh (Cangjie input method)
|
|
33
|
+
VOSK = "vosk" # ru — vosk-tts phoneme inventory (a0, bj, sch ...)
|
|
33
34
|
GRAPHEMES = "graphemes" # plain text / grapheme input (user-side)
|
|
34
35
|
|
|
35
36
|
|
|
@@ -73,6 +74,7 @@ class Phonemizer(str, Enum):
|
|
|
73
74
|
PYPINYIN = "pypinyin" # chinese
|
|
74
75
|
XPINYIN = "xpinyin" # chinese
|
|
75
76
|
JIEBA = "jieba" # chinese (not a real phonemizer!)
|
|
77
|
+
VOSK = "vosk" # russian (no ipa!)
|
|
76
78
|
SHAMI = "shami" # Levantine Arabic / English code-switching (ShamiVITS)
|
|
77
79
|
ARBTOK = "arbtok" # arabic (dialect-aware, undiacritized text; o2i lattice)
|
|
78
80
|
EUSKAPHONE = "euskaphone" # basque (dialect-aware; o2i lattice)
|
|
@@ -40,7 +40,14 @@ class CutletPhonemizer(BasePhonemizer):
|
|
|
40
40
|
# If `use_foreign_spelling` is true, output will use the foreign spelling
|
|
41
41
|
# provided in a UniDic lemma when available. For example, "カツ" will
|
|
42
42
|
# become "cutlet" instead of "katsu".
|
|
43
|
-
|
|
43
|
+
try:
|
|
44
|
+
import cutlet
|
|
45
|
+
except ImportError as e:
|
|
46
|
+
raise ImportError(
|
|
47
|
+
"cutlet is required for the Cutlet phonemizer. "
|
|
48
|
+
"Install it with 'pip install cutlet' "
|
|
49
|
+
"(or 'pip install scriptconv[ja-phonemizers]')."
|
|
50
|
+
) from e
|
|
44
51
|
self.g2p = cutlet.Cutlet(alphabet)
|
|
45
52
|
self.g2p.use_foreign_spelling = use_foreign_spelling
|
|
46
53
|
super().__init__(alphabet)
|
|
@@ -73,7 +80,14 @@ class PyKakasiPhonemizer(BasePhonemizer):
|
|
|
73
80
|
def __init__(self, alphabet=Alphabet.HEPBURN):
|
|
74
81
|
assert alphabet in [Alphabet.HEPBURN, Alphabet.KANA, Alphabet.HIRA]
|
|
75
82
|
# kana, hira, hepburn
|
|
76
|
-
|
|
83
|
+
try:
|
|
84
|
+
import pykakasi
|
|
85
|
+
except ImportError as e:
|
|
86
|
+
raise ImportError(
|
|
87
|
+
"pykakasi is required for the PyKakasi phonemizer. "
|
|
88
|
+
"Install it with 'pip install pykakasi' "
|
|
89
|
+
"(or 'pip install scriptconv[ja-phonemizers]')."
|
|
90
|
+
) from e
|
|
77
91
|
self.g2p = pykakasi.kakasi()
|
|
78
92
|
super().__init__(alphabet)
|
|
79
93
|
|
|
@@ -10,7 +10,14 @@ class G2PKPhonemizer(BasePhonemizer):
|
|
|
10
10
|
def __init__(self, descriptive=True, group_vowels=True, to_syl=True,
|
|
11
11
|
alphabet=Alphabet.IPA):
|
|
12
12
|
assert alphabet in [Alphabet.IPA, Alphabet.HANGUL]
|
|
13
|
-
|
|
13
|
+
try:
|
|
14
|
+
from g2pk import G2p
|
|
15
|
+
except ImportError as e:
|
|
16
|
+
raise ImportError(
|
|
17
|
+
"g2pk is required for the G2PK phonemizer. "
|
|
18
|
+
"Install it with 'pip install g2pk' "
|
|
19
|
+
"(or 'pip install scriptconv[ko]')."
|
|
20
|
+
) from e
|
|
14
21
|
self.g2p = G2p()
|
|
15
22
|
self.descriptive = descriptive
|
|
16
23
|
self.group_vowels = group_vowels
|
|
@@ -435,7 +435,14 @@ class GruutPhonemizer(BasePhonemizer):
|
|
|
435
435
|
Yields lists of word phonemes for each sentence.
|
|
436
436
|
"""
|
|
437
437
|
lang = self.get_lang(lang)
|
|
438
|
-
|
|
438
|
+
try:
|
|
439
|
+
import gruut
|
|
440
|
+
except ImportError as e:
|
|
441
|
+
raise ImportError(
|
|
442
|
+
"gruut is required for the Gruut phonemizer. "
|
|
443
|
+
"Install it with 'pip install gruut' "
|
|
444
|
+
"(or 'pip install scriptconv[gruut]')."
|
|
445
|
+
) from e
|
|
439
446
|
for sentence in gruut.sentences(text, lang=lang):
|
|
440
447
|
sent_phonemes = [w.phonemes for w in sentence if w.phonemes]
|
|
441
448
|
if sentence and not sent_phonemes:
|
|
@@ -601,8 +608,15 @@ class GoruutPhonemizer(BasePhonemizer):
|
|
|
601
608
|
|
|
602
609
|
def __init__(self, remote_url=None):
|
|
603
610
|
super().__init__(Alphabet.IPA)
|
|
604
|
-
|
|
605
|
-
|
|
611
|
+
try:
|
|
612
|
+
from pygoruut.pygoruut import Pygoruut
|
|
613
|
+
from pygoruut.pygoruut_languages import PygoruutLanguages
|
|
614
|
+
except ImportError as e:
|
|
615
|
+
raise ImportError(
|
|
616
|
+
"pygoruut is required for the Goruut phonemizer. "
|
|
617
|
+
"Install it with 'pip install pygoruut' "
|
|
618
|
+
"(or 'pip install scriptconv[goruut]')."
|
|
619
|
+
) from e
|
|
606
620
|
|
|
607
621
|
self.pygoruut_langs = PygoruutLanguages()
|
|
608
622
|
if remote_url is not None:
|
|
@@ -665,7 +679,14 @@ class EpitranPhonemizer(BasePhonemizer):
|
|
|
665
679
|
|
|
666
680
|
def __init__(self):
|
|
667
681
|
super().__init__(Alphabet.IPA)
|
|
668
|
-
|
|
682
|
+
try:
|
|
683
|
+
import epitran
|
|
684
|
+
except ImportError as e:
|
|
685
|
+
raise ImportError(
|
|
686
|
+
"epitran is required for the Epitran phonemizer. "
|
|
687
|
+
"Install it with 'pip install epitran' "
|
|
688
|
+
"(or 'pip install scriptconv[epitran]')."
|
|
689
|
+
) from e
|
|
669
690
|
self.epitran = epitran
|
|
670
691
|
self._epis: Dict[str, epitran.Epitran] = {}
|
|
671
692
|
|
|
@@ -735,35 +756,42 @@ class MisakiPhonemizer(BasePhonemizer):
|
|
|
735
756
|
"""
|
|
736
757
|
lang = self.get_lang(lang)
|
|
737
758
|
|
|
738
|
-
|
|
739
|
-
if
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
759
|
+
try:
|
|
760
|
+
if lang == "zh":
|
|
761
|
+
if self.g2p_zh is None:
|
|
762
|
+
from misaki.zh import ZHG2P
|
|
763
|
+
self.g2p_zh = ZHG2P(version=self.zh_version)
|
|
764
|
+
return self.g2p_zh
|
|
765
|
+
elif lang == "ko":
|
|
766
|
+
if self.g2p_ko is None:
|
|
767
|
+
from misaki.ko import KOG2P
|
|
768
|
+
self.g2p_ko = KOG2P()
|
|
769
|
+
return self.g2p_ko
|
|
770
|
+
elif lang == "vi":
|
|
771
|
+
if self.g2p_vi is None:
|
|
772
|
+
from misaki.vi import VIG2P
|
|
773
|
+
self.g2p_vi = VIG2P()
|
|
774
|
+
return self.g2p_vi
|
|
775
|
+
elif lang == "ja":
|
|
776
|
+
if self.g2p_ja is None:
|
|
777
|
+
from misaki.ja import JAG2P
|
|
778
|
+
self.g2p_ja = JAG2P()
|
|
779
|
+
return self.g2p_ja
|
|
780
|
+
else:
|
|
781
|
+
if self.g2p_en is None:
|
|
782
|
+
from misaki import en
|
|
783
|
+
self.g2p_en = en.G2P()
|
|
784
|
+
if lang == "en-GB":
|
|
785
|
+
self.g2p_en.british = True
|
|
786
|
+
elif lang == "en-US":
|
|
787
|
+
self.g2p_en.british = False
|
|
788
|
+
return self.g2p_en
|
|
789
|
+
except ImportError as e:
|
|
790
|
+
raise ImportError(
|
|
791
|
+
"misaki is required for the Misaki phonemizer. "
|
|
792
|
+
"Install it with 'pip install misaki' "
|
|
793
|
+
"(or 'pip install scriptconv[misaki]')."
|
|
794
|
+
) from e
|
|
767
795
|
|
|
768
796
|
def phonemize_string(self, text: str, lang: str) -> str:
|
|
769
797
|
pho = self._get_phonemizer(lang)
|
|
@@ -1386,7 +1414,14 @@ class TransphonePhonemizer(BasePhonemizer):
|
|
|
1386
1414
|
|
|
1387
1415
|
def __init__(self):
|
|
1388
1416
|
super().__init__(Alphabet.IPA)
|
|
1389
|
-
|
|
1417
|
+
try:
|
|
1418
|
+
from transphone import read_tokenizer
|
|
1419
|
+
except ImportError as e:
|
|
1420
|
+
raise ImportError(
|
|
1421
|
+
"transphone is required for the Transphone phonemizer. "
|
|
1422
|
+
"Install it with 'pip install transphone' "
|
|
1423
|
+
"(or 'pip install scriptconv[transphone]')."
|
|
1424
|
+
) from e
|
|
1390
1425
|
self.read_tokenizer = read_tokenizer
|
|
1391
1426
|
self._models = {}
|
|
1392
1427
|
|
|
@@ -7,7 +7,14 @@ from scriptconv.phonemizers.enums import Alphabet
|
|
|
7
7
|
class TugaphonePhonemizer(BasePhonemizer):
|
|
8
8
|
|
|
9
9
|
def __init__(self):
|
|
10
|
-
|
|
10
|
+
try:
|
|
11
|
+
from tugaphone import TugaPhonemizer
|
|
12
|
+
except ImportError as e:
|
|
13
|
+
raise ImportError(
|
|
14
|
+
"tugaphone is required for the Tugaphone phonemizer. "
|
|
15
|
+
"Install it with 'pip install tugaphone' "
|
|
16
|
+
"(or 'pip install scriptconv[pt-phonemizers]')."
|
|
17
|
+
) from e
|
|
11
18
|
self.tuga = TugaPhonemizer()
|
|
12
19
|
super().__init__(Alphabet.IPA)
|
|
13
20
|
|
|
@@ -71,6 +71,7 @@ PHONEMIZER_REGISTRY: Dict[Phonemizer, Tuple[str, str, Optional[str]]] = {
|
|
|
71
71
|
_P.MANTOQ: (f"{_BASE}.ar", "MantoqPhonemizer", "ar-phonemizers"),
|
|
72
72
|
_P.ARBTOK: (f"{_BASE}.ar", "ArbtokPhonemizer", "ar-phonemizers"),
|
|
73
73
|
_P.SHAMI: (f"{_BASE}.shami", "ShamiPhonemizer", "shami"),
|
|
74
|
+
_P.VOSK: (f"{_BASE}.ru", "VoskPhonemizer", "phonemizers"),
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
|
|
@@ -135,6 +136,7 @@ _EMITS: Dict[Phonemizer, Tuple[Alphabet, ...]] = {
|
|
|
135
136
|
_P.TUGAPHONE: (Alphabet.IPA,),
|
|
136
137
|
_P.PHONIKUD: (Alphabet.IPA,),
|
|
137
138
|
_P.COTOVIA: (Alphabet.COTOVIA,),
|
|
139
|
+
_P.VOSK: (Alphabet.VOSK,),
|
|
138
140
|
_P.ESPEAK: (Alphabet.IPA,),
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -147,6 +149,9 @@ LANG_DEFAULTS: Dict[str, Tuple[Phonemizer, ...]] = {
|
|
|
147
149
|
"pt": (_P.TUGAPHONE,),
|
|
148
150
|
"he": (_P.PHONIKUD,),
|
|
149
151
|
"gl": (_P.COTOVIA, _P.ORTHOGRAPHY2IPA, _P.ESPEAK),
|
|
152
|
+
# vosk emits its own inventory, so it is the Russian default only
|
|
153
|
+
# when that notation is requested (same shape as Cotovía above)
|
|
154
|
+
"ru": (_P.VOSK, _P.ORTHOGRAPHY2IPA, _P.ESPEAK),
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
# Languages whose explicit entry is exhaustive: no generic fallback beyond it.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Russian phonemizers.
|
|
2
|
+
|
|
3
|
+
Currently one backend: the Vosk-TTS front-end, wrapping the vendored
|
|
4
|
+
:mod:`scriptconv.phonemizers._thirdparty.vosk_g2p` rules so the alphacep
|
|
5
|
+
Russian voices can be driven from text without the ``vosk-tts`` package.
|
|
6
|
+
"""
|
|
7
|
+
import re
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
from quebra_frases import sentence_tokenize
|
|
11
|
+
|
|
12
|
+
from scriptconv.phonemizers.base import BasePhonemizer, PhonemizedChunks
|
|
13
|
+
from scriptconv.phonemizers.enums import Alphabet
|
|
14
|
+
from scriptconv.phonemizers._thirdparty.vosk_g2p import convert, load_dictionary
|
|
15
|
+
|
|
16
|
+
__all__ = ["VoskPhonemizer"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class VoskPhonemizer(BasePhonemizer):
|
|
20
|
+
"""
|
|
21
|
+
Russian phonemizer for the Vosk-TTS voices (alphacep).
|
|
22
|
+
|
|
23
|
+
It reproduces ``vosk_tts``'s grapheme-to-phoneme exactly: each word is
|
|
24
|
+
looked up in the voice's pronunciation ``dictionary`` (word -> phonemes),
|
|
25
|
+
falling back to the rule-based
|
|
26
|
+
:func:`~scriptconv.phonemizers._thirdparty.vosk_g2p.convert` for
|
|
27
|
+
out-of-dictionary words. Spaces and punctuation are kept as their own
|
|
28
|
+
tokens (Vosk feeds them to the model as short/long pauses); the BOS ``^`` /
|
|
29
|
+
EOS ``$`` markers and the inter-phoneme blanks belong to the consumer's
|
|
30
|
+
tokenizer, so they are *not* emitted here.
|
|
31
|
+
|
|
32
|
+
The output is the Vosk phoneme inventory (``a0``, ``bj``, ``sch`` …), not
|
|
33
|
+
IPA, so the only supported alphabet is :attr:`Alphabet.VOSK` — like
|
|
34
|
+
Cotovía, this backend is eligible only when its own notation is requested.
|
|
35
|
+
|
|
36
|
+
The dictionary is optional: without it the rules alone still produce usable
|
|
37
|
+
Russian (only the curated stress and exception entries are lost).
|
|
38
|
+
scriptconv never downloads anything — the caller resolves the file and
|
|
39
|
+
passes its path as ``model`` (the registry's ``phonemizer_model`` knob).
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
alphabet (Alphabet): must be :attr:`Alphabet.VOSK`.
|
|
43
|
+
model (Optional[str]): path to the voice's ``dictionary`` file. When
|
|
44
|
+
absent or missing, only the rule-based fallback is used.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Matches the per-character split used by vosk_tts: spaces and punctuation
|
|
48
|
+
# are captured so they survive as standalone pause tokens.
|
|
49
|
+
_SPLIT = re.compile(r'([,.?!;:"() ])')
|
|
50
|
+
|
|
51
|
+
def __init__(self, alphabet: Alphabet = Alphabet.VOSK,
|
|
52
|
+
model: Optional[str] = None):
|
|
53
|
+
if alphabet != Alphabet.VOSK:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"VoskPhonemizer emits the vosk-tts phoneme inventory, not "
|
|
56
|
+
f"{Alphabet(alphabet).value!r} — use Alphabet.VOSK")
|
|
57
|
+
self._dict_path = model
|
|
58
|
+
self._dictionary: Optional[dict] = None # lazy: dictionaries are large
|
|
59
|
+
super().__init__(alphabet)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def dictionary(self) -> dict:
|
|
63
|
+
"""The loaded ``word -> [phoneme, …]`` map (empty without a path)."""
|
|
64
|
+
if self._dictionary is None:
|
|
65
|
+
self._dictionary = load_dictionary(self._dict_path)
|
|
66
|
+
return self._dictionary
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def get_lang(cls, target_lang: str) -> str:
|
|
70
|
+
return cls.match_lang(target_lang, ["ru-RU"])
|
|
71
|
+
|
|
72
|
+
def _g2p_tokens(self, text: str) -> List[str]:
|
|
73
|
+
"""Word/punctuation stream -> Vosk phoneme tokens (no BOS/EOS/blanks)."""
|
|
74
|
+
tokens: List[str] = []
|
|
75
|
+
# the em dash is a pause, and vosk only knows the ASCII hyphen
|
|
76
|
+
text = text.replace("—", "-")
|
|
77
|
+
for word in self._SPLIT.split(text.lower()):
|
|
78
|
+
if word == "":
|
|
79
|
+
continue
|
|
80
|
+
if self._SPLIT.match(word) or word == "-":
|
|
81
|
+
# space or punctuation: kept verbatim as a pause token
|
|
82
|
+
tokens.append(word)
|
|
83
|
+
elif word in self.dictionary:
|
|
84
|
+
tokens.extend(self.dictionary[word])
|
|
85
|
+
else:
|
|
86
|
+
tokens.extend(convert(word).split())
|
|
87
|
+
return tokens
|
|
88
|
+
|
|
89
|
+
def phonemize(self, text: str, lang: str) -> PhonemizedChunks:
|
|
90
|
+
"""Sentence-level lists of Vosk phoneme tokens.
|
|
91
|
+
|
|
92
|
+
Punctuation is preserved (it drives pausing); each sentence becomes one
|
|
93
|
+
synthesis chunk. Multi-character tokens (``sch``, ``bj``, ``a1``) stay
|
|
94
|
+
whole, so :meth:`BasePhonemizer.phonemize`'s per-character split is
|
|
95
|
+
deliberately bypassed.
|
|
96
|
+
"""
|
|
97
|
+
self.get_lang(lang)
|
|
98
|
+
if not text:
|
|
99
|
+
return []
|
|
100
|
+
if self.normalizer is not None:
|
|
101
|
+
text = self.normalizer(text, lang)
|
|
102
|
+
results: PhonemizedChunks = []
|
|
103
|
+
for sentence in sentence_tokenize(text):
|
|
104
|
+
tokens = self._g2p_tokens(sentence)
|
|
105
|
+
if tokens:
|
|
106
|
+
results.append(tokens)
|
|
107
|
+
return results
|
|
108
|
+
|
|
109
|
+
def phonemize_to_list(self, text: str, lang: str) -> List[str]:
|
|
110
|
+
self.get_lang(lang)
|
|
111
|
+
return self._g2p_tokens(text.lower())
|
|
112
|
+
|
|
113
|
+
def phonemize_string(self, text: str, lang: str) -> str:
|
|
114
|
+
return " ".join(self.phonemize_to_list(text, lang))
|
|
@@ -5,7 +5,14 @@ from scriptconv.phonemizers.enums import Alphabet
|
|
|
5
5
|
class VIPhonemePhonemizer(BasePhonemizer):
|
|
6
6
|
"""https://github.com/v-nhandt21/Viphoneme"""
|
|
7
7
|
def __init__(self):
|
|
8
|
-
|
|
8
|
+
try:
|
|
9
|
+
from viphoneme import vi2IPA
|
|
10
|
+
except ImportError as e:
|
|
11
|
+
raise ImportError(
|
|
12
|
+
"viphoneme is required for the VIPhoneme phonemizer. "
|
|
13
|
+
"Install it with 'pip install viphoneme' "
|
|
14
|
+
"(or 'pip install scriptconv[vi]')."
|
|
15
|
+
) from e
|
|
9
16
|
self.g2p = vi2IPA
|
|
10
17
|
super().__init__(Alphabet.IPA)
|
|
11
18
|
|
|
@@ -44,7 +44,14 @@ class JiebaPhonemizer(BasePhonemizer):
|
|
|
44
44
|
Returns:
|
|
45
45
|
str: Tokenized text with words separated by spaces.
|
|
46
46
|
"""
|
|
47
|
-
|
|
47
|
+
try:
|
|
48
|
+
import jieba
|
|
49
|
+
except ImportError as e:
|
|
50
|
+
raise ImportError(
|
|
51
|
+
"jieba is required for the Jieba phonemizer. "
|
|
52
|
+
"Install it with 'pip install jieba' "
|
|
53
|
+
"(or 'pip install scriptconv[zh-phonemizers]')."
|
|
54
|
+
) from e
|
|
48
55
|
lang = self.get_lang(lang)
|
|
49
56
|
seg_list = jieba.cut(text, cut_all=False)
|
|
50
57
|
# NFKC folds full-width digits (e.g. "3") to ASCII so num2str's
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4a10
|
|
4
4
|
Summary: Zero-dependency script & phoneme-notation core — ISO-15924 detection & metadata, IPA↔ARPABET/X-SAMPA/Lexique/Kirshenbaum/Cotovía/RFE, Buckwalter↔Arabic, Hangul→jamo, kana
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
|
|
@@ -350,7 +350,8 @@ phonemize("hello", "en", override=Phonemizer.GRUUT)
|
|
|
350
350
|
|
|
351
351
|
Defaults resolve in-house engines first: an explicit per-language chain
|
|
352
352
|
(Arabic → arbtok, Basque → euskaphone, Mirandese, Portuguese → tugaphone,
|
|
353
|
-
Hebrew → phonikud, Galician → Cotovía for
|
|
353
|
+
Hebrew → phonikud, Galician → Cotovía and Russian → vosk for their own
|
|
354
|
+
notations), then
|
|
354
355
|
orthography2ipa wherever it has a language spec, then espeak as the last
|
|
355
356
|
resort. Arabic never falls back past arbtok — a missing engine raises rather
|
|
356
357
|
than silently degrading. Every backend resolves lazily; a missing package
|