Espeak phonemizer pipeline for spanish (and also english)

This commit is contained in:
Mathias Gatti
2022-07-14 22:14:32 -03:00
parent 2e6be74403
commit 840a97e70e
13 changed files with 491 additions and 15 deletions
@@ -0,0 +1,127 @@
import re
from phonemizer.backend import EspeakBackend
from phonemizer.separator import Separator
from typing import List
from text.pipeline_components.input_parsing.parser import ArpabetSequence, GraphemeSequence, ParsedSequence
from text.pipeline_components.phonetization.base_phonetization import BasePhonetization
from text.pipeline_components.input_parsing.parser import parse_grapheme_and_arpabet_sequence
# IPA to ARPABET
basic_replacements = {
'aɪ': 'AA0 Y',
'aʊ': 'AA0 W',
'b': 'B',
'd': 'D',
'dʒ': 'JH',
'eɪ': 'EY0',
'f': 'F',
'h': 'HH',
'i': 'IY0',
'j': 'Y',
'k': 'K',
'l': 'L',
'l̩': 'EL',
'm': 'M',
'm̩': 'EM',
'n': 'N',
'n̩': 'EN',
'oʊ': 'OW',
'p': 'P',
's': 'S',
't': 'T',
'tʃ': 'CH',
'u': 'UW0',
'v': 'V',
'w': 'W',
'z': 'Z',
'æ': 'AE0',
'ð': 'DH',
'ŋ': 'NG',
'ɑ': 'AA0',
'ɔ': 'AO0',
'ɔɪ': 'OY0',
'ə': 'AX',
'ɚ': 'AXR',
'ɛ': 'EH0',
'ɝ': 'ER',
'ɡ': 'G',
'ɨ': 'IX',
'ɪ': 'IH0',
'ɹ': 'R',
'ɾ': 'R',
'ɾ̃': 'NX',
'ʃ': 'SH',
'ʉ': 'UX',
'ʊ': 'UH0',
'ʌ': 'AH0',
'ʍ': 'WH',
'ʒ': 'ZH',
'ʔ': 'Q',
'θ': 'TH'
}
class EspeakPhonetization(BasePhonetization):
def __init__(self, lang="en"):
super().__init__(None)
if lang == 'en':
from text.pipeline_components.phonetization.lang.es import valid_symbols, extra_replacements
elif lang == 'es':
from text.pipeline_components.phonetization.lang.es import valid_symbols, extra_replacements
else:
raise Exception("Not supported language")
self.replacements = {**basic_replacements, **extra_replacements}
self.separator = Separator(phone='|', word=None)
lang2espeaklang = {"en": "en-us", "es": "es", "fr": "fr-fr"}
self.backend = EspeakBackend(lang2espeaklang[lang],with_stress=True, preserve_punctuation=False)
def grapheme2ipa(self, word):
return self.backend.phonemize([word],separator=self.separator)[0].strip().strip("|")
def ipa2arpabet(self, ipa_word):
# print(ipa_word)
ipa_word = ipa_word.replace("ɾ|ɾ", "r")
if ipa_word.endswith("j|j"):
ipa_word = ipa_word.replace("j|j", "i")
else:
ipa_word = ipa_word.replace("j|j", "ʎ")
# Remove secondary accents, they are not important in spanish
ipa_word = ipa_word.replace("ˌ", "")
# Remove long consonant indication
ipa_word = ipa_word.replace("ː", "")
arpabet_word = ""
for c in ipa_word.split("|"):
accent = False
if c.startswith("ˈ"):
c = c[1:]
accent = True
if c in self.replacements:
replacement = self.replacements[c]
if accent:
replacement = replacement.replace("0", "1")
arpabet_word += replacement + " "
else:
print(ipa_word)
print(f"'{c}' is not in the replacements dictionary")
raise Exception()
return "{" + arpabet_word.strip() + "}"
def word2phonemes(self, word):
return self.ipa2arpabet(self.grapheme2ipa(word))
def __len__(self):
return len(self._entries)
def try_convert_to_phonemes(self, grapheme_sequence: str) -> ParsedSequence:
'''Converts word to spanish adapted ARPAbet'''
# Apply it only on words, keep punctuation unchanged
for match in re.findall(r"[A-Za-zÀ-ÿ]+", grapheme_sequence):
grapheme_sequence = grapheme_sequence.replace(match, self.word2phonemes(match), 1)
return parse_grapheme_and_arpabet_sequence(grapheme_sequence)
@@ -0,0 +1,19 @@
_pad = '_'
_punctuation = '!\'(),.:;? ' #NB: Includes space
_special = '-'
_letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
# https://en.wikipedia.org/wiki/ARPABET
valid_symbols = [
'AA', 'AA0', 'AA1', 'AA2', 'AE', 'AE0', 'AE1', 'AE2', 'AH', 'AH0', 'AH1', 'AH2',
'AO', 'AO0', 'AO1', 'AO2', 'AW', 'AW0', 'AW1', 'AW2', 'AY', 'AY0', 'AY1', 'AY2',
'B', 'CH', 'D', 'DH', 'EH', 'EH0', 'EH1', 'EH2', 'ER', 'ER0', 'ER1', 'ER2', 'EY',
'EY0', 'EY1', 'EY2', 'F', 'G', 'HH', 'IH', 'IH0', 'IH1', 'IH2', 'IY', 'IY0', 'IY1',
'IY2', 'JH', 'K', 'L', 'M', 'N', 'NG', 'OW', 'OW0', 'OW1', 'OW2', 'OY', 'OY0',
'OY1', 'OY2', 'P', 'R', 'S', 'SH', 'T', 'TH', 'UH', 'UH0', 'UH1', 'UH2', 'UW',
'UW0', 'UW1', 'UW2', 'V', 'W', 'Y', 'Z', 'ZH'
]
extra_replacements = {}
@@ -0,0 +1,34 @@
# https://en.wikipedia.org/wiki/ARPABET
valid_symbols = [
'AA', 'AA0', 'AA1', 'AA2', 'AE', 'AE0', 'AE1', 'AE2', 'AH', 'AH0', 'AH1', 'AH2',
'AO', 'AO0', 'AO1', 'AO2', 'AW', 'AW0', 'AW1', 'AW2', 'AY', 'AY0', 'AY1', 'AY2',
'B', 'CH', 'D', 'DH', 'EH', 'EH0', 'EH1', 'EH2', 'ER', 'ER0', 'ER1', 'ER2', 'EY',
'EY0', 'EY1', 'EY2', 'F', 'G', 'HH', 'IH', 'IH0', 'IH1', 'IH2', 'IY', 'IY0', 'IY1',
'IY2', 'JH', 'K', 'L', 'M', 'N', 'NG', 'OW', 'OW0', 'OW1', 'OW2', 'OY', 'OY0',
'OY1', 'OY2', 'P', 'R', 'S', 'SH', 'T', 'TH', 'UH', 'UH0', 'UH1', 'UH2', 'UW',
'UW0', 'UW1', 'UW2', 'V', 'W', 'Y', "RR", "NI"
]
extra_replacements = {
"a": "AA0",
"o": "OW0",
"β": "V",
"e": "EY0",
"ɲ": "NI",
"r": "RR",
"ɣ": "G",
"ʎ": "SH",
"x": "HH",
"ts": "TH", # jacuzzi / pizza
"oɪ": "OW0 Y", # acoitar
"eʊ": "EY0 W", # adeuda
"ʝ": "SH" # ya
}
# NB: DO NOT CHANGE THE ORDER OF SYMBOLS!
# Models are trained on and depend upon the order.
pad = '_'
punctuation = '!\'(),.:;? '
special = '-'
# Little hack, since I'm using arpabet letters are not useful anymore, I just replace it with spanish symbols that I need like ¡ and ¿
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx¡¿'