This commit is contained in:
litagin02
2024-02-02 21:11:23 +09:00
parent 7a8477b9bc
commit e566ac62f3
17 changed files with 1535 additions and 1162 deletions

View File

@@ -4,10 +4,13 @@ from text import chinese, japanese, english, cleaned_text_to_sequence
language_module_map = {"ZH": chinese, "JP": japanese, "EN": english}
def clean_text(text, language):
def clean_text(text, language, use_jp_extra=True):
language_module = language_module_map[language]
norm_text = language_module.text_normalize(text)
phones, tones, word2ph = language_module.g2p(norm_text)
if language == "JP":
phones, tones, word2ph = language_module.g2p(norm_text, use_jp_extra)
else:
phones, tones, word2ph = language_module.g2p(norm_text)
return norm_text, phones, tones, word2ph

View File

@@ -23,8 +23,8 @@ COSONANTS = set(
]
)
# 母音の集合
VOWELS = {"a", "i", "u", "e", "o"}
# 母音の集合、便宜上「ん」を含める
VOWELS = {"a", "i", "u", "e", "o", "N"}
# 正規化で記号を変換するための辞書
@@ -62,7 +62,7 @@ rep_map = {
"": "-",
"": "-",
# "": "-", # これは長音記号「ー」として扱うよう変更
"~": "-",
# "~": "-", # これも長音記号「ー」として扱うよう変更
"": "'",
"": "'",
}
@@ -145,7 +145,9 @@ def japanese_convert_numbers_to_words(text: str) -> str:
return res
def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
def g2p(
norm_text: str, use_jp_extra: bool = True
) -> tuple[list[str], list[int], list[int]]:
"""
他で使われるメインの関数。`text_normalize()`で正規化された`norm_text`を受け取り、
- phones: 音素のリスト(ただし`!`や`,`や`.`等punctuationが含まれうる
@@ -153,6 +155,7 @@ def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
- word2ph: 元のテキストの各文字に音素が何個割り当てられるかを表すリスト
のタプルを返す。
ただし`phones`と`tones`の最初と終わりに`_`が入り、応じて`word2ph`の最初と最後に1が追加される。
use_jp_extra: Falseの場合、「ん」の音素を「N」ではなく「n」とする。
"""
# pyopenjtalkのフルコンテキストラベルを使ってアクセントを取り出すと、punctuationの位置が消えてしまい情報が失われてしまう
# 「こんにちは、世界。」と「こんにちは!世界。」と「こんにちは!!!???世界……。」は全て同じになる。
@@ -160,7 +163,7 @@ def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
# それとは別にpyopenjtalk.run_frontend()で得られる音素リストこちらはpunctuationが保持されるを使い、
# アクセント割当をしなおすことによってpunctuationを含めた音素とアクセントのリストを作る。
# punctuationがすべて消えた、音素とアクセントのタプルのリスト
# punctuationがすべて消えた、音素とアクセントのタプルのリスト「ん」は「N」
phone_tone_list_wo_punct = g2phone_tone_wo_punct(norm_text)
# sep_text: 単語単位の単語のリスト
@@ -185,7 +188,9 @@ def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
sep_tokenized: list[list[str]] = []
for i in sep_text:
if i not in punctuation:
sep_tokenized.append(tokenizer.tokenize(i)) # ここでおそらく`i`が文字単位に分割される
sep_tokenized.append(
tokenizer.tokenize(i)
) # ここでおそらく`i`が文字単位に分割される
else:
sep_tokenized.append([i])
@@ -205,11 +210,15 @@ def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
assert len(phones) == sum(word2ph), f"{len(phones)} != {sum(word2ph)}"
# 最後にuse_jp_extraでない場合は「N」を「n」に変換
if not use_jp_extra:
phones = [phone if phone != "N" else "n" for phone in phones]
return phones, tones, word2ph
def g2kata_tone(norm_text: str) -> list[tuple[str, int]]:
phones, tones, _ = g2p(norm_text)
phones, tones, _ = g2p(norm_text, use_jp_extra=True)
return phone_tone2kata_tone(list(zip(phones, tones)))
@@ -225,22 +234,12 @@ def phone_tone2kata_tone(phone_tone: list[tuple[str, int]]) -> list[tuple[str, i
if phone in punctuation:
result.append((phone, tone))
continue
if phone in COSONANTS and phone != "n": # n以外の子音の場合
if phone in COSONANTS: # n以外の子音の場合
assert current_mora == "", f"Unexpected {phone} after {current_mora}"
assert tone == next_tone, f"Unexpected {phone} tone {tone} != {next_tone}"
current_mora = phone
elif phone == "n":
assert current_mora == "", f"Unexpected {phone} after {current_mora}"
if next_phone not in VOWELS: # 次の音素が母音でない場合
result.append(("", tone))
else:
# 子音のnの場合
assert (
tone == next_tone
), f"Unexpected {phone} tone {tone} != {next_tone}"
current_mora = "n"
else:
# phoneが母音
# phoneが母音もしくは「N」
current_mora += phone
result.append((mora_phonemes_to_mora_kata[current_mora], tone))
current_mora = ""
@@ -269,9 +268,9 @@ def g2phone_tone_wo_punct(text: str) -> list[tuple[str, int]]:
テキストに対して、音素とアクセント0か1のペアのリストを返す。
ただし「!」「.」「?」等の非音素記号(punctuation)は全て消える(ポーズ記号も残さない)。
非音素記号を含める処理は`align_tones()`で行われる。
また「っ」は「q」に、「ん」は「n」に変換される。
また「っ」は「q」に、「ん」は「N」に変換される。
例: "こんにちは、世界ー。。元気?!"
[('k', 0), ('o', 0), ('n', 1), ('n', 1), ('i', 1), ('ch', 1), ('i', 1), ('w', 1), ('a', 1), ('s', 1), ('e', 1), ('k', 0), ('a', 0), ('i', 0), ('i', 0), ('g', 1), ('e', 1), ('n', 0), ('k', 0), ('i', 0)]
[('k', 0), ('o', 0), ('N', 1), ('n', 1), ('i', 1), ('ch', 1), ('i', 1), ('w', 1), ('a', 1), ('s', 1), ('e', 1), ('k', 0), ('a', 0), ('i', 0), ('i', 0), ('g', 1), ('e', 1), ('N', 0), ('k', 0), ('i', 0)]
"""
prosodies = pyopenjtalk_g2p_prosody(text, drop_unvoiced_vowels=True)
# logger.debug(f"prosodies: {prosodies}")
@@ -306,8 +305,8 @@ def g2phone_tone_wo_punct(text: str) -> list[tuple[str, int]]:
else:
if letter == "cl": # 「っ」の処理
letter = "q"
elif letter == "N": # 「ん」の処理
letter = "n"
# elif letter == "N": # 「ん」の処理
# letter = "n"
current_phrase.append((letter, current_tone))
return result
@@ -364,7 +363,7 @@ def text2sep_kata(norm_text: str) -> tuple[list[str], list[str]]:
return sep_text, sep_kata
# ESPnetの実装から引用、変更点無し
# ESPnetの実装から引用、変更点無し。「ん」は「N」なことに注意。
# https://github.com/espnet/espnet/blob/master/espnet2/text/phoneme_tokenizer.py
def pyopenjtalk_g2p_prosody(text: str, drop_unvoiced_vowels: bool = True) -> list[str]:
"""Extract phoneme + prosoody symbol sequence from input full-context labels.

View File

@@ -2,6 +2,7 @@
VOICEVOXのソースコードからお借りして最低限に改造したコード。
https://github.com/VOICEVOX/voicevox_engine/blob/master/voicevox_engine/tts_pipeline/mora_list.py
"""
"""
以下のモーラ対応表はOpenJTalkのソースコードから取得し、
カタカナ表記とモーラが一対一対応するように改造した。
@@ -48,8 +49,8 @@ POSSIBILITY OF SUCH DAMAGE.
from typing import Optional
# (カタカナ, 子音, 母音)の順。子音がない場合はNoneを入れる。
# 但し「ン」と「ッ」は母音のみという扱いで、「ン」は「n」、「ッ」は「q」に変更
# (元々はそれぞれ「N」「cl」
# 但し「ン」と「ッ」は母音のみという扱いで、「ン」は「N」、「ッ」は「q」とする。
# (元々「ッ」は「cl」
# また「デェ = dy e」はpyopenjtalkの出力de eと合わないため削除
_mora_list_minimum: list[tuple[str, Optional[str], str]] = [
("ヴォ", "v", "o"),
@@ -57,7 +58,7 @@ _mora_list_minimum: list[tuple[str, Optional[str], str]] = [
("ヴィ", "v", "i"),
("ヴァ", "v", "a"),
("", "v", "u"),
("", None, "n"), # 「N」から「n」に変更
("", None, "N"),
("", "w", "a"),
("", "r", "o"),
("", "r", "e"),