Update user accent control
This commit is contained in:
100
app.py
100
app.py
@@ -1,7 +1,9 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
import torch
|
import torch
|
||||||
@@ -21,6 +23,8 @@ from common.constants import (
|
|||||||
)
|
)
|
||||||
from common.log import logger
|
from common.log import logger
|
||||||
from common.tts_model import ModelHolder
|
from common.tts_model import ModelHolder
|
||||||
|
from infer import InvalidToneError
|
||||||
|
from text.japanese import g2kata_tone, kata_tone2phone_tone, text_normalize
|
||||||
|
|
||||||
# Get path settings
|
# Get path settings
|
||||||
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f:
|
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f:
|
||||||
@@ -46,36 +50,76 @@ def tts_fn(
|
|||||||
use_assist_text,
|
use_assist_text,
|
||||||
style,
|
style,
|
||||||
style_weight,
|
style_weight,
|
||||||
given_tone,
|
kata_tone_json_str,
|
||||||
|
use_tone,
|
||||||
):
|
):
|
||||||
assert model_holder.current_model is not None
|
assert model_holder.current_model is not None
|
||||||
if given_tone == "":
|
|
||||||
given_tone = None
|
wrong_tone_message = ""
|
||||||
else:
|
kata_tone: Optional[list[tuple[str, int]]] = None
|
||||||
given_tone = [int(i) for i in given_tone]
|
if use_tone and kata_tone_json_str != "":
|
||||||
|
if language != "JP":
|
||||||
|
logger.warning("Only Japanese is supported for tone generation.")
|
||||||
|
wrong_tone_message = "アクセント指定は現在日本語のみ対応しています。"
|
||||||
|
if line_split:
|
||||||
|
logger.warning("Tone generation is not supported for line split.")
|
||||||
|
wrong_tone_message = "アクセント指定は改行で分けて生成を使わない場合のみ対応しています。"
|
||||||
|
try:
|
||||||
|
kata_tone = []
|
||||||
|
json_data = json.loads(kata_tone_json_str)
|
||||||
|
# tupleを使うように変換
|
||||||
|
for kana, tone in json_data:
|
||||||
|
assert isinstance(kana, str) and isinstance(tone, int)
|
||||||
|
kata_tone.append((kana, tone))
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error occurred when parsing kana_tone_json: {e}")
|
||||||
|
wrong_tone_message = f"アクセント指定が不正です: {e}"
|
||||||
|
kata_tone = None
|
||||||
|
|
||||||
|
# toneは実際に音声合成に代入される際のみnot Noneになる
|
||||||
|
tone: Optional[list[int]] = None
|
||||||
|
if kata_tone is not None:
|
||||||
|
phone_tone = kata_tone2phone_tone(kata_tone)
|
||||||
|
tone = [t for _, t in phone_tone]
|
||||||
|
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
|
|
||||||
sr, audio = model_holder.current_model.infer(
|
try:
|
||||||
text=text,
|
sr, audio = model_holder.current_model.infer(
|
||||||
language=language,
|
text=text,
|
||||||
reference_audio_path=reference_audio_path,
|
language=language,
|
||||||
sdp_ratio=sdp_ratio,
|
reference_audio_path=reference_audio_path,
|
||||||
noise=noise_scale,
|
sdp_ratio=sdp_ratio,
|
||||||
noisew=noise_scale_w,
|
noise=noise_scale,
|
||||||
length=length_scale,
|
noisew=noise_scale_w,
|
||||||
line_split=line_split,
|
length=length_scale,
|
||||||
split_interval=split_interval,
|
line_split=line_split,
|
||||||
assist_text=assist_text,
|
split_interval=split_interval,
|
||||||
assist_text_weight=assist_text_weight,
|
assist_text=assist_text,
|
||||||
use_assist_text=use_assist_text,
|
assist_text_weight=assist_text_weight,
|
||||||
style=style,
|
use_assist_text=use_assist_text,
|
||||||
style_weight=style_weight,
|
style=style,
|
||||||
given_tone=given_tone,
|
style_weight=style_weight,
|
||||||
)
|
given_tone=tone,
|
||||||
|
)
|
||||||
|
except InvalidToneError as e:
|
||||||
|
logger.error(f"Tone error: {e}")
|
||||||
|
return f"Error: アクセント指定が不正です:\n{e}", None, kata_tone_json_str
|
||||||
|
|
||||||
end_time = datetime.datetime.now()
|
end_time = datetime.datetime.now()
|
||||||
duration = (end_time - start_time).total_seconds()
|
duration = (end_time - start_time).total_seconds()
|
||||||
return f"Success, time: {duration} seconds.", (sr, audio)
|
|
||||||
|
if tone is None and language == "JP" and not line_split:
|
||||||
|
# アクセント指定に使えるようにアクセント情報を返す
|
||||||
|
norm_text = text_normalize(text)
|
||||||
|
kata_tone = g2kata_tone(norm_text)
|
||||||
|
kata_tone_json_str = json.dumps(kata_tone, ensure_ascii=False, indent=2)
|
||||||
|
elif tone is None:
|
||||||
|
kata_tone_json_str = ""
|
||||||
|
message = f"Success, time: {duration} seconds."
|
||||||
|
if wrong_tone_message != "":
|
||||||
|
message = wrong_tone_message + "\n" + message
|
||||||
|
return message, (sr, audio), kata_tone_json_str
|
||||||
|
|
||||||
|
|
||||||
initial_text = "こんにちは、初めまして。あなたの名前はなんていうの?"
|
initial_text = "こんにちは、初めまして。あなたの名前はなんていうの?"
|
||||||
@@ -243,7 +287,10 @@ if __name__ == "__main__":
|
|||||||
step=0.1,
|
step=0.1,
|
||||||
label="分けた場合に挟む無音の長さ(秒)",
|
label="分けた場合に挟む無音の長さ(秒)",
|
||||||
)
|
)
|
||||||
given_tone = gr.Textbox("トーン、0と1の数値列")
|
tone = gr.Textbox(label="アクセント指定")
|
||||||
|
use_tone = gr.Checkbox(
|
||||||
|
label="アクセント指定を使う", info="改行で分けない場合のみ使えます", value=False
|
||||||
|
)
|
||||||
language = gr.Dropdown(choices=languages, value="JP", label="Language")
|
language = gr.Dropdown(choices=languages, value="JP", label="Language")
|
||||||
with gr.Accordion(label="詳細設定", open=False):
|
with gr.Accordion(label="詳細設定", open=False):
|
||||||
sdp_ratio = gr.Slider(
|
sdp_ratio = gr.Slider(
|
||||||
@@ -340,9 +387,10 @@ if __name__ == "__main__":
|
|||||||
use_assist_text,
|
use_assist_text,
|
||||||
style,
|
style,
|
||||||
style_weight,
|
style_weight,
|
||||||
given_tone,
|
tone,
|
||||||
|
use_tone,
|
||||||
],
|
],
|
||||||
outputs=[text_output, audio_output],
|
outputs=[text_output, audio_output, tone],
|
||||||
)
|
)
|
||||||
|
|
||||||
model_name.change(
|
model_name.change(
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ class Model:
|
|||||||
def get_style_vector_from_audio(
|
def get_style_vector_from_audio(
|
||||||
self, audio_path: str, weight: float = 1.0
|
self, audio_path: str, weight: float = 1.0
|
||||||
) -> np.ndarray:
|
) -> np.ndarray:
|
||||||
from style_gen import extract_style_vector
|
from style_gen import get_style_vector
|
||||||
|
|
||||||
xvec = extract_style_vector(audio_path)
|
xvec = get_style_vector(audio_path)
|
||||||
mean = self.style_vectors[0]
|
mean = self.style_vectors[0]
|
||||||
xvec = mean + (xvec - mean) * weight
|
xvec = mean + (xvec - mean) * weight
|
||||||
return xvec
|
return xvec
|
||||||
@@ -190,7 +190,7 @@ class ModelHolder:
|
|||||||
if f.endswith(".pth") or f.endswith(".pt") or f.endswith(".safetensors")
|
if f.endswith(".pth") or f.endswith(".pt") or f.endswith(".safetensors")
|
||||||
]
|
]
|
||||||
if len(model_files) == 0:
|
if len(model_files) == 0:
|
||||||
logger.info(
|
logger.warning(
|
||||||
f"No model files found in {self.root_dir}/{model_name}, so skip it"
|
f"No model files found in {self.root_dir}/{model_name}, so skip it"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|||||||
10
infer.py
10
infer.py
@@ -11,6 +11,10 @@ from common.log import logger
|
|||||||
# latest_version = "1.0"
|
# latest_version = "1.0"
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidToneError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_net_g(model_path: str, version: str, device: str, hps):
|
def get_net_g(model_path: str, version: str, device: str, hps):
|
||||||
net_g = SynthesizerTrn(
|
net_g = SynthesizerTrn(
|
||||||
len(symbols),
|
len(symbols),
|
||||||
@@ -41,9 +45,11 @@ def get_text(
|
|||||||
):
|
):
|
||||||
# 在此处实现当前版本的get_text
|
# 在此处实现当前版本的get_text
|
||||||
norm_text, phone, tone, word2ph = clean_text(text, language_str)
|
norm_text, phone, tone, word2ph = clean_text(text, language_str)
|
||||||
logger.info(f"Original tone: {''.join(str(num) for num in tone)}")
|
|
||||||
if given_tone is not None:
|
if given_tone is not None:
|
||||||
logger.debug(f"Tone given: {given_tone}")
|
if len(given_tone) != len(phone):
|
||||||
|
raise InvalidToneError(
|
||||||
|
f"Length of given_tone ({len(given_tone)}) != length of phone ({len(phone)})"
|
||||||
|
)
|
||||||
tone = given_tone
|
tone = given_tone
|
||||||
phone, tone, language = cleaned_text_to_sequence(phone, tone, language_str)
|
phone, tone, language = cleaned_text_to_sequence(phone, tone, language_str)
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,14 @@ device = torch.device(config.style_gen_config.device)
|
|||||||
inference.to(device)
|
inference.to(device)
|
||||||
|
|
||||||
|
|
||||||
|
# 推論時にインポートするために短いが関数を書く
|
||||||
|
def get_style_vector(wav_path):
|
||||||
|
return inference(wav_path)
|
||||||
|
|
||||||
|
|
||||||
def save_style_vector(wav_path):
|
def save_style_vector(wav_path):
|
||||||
try:
|
try:
|
||||||
style_vec = inference(wav_path)
|
style_vec = get_style_vector(wav_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"\nError occurred with file: {wav_path}, Details:\n{e}\n")
|
logger.error(f"\nError occurred with file: {wav_path}, Details:\n{e}\n")
|
||||||
raise
|
raise
|
||||||
|
|||||||
482
text/japanese.py
482
text/japanese.py
@@ -3,13 +3,29 @@
|
|||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
import jaconv
|
|
||||||
import pyopenjtalk
|
import pyopenjtalk
|
||||||
from num2words import num2words
|
from num2words import num2words
|
||||||
from transformers import AutoTokenizer
|
from transformers import AutoTokenizer
|
||||||
|
|
||||||
from common.log import logger
|
from common.log import logger
|
||||||
from text import punctuation
|
from text import punctuation
|
||||||
|
from text.japanese_mora_list import (
|
||||||
|
mora_kata_to_mora_phonemes,
|
||||||
|
mora_phonemes_to_mora_kata,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 子音の集合
|
||||||
|
COSONANTS = set(
|
||||||
|
[
|
||||||
|
cosonant
|
||||||
|
for cosonant, _ in mora_kata_to_mora_phonemes.values()
|
||||||
|
if cosonant is not None
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 母音の集合
|
||||||
|
VOWELS = {"a", "i", "u", "e", "o"}
|
||||||
|
|
||||||
|
|
||||||
# 正規化で記号を変換するための辞書
|
# 正規化で記号を変換するための辞書
|
||||||
rep_map = {
|
rep_map = {
|
||||||
@@ -145,14 +161,14 @@ def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
|
|||||||
# アクセント割当をしなおすことによってpunctuationを含めた音素とアクセントのリストを作る。
|
# アクセント割当をしなおすことによってpunctuationを含めた音素とアクセントのリストを作る。
|
||||||
|
|
||||||
# punctuationがすべて消えた、音素とアクセントのタプルのリスト
|
# punctuationがすべて消えた、音素とアクセントのタプルのリスト
|
||||||
phone_tone_list_wo_punct = g2phone_tone_list(norm_text)
|
phone_tone_list_wo_punct = g2phone_tone_wo_punct(norm_text)
|
||||||
|
|
||||||
# sep_text: 単語単位の単語のリスト
|
# sep_text: 単語単位の単語のリスト
|
||||||
# sep_kata: 単語単位の単語のカタカナ読みのリスト
|
# sep_kata: 単語単位の単語のカタカナ読みのリスト
|
||||||
sep_text, sep_kata = text2sep_kata(norm_text)
|
sep_text, sep_kata = text2sep_kata(norm_text)
|
||||||
|
|
||||||
# sep_phonemes: 各単語ごとの音素のリストのリスト
|
# sep_phonemes: 各単語ごとの音素のリストのリスト
|
||||||
sep_phonemes = handle_long([kata2phoneme(i) for i in sep_kata])
|
sep_phonemes = handle_long([kata2phoneme_list(i) for i in sep_kata])
|
||||||
|
|
||||||
# phone_w_punct: sep_phonemesを結合した、punctuationを元のまま保持した音素列
|
# phone_w_punct: sep_phonemesを結合した、punctuationを元のまま保持した音素列
|
||||||
phone_w_punct: list[str] = []
|
phone_w_punct: list[str] = []
|
||||||
@@ -192,10 +208,71 @@ def g2p(norm_text: str) -> tuple[list[str], list[int], list[int]]:
|
|||||||
return phones, tones, word2ph
|
return phones, tones, word2ph
|
||||||
|
|
||||||
|
|
||||||
def g2phone_tone_list(text: str) -> list[tuple[str, int]]:
|
def g2kata_tone(norm_text: str) -> list[tuple[str, int]]:
|
||||||
|
phones, tones, _ = g2p(norm_text)
|
||||||
|
return phone_tone2kata_tone(list(zip(phones, tones)))
|
||||||
|
|
||||||
|
|
||||||
|
def phone_tone2kata_tone(phone_tone: list[tuple[str, int]]) -> list[tuple[str, int]]:
|
||||||
|
"""phone_toneをのphone部分をカタカナに変換する。ただし最初と最後の("_", 0)は無視"""
|
||||||
|
phone_tone = phone_tone[1:] # 最初の("_", 0)を無視
|
||||||
|
phones = [phone for phone, _ in phone_tone]
|
||||||
|
tones = [tone for _, tone in phone_tone]
|
||||||
|
logger.debug(f"phones: {phones}")
|
||||||
|
logger.debug(f"tones: {tones}")
|
||||||
|
result: list[tuple[str, int]] = []
|
||||||
|
current_mora = ""
|
||||||
|
for phone, next_phone, tone, next_tone in zip(phones, phones[1:], tones, tones[1:]):
|
||||||
|
# zipの関係で最後の("_", 0)は無視されている
|
||||||
|
if phone in punctuation:
|
||||||
|
result.append((phone, tone))
|
||||||
|
continue
|
||||||
|
if phone in COSONANTS and phone != "n": # 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":
|
||||||
|
logger.debug(f"current_mora: {current_mora}")
|
||||||
|
logger.debug(f"next_phone: {next_phone}")
|
||||||
|
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が母音
|
||||||
|
current_mora += phone
|
||||||
|
result.append((mora_phonemes_to_mora_kata[current_mora], tone))
|
||||||
|
current_mora = ""
|
||||||
|
logger.debug(f"result: {result}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def kata_tone2phone_tone(kata_tone: list[tuple[str, int]]) -> list[tuple[str, int]]:
|
||||||
|
"""`phone_tone2kata_tone()`の逆。"""
|
||||||
|
result: list[tuple[str, int]] = [("_", 0)]
|
||||||
|
for mora, tone in kata_tone:
|
||||||
|
if mora in punctuation:
|
||||||
|
result.append((mora, tone))
|
||||||
|
else:
|
||||||
|
cosonant, vowel = mora_kata_to_mora_phonemes[mora]
|
||||||
|
if cosonant is None:
|
||||||
|
result.append((vowel, tone))
|
||||||
|
else:
|
||||||
|
result.append((cosonant, tone))
|
||||||
|
result.append((vowel, tone))
|
||||||
|
result.append(("_", 0))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def g2phone_tone_wo_punct(text: str) -> list[tuple[str, int]]:
|
||||||
"""
|
"""
|
||||||
テキストに対して、音素とアクセント(0か1)のペアのリストを返す。
|
テキストに対して、音素とアクセント(0か1)のペアのリストを返す。
|
||||||
ただし「!」「.」「?」等の非音素記号は全て消える(ポーズ記号も残さない)。
|
ただし「!」「.」「?」等の非音素記号(punctuation)は全て消える(ポーズ記号も残さない)。
|
||||||
非音素記号を含める処理は`align_tones()`で行われる。
|
非音素記号を含める処理は`align_tones()`で行われる。
|
||||||
また「っ」は「q」に、「ん」は「n」に変換される。
|
また「っ」は「q」に、「ん」は「n」に変換される。
|
||||||
例: "こんにちは、世界ー。。元気?!" →
|
例: "こんにちは、世界ー。。元気?!" →
|
||||||
@@ -272,8 +349,7 @@ def text2sep_kata(norm_text: str) -> tuple[list[str], list[str]]:
|
|||||||
"""
|
"""
|
||||||
assert yomi != "", f"Empty yomi: {word}"
|
assert yomi != "", f"Empty yomi: {word}"
|
||||||
if yomi == "、":
|
if yomi == "、":
|
||||||
assert len(word) == 1, f"yomi `、` comes from: {word}"
|
# wordは正規化されているので、`.`, `,`, `!`, `'`, `-`のいずれか
|
||||||
# wordは1文字の記号。正規化されているので、`.`, `,`, `!`, `'`, `-`のいずれか
|
|
||||||
if word not in (
|
if word not in (
|
||||||
".",
|
".",
|
||||||
",",
|
",",
|
||||||
@@ -281,6 +357,7 @@ def text2sep_kata(norm_text: str) -> tuple[list[str], list[str]]:
|
|||||||
"'",
|
"'",
|
||||||
"-",
|
"-",
|
||||||
):
|
):
|
||||||
|
# ここはpyopenjtalkが読めない文字等のときに起こる
|
||||||
raise ValueError(f"Cannot read: {word} in:\n{norm_text}")
|
raise ValueError(f"Cannot read: {word} in:\n{norm_text}")
|
||||||
# yomiは元の記号のままに変更
|
# yomiは元の記号のままに変更
|
||||||
yomi = word
|
yomi = word
|
||||||
@@ -292,35 +369,6 @@ def text2sep_kata(norm_text: str) -> tuple[list[str], list[str]]:
|
|||||||
return sep_text, sep_kata
|
return sep_text, sep_kata
|
||||||
|
|
||||||
|
|
||||||
def kata2phoneme(text: str) -> list[str]:
|
|
||||||
"""
|
|
||||||
原則カタカナの`text`を受け取り、それをそのままいじらずに音素記号のリストに変換。
|
|
||||||
注意点:
|
|
||||||
- punctuationが来た場合(punctuationが1文字の場合がありうる)、処理せず1文字のリストを返す
|
|
||||||
- 冒頭に続く「ー」はそのまま「ー」のままにする(`handle_long()`で処理される)
|
|
||||||
- 文中の「ー」は前の音素記号の最後の音素記号に変換される。
|
|
||||||
例:
|
|
||||||
`ーーソーナノカーー` → ["ー", "ー", "s", "o", "o", "n", "a", "n", "o", "k", "a", "a", "a"]
|
|
||||||
`?` → ["?"]
|
|
||||||
"""
|
|
||||||
if text in punctuation:
|
|
||||||
return [text]
|
|
||||||
# `text`がカタカナ(`ー`含む)のみからなるかどうかをチェック
|
|
||||||
if re.fullmatch(r"[\u30A0-\u30FF]+", text) is None:
|
|
||||||
raise ValueError(f"Non-punctuation input must be katakana only: {text}")
|
|
||||||
# 以降`text`はカタカナのみからなる
|
|
||||||
if text == "ー":
|
|
||||||
return ["ー"]
|
|
||||||
elif text.startswith("ー"):
|
|
||||||
return ["ー"] + kata2phoneme(text[1:])
|
|
||||||
res: list[str] = []
|
|
||||||
while text:
|
|
||||||
# カタカナをひらがなに変換してから`hiragana2p`をかける
|
|
||||||
res += hiragana2p(jaconv.kata2hira(text)).split(" ")
|
|
||||||
break
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
# ESPnetの実装から引用、変更点無し
|
# ESPnetの実装から引用、変更点無し
|
||||||
# https://github.com/espnet/espnet/blob/master/espnet2/text/phoneme_tokenizer.py
|
# 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]:
|
def pyopenjtalk_g2p_prosody(text: str, drop_unvoiced_vowels: bool = True) -> list[str]:
|
||||||
@@ -492,346 +540,38 @@ def align_tones(
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
# jaconvから借りて修正
|
def kata2phoneme_list(text: str) -> list[str]:
|
||||||
# https://github.com/ikegami-yukino/jaconv/blob/master/jaconv/jaconv.py
|
|
||||||
def hiragana2p(text: str) -> str:
|
|
||||||
"""
|
"""
|
||||||
Modification of `jaconv.hiragana2julius`.
|
原則カタカナの`text`を受け取り、それをそのままいじらずに音素記号のリストに変換。
|
||||||
- avoid using `:`, instead, `あーーー` -> `a a a a`.
|
注意点:
|
||||||
- avoid converting `o u` to `o o` (because the input is already actual `yomi`).
|
- punctuationが来た場合(punctuationが1文字の場合がありうる)、処理せず1文字のリストを返す
|
||||||
- avoid using `N` for `ん` (for compatibility)
|
- 冒頭に続く「ー」はそのまま「ー」のままにする(`handle_long()`で処理される)
|
||||||
- use `v` for `ゔ` related text.
|
- 文中の「ー」は前の音素記号の最後の音素記号に変換される。
|
||||||
- add bare `ゃ` `ゅ` `ょ` to `y a` `y u` `y o` (for compatibility).
|
例:
|
||||||
|
`ーーソーナノカーー` → ["ー", "ー", "s", "o", "o", "n", "a", "n", "o", "k", "a", "a", "a"]
|
||||||
|
`?` → ["?"]
|
||||||
"""
|
"""
|
||||||
# 3文字以上からなる変換規則
|
if text in punctuation:
|
||||||
text = text.replace("う゛ぁ", " v a")
|
return [text]
|
||||||
text = text.replace("う゛ぃ", " v i")
|
# `text`がカタカナ(`ー`含む)のみからなるかどうかをチェック
|
||||||
text = text.replace("う゛ぇ", " v e")
|
if re.fullmatch(r"[\u30A0-\u30FF]+", text) is None:
|
||||||
text = text.replace("う゛ぉ", " v o")
|
raise ValueError(f"Input must be katakana only: {text}")
|
||||||
text = text.replace("う゛ゅ", " by u")
|
sorted_keys = sorted(mora_kata_to_mora_phonemes.keys(), key=len, reverse=True)
|
||||||
|
pattern = "|".join(map(re.escape, sorted_keys))
|
||||||
|
|
||||||
# ゔ等の処理を追加
|
def mora2phonemes(mora: str) -> str:
|
||||||
text = text.replace("ゔぁ", " v a")
|
cosonant, vowel = mora_kata_to_mora_phonemes[mora]
|
||||||
text = text.replace("ゔぃ", " v i")
|
if cosonant is None:
|
||||||
text = text.replace("ゔぇ", " v e")
|
return f" {vowel}"
|
||||||
text = text.replace("ゔぉ", " v o")
|
return f" {cosonant} {vowel}"
|
||||||
text = text.replace("ゔゅ", " by u")
|
|
||||||
|
|
||||||
# 2文字からなる変換規則
|
spaced_phonemes = re.sub(pattern, lambda m: mora2phonemes(m.group()), text)
|
||||||
text = text.replace("ぅ゛", " v u")
|
|
||||||
|
|
||||||
text = text.replace("あぁ", " a a")
|
# 長音記号「ー」の処理
|
||||||
text = text.replace("いぃ", " i i")
|
long_pattern = r"(\w)(ー*)"
|
||||||
text = text.replace("いぇ", " i e")
|
long_replacement = lambda m: m.group(1) + (" " + m.group(1)) * len(m.group(2))
|
||||||
text = text.replace("いゃ", " y a")
|
spaced_phonemes = re.sub(long_pattern, long_replacement, spaced_phonemes)
|
||||||
text = text.replace("うぅ", " u:")
|
return spaced_phonemes.strip().split(" ")
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
229
text/japanese_mora_list.py
Normal file
229
text/japanese_mora_list.py
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
"""
|
||||||
|
VOICEVOXのソースコードからお借りして最低限に改造したコード。
|
||||||
|
https://github.com/VOICEVOX/voicevox_engine/blob/master/voicevox_engine/tts_pipeline/mora_list.py
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
以下のモーラ対応表はOpenJTalkのソースコードから取得し、
|
||||||
|
カタカナ表記とモーラが一対一対応するように改造した。
|
||||||
|
ライセンス表記:
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
The Japanese TTS System "Open JTalk"
|
||||||
|
developed by HTS Working Group
|
||||||
|
http://open-jtalk.sourceforge.net/
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2008-2014 Nagoya Institute of Technology
|
||||||
|
Department of Computer Science
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or
|
||||||
|
without modification, are permitted provided that the following
|
||||||
|
conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
- Neither the name of the HTS working group nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||||
|
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# カタカナ, 子音, 母音)の順。子音がない場合はNoneを入れる。
|
||||||
|
# 但し「ン」と「ッ」は母音のみという扱いで、「ン」は「N」、「ッ」は「q」に変更
|
||||||
|
# (元々はそれぞれ「N」「cl」)
|
||||||
|
_mora_list_minimum: list[tuple[str, str | None, str]] = [
|
||||||
|
("ヴォ", "v", "o"),
|
||||||
|
("ヴェ", "v", "e"),
|
||||||
|
("ヴィ", "v", "i"),
|
||||||
|
("ヴァ", "v", "a"),
|
||||||
|
("ヴ", "v", "u"),
|
||||||
|
("ン", None, "n"), # 「N」から「n」に変更
|
||||||
|
("ワ", "w", "a"),
|
||||||
|
("ロ", "r", "o"),
|
||||||
|
("レ", "r", "e"),
|
||||||
|
("ル", "r", "u"),
|
||||||
|
("リョ", "ry", "o"),
|
||||||
|
("リュ", "ry", "u"),
|
||||||
|
("リャ", "ry", "a"),
|
||||||
|
("リェ", "ry", "e"),
|
||||||
|
("リ", "r", "i"),
|
||||||
|
("ラ", "r", "a"),
|
||||||
|
("ヨ", "y", "o"),
|
||||||
|
("ユ", "y", "u"),
|
||||||
|
("ヤ", "y", "a"),
|
||||||
|
("モ", "m", "o"),
|
||||||
|
("メ", "m", "e"),
|
||||||
|
("ム", "m", "u"),
|
||||||
|
("ミョ", "my", "o"),
|
||||||
|
("ミュ", "my", "u"),
|
||||||
|
("ミャ", "my", "a"),
|
||||||
|
("ミェ", "my", "e"),
|
||||||
|
("ミ", "m", "i"),
|
||||||
|
("マ", "m", "a"),
|
||||||
|
("ポ", "p", "o"),
|
||||||
|
("ボ", "b", "o"),
|
||||||
|
("ホ", "h", "o"),
|
||||||
|
("ペ", "p", "e"),
|
||||||
|
("ベ", "b", "e"),
|
||||||
|
("ヘ", "h", "e"),
|
||||||
|
("プ", "p", "u"),
|
||||||
|
("ブ", "b", "u"),
|
||||||
|
("フォ", "f", "o"),
|
||||||
|
("フェ", "f", "e"),
|
||||||
|
("フィ", "f", "i"),
|
||||||
|
("ファ", "f", "a"),
|
||||||
|
("フ", "f", "u"),
|
||||||
|
("ピョ", "py", "o"),
|
||||||
|
("ピュ", "py", "u"),
|
||||||
|
("ピャ", "py", "a"),
|
||||||
|
("ピェ", "py", "e"),
|
||||||
|
("ピ", "p", "i"),
|
||||||
|
("ビョ", "by", "o"),
|
||||||
|
("ビュ", "by", "u"),
|
||||||
|
("ビャ", "by", "a"),
|
||||||
|
("ビェ", "by", "e"),
|
||||||
|
("ビ", "b", "i"),
|
||||||
|
("ヒョ", "hy", "o"),
|
||||||
|
("ヒュ", "hy", "u"),
|
||||||
|
("ヒャ", "hy", "a"),
|
||||||
|
("ヒェ", "hy", "e"),
|
||||||
|
("ヒ", "h", "i"),
|
||||||
|
("パ", "p", "a"),
|
||||||
|
("バ", "b", "a"),
|
||||||
|
("ハ", "h", "a"),
|
||||||
|
("ノ", "n", "o"),
|
||||||
|
("ネ", "n", "e"),
|
||||||
|
("ヌ", "n", "u"),
|
||||||
|
("ニョ", "ny", "o"),
|
||||||
|
("ニュ", "ny", "u"),
|
||||||
|
("ニャ", "ny", "a"),
|
||||||
|
("ニェ", "ny", "e"),
|
||||||
|
("ニ", "n", "i"),
|
||||||
|
("ナ", "n", "a"),
|
||||||
|
("ドゥ", "d", "u"),
|
||||||
|
("ド", "d", "o"),
|
||||||
|
("トゥ", "t", "u"),
|
||||||
|
("ト", "t", "o"),
|
||||||
|
("デョ", "dy", "o"),
|
||||||
|
("デュ", "dy", "u"),
|
||||||
|
("デャ", "dy", "a"),
|
||||||
|
("デェ", "dy", "e"),
|
||||||
|
("ディ", "d", "i"),
|
||||||
|
("デ", "d", "e"),
|
||||||
|
("テョ", "ty", "o"),
|
||||||
|
("テュ", "ty", "u"),
|
||||||
|
("テャ", "ty", "a"),
|
||||||
|
("ティ", "t", "i"),
|
||||||
|
("テ", "t", "e"),
|
||||||
|
("ツォ", "ts", "o"),
|
||||||
|
("ツェ", "ts", "e"),
|
||||||
|
("ツィ", "ts", "i"),
|
||||||
|
("ツァ", "ts", "a"),
|
||||||
|
("ツ", "ts", "u"),
|
||||||
|
("ッ", None, "q"), # 「cl」から「q」に変更
|
||||||
|
("チョ", "ch", "o"),
|
||||||
|
("チュ", "ch", "u"),
|
||||||
|
("チャ", "ch", "a"),
|
||||||
|
("チェ", "ch", "e"),
|
||||||
|
("チ", "ch", "i"),
|
||||||
|
("ダ", "d", "a"),
|
||||||
|
("タ", "t", "a"),
|
||||||
|
("ゾ", "z", "o"),
|
||||||
|
("ソ", "s", "o"),
|
||||||
|
("ゼ", "z", "e"),
|
||||||
|
("セ", "s", "e"),
|
||||||
|
("ズィ", "z", "i"),
|
||||||
|
("ズ", "z", "u"),
|
||||||
|
("スィ", "s", "i"),
|
||||||
|
("ス", "s", "u"),
|
||||||
|
("ジョ", "j", "o"),
|
||||||
|
("ジュ", "j", "u"),
|
||||||
|
("ジャ", "j", "a"),
|
||||||
|
("ジェ", "j", "e"),
|
||||||
|
("ジ", "j", "i"),
|
||||||
|
("ショ", "sh", "o"),
|
||||||
|
("シュ", "sh", "u"),
|
||||||
|
("シャ", "sh", "a"),
|
||||||
|
("シェ", "sh", "e"),
|
||||||
|
("シ", "sh", "i"),
|
||||||
|
("ザ", "z", "a"),
|
||||||
|
("サ", "s", "a"),
|
||||||
|
("ゴ", "g", "o"),
|
||||||
|
("コ", "k", "o"),
|
||||||
|
("ゲ", "g", "e"),
|
||||||
|
("ケ", "k", "e"),
|
||||||
|
("グヮ", "gw", "a"),
|
||||||
|
("グ", "g", "u"),
|
||||||
|
("クヮ", "kw", "a"),
|
||||||
|
("ク", "k", "u"),
|
||||||
|
("ギョ", "gy", "o"),
|
||||||
|
("ギュ", "gy", "u"),
|
||||||
|
("ギャ", "gy", "a"),
|
||||||
|
("ギェ", "gy", "e"),
|
||||||
|
("ギ", "g", "i"),
|
||||||
|
("キョ", "ky", "o"),
|
||||||
|
("キュ", "ky", "u"),
|
||||||
|
("キャ", "ky", "a"),
|
||||||
|
("キェ", "ky", "e"),
|
||||||
|
("キ", "k", "i"),
|
||||||
|
("ガ", "g", "a"),
|
||||||
|
("カ", "k", "a"),
|
||||||
|
("オ", None, "o"),
|
||||||
|
("エ", None, "e"),
|
||||||
|
("ウォ", "w", "o"),
|
||||||
|
("ウェ", "w", "e"),
|
||||||
|
("ウィ", "w", "i"),
|
||||||
|
("ウ", None, "u"),
|
||||||
|
("イェ", "y", "e"),
|
||||||
|
("イ", None, "i"),
|
||||||
|
("ア", None, "a"),
|
||||||
|
]
|
||||||
|
_mora_list_additional: list[tuple[str, str | None, str]] = [
|
||||||
|
("ヴョ", "by", "o"),
|
||||||
|
("ヴュ", "by", "u"),
|
||||||
|
("ヴャ", "by", "a"),
|
||||||
|
("ヲ", None, "o"),
|
||||||
|
("ヱ", None, "e"),
|
||||||
|
("ヰ", None, "i"),
|
||||||
|
("ヮ", "w", "a"),
|
||||||
|
("ョ", "y", "o"),
|
||||||
|
("ュ", "y", "u"),
|
||||||
|
("ヅ", "z", "u"),
|
||||||
|
("ヂ", "j", "i"),
|
||||||
|
("ヶ", "k", "e"),
|
||||||
|
("ャ", "y", "a"),
|
||||||
|
("ォ", None, "o"),
|
||||||
|
("ェ", None, "e"),
|
||||||
|
("ゥ", None, "u"),
|
||||||
|
("ィ", None, "i"),
|
||||||
|
("ァ", None, "a"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# 例: "vo" -> "ヴォ", "a" -> "ア"
|
||||||
|
mora_phonemes_to_mora_kata: dict[str, str] = {
|
||||||
|
(consonant or "") + vowel: kana for [kana, consonant, vowel] in _mora_list_minimum
|
||||||
|
}
|
||||||
|
|
||||||
|
# 例: "ヴォ" -> ("v", "o"), "ア" -> (None, "a")
|
||||||
|
mora_kata_to_mora_phonemes: dict[str, tuple[str | None, str]] = {
|
||||||
|
kana: (consonant, vowel)
|
||||||
|
for [kana, consonant, vowel] in _mora_list_minimum + _mora_list_additional
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user