731 lines
24 KiB
Python
731 lines
24 KiB
Python
# Convert Japanese text to phonemes which is
|
||
# compatible with Julius https://github.com/julius-speech/segmentation-kit
|
||
import re
|
||
import unicodedata
|
||
|
||
from transformers import AutoTokenizer
|
||
|
||
from text import punctuation, symbols
|
||
|
||
from num2words import num2words
|
||
|
||
import pyopenjtalk
|
||
import jaconv
|
||
|
||
|
||
def hiragana2p(text: str) -> str:
|
||
"""
|
||
Modification of `jaconv.hiragana2julius`.
|
||
- avoid using `:`, instead, `あーーー` -> `a a a a`.
|
||
- avoid converting `o u` to `o o` (because the input is already actual `yomi`).
|
||
- avoid using `N` for `ん` (for compatibility)
|
||
- use `v` for `ゔ` related text.
|
||
- add bare `ゃ` `ゅ` `ょ` to `y a` `y u` `y o` (for compatibility).
|
||
"""
|
||
# 3文字以上からなる変換規則
|
||
text = text.replace("う゛ぁ", " v a")
|
||
text = text.replace("う゛ぃ", " v i")
|
||
text = text.replace("う゛ぇ", " v e")
|
||
text = text.replace("う゛ぉ", " v o")
|
||
text = text.replace("う゛ゅ", " by u")
|
||
|
||
# ゔ等の処理を追加
|
||
text = text.replace("ゔぁ", " v a")
|
||
text = text.replace("ゔぃ", " v i")
|
||
text = text.replace("ゔぇ", " v e")
|
||
text = text.replace("ゔぉ", " v o")
|
||
text = text.replace("ゔゅ", " by u")
|
||
|
||
# 2文字からなる変換規則
|
||
text = text.replace("ぅ゛", " v u")
|
||
|
||
text = text.replace("あぁ", " a a")
|
||
text = text.replace("いぃ", " i i")
|
||
text = text.replace("いぇ", " i e")
|
||
text = text.replace("いゃ", " y a")
|
||
text = text.replace("うぅ", " u:")
|
||
text = text.replace("えぇ", " e e")
|
||
text = text.replace("おぉ", " o:")
|
||
text = text.replace("かぁ", " k a:")
|
||
text = text.replace("きぃ", " k i:")
|
||
text = text.replace("くぅ", " k u:")
|
||
text = text.replace("くゃ", " ky a")
|
||
text = text.replace("くゅ", " ky u")
|
||
text = text.replace("くょ", " ky o")
|
||
text = text.replace("けぇ", " k e:")
|
||
text = text.replace("こぉ", " k o:")
|
||
text = text.replace("がぁ", " g a:")
|
||
text = text.replace("ぎぃ", " g i:")
|
||
text = text.replace("ぐぅ", " g u:")
|
||
text = text.replace("ぐゃ", " gy a")
|
||
text = text.replace("ぐゅ", " gy u")
|
||
text = text.replace("ぐょ", " gy o")
|
||
text = text.replace("げぇ", " g e:")
|
||
text = text.replace("ごぉ", " g o:")
|
||
text = text.replace("さぁ", " s a:")
|
||
text = text.replace("しぃ", " sh i:")
|
||
text = text.replace("すぅ", " s u:")
|
||
text = text.replace("すゃ", " sh a")
|
||
text = text.replace("すゅ", " sh u")
|
||
text = text.replace("すょ", " sh o")
|
||
text = text.replace("せぇ", " s e:")
|
||
text = text.replace("そぉ", " s o:")
|
||
text = text.replace("ざぁ", " z a:")
|
||
text = text.replace("じぃ", " j i:")
|
||
text = text.replace("ずぅ", " z u:")
|
||
text = text.replace("ずゃ", " zy a")
|
||
text = text.replace("ずゅ", " zy u")
|
||
text = text.replace("ずょ", " zy o")
|
||
text = text.replace("ぜぇ", " z e:")
|
||
text = text.replace("ぞぉ", " z o:")
|
||
text = text.replace("たぁ", " t a:")
|
||
text = text.replace("ちぃ", " ch i:")
|
||
text = text.replace("つぁ", " ts a")
|
||
text = text.replace("つぃ", " ts i")
|
||
text = text.replace("つぅ", " ts u:")
|
||
text = text.replace("つゃ", " ch a")
|
||
text = text.replace("つゅ", " ch u")
|
||
text = text.replace("つょ", " ch o")
|
||
text = text.replace("つぇ", " ts e")
|
||
text = text.replace("つぉ", " ts o")
|
||
text = text.replace("てぇ", " t e:")
|
||
text = text.replace("とぉ", " t o:")
|
||
text = text.replace("だぁ", " d a:")
|
||
text = text.replace("ぢぃ", " j i:")
|
||
text = text.replace("づぅ", " d u:")
|
||
text = text.replace("づゃ", " zy a")
|
||
text = text.replace("づゅ", " zy u")
|
||
text = text.replace("づょ", " zy o")
|
||
text = text.replace("でぇ", " d e:")
|
||
text = text.replace("どぉ", " d o:")
|
||
text = text.replace("なぁ", " n a:")
|
||
text = text.replace("にぃ", " n i:")
|
||
text = text.replace("ぬぅ", " n u:")
|
||
text = text.replace("ぬゃ", " ny a")
|
||
text = text.replace("ぬゅ", " ny u")
|
||
text = text.replace("ぬょ", " ny o")
|
||
text = text.replace("ねぇ", " n e:")
|
||
text = text.replace("のぉ", " n o:")
|
||
text = text.replace("はぁ", " h a:")
|
||
text = text.replace("ひぃ", " h i:")
|
||
text = text.replace("ふぅ", " f u:")
|
||
text = text.replace("ふゃ", " hy a")
|
||
text = text.replace("ふゅ", " hy u")
|
||
text = text.replace("ふょ", " hy o")
|
||
text = text.replace("へぇ", " h e:")
|
||
text = text.replace("ほぉ", " h o:")
|
||
text = text.replace("ばぁ", " b a:")
|
||
text = text.replace("びぃ", " b i:")
|
||
text = text.replace("ぶぅ", " b u:")
|
||
text = text.replace("ふゃ", " hy a")
|
||
text = text.replace("ぶゅ", " by u")
|
||
text = text.replace("ふょ", " hy o")
|
||
text = text.replace("べぇ", " b e:")
|
||
text = text.replace("ぼぉ", " b o:")
|
||
text = text.replace("ぱぁ", " p a:")
|
||
text = text.replace("ぴぃ", " p i:")
|
||
text = text.replace("ぷぅ", " p u:")
|
||
text = text.replace("ぷゃ", " py a")
|
||
text = text.replace("ぷゅ", " py u")
|
||
text = text.replace("ぷょ", " py o")
|
||
text = text.replace("ぺぇ", " p e:")
|
||
text = text.replace("ぽぉ", " p o:")
|
||
text = text.replace("まぁ", " m a:")
|
||
text = text.replace("みぃ", " m i:")
|
||
text = text.replace("むぅ", " m u:")
|
||
text = text.replace("むゃ", " my a")
|
||
text = text.replace("むゅ", " my u")
|
||
text = text.replace("むょ", " my o")
|
||
text = text.replace("めぇ", " m e:")
|
||
text = text.replace("もぉ", " m o:")
|
||
text = text.replace("やぁ", " y a:")
|
||
text = text.replace("ゆぅ", " y u:")
|
||
text = text.replace("ゆゃ", " y a:")
|
||
text = text.replace("ゆゅ", " y u:")
|
||
text = text.replace("ゆょ", " y o:")
|
||
text = text.replace("よぉ", " y o:")
|
||
text = text.replace("らぁ", " r a:")
|
||
text = text.replace("りぃ", " r i:")
|
||
text = text.replace("るぅ", " r u:")
|
||
text = text.replace("るゃ", " ry a")
|
||
text = text.replace("るゅ", " ry u")
|
||
text = text.replace("るょ", " ry o")
|
||
text = text.replace("れぇ", " r e:")
|
||
text = text.replace("ろぉ", " r o:")
|
||
text = text.replace("わぁ", " w a:")
|
||
text = text.replace("をぉ", " o:")
|
||
|
||
text = text.replace("う゛", " b u")
|
||
text = text.replace("でぃ", " d i")
|
||
text = text.replace("でぇ", " d e:")
|
||
text = text.replace("でゃ", " dy a")
|
||
text = text.replace("でゅ", " dy u")
|
||
text = text.replace("でょ", " dy o")
|
||
text = text.replace("てぃ", " t i")
|
||
text = text.replace("てぇ", " t e:")
|
||
text = text.replace("てゃ", " ty a")
|
||
text = text.replace("てゅ", " ty u")
|
||
text = text.replace("てょ", " ty o")
|
||
text = text.replace("すぃ", " s i")
|
||
text = text.replace("ずぁ", " z u a")
|
||
text = text.replace("ずぃ", " z i")
|
||
text = text.replace("ずぅ", " z u")
|
||
text = text.replace("ずゃ", " zy a")
|
||
text = text.replace("ずゅ", " zy u")
|
||
text = text.replace("ずょ", " zy o")
|
||
text = text.replace("ずぇ", " z e")
|
||
text = text.replace("ずぉ", " z o")
|
||
text = text.replace("きゃ", " ky a")
|
||
text = text.replace("きゅ", " ky u")
|
||
text = text.replace("きょ", " ky o")
|
||
text = text.replace("しゃ", " sh a")
|
||
text = text.replace("しゅ", " sh u")
|
||
text = text.replace("しぇ", " sh e")
|
||
text = text.replace("しょ", " sh o")
|
||
text = text.replace("ちゃ", " ch a")
|
||
text = text.replace("ちゅ", " ch u")
|
||
text = text.replace("ちぇ", " ch e")
|
||
text = text.replace("ちょ", " ch o")
|
||
text = text.replace("とぅ", " t u")
|
||
text = text.replace("とゃ", " ty a")
|
||
text = text.replace("とゅ", " ty u")
|
||
text = text.replace("とょ", " ty o")
|
||
text = text.replace("どぁ", " d o a")
|
||
text = text.replace("どぅ", " d u")
|
||
text = text.replace("どゃ", " dy a")
|
||
text = text.replace("どゅ", " dy u")
|
||
text = text.replace("どょ", " dy o")
|
||
text = text.replace("どぉ", " d o:")
|
||
text = text.replace("にゃ", " ny a")
|
||
text = text.replace("にゅ", " ny u")
|
||
text = text.replace("にょ", " ny o")
|
||
text = text.replace("ひゃ", " hy a")
|
||
text = text.replace("ひゅ", " hy u")
|
||
text = text.replace("ひょ", " hy o")
|
||
text = text.replace("みゃ", " my a")
|
||
text = text.replace("みゅ", " my u")
|
||
text = text.replace("みょ", " my o")
|
||
text = text.replace("りゃ", " ry a")
|
||
text = text.replace("りゅ", " ry u")
|
||
text = text.replace("りょ", " ry o")
|
||
text = text.replace("ぎゃ", " gy a")
|
||
text = text.replace("ぎゅ", " gy u")
|
||
text = text.replace("ぎょ", " gy o")
|
||
text = text.replace("ぢぇ", " j e")
|
||
text = text.replace("ぢゃ", " j a")
|
||
text = text.replace("ぢゅ", " j u")
|
||
text = text.replace("ぢょ", " j o")
|
||
text = text.replace("じぇ", " j e")
|
||
text = text.replace("じゃ", " j a")
|
||
text = text.replace("じゅ", " j u")
|
||
text = text.replace("じょ", " j o")
|
||
text = text.replace("びゃ", " by a")
|
||
text = text.replace("びゅ", " by u")
|
||
text = text.replace("びょ", " by o")
|
||
text = text.replace("ぴゃ", " py a")
|
||
text = text.replace("ぴゅ", " py u")
|
||
text = text.replace("ぴょ", " py o")
|
||
text = text.replace("うぁ", " u a")
|
||
text = text.replace("うぃ", " w i")
|
||
text = text.replace("うぇ", " w e")
|
||
text = text.replace("うぉ", " w o")
|
||
text = text.replace("ふぁ", " f a")
|
||
text = text.replace("ふぃ", " f i")
|
||
text = text.replace("ふぅ", " f u")
|
||
text = text.replace("ふゃ", " hy a")
|
||
text = text.replace("ふゅ", " hy u")
|
||
text = text.replace("ふょ", " hy o")
|
||
text = text.replace("ふぇ", " f e")
|
||
text = text.replace("ふぉ", " f o")
|
||
|
||
# 1音からなる変換規則
|
||
text = text.replace("あ", " a")
|
||
text = text.replace("い", " i")
|
||
text = text.replace("う", " u")
|
||
text = text.replace("ゔ", " v u") # ゔの処理を追加
|
||
text = text.replace("え", " e")
|
||
text = text.replace("お", " o")
|
||
text = text.replace("か", " k a")
|
||
text = text.replace("き", " k i")
|
||
text = text.replace("く", " k u")
|
||
text = text.replace("け", " k e")
|
||
text = text.replace("こ", " k o")
|
||
text = text.replace("さ", " s a")
|
||
text = text.replace("し", " sh i")
|
||
text = text.replace("す", " s u")
|
||
text = text.replace("せ", " s e")
|
||
text = text.replace("そ", " s o")
|
||
text = text.replace("た", " t a")
|
||
text = text.replace("ち", " ch i")
|
||
text = text.replace("つ", " ts u")
|
||
text = text.replace("て", " t e")
|
||
text = text.replace("と", " t o")
|
||
text = text.replace("な", " n a")
|
||
text = text.replace("に", " n i")
|
||
text = text.replace("ぬ", " n u")
|
||
text = text.replace("ね", " n e")
|
||
text = text.replace("の", " n o")
|
||
text = text.replace("は", " h a")
|
||
text = text.replace("ひ", " h i")
|
||
text = text.replace("ふ", " f u")
|
||
text = text.replace("へ", " h e")
|
||
text = text.replace("ほ", " h o")
|
||
text = text.replace("ま", " m a")
|
||
text = text.replace("み", " m i")
|
||
text = text.replace("む", " m u")
|
||
text = text.replace("め", " m e")
|
||
text = text.replace("も", " m o")
|
||
text = text.replace("ら", " r a")
|
||
text = text.replace("り", " r i")
|
||
text = text.replace("る", " r u")
|
||
text = text.replace("れ", " r e")
|
||
text = text.replace("ろ", " r o")
|
||
text = text.replace("が", " g a")
|
||
text = text.replace("ぎ", " g i")
|
||
text = text.replace("ぐ", " g u")
|
||
text = text.replace("げ", " g e")
|
||
text = text.replace("ご", " g o")
|
||
text = text.replace("ざ", " z a")
|
||
text = text.replace("じ", " j i")
|
||
text = text.replace("ず", " z u")
|
||
text = text.replace("ぜ", " z e")
|
||
text = text.replace("ぞ", " z o")
|
||
text = text.replace("だ", " d a")
|
||
text = text.replace("ぢ", " j i")
|
||
text = text.replace("づ", " z u")
|
||
text = text.replace("で", " d e")
|
||
text = text.replace("ど", " d o")
|
||
text = text.replace("ば", " b a")
|
||
text = text.replace("び", " b i")
|
||
text = text.replace("ぶ", " b u")
|
||
text = text.replace("べ", " b e")
|
||
text = text.replace("ぼ", " b o")
|
||
text = text.replace("ぱ", " p a")
|
||
text = text.replace("ぴ", " p i")
|
||
text = text.replace("ぷ", " p u")
|
||
text = text.replace("ぺ", " p e")
|
||
text = text.replace("ぽ", " p o")
|
||
text = text.replace("や", " y a")
|
||
text = text.replace("ゆ", " y u")
|
||
text = text.replace("よ", " y o")
|
||
text = text.replace("わ", " w a")
|
||
text = text.replace("ゐ", " i")
|
||
text = text.replace("ゑ", " e")
|
||
text = text.replace("ん", " N")
|
||
text = text.replace("っ", " q")
|
||
# ここまでに処理されてない ぁぃぅぇぉ はそのまま大文字扱い
|
||
text = text.replace("ぁ", " a")
|
||
text = text.replace("ぃ", " i")
|
||
text = text.replace("ぅ", " u")
|
||
text = text.replace("ぇ", " e")
|
||
text = text.replace("ぉ", " o")
|
||
text = text.replace("ゎ", " w a")
|
||
text = text.replace("ぉ", " o")
|
||
|
||
# ここまでに処理されていないゅ等もそのまま大文字扱い(追加)
|
||
text = text.replace("ゃ", " y a")
|
||
text = text.replace("ゅ", " y u")
|
||
text = text.replace("ょ", " y o")
|
||
|
||
# 長音の処理
|
||
# for (pattern, replace_str) in JULIUS_LONG_VOWEL:
|
||
# text = pattern.sub(replace_str, text)
|
||
# text = text.replace("o u", "o:") # おう -> おーの音便
|
||
text = text.replace("ー", ":")
|
||
text = text.replace("〜", ":")
|
||
text = text.replace("−", ":")
|
||
text = text.replace("-", ":")
|
||
|
||
# その他特別な処理
|
||
text = text.replace("を", " o")
|
||
|
||
text = text.strip()
|
||
|
||
text = text.replace(":+", ":")
|
||
|
||
# ここまで`jaconv.hiragana2julius`と音便処理と長音処理をのぞいて同じ
|
||
# ここから`k a:: k i:`→`k a a a k i i`のように`:`の数だけ繰り返す処理
|
||
pattern = r"(\w)(:*)"
|
||
replacement = lambda m: m.group(1) + (" " + m.group(1)) * len(m.group(2))
|
||
|
||
text = re.sub(pattern, replacement, text)
|
||
text = text.replace("N", "n") # 促音のNをnに変換
|
||
return text
|
||
|
||
|
||
def kata2phoneme(text: str) -> str:
|
||
"""Convert katakana text to phonemes."""
|
||
text = text.strip()
|
||
if text == "ー":
|
||
return ["ー"]
|
||
elif text.startswith("ー"):
|
||
return ["ー"] + kata2phoneme(text[1:])
|
||
res = []
|
||
prev = None
|
||
while text:
|
||
if re.match(_MARKS, text):
|
||
res.append(text)
|
||
text = text[1:]
|
||
continue
|
||
if text.startswith("ー"):
|
||
if prev:
|
||
res.append(prev[-1])
|
||
text = text[1:]
|
||
continue
|
||
res += hiragana2p(jaconv.kata2hira(text)).split(" ")
|
||
break
|
||
# res = _COLON_RX.sub(":", res)
|
||
return res
|
||
|
||
|
||
_SYMBOL_TOKENS = set(list("・、。?!"))
|
||
_NO_YOMI_TOKENS = set(list("「」『』―()[][]"))
|
||
_MARKS = re.compile(
|
||
r"[^A-Za-z\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d]"
|
||
)
|
||
|
||
|
||
def text2sep_kata(text: str):
|
||
parsed = pyopenjtalk.run_frontend(text)
|
||
res = []
|
||
sep = []
|
||
for parts in parsed:
|
||
word, yomi = replace_punctuation(parts["string"]), parts["pron"].replace(
|
||
"’", ""
|
||
)
|
||
if yomi:
|
||
if re.match(_MARKS, yomi):
|
||
if len(word) > 1:
|
||
word = [replace_punctuation(i) for i in list(word)]
|
||
yomi = word
|
||
res += yomi
|
||
sep += word
|
||
continue
|
||
elif word not in rep_map.keys() and word not in rep_map.values():
|
||
word = ","
|
||
yomi = word
|
||
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)
|
||
sep.append(word)
|
||
logger.debug(f"Text2sep_kata: {text} -> {res}")
|
||
logger.debug(f"get_accent: {parsed}")
|
||
return sep, res, get_accent(parsed)
|
||
|
||
|
||
def get_accent(parsed):
|
||
labels = pyopenjtalk.make_label(parsed)
|
||
|
||
phonemes = []
|
||
accents = []
|
||
for n, label in enumerate(labels):
|
||
phoneme = re.search(r"\-([^\+]*)\+", label).group(1)
|
||
if phoneme not in ["sil", "pau"]:
|
||
phonemes.append(phoneme.replace("cl", "q").lower())
|
||
else:
|
||
continue
|
||
a1 = int(re.search(r"/A:(\-?[0-9]+)\+", label).group(1))
|
||
a2 = int(re.search(r"\+(\d+)\+", label).group(1))
|
||
if re.search(r"\-([^\+]*)\+", labels[n + 1]).group(1) in ["sil", "pau"]:
|
||
a2_next = -1
|
||
else:
|
||
a2_next = int(re.search(r"\+(\d+)\+", labels[n + 1]).group(1))
|
||
# Falling
|
||
if a1 == 0 and a2_next == a2 + 1:
|
||
accents.append(-1)
|
||
# Rising
|
||
elif a2 == 1 and a2_next == 2:
|
||
accents.append(1)
|
||
else:
|
||
accents.append(0)
|
||
return list(zip(phonemes, accents))
|
||
|
||
|
||
_ALPHASYMBOL_YOMI = {
|
||
"#": "シャープ",
|
||
"%": "パーセント",
|
||
"&": "アンド",
|
||
"+": "プラス",
|
||
"-": "マイナス",
|
||
":": "コロン",
|
||
";": "セミコロン",
|
||
"<": "小なり",
|
||
"=": "イコール",
|
||
">": "大なり",
|
||
"@": "アット",
|
||
"a": "エー",
|
||
"b": "ビー",
|
||
"c": "シー",
|
||
"d": "ディー",
|
||
"e": "イー",
|
||
"f": "エフ",
|
||
"g": "ジー",
|
||
"h": "エイチ",
|
||
"i": "アイ",
|
||
"j": "ジェー",
|
||
"k": "ケー",
|
||
"l": "エル",
|
||
"m": "エム",
|
||
"n": "エヌ",
|
||
"o": "オー",
|
||
"p": "ピー",
|
||
"q": "キュー",
|
||
"r": "アール",
|
||
"s": "エス",
|
||
"t": "ティー",
|
||
"u": "ユー",
|
||
"v": "ブイ",
|
||
"w": "ダブリュー",
|
||
"x": "エックス",
|
||
"y": "ワイ",
|
||
"z": "ゼット",
|
||
"α": "アルファ",
|
||
"β": "ベータ",
|
||
"γ": "ガンマ",
|
||
"δ": "デルタ",
|
||
"ε": "イプシロン",
|
||
"ζ": "ゼータ",
|
||
"η": "イータ",
|
||
"θ": "シータ",
|
||
"ι": "イオタ",
|
||
"κ": "カッパ",
|
||
"λ": "ラムダ",
|
||
"μ": "ミュー",
|
||
"ν": "ニュー",
|
||
"ξ": "クサイ",
|
||
"ο": "オミクロン",
|
||
"π": "パイ",
|
||
"ρ": "ロー",
|
||
"σ": "シグマ",
|
||
"τ": "タウ",
|
||
"υ": "ウプシロン",
|
||
"φ": "ファイ",
|
||
"χ": "カイ",
|
||
"ψ": "プサイ",
|
||
"ω": "オメガ",
|
||
}
|
||
|
||
|
||
_NUMBER_WITH_SEPARATOR_RX = re.compile("[0-9]{1,3}(,[0-9]{3})+")
|
||
_CURRENCY_MAP = {"$": "ドル", "¥": "円", "£": "ポンド", "€": "ユーロ"}
|
||
_CURRENCY_RX = re.compile(r"([$¥£€])([0-9.]*[0-9])")
|
||
_NUMBER_RX = re.compile(r"[0-9]+(\.[0-9]+)?")
|
||
|
||
|
||
def japanese_convert_numbers_to_words(text: str) -> str:
|
||
res = _NUMBER_WITH_SEPARATOR_RX.sub(lambda m: m[0].replace(",", ""), text)
|
||
res = _CURRENCY_RX.sub(lambda m: m[2] + _CURRENCY_MAP.get(m[1], m[1]), res)
|
||
res = _NUMBER_RX.sub(lambda m: num2words(m[0], lang="ja"), res)
|
||
return res
|
||
|
||
|
||
def japanese_convert_alpha_symbols_to_words(text: str) -> str:
|
||
return "".join([_ALPHASYMBOL_YOMI.get(ch, ch) for ch in text.lower()])
|
||
|
||
|
||
def is_japanese_character(char):
|
||
# 定义日语文字系统的 Unicode 范围
|
||
japanese_ranges = [
|
||
(0x3040, 0x309F), # 平假名
|
||
(0x30A0, 0x30FF), # 片假名
|
||
(0x4E00, 0x9FFF), # 汉字 (CJK Unified Ideographs)
|
||
(0x3400, 0x4DBF), # 汉字扩展 A
|
||
(0x20000, 0x2A6DF), # 汉字扩展 B
|
||
# 可以根据需要添加其他汉字扩展范围
|
||
]
|
||
|
||
# 将字符的 Unicode 编码转换为整数
|
||
char_code = ord(char)
|
||
|
||
# 检查字符是否在任何一个日语范围内
|
||
for start, end in japanese_ranges:
|
||
if start <= char_code <= end:
|
||
return True
|
||
|
||
return False
|
||
|
||
|
||
rep_map = {
|
||
":": ",",
|
||
";": ",",
|
||
",": ",",
|
||
"。": ".",
|
||
"!": "!",
|
||
"?": "?",
|
||
"\n": ".",
|
||
".": ".",
|
||
"…": "...",
|
||
"···": "...",
|
||
"・・・": "...",
|
||
"·": ",",
|
||
"・": ",",
|
||
"、": ",",
|
||
"$": ".",
|
||
"“": "'",
|
||
"”": "'",
|
||
'"': "'",
|
||
"‘": "'",
|
||
"’": "'",
|
||
"(": "'",
|
||
")": "'",
|
||
"(": "'",
|
||
")": "'",
|
||
"《": "'",
|
||
"》": "'",
|
||
"【": "'",
|
||
"】": "'",
|
||
"[": "'",
|
||
"]": "'",
|
||
"—": "-",
|
||
"−": "-",
|
||
"~": "-",
|
||
"~": "-",
|
||
"「": "'",
|
||
"」": "'",
|
||
}
|
||
|
||
|
||
def replace_punctuation(text):
|
||
pattern = re.compile("|".join(re.escape(p) for p in rep_map.keys()))
|
||
|
||
replaced_text = pattern.sub(lambda x: rep_map[x.group()], text)
|
||
|
||
replaced_text = re.sub(
|
||
r"[^\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\u3005"
|
||
+ "".join(punctuation)
|
||
+ r"]+",
|
||
"",
|
||
replaced_text,
|
||
)
|
||
|
||
return replaced_text
|
||
|
||
|
||
def text_normalize(text):
|
||
res = unicodedata.normalize("NFKC", text)
|
||
res = japanese_convert_numbers_to_words(res)
|
||
# res = "".join([i for i in res if is_japanese_character(i)])
|
||
res = replace_punctuation(res)
|
||
res = res.replace("゙", "")
|
||
return res
|
||
|
||
|
||
def distribute_phone(n_phone, n_word):
|
||
phones_per_word = [0] * n_word
|
||
for task in range(n_phone):
|
||
min_tasks = min(phones_per_word)
|
||
min_index = phones_per_word.index(min_tasks)
|
||
phones_per_word[min_index] += 1
|
||
return phones_per_word
|
||
|
||
|
||
def handle_long(sep_phonemes):
|
||
for i in range(len(sep_phonemes)):
|
||
if sep_phonemes[i][0] == "ー":
|
||
sep_phonemes[i][0] = sep_phonemes[i - 1][-1]
|
||
if "ー" in sep_phonemes[i]:
|
||
for j in range(len(sep_phonemes[i])):
|
||
if sep_phonemes[i][j] == "ー":
|
||
sep_phonemes[i][j] = sep_phonemes[i][j - 1][-1]
|
||
return sep_phonemes
|
||
|
||
|
||
tokenizer = AutoTokenizer.from_pretrained("./bert/deberta-v2-large-japanese-char-wwm")
|
||
|
||
|
||
def align_tones(phones, tones):
|
||
res = []
|
||
for pho in phones:
|
||
temp = [0] * len(pho)
|
||
for idx, p in enumerate(pho):
|
||
if len(tones) == 0:
|
||
break
|
||
if p == tones[0][0]:
|
||
temp[idx] = tones[0][1]
|
||
if idx > 0:
|
||
temp[idx] += temp[idx - 1]
|
||
tones.pop(0)
|
||
temp = [0] + temp
|
||
temp = temp[:-1]
|
||
if -1 in temp:
|
||
temp = [i + 1 for i in temp]
|
||
res.append(temp)
|
||
res = [i for j in res for i in j]
|
||
assert not any([i < 0 for i in res]) and not any([i > 1 for i in res])
|
||
return res
|
||
|
||
|
||
def rearrange_tones(tones, phones):
|
||
res = [0] * len(tones)
|
||
for i in range(len(tones)):
|
||
if i == 0:
|
||
if tones[i] not in punctuation:
|
||
res[i] = 1
|
||
elif tones[i] == prev:
|
||
if phones[i] in punctuation:
|
||
res[i] = 0
|
||
else:
|
||
res[i] = 1
|
||
elif tones[i] > prev:
|
||
res[i] = 2
|
||
elif tones[i] < prev:
|
||
res[i - 1] = 3
|
||
res[i] = 1
|
||
prev = tones[i]
|
||
return res
|
||
|
||
|
||
def g2p(norm_text):
|
||
sep_text, sep_kata, acc = text2sep_kata(norm_text)
|
||
logger.debug(f"Sep_text: {sep_text}")
|
||
logger.debug(f"Sep_kata: {sep_kata}")
|
||
logger.debug(f"Acc: {acc}")
|
||
sep_tokenized = []
|
||
for i in sep_text:
|
||
if i not in punctuation:
|
||
sep_tokenized.append(tokenizer.tokenize(i))
|
||
else:
|
||
sep_tokenized.append([i])
|
||
|
||
sep_phonemes = handle_long([kata2phoneme(i) for i in sep_kata])
|
||
# 异常处理,MeCab不认识的词的话会一路传到这里来,然后炸掉。目前来看只有那些超级稀有的生僻词会出现这种情况
|
||
for i in sep_phonemes:
|
||
for j in i:
|
||
assert j in symbols, (sep_text, sep_kata, sep_phonemes)
|
||
tones = align_tones(sep_phonemes, acc)
|
||
|
||
word2ph = []
|
||
for token, phoneme in zip(sep_tokenized, sep_phonemes):
|
||
phone_len = len(phoneme)
|
||
word_len = len(token)
|
||
|
||
aaa = distribute_phone(phone_len, word_len)
|
||
word2ph += aaa
|
||
phones = ["_"] + [j for i in sep_phonemes for j in i] + ["_"]
|
||
# tones = [0] + rearrange_tones(tones, phones[1:-1]) + [0]
|
||
tones = [0] + tones + [0]
|
||
word2ph = [1] + word2ph + [1]
|
||
assert len(phones) == len(tones)
|
||
return phones, tones, word2ph
|
||
|
||
|
||
if __name__ == "__main__":
|
||
tokenizer = AutoTokenizer.from_pretrained("./bert/deberta-v2-large-japanese")
|
||
text = "hello,こんにちは、世界ー!……"
|
||
from text.japanese_bert import get_bert_feature
|
||
|
||
text = text_normalize(text)
|
||
print(text)
|
||
|
||
phones, tones, word2ph = g2p(text)
|
||
bert = get_bert_feature(text, word2ph)
|
||
|
||
print(phones, tones, word2ph, bert.shape)
|