phoonnx 0.0.2a1__py3-none-any.whl → 0.1.0__py3-none-any.whl
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.
- phoonnx/config.py +6 -1
- phoonnx/phonemizers/ar.py +65 -9
- phoonnx/phonemizers/base.py +27 -1
- phoonnx/phonemizers/gl.py +56 -3
- phoonnx/phonemizers/he.py +6 -25
- phoonnx/phonemizers/mul.py +617 -4
- phoonnx/thirdparty/bw2ipa.py +66 -0
- phoonnx/thirdparty/hangul2ipa.py +1 -0
- phoonnx/thirdparty/mantoq/__init__.py +1 -26
- phoonnx/thirdparty/phonikud/__init__.py +24 -0
- phoonnx/version.py +7 -3
- phoonnx/voice.py +4 -16
- {phoonnx-0.0.2a1.dist-info → phoonnx-0.1.0.dist-info}/METADATA +9 -2
- {phoonnx-0.0.2a1.dist-info → phoonnx-0.1.0.dist-info}/RECORD +20 -18
- phoonnx_train/export_onnx.py +307 -56
- phoonnx_train/preprocess.py +36 -9
- phoonnx_train/vits/dataset.py +4 -0
- phoonnx_train/vits/lightning.py +3 -3
- {phoonnx-0.0.2a1.dist-info → phoonnx-0.1.0.dist-info}/WHEEL +0 -0
- {phoonnx-0.0.2a1.dist-info → phoonnx-0.1.0.dist-info}/top_level.txt +0 -0
phoonnx/config.py
CHANGED
@@ -6,7 +6,6 @@ from phoonnx.phoneme_ids import (load_phoneme_ids, BlankBetween,
|
|
6
6
|
DEFAULT_BLANK_WORD_TOKEN, DEFAULT_BLANK_TOKEN,
|
7
7
|
DEFAULT_PAD_TOKEN, DEFAULT_BOS_TOKEN, DEFAULT_EOS_TOKEN)
|
8
8
|
|
9
|
-
|
10
9
|
DEFAULT_NOISE_SCALE = 0.667
|
11
10
|
DEFAULT_LENGTH_SCALE = 1.0
|
12
11
|
DEFAULT_NOISE_W_SCALE = 0.8
|
@@ -22,6 +21,8 @@ class Alphabet(str, Enum):
|
|
22
21
|
UNICODE = "unicode"
|
23
22
|
IPA = "ipa"
|
24
23
|
ARPA = "arpa" # en
|
24
|
+
SAMPA = "sampa"
|
25
|
+
XSAMPA = "x-sampa"
|
25
26
|
HANGUL = "hangul" # ko
|
26
27
|
KANA = "kana" # ja
|
27
28
|
HIRA = "hira" # ja
|
@@ -32,6 +33,7 @@ class Alphabet(str, Enum):
|
|
32
33
|
ERAAB = "eraab" # fa
|
33
34
|
COTOVIA = "cotovia" # gl
|
34
35
|
HANZI = "hanzi" # zh
|
36
|
+
BUCKWALTER = "buckwalter" # ar
|
35
37
|
|
36
38
|
|
37
39
|
|
@@ -380,6 +382,9 @@ class SynthesisConfig:
|
|
380
382
|
|
381
383
|
enable_phonetic_spellings: bool = True
|
382
384
|
|
385
|
+
"""for arabic and hebrew models"""
|
386
|
+
add_diacritics: bool = True
|
387
|
+
|
383
388
|
|
384
389
|
def get_phonemizer(phoneme_type: PhonemeType,
|
385
390
|
alphabet: Alphabet = Alphabet.IPA,
|
phoonnx/phonemizers/ar.py
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
+
from phoonnx.config import Alphabet
|
1
2
|
from phoonnx.phonemizers.base import BasePhonemizer
|
3
|
+
from phoonnx.thirdparty.bw2ipa import translate as bw2ipa
|
2
4
|
from phoonnx.thirdparty.mantoq import g2p as mantoq
|
3
|
-
|
5
|
+
|
4
6
|
|
5
7
|
class MantoqPhonemizer(BasePhonemizer):
|
6
8
|
|
7
|
-
def __init__(self):
|
8
|
-
|
9
|
+
def __init__(self, alphabet=Alphabet.BUCKWALTER):
|
10
|
+
if alphabet not in [Alphabet.IPA, Alphabet.BUCKWALTER]:
|
11
|
+
raise ValueError("unsupported alphabet")
|
12
|
+
super().__init__(alphabet)
|
9
13
|
|
10
14
|
@classmethod
|
11
15
|
def get_lang(cls, target_lang: str) -> str:
|
@@ -26,17 +30,69 @@ class MantoqPhonemizer(BasePhonemizer):
|
|
26
30
|
|
27
31
|
def phonemize_string(self, text: str, lang: str = "ar") -> str:
|
28
32
|
"""
|
33
|
+
Phonemizes an Arabic string using the Mantoq G2P tool.
|
34
|
+
If the alphabet is set to IPA, it then converts the result using bw2ipa.
|
29
35
|
"""
|
30
36
|
lang = self.get_lang(lang)
|
37
|
+
# The mantoq function returns a tuple of (normalized_text, phonemes)
|
31
38
|
normalized_text, phonemes = mantoq(text)
|
32
|
-
|
39
|
+
|
40
|
+
# The phonemes are a list of characters, we join them into a string
|
41
|
+
# and replace the word separator token with a space.
|
42
|
+
phonemes = "".join(phonemes).replace("_+_", " ")
|
43
|
+
|
44
|
+
if self.alphabet == Alphabet.IPA:
|
45
|
+
# If the alphabet is IPA, we use the bw2ipa function to translate
|
46
|
+
# the Buckwalter-like phonemes into IPA.
|
47
|
+
return bw2ipa(phonemes)
|
48
|
+
|
49
|
+
# Otherwise, we return the phonemes in the default Mantoq alphabet.
|
50
|
+
return phonemes
|
33
51
|
|
34
52
|
|
35
53
|
if __name__ == "__main__":
|
54
|
+
from phoonnx.phonemizers.mul import EspeakPhonemizer
|
55
|
+
|
56
|
+
espeak = EspeakPhonemizer()
|
57
|
+
|
58
|
+
# Initialize phonemizers for both MANTOQ and IPA alphabets
|
59
|
+
pho_mantoq = MantoqPhonemizer(alphabet=Alphabet.IPA)
|
60
|
+
|
61
|
+
|
62
|
+
def compare(text):
|
63
|
+
print(f"Original Text: {text}")
|
64
|
+
print(f" Mantoq: {pho_mantoq.phonemize_string(text, 'ar')}")
|
65
|
+
print(f" Espeak: {espeak.phonemize_string(text, 'ar')}")
|
66
|
+
|
67
|
+
ts = pho_mantoq.add_diacritics(text, 'ar')
|
68
|
+
print(f"Tashkeel Text: {ts}")
|
69
|
+
print(f" Mantoq: {pho_mantoq.phonemize_string(ts, 'ar')}")
|
70
|
+
print(f" Espeak: {espeak.phonemize_string(ts, 'ar')}")
|
71
|
+
print("\n#########################")
|
72
|
+
|
73
|
+
|
36
74
|
text = "مرحبا بالعالم"
|
37
|
-
|
38
|
-
|
75
|
+
compare(text)
|
76
|
+
|
77
|
+
text = "ذهب الطالب إلى المكتبة لقراءة كتاب عن تاريخ الأندلس."
|
78
|
+
compare(text)
|
79
|
+
|
80
|
+
# 1. Test for gemination of a sun letter (e.g., ash-shams)
|
81
|
+
text = "الشمس"
|
82
|
+
compare(text)
|
83
|
+
|
84
|
+
# 2. Test for long vowels (e.g., 'fil' - elephant)
|
85
|
+
text = "فيل"
|
86
|
+
compare(text)
|
87
|
+
|
88
|
+
# 3. Test for glide (e.g., 'yawm' - day)
|
89
|
+
text = "يوم"
|
90
|
+
compare(text)
|
91
|
+
|
92
|
+
# 4. Test for long vowels (e.g., 'suwr' - wall)
|
93
|
+
text = "سور"
|
94
|
+
compare(text)
|
39
95
|
|
40
|
-
|
41
|
-
|
42
|
-
|
96
|
+
# 5. Test for glide (e.g., 'law' - if)
|
97
|
+
text = "لو"
|
98
|
+
compare(text)
|
phoonnx/phonemizers/base.py
CHANGED
@@ -8,6 +8,8 @@ from langcodes import tag_distance
|
|
8
8
|
from quebra_frases import sentence_tokenize
|
9
9
|
from phoonnx.config import Alphabet
|
10
10
|
from phoonnx.util import normalize
|
11
|
+
from phoonnx.thirdparty.phonikud import PhonikudDiacritizer
|
12
|
+
from phoonnx.thirdparty.tashkeel import TashkeelDiacritizer
|
11
13
|
|
12
14
|
# list of (substring, terminator, end_of_sentence) tuples.
|
13
15
|
TextChunks = List[Tuple[str, str, bool]]
|
@@ -18,10 +20,27 @@ PhonemizedChunks = list[list[str]]
|
|
18
20
|
|
19
21
|
|
20
22
|
class BasePhonemizer(metaclass=abc.ABCMeta):
|
21
|
-
def __init__(self, alphabet: Alphabet = Alphabet.UNICODE
|
23
|
+
def __init__(self, alphabet: Alphabet = Alphabet.UNICODE,
|
24
|
+
taskeen_threshold: Optional[float] = 0.8):
|
22
25
|
super().__init__()
|
23
26
|
self.alphabet = alphabet
|
24
27
|
|
28
|
+
self.taskeen_threshold = taskeen_threshold # arabic only
|
29
|
+
self._tashkeel: Optional[TashkeelDiacritizer] = None
|
30
|
+
self._phonikud: Optional[PhonikudDiacritizer] = None # hebrew only
|
31
|
+
|
32
|
+
@property
|
33
|
+
def phonikud(self) -> PhonikudDiacritizer:
|
34
|
+
if self._phonikud is None:
|
35
|
+
self._phonikud = PhonikudDiacritizer()
|
36
|
+
return self._phonikud
|
37
|
+
|
38
|
+
@property
|
39
|
+
def tashkeel(self) -> TashkeelDiacritizer:
|
40
|
+
if self._tashkeel is None:
|
41
|
+
self._tashkeel = TashkeelDiacritizer()
|
42
|
+
return self._tashkeel
|
43
|
+
|
25
44
|
@abc.abstractmethod
|
26
45
|
def phonemize_string(self, text: str, lang: str) -> str:
|
27
46
|
raise NotImplementedError
|
@@ -29,6 +48,13 @@ class BasePhonemizer(metaclass=abc.ABCMeta):
|
|
29
48
|
def phonemize_to_list(self, text: str, lang: str) -> List[str]:
|
30
49
|
return list(self.phonemize_string(text, lang))
|
31
50
|
|
51
|
+
def add_diacritics(self, text: str, lang: str) -> str:
|
52
|
+
if lang.startswith("he"):
|
53
|
+
return self.phonikud.diacritize(text)
|
54
|
+
elif lang.startswith("ar"):
|
55
|
+
return self.tashkeel.diacritize(text, self.taskeen_threshold)
|
56
|
+
return text
|
57
|
+
|
32
58
|
def phonemize(self, text: str, lang: str) -> PhonemizedChunks:
|
33
59
|
if not text:
|
34
60
|
return [('', '', True)]
|
phoonnx/phonemizers/gl.py
CHANGED
@@ -12,6 +12,57 @@ class CotoviaError(Exception):
|
|
12
12
|
pass
|
13
13
|
|
14
14
|
|
15
|
+
COTOVIA2IPA = {
|
16
|
+
"pau": " ",
|
17
|
+
"a": "a",
|
18
|
+
"E": "ɛ",
|
19
|
+
"e": "e",
|
20
|
+
"i": "i",
|
21
|
+
"j": "j",
|
22
|
+
"O": "ɔ",
|
23
|
+
"o": "o",
|
24
|
+
"u": "u",
|
25
|
+
"w": "w",
|
26
|
+
"p": "p",
|
27
|
+
"b": "b",
|
28
|
+
"B": "β",
|
29
|
+
"t": "t",
|
30
|
+
"d": "d",
|
31
|
+
"D": "ð",
|
32
|
+
"k": "k",
|
33
|
+
"g": "g",
|
34
|
+
"G": "ɣ",
|
35
|
+
"f": "f",
|
36
|
+
"T": "θ",
|
37
|
+
"s": "s",
|
38
|
+
"S": "ʃ",
|
39
|
+
"tS": "tʃ",
|
40
|
+
"m": "m",
|
41
|
+
"n": "n",
|
42
|
+
"J": "ɲ",
|
43
|
+
"N": "ŋ",
|
44
|
+
"l": "l",
|
45
|
+
"Z": "ʎ",
|
46
|
+
"jj": "ʎ",
|
47
|
+
"L": "ʎ",
|
48
|
+
"r": "ɾ",
|
49
|
+
"rr": "r",
|
50
|
+
"X": "x"
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
def cotovia2ipa(text: str) -> str:
|
55
|
+
"""
|
56
|
+
Converts a string of Cotovía phonemes to IPA.
|
57
|
+
"""
|
58
|
+
# Sort the dictionary keys by length in descending order to handle multi-character phonemes first
|
59
|
+
sorted_cotovia_keys = sorted(COTOVIA2IPA.keys(), key=len, reverse=True)
|
60
|
+
ipa_str = text
|
61
|
+
for cotovia_char in sorted_cotovia_keys:
|
62
|
+
ipa_str = ipa_str.replace(cotovia_char, COTOVIA2IPA[cotovia_char])
|
63
|
+
return ipa_str
|
64
|
+
|
65
|
+
|
15
66
|
class CotoviaPhonemizer(BasePhonemizer):
|
16
67
|
"""
|
17
68
|
A phonemizer class that uses the Cotovia TTS binary to convert text into phonemes.
|
@@ -19,7 +70,7 @@ class CotoviaPhonemizer(BasePhonemizer):
|
|
19
70
|
regular expression transformations to clean and normalize the phonetic representation.
|
20
71
|
"""
|
21
72
|
|
22
|
-
def __init__(self, cotovia_bin_path: Optional[str] = None):
|
73
|
+
def __init__(self, cotovia_bin_path: Optional[str] = None, alphabet: Alphabet = Alphabet.IPA):
|
23
74
|
"""
|
24
75
|
Initializes the CotoviaPhonemizer.
|
25
76
|
|
@@ -31,7 +82,7 @@ class CotoviaPhonemizer(BasePhonemizer):
|
|
31
82
|
if not os.path.exists(self.cotovia_bin):
|
32
83
|
raise FileNotFoundError(f"Cotovia binary not found at {self.cotovia_bin}. "
|
33
84
|
"Please ensure it's installed or provide the correct path.")
|
34
|
-
super().__init__(
|
85
|
+
super().__init__(alphabet)
|
35
86
|
|
36
87
|
@classmethod
|
37
88
|
def get_lang(cls, target_lang: str) -> str:
|
@@ -127,6 +178,8 @@ class CotoviaPhonemizer(BasePhonemizer):
|
|
127
178
|
# substitute ' ( text )' to ', text,'
|
128
179
|
str_ext = re.sub(r"(\w+)\s*\(\s*([^\(\)]*?)\s*\)", r"\1, \\2,", str_ext)
|
129
180
|
|
181
|
+
if self.alphabet == Alphabet.IPA:
|
182
|
+
return cotovia2ipa(str_ext)
|
130
183
|
return str_ext
|
131
184
|
|
132
185
|
|
@@ -138,5 +191,5 @@ if __name__ == "__main__":
|
|
138
191
|
lang = "gl"
|
139
192
|
text_gl = "Este é un sistema de conversión de texto a voz en lingua galega baseado en redes neuronais artificiais. Ten en conta que as funcionalidades incluídas nesta páxina ofrécense unicamente con fins de demostración. Se tes algún comentario, suxestión ou detectas algún problema durante a demostración, ponte en contacto connosco."
|
140
193
|
print(f"\n--- Getting phonemes for '{text_gl}' (Cotovia) ---")
|
141
|
-
phonemes_cotovia = cotovia.
|
194
|
+
phonemes_cotovia = cotovia.phonemize_string(text_gl, lang)
|
142
195
|
print(f" Cotovia Phonemes: {phonemes_cotovia}")
|
phoonnx/phonemizers/he.py
CHANGED
@@ -1,30 +1,12 @@
|
|
1
|
-
import os.path
|
2
|
-
|
3
|
-
import requests
|
4
|
-
|
5
|
-
from phoonnx.phonemizers.base import BasePhonemizer
|
6
1
|
from phoonnx.config import Alphabet
|
2
|
+
from phoonnx.phonemizers.base import BasePhonemizer
|
7
3
|
|
8
4
|
|
9
5
|
class PhonikudPhonemizer(BasePhonemizer):
|
10
|
-
dl_url = "https://huggingface.co/thewh1teagle/phonikud-onnx/resolve/main/phonikud-1.0.int8.onnx"
|
11
6
|
|
12
|
-
def __init__(self
|
13
|
-
from phonikud_onnx import Phonikud
|
7
|
+
def __init__(self):
|
14
8
|
from phonikud import phonemize
|
15
9
|
self.g2p = phonemize
|
16
|
-
self.diacritics = diacritics
|
17
|
-
if model is None:
|
18
|
-
base_path = os.path.expanduser("~/.local/share/phonikud")
|
19
|
-
fname = self.dl_url.split("/")[-1]
|
20
|
-
model = f"{base_path}/{fname}"
|
21
|
-
if not os.path.isfile(model):
|
22
|
-
os.makedirs(base_path, exist_ok=True)
|
23
|
-
# TODO - streaming download
|
24
|
-
data = requests.get(self.dl_url).content
|
25
|
-
with open(model, "wb") as f:
|
26
|
-
f.write(data)
|
27
|
-
self.phonikud = Phonikud(model) if diacritics else None
|
28
10
|
super().__init__(Alphabet.IPA)
|
29
11
|
|
30
12
|
@classmethod
|
@@ -48,20 +30,19 @@ class PhonikudPhonemizer(BasePhonemizer):
|
|
48
30
|
"""
|
49
31
|
"""
|
50
32
|
lang = self.get_lang(lang)
|
51
|
-
if self.diacritics:
|
52
|
-
text = self.phonikud.add_diacritics(text)
|
53
33
|
return self.g2p(text)
|
54
34
|
|
55
35
|
|
56
36
|
if __name__ == "__main__":
|
57
|
-
#text = "מתכת יקרה"
|
37
|
+
# text = "מתכת יקרה"
|
58
38
|
text = 'שָׁלוֹם עוֹלָם'
|
59
39
|
|
60
|
-
pho = PhonikudPhonemizer(
|
40
|
+
pho = PhonikudPhonemizer()
|
61
41
|
lang = "he"
|
62
42
|
|
63
43
|
print(f"\n--- Getting phonemes for '{text}' ---")
|
44
|
+
# text = pho.add_diacritics(text, lang)
|
64
45
|
phonemes = pho.phonemize(text, lang)
|
65
46
|
print(f" Phonemes: {phonemes}")
|
66
47
|
# --- Getting phonemes for 'שָׁלוֹם עוֹלָם' ---
|
67
|
-
# Phonemes: [('ʃalˈom ʔolˈam', '.', True)]
|
48
|
+
# Phonemes: [('ʃalˈom ʔolˈam', '.', True)]
|