mirror of
https://github.com/storytold/storyteller-ml.git
synced 2026-10-17 00:23:14 +00:00
127 lines
3.6 KiB
Python
127 lines
3.6 KiB
Python
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) |