# Convert Japanese text to phonemes which is # compatible with Julius https://github.com/julius-speech/segmentation-kit import re import MeCab _CONVRULES = [ # Conversion of 2 letters 'アァ/ a a', 'イィ/ i i', 'イェ/ i e', 'イャ/ y a', 'ウゥ/ u:', 'エェ/ e e', 'オォ/ o:', 'カァ/ k a:', 'キィ/ k i:', 'クゥ/ k u:', 'クャ/ ky a', 'クュ/ ky u', 'クョ/ ky o', 'ケェ/ k e:', 'コォ/ k o:', 'ガァ/ g a:', 'ギィ/ g i:', 'グゥ/ g u:', 'グャ/ gy a', 'グュ/ gy u', 'グョ/ gy o', 'ゲェ/ g e:', 'ゴォ/ g o:', 'サァ/ s a:', 'シィ/ sh i:', 'スゥ/ s u:', 'スャ/ sh a', 'スュ/ sh u', 'スョ/ sh o', 'セェ/ s e:', 'ソォ/ s o:', 'ザァ/ z a:', 'ジィ/ j i:', 'ズゥ/ z u:', 'ズャ/ zy a', 'ズュ/ zy u', 'ズョ/ zy o', 'ゼェ/ z e:', 'ゾォ/ z o:', 'タァ/ t a:', 'チィ/ ch i:', 'ツァ/ ts a', 'ツィ/ ts i', 'ツゥ/ ts u:', 'ツャ/ ch a', 'ツュ/ ch u', 'ツョ/ ch o', 'ツェ/ ts e', 'ツォ/ ts o', 'テェ/ t e:', 'トォ/ t o:', 'ダァ/ d a:', 'ヂィ/ j i:', 'ヅゥ/ d u:', 'ヅャ/ zy a', 'ヅュ/ zy u', 'ヅョ/ zy o', 'デェ/ d e:', 'ドォ/ d o:', 'ナァ/ n a:', 'ニィ/ n i:', 'ヌゥ/ n u:', 'ヌャ/ ny a', 'ヌュ/ ny u', 'ヌョ/ ny o', 'ネェ/ n e:', 'ノォ/ n o:', 'ハァ/ h a:', 'ヒィ/ h i:', 'フゥ/ f u:', 'フャ/ hy a', 'フュ/ hy u', 'フョ/ hy o', 'ヘェ/ h e:', 'ホォ/ h o:', 'バァ/ b a:', 'ビィ/ b i:', 'ブゥ/ b u:', 'フャ/ hy a', 'ブュ/ by u', 'フョ/ hy o', 'ベェ/ b e:', 'ボォ/ b o:', 'パァ/ p a:', 'ピィ/ p i:', 'プゥ/ p u:', 'プャ/ py a', 'プュ/ py u', 'プョ/ py o', 'ペェ/ p e:', 'ポォ/ p o:', 'マァ/ m a:', 'ミィ/ m i:', 'ムゥ/ m u:', 'ムャ/ my a', 'ムュ/ my u', 'ムョ/ my o', 'メェ/ m e:', 'モォ/ m o:', 'ヤァ/ y a:', 'ユゥ/ y u:', 'ユャ/ y a:', 'ユュ/ y u:', 'ユョ/ y o:', 'ヨォ/ y o:', 'ラァ/ r a:', 'リィ/ r i:', 'ルゥ/ r u:', 'ルャ/ ry a', 'ルュ/ ry u', 'ルョ/ ry o', 'レェ/ r e:', 'ロォ/ r o:', 'ワァ/ w a:', 'ヲォ/ o:', 'ディ/ d i', 'デェ/ d e:', 'デャ/ dy a', 'デュ/ dy u', 'デョ/ dy o', 'ティ/ t i', 'テェ/ t e:', 'テャ/ ty a', 'テュ/ ty u', 'テョ/ ty o', 'スィ/ s i', 'ズァ/ z u a', 'ズィ/ z i', 'ズゥ/ z u', 'ズャ/ zy a', 'ズュ/ zy u', 'ズョ/ zy o', 'ズェ/ z e', 'ズォ/ z o', 'キャ/ ky a', 'キュ/ ky u', 'キョ/ ky o', 'シャ/ sh a', 'シュ/ sh u', 'シェ/ sh e', 'ショ/ sh o', 'チャ/ ch a', 'チュ/ ch u', 'チェ/ ch e', 'チョ/ ch o', 'トゥ/ t u', 'トャ/ ty a', 'トュ/ ty u', 'トョ/ ty o', 'ドァ/ d o a', 'ドゥ/ d u', 'ドャ/ dy a', 'ドュ/ dy u', 'ドョ/ dy o', 'ドォ/ d o:', 'ニャ/ ny a', 'ニュ/ ny u', 'ニョ/ ny o', 'ヒャ/ hy a', 'ヒュ/ hy u', 'ヒョ/ hy o', 'ミャ/ my a', 'ミュ/ my u', 'ミョ/ my o', 'リャ/ ry a', 'リュ/ ry u', 'リョ/ ry o', 'ギャ/ gy a', 'ギュ/ gy u', 'ギョ/ gy o', 'ヂェ/ j e', 'ヂャ/ j a', 'ヂュ/ j u', 'ヂョ/ j o', 'ジェ/ j e', 'ジャ/ j a', 'ジュ/ j u', 'ジョ/ j o', 'ビャ/ by a', 'ビュ/ by u', 'ビョ/ by o', 'ピャ/ py a', 'ピュ/ py u', 'ピョ/ py o', 'ウァ/ u a', 'ウィ/ w i', 'ウェ/ w e', 'ウォ/ w o', 'ファ/ f a', 'フィ/ f i', 'フゥ/ f u', 'フャ/ hy a', 'フュ/ hy u', 'フョ/ hy o', 'フェ/ f e', 'フォ/ f o', 'ヴァ/ b a', 'ヴィ/ b i', 'ヴェ/ b e', 'ヴォ/ b o', 'ヴュ/ by u', # Conversion of 1 letter 'ア/ a', 'イ/ i', 'ウ/ u', 'エ/ e', 'オ/ o', 'カ/ k a', 'キ/ k i', 'ク/ k u', 'ケ/ k e', 'コ/ k o', 'サ/ s a', 'シ/ sh i', 'ス/ s u', 'セ/ s e', 'ソ/ s o', 'タ/ t a', 'チ/ ch i', 'ツ/ ts u', 'テ/ t e', 'ト/ t o', 'ナ/ n a', 'ニ/ n i', 'ヌ/ n u', 'ネ/ n e', 'ノ/ n o', 'ハ/ h a', 'ヒ/ h i', 'フ/ f u', 'ヘ/ h e', 'ホ/ h o', 'マ/ m a', 'ミ/ m i', 'ム/ m u', 'メ/ m e', 'モ/ m o', 'ラ/ r a', 'リ/ r i', 'ル/ r u', 'レ/ r e', 'ロ/ r o', 'ガ/ g a', 'ギ/ g i', 'グ/ g u', 'ゲ/ g e', 'ゴ/ g o', 'ザ/ z a', 'ジ/ j i', 'ズ/ z u', 'ゼ/ z e', 'ゾ/ z o', 'ダ/ d a', 'ヂ/ j i', 'ヅ/ z u', 'デ/ d e', 'ド/ d o', 'バ/ b a', 'ビ/ b i', 'ブ/ b u', 'ベ/ b e', 'ボ/ b o', 'パ/ p a', 'ピ/ p i', 'プ/ p u', 'ペ/ p e', 'ポ/ p o', 'ヤ/ y a', 'ユ/ y u', 'ヨ/ y o', 'ワ/ w a', 'ヰ/ i', 'ヱ/ e', 'ヲ/ o', 'ン/ N', 'ッ/ q', 'ヴ/ b u', 'ー/:', # Try converting broken text 'ァ/ a', 'ィ/ i', 'ゥ/ u', 'ェ/ e', 'ォ/ o', 'ヮ/ w a', 'ォ/ o', # Symbols '、/ ,', '。/ .', '!/ !', '?/ ?', '・/ ,' ] _COLON_RX = re.compile(':+') _REJECT_RX = re.compile('[^ a-zA-Z:,.?]') def _makerulemap(): l = [tuple(x.split('/')) for x in _CONVRULES] return tuple( {k: v for k, v in l if len(k) == i} for i in (1, 2) ) _RULEMAP1, _RULEMAP2 = _makerulemap() def kata2phoneme(text: str) -> str: """Convert katakana text to phonemes. """ text = text.strip() res = '' while text: if len(text) >= 2: x = _RULEMAP2.get(text[:2]) if x is not None: text = text[2:] res += x continue x = _RULEMAP1.get(text[0]) if x is not None: text = text[1:] res += x continue res += ' ' + text[0] text = text[1:] res = _COLON_RX.sub(':', res) return res[1:] _KATAKANA = ''.join(chr(ch) for ch in range(ord('ァ'), ord('ン') + 1)) _HIRAGANA = ''.join(chr(ch) for ch in range(ord('ぁ'), ord('ん') + 1)) _HIRA2KATATRANS = str.maketrans(_HIRAGANA, _KATAKANA) def hira2kata(text: str) -> str: text = text.translate(_HIRA2KATATRANS) return text.replace('う゛', 'ヴ') _SYMBOL_TOKENS = set(list('・、。?!')) _NO_YOMI_TOKENS = set(list('「」『』―()[][] …')) _TAGGER = MeCab.Tagger() def text2kata(text: str) -> str: parsed = _TAGGER.parse(text) res = [] for line in parsed.split('\n'): if line == 'EOS': break parts = line.split('\t') word, yomi = parts[0], parts[1] if yomi: res.append(yomi) else: if word in _SYMBOL_TOKENS: res.append(word) elif word in ('っ', 'ッ'): res.append('ッ') elif word in _NO_YOMI_TOKENS: pass else: res.append(word) return hira2kata(''.join(res)) def japanese_text_to_phonemes(text: str) -> str: """Convert Japanese text to phonemes. """ res = text2kata(text) res = kata2phoneme(res) return res.replace(' ', '')