Spaces:
Sleeping
Sleeping
update: bert_vits2 japanese
Browse files- app.py +1 -1
- bert_vits2/bert_vits2.py +13 -2
- bert_vits2/models.py +12 -3
- bert_vits2/text/__init__.py +19 -18
- bert_vits2/text/chinese_bert.py +3 -6
- bert_vits2/text/japanese.py +572 -92
- bert_vits2/text/japanese_bert.py +34 -0
- bert_vits2/text/symbols.py +189 -41
- bert_vits2/text/tone_sandhi.py +514 -96
- config.py +4 -0
- requirements.txt +2 -1
- utils/{merge.py → load_model.py} +0 -0
- utils/{nlp.py → sentence.py} +0 -0
- vits/vits.py +1 -1
app.py
CHANGED
|
@@ -7,7 +7,7 @@ from werkzeug.utils import secure_filename
|
|
| 7 |
from flask_apscheduler import APScheduler
|
| 8 |
from functools import wraps
|
| 9 |
from utils.utils import clean_folder, check_is_none
|
| 10 |
-
from utils.
|
| 11 |
from io import BytesIO
|
| 12 |
|
| 13 |
app = Flask(__name__)
|
|
|
|
| 7 |
from flask_apscheduler import APScheduler
|
| 8 |
from functools import wraps
|
| 9 |
from utils.utils import clean_folder, check_is_none
|
| 10 |
+
from utils.load_model import merge_model
|
| 11 |
from io import BytesIO
|
| 12 |
|
| 13 |
app = Flask(__name__)
|
bert_vits2/bert_vits2.py
CHANGED
|
@@ -7,7 +7,8 @@ from bert_vits2 import utils, commons
|
|
| 7 |
from bert_vits2.models import SynthesizerTrn
|
| 8 |
from bert_vits2.text import symbols, cleaned_text_to_sequence, get_bert
|
| 9 |
from bert_vits2.text.cleaner import clean_text
|
| 10 |
-
from
|
|
|
|
| 11 |
|
| 12 |
|
| 13 |
class Bert_VITS2:
|
|
@@ -16,11 +17,20 @@ class Bert_VITS2:
|
|
| 16 |
self.n_speakers = getattr(self.hps_ms.data, 'n_speakers', 0)
|
| 17 |
self.speakers = [item[0] for item in
|
| 18 |
sorted(list(getattr(self.hps_ms.data, 'spk2id', {'0': 0}).items()), key=lambda x: x[1])]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
self.net_g = SynthesizerTrn(
|
| 20 |
len(symbols),
|
| 21 |
self.hps_ms.data.filter_length // 2 + 1,
|
| 22 |
self.hps_ms.train.segment_size // self.hps_ms.data.hop_length,
|
| 23 |
n_speakers=self.hps_ms.data.n_speakers,
|
|
|
|
|
|
|
|
|
|
| 24 |
**self.hps_ms.model).to(device)
|
| 25 |
_ = self.net_g.eval()
|
| 26 |
self.device = device
|
|
@@ -35,7 +45,8 @@ class Bert_VITS2:
|
|
| 35 |
def get_text(self, text, language_str, hps):
|
| 36 |
norm_text, phone, tone, word2ph = clean_text(text, language_str)
|
| 37 |
# print([f"{p}{t}" for p, t in zip(phone, tone)])
|
| 38 |
-
phone, tone, language = cleaned_text_to_sequence(phone, tone, language_str
|
|
|
|
| 39 |
|
| 40 |
if hps.data.add_blank:
|
| 41 |
phone = commons.intersperse(phone, 0)
|
|
|
|
| 7 |
from bert_vits2.models import SynthesizerTrn
|
| 8 |
from bert_vits2.text import symbols, cleaned_text_to_sequence, get_bert
|
| 9 |
from bert_vits2.text.cleaner import clean_text
|
| 10 |
+
from bert_vits2.text.symbols import get_symbols
|
| 11 |
+
from utils.sentence import sentence_split, cut
|
| 12 |
|
| 13 |
|
| 14 |
class Bert_VITS2:
|
|
|
|
| 17 |
self.n_speakers = getattr(self.hps_ms.data, 'n_speakers', 0)
|
| 18 |
self.speakers = [item[0] for item in
|
| 19 |
sorted(list(getattr(self.hps_ms.data, 'spk2id', {'0': 0}).items()), key=lambda x: x[1])]
|
| 20 |
+
|
| 21 |
+
self.legacy = getattr(self.hps_ms.data, 'legacy', False)
|
| 22 |
+
symbols, num_tones, self.language_id_map, num_languages, self.language_tone_start_map = get_symbols(
|
| 23 |
+
legacy=self.legacy)
|
| 24 |
+
self._symbol_to_id = {s: i for i, s in enumerate(symbols)}
|
| 25 |
+
|
| 26 |
self.net_g = SynthesizerTrn(
|
| 27 |
len(symbols),
|
| 28 |
self.hps_ms.data.filter_length // 2 + 1,
|
| 29 |
self.hps_ms.train.segment_size // self.hps_ms.data.hop_length,
|
| 30 |
n_speakers=self.hps_ms.data.n_speakers,
|
| 31 |
+
symbols=symbols,
|
| 32 |
+
num_tones=num_tones,
|
| 33 |
+
num_languages=num_languages,
|
| 34 |
**self.hps_ms.model).to(device)
|
| 35 |
_ = self.net_g.eval()
|
| 36 |
self.device = device
|
|
|
|
| 45 |
def get_text(self, text, language_str, hps):
|
| 46 |
norm_text, phone, tone, word2ph = clean_text(text, language_str)
|
| 47 |
# print([f"{p}{t}" for p, t in zip(phone, tone)])
|
| 48 |
+
phone, tone, language = cleaned_text_to_sequence(phone, tone, language_str, self._symbol_to_id,
|
| 49 |
+
self.language_tone_start_map, self.language_id_map)
|
| 50 |
|
| 51 |
if hps.data.add_blank:
|
| 52 |
phone = commons.intersperse(phone, 0)
|
bert_vits2/models.py
CHANGED
|
@@ -11,7 +11,6 @@ from torch.nn import Conv1d, ConvTranspose1d, AvgPool1d, Conv2d
|
|
| 11 |
from torch.nn.utils import weight_norm, remove_weight_norm, spectral_norm
|
| 12 |
|
| 13 |
from bert_vits2.commons import init_weights, get_padding
|
| 14 |
-
from bert_vits2.text import symbols, num_tones, num_languages
|
| 15 |
|
| 16 |
|
| 17 |
class DurationDiscriminator(nn.Module): # vits2
|
|
@@ -254,7 +253,10 @@ class TextEncoder(nn.Module):
|
|
| 254 |
n_layers,
|
| 255 |
kernel_size,
|
| 256 |
p_dropout,
|
| 257 |
-
gin_channels=0
|
|
|
|
|
|
|
|
|
|
| 258 |
super().__init__()
|
| 259 |
self.n_vocab = n_vocab
|
| 260 |
self.out_channels = out_channels
|
|
@@ -620,6 +622,9 @@ class SynthesizerTrn(nn.Module):
|
|
| 620 |
self.current_mas_noise_scale = self.mas_noise_scale_initial
|
| 621 |
if self.use_spk_conditioned_encoder and gin_channels > 0:
|
| 622 |
self.enc_gin_channels = gin_channels
|
|
|
|
|
|
|
|
|
|
| 623 |
self.enc_p = TextEncoder(n_vocab,
|
| 624 |
inter_channels,
|
| 625 |
hidden_channels,
|
|
@@ -628,7 +633,11 @@ class SynthesizerTrn(nn.Module):
|
|
| 628 |
n_layers,
|
| 629 |
kernel_size,
|
| 630 |
p_dropout,
|
| 631 |
-
gin_channels=self.enc_gin_channels
|
|
|
|
|
|
|
|
|
|
|
|
|
| 632 |
self.dec = Generator(inter_channels, resblock, resblock_kernel_sizes, resblock_dilation_sizes, upsample_rates,
|
| 633 |
upsample_initial_channel, upsample_kernel_sizes, gin_channels=gin_channels)
|
| 634 |
self.enc_q = PosteriorEncoder(spec_channels, inter_channels, hidden_channels, 5, 1, 16,
|
|
|
|
| 11 |
from torch.nn.utils import weight_norm, remove_weight_norm, spectral_norm
|
| 12 |
|
| 13 |
from bert_vits2.commons import init_weights, get_padding
|
|
|
|
| 14 |
|
| 15 |
|
| 16 |
class DurationDiscriminator(nn.Module): # vits2
|
|
|
|
| 253 |
n_layers,
|
| 254 |
kernel_size,
|
| 255 |
p_dropout,
|
| 256 |
+
gin_channels=0,
|
| 257 |
+
symbols=None,
|
| 258 |
+
num_tones=None,
|
| 259 |
+
num_languages=None):
|
| 260 |
super().__init__()
|
| 261 |
self.n_vocab = n_vocab
|
| 262 |
self.out_channels = out_channels
|
|
|
|
| 622 |
self.current_mas_noise_scale = self.mas_noise_scale_initial
|
| 623 |
if self.use_spk_conditioned_encoder and gin_channels > 0:
|
| 624 |
self.enc_gin_channels = gin_channels
|
| 625 |
+
symbols = kwargs.get("symbols")
|
| 626 |
+
num_tones = kwargs.get("num_tones")
|
| 627 |
+
num_languages = kwargs.get("num_languages")
|
| 628 |
self.enc_p = TextEncoder(n_vocab,
|
| 629 |
inter_channels,
|
| 630 |
hidden_channels,
|
|
|
|
| 633 |
n_layers,
|
| 634 |
kernel_size,
|
| 635 |
p_dropout,
|
| 636 |
+
gin_channels=self.enc_gin_channels,
|
| 637 |
+
symbols=symbols,
|
| 638 |
+
num_tones=num_tones,
|
| 639 |
+
num_languages=num_languages
|
| 640 |
+
)
|
| 641 |
self.dec = Generator(inter_channels, resblock, resblock_kernel_sizes, resblock_dilation_sizes, upsample_rates,
|
| 642 |
upsample_initial_channel, upsample_kernel_sizes, gin_channels=gin_channels)
|
| 643 |
self.enc_q = PosteriorEncoder(spec_channels, inter_channels, hidden_channels, 5, 1, 16,
|
bert_vits2/text/__init__.py
CHANGED
|
@@ -1,17 +1,12 @@
|
|
| 1 |
-
from bert_vits2.text.symbols import
|
| 2 |
-
from .chinese_bert import get_bert_feature as zh_bert
|
| 3 |
-
from .english_bert_mock import get_bert_feature as en_bert
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
Returns:
|
| 13 |
-
List of integers corresponding to the symbols in the text
|
| 14 |
-
'''
|
| 15 |
phones = [_symbol_to_id[symbol] for symbol in cleaned_text]
|
| 16 |
tone_start = language_tone_start_map[language]
|
| 17 |
tones = [i + tone_start for i in tones]
|
|
@@ -21,9 +16,15 @@ def cleaned_text_to_sequence(cleaned_text, tones, language):
|
|
| 21 |
|
| 22 |
|
| 23 |
def get_bert(norm_text, word2ph, language):
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
return bert
|
|
|
|
| 1 |
+
from bert_vits2.text.symbols import punctuation
|
|
|
|
|
|
|
| 2 |
|
| 3 |
+
def cleaned_text_to_sequence(cleaned_text, tones, language, _symbol_to_id, language_tone_start_map, language_id_map):
|
| 4 |
+
"""Converts a string of text to a sequence of IDs corresponding to the symbols in the text.
|
| 5 |
+
Args:
|
| 6 |
+
text: string to convert to a sequence
|
| 7 |
+
Returns:
|
| 8 |
+
List of integers corresponding to the symbols in the text
|
| 9 |
+
"""
|
|
|
|
|
|
|
|
|
|
| 10 |
phones = [_symbol_to_id[symbol] for symbol in cleaned_text]
|
| 11 |
tone_start = language_tone_start_map[language]
|
| 12 |
tones = [i + tone_start for i in tones]
|
|
|
|
| 16 |
|
| 17 |
|
| 18 |
def get_bert(norm_text, word2ph, language):
|
| 19 |
+
if language == "ZH":
|
| 20 |
+
from .chinese_bert import get_bert_feature as zh_bert
|
| 21 |
+
lang_bert_func = zh_bert
|
| 22 |
+
elif language == "EN":
|
| 23 |
+
from .english_bert_mock import get_bert_feature as en_bert
|
| 24 |
+
lang_bert_func = en_bert
|
| 25 |
+
elif language == "JP":
|
| 26 |
+
from .japanese_bert import get_bert_feature as jp_bert
|
| 27 |
+
lang_bert_func = jp_bert
|
| 28 |
+
|
| 29 |
+
bert = lang_bert_func(norm_text, word2ph)
|
| 30 |
return bert
|
bert_vits2/text/chinese_bert.py
CHANGED
|
@@ -3,20 +3,18 @@ import torch
|
|
| 3 |
from transformers import AutoTokenizer, AutoModelForMaskedLM
|
| 4 |
from logger import logger
|
| 5 |
|
| 6 |
-
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 7 |
-
|
| 8 |
try:
|
| 9 |
logger.info("Loading chinese-roberta-wwm-ext-large...")
|
| 10 |
tokenizer = AutoTokenizer.from_pretrained(config.ABS_PATH + "/bert_vits2/bert/chinese-roberta-wwm-ext-large")
|
| 11 |
model = AutoModelForMaskedLM.from_pretrained(config.ABS_PATH + "/bert_vits2/bert/chinese-roberta-wwm-ext-large").to(
|
| 12 |
-
|
| 13 |
logger.info("Loading finished.")
|
| 14 |
except Exception as e:
|
| 15 |
logger.error(e)
|
| 16 |
-
logger.error(f"Please download
|
| 17 |
|
| 18 |
|
| 19 |
-
def get_bert_feature(text, word2ph):
|
| 20 |
with torch.no_grad():
|
| 21 |
inputs = tokenizer(text, return_tensors='pt')
|
| 22 |
for i in inputs:
|
|
@@ -37,7 +35,6 @@ def get_bert_feature(text, word2ph):
|
|
| 37 |
|
| 38 |
|
| 39 |
if __name__ == '__main__':
|
| 40 |
-
# feature = get_bert_feature('你好,我是说的道理。')
|
| 41 |
import torch
|
| 42 |
|
| 43 |
word_level_feature = torch.rand(38, 1024) # 12个词,每个词1024维特征
|
|
|
|
| 3 |
from transformers import AutoTokenizer, AutoModelForMaskedLM
|
| 4 |
from logger import logger
|
| 5 |
|
|
|
|
|
|
|
| 6 |
try:
|
| 7 |
logger.info("Loading chinese-roberta-wwm-ext-large...")
|
| 8 |
tokenizer = AutoTokenizer.from_pretrained(config.ABS_PATH + "/bert_vits2/bert/chinese-roberta-wwm-ext-large")
|
| 9 |
model = AutoModelForMaskedLM.from_pretrained(config.ABS_PATH + "/bert_vits2/bert/chinese-roberta-wwm-ext-large").to(
|
| 10 |
+
config.DEVICE)
|
| 11 |
logger.info("Loading finished.")
|
| 12 |
except Exception as e:
|
| 13 |
logger.error(e)
|
| 14 |
+
logger.error(f"Please download pytorch_model.bin from hfl/chinese-roberta-wwm-ext-large.")
|
| 15 |
|
| 16 |
|
| 17 |
+
def get_bert_feature(text, word2ph, device=config.DEVICE):
|
| 18 |
with torch.no_grad():
|
| 19 |
inputs = tokenizer(text, return_tensors='pt')
|
| 20 |
for i in inputs:
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
if __name__ == '__main__':
|
|
|
|
| 38 |
import torch
|
| 39 |
|
| 40 |
word_level_feature = torch.rand(38, 1024) # 12个词,每个词1024维特征
|
bert_vits2/text/japanese.py
CHANGED
|
@@ -1,104 +1,584 @@
|
|
| 1 |
-
#
|
|
|
|
| 2 |
import re
|
| 3 |
-
import
|
| 4 |
-
|
| 5 |
-
import
|
| 6 |
-
|
| 7 |
-
from bert_vits2.text import symbols
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
#
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
|
| 84 |
def text_normalize(text):
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
|
| 88 |
def g2p(norm_text):
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
|
|
|
|
|
|
| 92 |
tones = [0 for i in phones]
|
| 93 |
-
word2ph = [1
|
| 94 |
return phones, tones, word2ph
|
| 95 |
|
| 96 |
|
| 97 |
-
if __name__ ==
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Convert Japanese text to phonemes which is
|
| 2 |
+
# compatible with Julius https://github.com/julius-speech/segmentation-kit
|
| 3 |
import re
|
| 4 |
+
import unicodedata
|
| 5 |
+
|
| 6 |
+
from transformers import AutoTokenizer
|
| 7 |
+
|
| 8 |
+
from bert_vits2.text import punctuation, symbols
|
| 9 |
+
from bert_vits2.text.japanese_bert import tokenizer
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
import MeCab
|
| 13 |
+
except ImportError as e:
|
| 14 |
+
raise ImportError("Japanese requires mecab-python3 and unidic-lite.") from e
|
| 15 |
+
from num2words import num2words
|
| 16 |
+
|
| 17 |
+
_CONVRULES = [
|
| 18 |
+
# Conversion of 2 letters
|
| 19 |
+
"アァ/ a a",
|
| 20 |
+
"イィ/ i i",
|
| 21 |
+
"イェ/ i e",
|
| 22 |
+
"イャ/ y a",
|
| 23 |
+
"ウゥ/ u:",
|
| 24 |
+
"エェ/ e e",
|
| 25 |
+
"オォ/ o:",
|
| 26 |
+
"カァ/ k a:",
|
| 27 |
+
"キィ/ k i:",
|
| 28 |
+
"クゥ/ k u:",
|
| 29 |
+
"クャ/ ky a",
|
| 30 |
+
"クュ/ ky u",
|
| 31 |
+
"クョ/ ky o",
|
| 32 |
+
"ケェ/ k e:",
|
| 33 |
+
"コォ/ k o:",
|
| 34 |
+
"ガァ/ g a:",
|
| 35 |
+
"ギィ/ g i:",
|
| 36 |
+
"グゥ/ g u:",
|
| 37 |
+
"グャ/ gy a",
|
| 38 |
+
"グュ/ gy u",
|
| 39 |
+
"グョ/ gy o",
|
| 40 |
+
"ゲェ/ g e:",
|
| 41 |
+
"ゴォ/ g o:",
|
| 42 |
+
"サァ/ s a:",
|
| 43 |
+
"シィ/ sh i:",
|
| 44 |
+
"スゥ/ s u:",
|
| 45 |
+
"スャ/ sh a",
|
| 46 |
+
"スュ/ sh u",
|
| 47 |
+
"スョ/ sh o",
|
| 48 |
+
"セェ/ s e:",
|
| 49 |
+
"ソォ/ s o:",
|
| 50 |
+
"ザァ/ z a:",
|
| 51 |
+
"ジィ/ j i:",
|
| 52 |
+
"ズゥ/ z u:",
|
| 53 |
+
"ズャ/ zy a",
|
| 54 |
+
"ズュ/ zy u",
|
| 55 |
+
"ズョ/ zy o",
|
| 56 |
+
"ゼェ/ z e:",
|
| 57 |
+
"ゾォ/ z o:",
|
| 58 |
+
"タァ/ t a:",
|
| 59 |
+
"チィ/ ch i:",
|
| 60 |
+
"ツァ/ ts a",
|
| 61 |
+
"ツィ/ ts i",
|
| 62 |
+
"ツゥ/ ts u:",
|
| 63 |
+
"ツャ/ ch a",
|
| 64 |
+
"ツュ/ ch u",
|
| 65 |
+
"ツョ/ ch o",
|
| 66 |
+
"ツェ/ ts e",
|
| 67 |
+
"ツォ/ ts o",
|
| 68 |
+
"テェ/ t e:",
|
| 69 |
+
"トォ/ t o:",
|
| 70 |
+
"ダァ/ d a:",
|
| 71 |
+
"ヂィ/ j i:",
|
| 72 |
+
"ヅゥ/ d u:",
|
| 73 |
+
"ヅャ/ zy a",
|
| 74 |
+
"ヅュ/ zy u",
|
| 75 |
+
"ヅョ/ zy o",
|
| 76 |
+
"デェ/ d e:",
|
| 77 |
+
"ドォ/ d o:",
|
| 78 |
+
"ナァ/ n a:",
|
| 79 |
+
"ニィ/ n i:",
|
| 80 |
+
"ヌゥ/ n u:",
|
| 81 |
+
"ヌャ/ ny a",
|
| 82 |
+
"ヌュ/ ny u",
|
| 83 |
+
"ヌョ/ ny o",
|
| 84 |
+
"ネェ/ n e:",
|
| 85 |
+
"ノォ/ n o:",
|
| 86 |
+
"ハァ/ h a:",
|
| 87 |
+
"ヒィ/ h i:",
|
| 88 |
+
"フゥ/ f u:",
|
| 89 |
+
"フャ/ hy a",
|
| 90 |
+
"フュ/ hy u",
|
| 91 |
+
"フョ/ hy o",
|
| 92 |
+
"ヘェ/ h e:",
|
| 93 |
+
"ホォ/ h o:",
|
| 94 |
+
"バァ/ b a:",
|
| 95 |
+
"ビィ/ b i:",
|
| 96 |
+
"ブゥ/ b u:",
|
| 97 |
+
"フャ/ hy a",
|
| 98 |
+
"ブュ/ by u",
|
| 99 |
+
"フョ/ hy o",
|
| 100 |
+
"ベェ/ b e:",
|
| 101 |
+
"ボォ/ b o:",
|
| 102 |
+
"パァ/ p a:",
|
| 103 |
+
"ピィ/ p i:",
|
| 104 |
+
"プゥ/ p u:",
|
| 105 |
+
"プャ/ py a",
|
| 106 |
+
"プュ/ py u",
|
| 107 |
+
"プョ/ py o",
|
| 108 |
+
"ペェ/ p e:",
|
| 109 |
+
"ポォ/ p o:",
|
| 110 |
+
"マァ/ m a:",
|
| 111 |
+
"ミィ/ m i:",
|
| 112 |
+
"ムゥ/ m u:",
|
| 113 |
+
"ムャ/ my a",
|
| 114 |
+
"ムュ/ my u",
|
| 115 |
+
"ムョ/ my o",
|
| 116 |
+
"メェ/ m e:",
|
| 117 |
+
"モォ/ m o:",
|
| 118 |
+
"ヤァ/ y a:",
|
| 119 |
+
"ユゥ/ y u:",
|
| 120 |
+
"ユャ/ y a:",
|
| 121 |
+
"ユュ/ y u:",
|
| 122 |
+
"ユョ/ y o:",
|
| 123 |
+
"ヨォ/ y o:",
|
| 124 |
+
"ラァ/ r a:",
|
| 125 |
+
"リィ/ r i:",
|
| 126 |
+
"ルゥ/ r u:",
|
| 127 |
+
"ルャ/ ry a",
|
| 128 |
+
"ルュ/ ry u",
|
| 129 |
+
"ルョ/ ry o",
|
| 130 |
+
"レェ/ r e:",
|
| 131 |
+
"ロォ/ r o:",
|
| 132 |
+
"ワァ/ w a:",
|
| 133 |
+
"ヲォ/ o:",
|
| 134 |
+
"ディ/ d i",
|
| 135 |
+
"デェ/ d e:",
|
| 136 |
+
"デャ/ dy a",
|
| 137 |
+
"デュ/ dy u",
|
| 138 |
+
"デョ/ dy o",
|
| 139 |
+
"ティ/ t i",
|
| 140 |
+
"テェ/ t e:",
|
| 141 |
+
"テャ/ ty a",
|
| 142 |
+
"テュ/ ty u",
|
| 143 |
+
"テョ/ ty o",
|
| 144 |
+
"スィ/ s i",
|
| 145 |
+
"ズァ/ z u a",
|
| 146 |
+
"ズィ/ z i",
|
| 147 |
+
"ズゥ/ z u",
|
| 148 |
+
"ズャ/ zy a",
|
| 149 |
+
"ズュ/ zy u",
|
| 150 |
+
"ズョ/ zy o",
|
| 151 |
+
"ズェ/ z e",
|
| 152 |
+
"ズォ/ z o",
|
| 153 |
+
"キャ/ ky a",
|
| 154 |
+
"キュ/ ky u",
|
| 155 |
+
"キョ/ ky o",
|
| 156 |
+
"シャ/ sh a",
|
| 157 |
+
"シュ/ sh u",
|
| 158 |
+
"シェ/ sh e",
|
| 159 |
+
"ショ/ sh o",
|
| 160 |
+
"チャ/ ch a",
|
| 161 |
+
"チュ/ ch u",
|
| 162 |
+
"チェ/ ch e",
|
| 163 |
+
"チョ/ ch o",
|
| 164 |
+
"トゥ/ t u",
|
| 165 |
+
"トャ/ ty a",
|
| 166 |
+
"トュ/ ty u",
|
| 167 |
+
"トョ/ ty o",
|
| 168 |
+
"ドァ/ d o a",
|
| 169 |
+
"ドゥ/ d u",
|
| 170 |
+
"ドャ/ dy a",
|
| 171 |
+
"ドュ/ dy u",
|
| 172 |
+
"ドョ/ dy o",
|
| 173 |
+
"ドォ/ d o:",
|
| 174 |
+
"ニャ/ ny a",
|
| 175 |
+
"ニュ/ ny u",
|
| 176 |
+
"ニョ/ ny o",
|
| 177 |
+
"ヒャ/ hy a",
|
| 178 |
+
"ヒュ/ hy u",
|
| 179 |
+
"ヒョ/ hy o",
|
| 180 |
+
"ミャ/ my a",
|
| 181 |
+
"ミュ/ my u",
|
| 182 |
+
"ミョ/ my o",
|
| 183 |
+
"リャ/ ry a",
|
| 184 |
+
"リュ/ ry u",
|
| 185 |
+
"リョ/ ry o",
|
| 186 |
+
"ギャ/ gy a",
|
| 187 |
+
"ギュ/ gy u",
|
| 188 |
+
"ギョ/ gy o",
|
| 189 |
+
"ヂェ/ j e",
|
| 190 |
+
"ヂャ/ j a",
|
| 191 |
+
"ヂュ/ j u",
|
| 192 |
+
"ヂョ/ j o",
|
| 193 |
+
"ジェ/ j e",
|
| 194 |
+
"ジャ/ j a",
|
| 195 |
+
"ジュ/ j u",
|
| 196 |
+
"ジョ/ j o",
|
| 197 |
+
"ビャ/ by a",
|
| 198 |
+
"ビュ/ by u",
|
| 199 |
+
"ビョ/ by o",
|
| 200 |
+
"ピャ/ py a",
|
| 201 |
+
"ピュ/ py u",
|
| 202 |
+
"ピョ/ py o",
|
| 203 |
+
"ウァ/ u a",
|
| 204 |
+
"ウィ/ w i",
|
| 205 |
+
"ウェ/ w e",
|
| 206 |
+
"ウォ/ w o",
|
| 207 |
+
"ファ/ f a",
|
| 208 |
+
"フィ/ f i",
|
| 209 |
+
"フゥ/ f u",
|
| 210 |
+
"フャ/ hy a",
|
| 211 |
+
"フュ/ hy u",
|
| 212 |
+
"フョ/ hy o",
|
| 213 |
+
"フェ/ f e",
|
| 214 |
+
"フォ/ f o",
|
| 215 |
+
"ヴァ/ b a",
|
| 216 |
+
"ヴィ/ b i",
|
| 217 |
+
"ヴェ/ b e",
|
| 218 |
+
"ヴォ/ b o",
|
| 219 |
+
"ヴュ/ by u",
|
| 220 |
+
# Conversion of 1 letter
|
| 221 |
+
"ア/ a",
|
| 222 |
+
"イ/ i",
|
| 223 |
+
"ウ/ u",
|
| 224 |
+
"エ/ e",
|
| 225 |
+
"オ/ o",
|
| 226 |
+
"カ/ k a",
|
| 227 |
+
"キ/ k i",
|
| 228 |
+
"ク/ k u",
|
| 229 |
+
"ケ/ k e",
|
| 230 |
+
"コ/ k o",
|
| 231 |
+
"サ/ s a",
|
| 232 |
+
"シ/ sh i",
|
| 233 |
+
"ス/ s u",
|
| 234 |
+
"セ/ s e",
|
| 235 |
+
"ソ/ s o",
|
| 236 |
+
"タ/ t a",
|
| 237 |
+
"チ/ ch i",
|
| 238 |
+
"ツ/ ts u",
|
| 239 |
+
"テ/ t e",
|
| 240 |
+
"ト/ t o",
|
| 241 |
+
"ナ/ n a",
|
| 242 |
+
"ニ/ n i",
|
| 243 |
+
"ヌ/ n u",
|
| 244 |
+
"ネ/ n e",
|
| 245 |
+
"ノ/ n o",
|
| 246 |
+
"ハ/ h a",
|
| 247 |
+
"ヒ/ h i",
|
| 248 |
+
"フ/ f u",
|
| 249 |
+
"ヘ/ h e",
|
| 250 |
+
"ホ/ h o",
|
| 251 |
+
"マ/ m a",
|
| 252 |
+
"ミ/ m i",
|
| 253 |
+
"ム/ m u",
|
| 254 |
+
"メ/ m e",
|
| 255 |
+
"モ/ m o",
|
| 256 |
+
"ラ/ r a",
|
| 257 |
+
"リ/ r i",
|
| 258 |
+
"ル/ r u",
|
| 259 |
+
"レ/ r e",
|
| 260 |
+
"ロ/ r o",
|
| 261 |
+
"ガ/ g a",
|
| 262 |
+
"ギ/ g i",
|
| 263 |
+
"グ/ g u",
|
| 264 |
+
"ゲ/ g e",
|
| 265 |
+
"ゴ/ g o",
|
| 266 |
+
"ザ/ z a",
|
| 267 |
+
"ジ/ j i",
|
| 268 |
+
"ズ/ z u",
|
| 269 |
+
"ゼ/ z e",
|
| 270 |
+
"ゾ/ z o",
|
| 271 |
+
"ダ/ d a",
|
| 272 |
+
"ヂ/ j i",
|
| 273 |
+
"ヅ/ z u",
|
| 274 |
+
"デ/ d e",
|
| 275 |
+
"ド/ d o",
|
| 276 |
+
"バ/ b a",
|
| 277 |
+
"ビ/ b i",
|
| 278 |
+
"ブ/ b u",
|
| 279 |
+
"ベ/ b e",
|
| 280 |
+
"ボ/ b o",
|
| 281 |
+
"パ/ p a",
|
| 282 |
+
"ピ/ p i",
|
| 283 |
+
"プ/ p u",
|
| 284 |
+
"ペ/ p e",
|
| 285 |
+
"ポ/ p o",
|
| 286 |
+
"ヤ/ y a",
|
| 287 |
+
"ユ/ y u",
|
| 288 |
+
"ヨ/ y o",
|
| 289 |
+
"ワ/ w a",
|
| 290 |
+
"ヰ/ i",
|
| 291 |
+
"ヱ/ e",
|
| 292 |
+
"ヲ/ o",
|
| 293 |
+
"ン/ N",
|
| 294 |
+
"ッ/ q",
|
| 295 |
+
"ヴ/ b u",
|
| 296 |
+
"ー/:",
|
| 297 |
+
# Try converting broken text
|
| 298 |
+
"ァ/ a",
|
| 299 |
+
"ィ/ i",
|
| 300 |
+
"ゥ/ u",
|
| 301 |
+
"ェ/ e",
|
| 302 |
+
"ォ/ o",
|
| 303 |
+
"ヮ/ w a",
|
| 304 |
+
"ォ/ o",
|
| 305 |
+
# Symbols
|
| 306 |
+
"、/ ,",
|
| 307 |
+
"。/ .",
|
| 308 |
+
"!/ !",
|
| 309 |
+
"?/ ?",
|
| 310 |
+
"・/ ,",
|
| 311 |
+
]
|
| 312 |
+
|
| 313 |
+
_COLON_RX = re.compile(":+")
|
| 314 |
+
_REJECT_RX = re.compile("[^ a-zA-Z:,.?]")
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def _makerulemap():
|
| 318 |
+
l = [tuple(x.split("/")) for x in _CONVRULES]
|
| 319 |
+
return tuple({k: v for k, v in l if len(k) == i} for i in (1, 2))
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
_RULEMAP1, _RULEMAP2 = _makerulemap()
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def kata2phoneme(text: str) -> str:
|
| 326 |
+
"""Convert katakana text to phonemes."""
|
| 327 |
+
text = text.strip()
|
| 328 |
+
res = []
|
| 329 |
+
while text:
|
| 330 |
+
if len(text) >= 2:
|
| 331 |
+
x = _RULEMAP2.get(text[:2])
|
| 332 |
+
if x is not None:
|
| 333 |
+
text = text[2:]
|
| 334 |
+
res += x.split(" ")[1:]
|
| 335 |
+
continue
|
| 336 |
+
x = _RULEMAP1.get(text[0])
|
| 337 |
+
if x is not None:
|
| 338 |
+
text = text[1:]
|
| 339 |
+
res += x.split(" ")[1:]
|
| 340 |
+
continue
|
| 341 |
+
res.append(text[0])
|
| 342 |
+
text = text[1:]
|
| 343 |
+
# res = _COLON_RX.sub(":", res)
|
| 344 |
+
return res
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
_KATAKANA = "".join(chr(ch) for ch in range(ord("ァ"), ord("ン") + 1))
|
| 348 |
+
_HIRAGANA = "".join(chr(ch) for ch in range(ord("ぁ"), ord("ん") + 1))
|
| 349 |
+
_HIRA2KATATRANS = str.maketrans(_HIRAGANA, _KATAKANA)
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
def hira2kata(text: str) -> str:
|
| 353 |
+
text = text.translate(_HIRA2KATATRANS)
|
| 354 |
+
return text.replace("う゛", "ヴ")
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
_SYMBOL_TOKENS = set(list("・、。?!"))
|
| 358 |
+
_NO_YOMI_TOKENS = set(list("「」『』―()[][]"))
|
| 359 |
+
_TAGGER = MeCab.Tagger()
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def text2kata(text: str) -> str:
|
| 363 |
+
parsed = _TAGGER.parse(text)
|
| 364 |
+
res = []
|
| 365 |
+
for line in parsed.split("\n"):
|
| 366 |
+
if line == "EOS":
|
| 367 |
+
break
|
| 368 |
+
parts = line.split("\t")
|
| 369 |
+
|
| 370 |
+
word, yomi = parts[0], parts[1]
|
| 371 |
+
if yomi:
|
| 372 |
+
res.append(yomi)
|
| 373 |
+
else:
|
| 374 |
+
if word in _SYMBOL_TOKENS:
|
| 375 |
+
res.append(word)
|
| 376 |
+
elif word in ("っ", "ッ"):
|
| 377 |
+
res.append("ッ")
|
| 378 |
+
elif word in _NO_YOMI_TOKENS:
|
| 379 |
+
pass
|
| 380 |
+
else:
|
| 381 |
+
res.append(word)
|
| 382 |
+
return hira2kata("".join(res))
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
_ALPHASYMBOL_YOMI = {
|
| 386 |
+
"#": "シャープ",
|
| 387 |
+
"%": "パーセント",
|
| 388 |
+
"&": "アンド",
|
| 389 |
+
"+": "プラス",
|
| 390 |
+
"-": "マイナス",
|
| 391 |
+
":": "コロン",
|
| 392 |
+
";": "セミコロン",
|
| 393 |
+
"<": "小なり",
|
| 394 |
+
"=": "イコール",
|
| 395 |
+
">": "大なり",
|
| 396 |
+
"@": "アット",
|
| 397 |
+
"a": "エー",
|
| 398 |
+
"b": "ビー",
|
| 399 |
+
"c": "シー",
|
| 400 |
+
"d": "ディー",
|
| 401 |
+
"e": "イー",
|
| 402 |
+
"f": "エフ",
|
| 403 |
+
"g": "ジー",
|
| 404 |
+
"h": "エイチ",
|
| 405 |
+
"i": "アイ",
|
| 406 |
+
"j": "ジェー",
|
| 407 |
+
"k": "ケー",
|
| 408 |
+
"l": "エル",
|
| 409 |
+
"m": "エム",
|
| 410 |
+
"n": "エヌ",
|
| 411 |
+
"o": "オー",
|
| 412 |
+
"p": "ピー",
|
| 413 |
+
"q": "キュー",
|
| 414 |
+
"r": "アール",
|
| 415 |
+
"s": "エス",
|
| 416 |
+
"t": "ティー",
|
| 417 |
+
"u": "ユー",
|
| 418 |
+
"v": "ブイ",
|
| 419 |
+
"w": "ダブリュー",
|
| 420 |
+
"x": "エックス",
|
| 421 |
+
"y": "ワイ",
|
| 422 |
+
"z": "ゼット",
|
| 423 |
+
"α": "アルファ",
|
| 424 |
+
"β": "ベータ",
|
| 425 |
+
"γ": "ガンマ",
|
| 426 |
+
"δ": "デルタ",
|
| 427 |
+
"ε": "イプシロン",
|
| 428 |
+
"ζ": "ゼータ",
|
| 429 |
+
"η": "イータ",
|
| 430 |
+
"θ": "シータ",
|
| 431 |
+
"ι": "イオタ",
|
| 432 |
+
"κ": "カッパ",
|
| 433 |
+
"λ": "ラムダ",
|
| 434 |
+
"μ": "ミュー",
|
| 435 |
+
"ν": "ニュー",
|
| 436 |
+
"ξ": "クサイ",
|
| 437 |
+
"ο": "オミクロン",
|
| 438 |
+
"π": "パイ",
|
| 439 |
+
"ρ": "ロー",
|
| 440 |
+
"σ": "シグマ",
|
| 441 |
+
"τ": "タウ",
|
| 442 |
+
"υ": "ウプシロン",
|
| 443 |
+
"φ": "ファイ",
|
| 444 |
+
"χ": "カイ",
|
| 445 |
+
"ψ": "プサイ",
|
| 446 |
+
"ω": "オメガ",
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
_NUMBER_WITH_SEPARATOR_RX = re.compile("[0-9]{1,3}(,[0-9]{3})+")
|
| 451 |
+
_CURRENCY_MAP = {"$": "ドル", "¥": "円", "£": "ポンド", "€": "ユーロ"}
|
| 452 |
+
_CURRENCY_RX = re.compile(r"([$¥£€])([0-9.]*[0-9])")
|
| 453 |
+
_NUMBER_RX = re.compile(r"[0-9]+(\.[0-9]+)?")
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def japanese_convert_numbers_to_words(text: str) -> str:
|
| 457 |
+
res = _NUMBER_WITH_SEPARATOR_RX.sub(lambda m: m[0].replace(",", ""), text)
|
| 458 |
+
res = _CURRENCY_RX.sub(lambda m: m[2] + _CURRENCY_MAP.get(m[1], m[1]), res)
|
| 459 |
+
res = _NUMBER_RX.sub(lambda m: num2words(m[0], lang="ja"), res)
|
| 460 |
+
return res
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
def japanese_convert_alpha_symbols_to_words(text: str) -> str:
|
| 464 |
+
return "".join([_ALPHASYMBOL_YOMI.get(ch, ch) for ch in text.lower()])
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
def japanese_text_to_phonemes(text: str) -> str:
|
| 468 |
+
"""Convert Japanese text to phonemes."""
|
| 469 |
+
res = unicodedata.normalize("NFKC", text)
|
| 470 |
+
res = japanese_convert_numbers_to_words(res)
|
| 471 |
+
# res = japanese_convert_alpha_symbols_to_words(res)
|
| 472 |
+
res = text2kata(res)
|
| 473 |
+
res = kata2phoneme(res)
|
| 474 |
+
return res
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
def is_japanese_character(char):
|
| 478 |
+
# 定义日语文字系统的 Unicode 范围
|
| 479 |
+
japanese_ranges = [
|
| 480 |
+
(0x3040, 0x309F), # 平假名
|
| 481 |
+
(0x30A0, 0x30FF), # 片假名
|
| 482 |
+
(0x4E00, 0x9FFF), # 汉字 (CJK Unified Ideographs)
|
| 483 |
+
(0x3400, 0x4DBF), # 汉字扩展 A
|
| 484 |
+
(0x20000, 0x2A6DF), # 汉字扩展 B
|
| 485 |
+
# 可以根据需要添加其他汉字扩展范围
|
| 486 |
+
]
|
| 487 |
+
|
| 488 |
+
# 将字符的 Unicode 编码转换为整数
|
| 489 |
+
char_code = ord(char)
|
| 490 |
+
|
| 491 |
+
# 检查字符是否在任何一个日语范围内
|
| 492 |
+
for start, end in japanese_ranges:
|
| 493 |
+
if start <= char_code <= end:
|
| 494 |
+
return True
|
| 495 |
+
|
| 496 |
+
return False
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
rep_map = {
|
| 500 |
+
":": ",",
|
| 501 |
+
";": ",",
|
| 502 |
+
",": ",",
|
| 503 |
+
"。": ".",
|
| 504 |
+
"!": "!",
|
| 505 |
+
"?": "?",
|
| 506 |
+
"\n": ".",
|
| 507 |
+
"·": ",",
|
| 508 |
+
"、": ",",
|
| 509 |
+
"...": "…",
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
def replace_punctuation(text):
|
| 514 |
+
pattern = re.compile("|".join(re.escape(p) for p in rep_map.keys()))
|
| 515 |
+
|
| 516 |
+
replaced_text = pattern.sub(lambda x: rep_map[x.group()], text)
|
| 517 |
+
|
| 518 |
+
replaced_text = re.sub(
|
| 519 |
+
r"[^\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF"
|
| 520 |
+
+ "".join(punctuation)
|
| 521 |
+
+ r"]+",
|
| 522 |
+
"",
|
| 523 |
+
replaced_text,
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
return replaced_text
|
| 527 |
|
| 528 |
|
| 529 |
def text_normalize(text):
|
| 530 |
+
res = unicodedata.normalize("NFKC", text)
|
| 531 |
+
res = japanese_convert_numbers_to_words(res)
|
| 532 |
+
# res = "".join([i for i in res if is_japanese_character(i)])
|
| 533 |
+
res = replace_punctuation(res)
|
| 534 |
+
return res
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
def distribute_phone(n_phone, n_word):
|
| 538 |
+
phones_per_word = [0] * n_word
|
| 539 |
+
for task in range(n_phone):
|
| 540 |
+
min_tasks = min(phones_per_word)
|
| 541 |
+
min_index = phones_per_word.index(min_tasks)
|
| 542 |
+
phones_per_word[min_index] += 1
|
| 543 |
+
return phones_per_word
|
| 544 |
|
| 545 |
|
| 546 |
def g2p(norm_text):
|
| 547 |
+
tokenized = tokenizer.tokenize(norm_text)
|
| 548 |
+
phs = []
|
| 549 |
+
ph_groups = []
|
| 550 |
+
for t in tokenized:
|
| 551 |
+
if not t.startswith("#"):
|
| 552 |
+
ph_groups.append([t])
|
| 553 |
+
else:
|
| 554 |
+
ph_groups[-1].append(t.replace("#", ""))
|
| 555 |
+
word2ph = []
|
| 556 |
+
for group in ph_groups:
|
| 557 |
+
phonemes = kata2phoneme(text2kata("".join(group)))
|
| 558 |
+
# phonemes = [i for i in phonemes if i in symbols]
|
| 559 |
+
for i in phonemes:
|
| 560 |
+
assert i in symbols, (group, norm_text, tokenized)
|
| 561 |
+
phone_len = len(phonemes)
|
| 562 |
+
word_len = len(group)
|
| 563 |
+
|
| 564 |
+
aaa = distribute_phone(phone_len, word_len)
|
| 565 |
+
word2ph += aaa
|
| 566 |
|
| 567 |
+
phs += phonemes
|
| 568 |
+
phones = ["_"] + phs + ["_"]
|
| 569 |
tones = [0 for i in phones]
|
| 570 |
+
word2ph = [1] + word2ph + [1]
|
| 571 |
return phones, tones, word2ph
|
| 572 |
|
| 573 |
|
| 574 |
+
if __name__ == "__main__":
|
| 575 |
+
tokenizer = AutoTokenizer.from_pretrained("./bert/bert-base-japanese-v3")
|
| 576 |
+
text = "hello,こんにちは、世界!……"
|
| 577 |
+
from bert_vits2.text.japanese_bert import get_bert_feature
|
| 578 |
+
|
| 579 |
+
text = text_normalize(text)
|
| 580 |
+
print(text)
|
| 581 |
+
phones, tones, word2ph = g2p(text)
|
| 582 |
+
bert = get_bert_feature(text, word2ph)
|
| 583 |
+
|
| 584 |
+
print(phones, tones, word2ph, bert.shape)
|
bert_vits2/text/japanese_bert.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from transformers import AutoTokenizer, AutoModelForMaskedLM
|
| 3 |
+
|
| 4 |
+
import config
|
| 5 |
+
from logger import logger
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
logger.info("Loading bert-base-japanese-v3...")
|
| 9 |
+
tokenizer = AutoTokenizer.from_pretrained(config.ABS_PATH + "/bert_vits2/bert/bert-base-japanese-v3")
|
| 10 |
+
model = AutoModelForMaskedLM.from_pretrained(config.ABS_PATH + "/bert_vits2/bert/bert-base-japanese-v3").to(
|
| 11 |
+
config.DEVICE)
|
| 12 |
+
logger.info("Loading finished.")
|
| 13 |
+
except Exception as e:
|
| 14 |
+
logger.error(e)
|
| 15 |
+
logger.error(f"Please download pytorch_model.bin from cl-tohoku/bert-base-japanese-v3.")
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def get_bert_feature(text, word2ph, device=config.DEVICE):
|
| 19 |
+
with torch.no_grad():
|
| 20 |
+
inputs = tokenizer(text, return_tensors="pt")
|
| 21 |
+
for i in inputs:
|
| 22 |
+
inputs[i] = inputs[i].to(device)
|
| 23 |
+
res = model(**inputs, output_hidden_states=True)
|
| 24 |
+
res = torch.cat(res["hidden_states"][-3:-2], -1)[0].cpu()
|
| 25 |
+
assert inputs["input_ids"].shape[-1] == len(word2ph)
|
| 26 |
+
word2phone = word2ph
|
| 27 |
+
phone_level_feature = []
|
| 28 |
+
for i in range(len(word2phone)):
|
| 29 |
+
repeat_feature = res[i].repeat(word2phone[i], 1)
|
| 30 |
+
phone_level_feature.append(repeat_feature)
|
| 31 |
+
|
| 32 |
+
phone_level_feature = torch.cat(phone_level_feature, dim=0)
|
| 33 |
+
|
| 34 |
+
return phone_level_feature.T
|
bert_vits2/text/symbols.py
CHANGED
|
@@ -1,52 +1,200 @@
|
|
| 1 |
-
punctuation = [
|
| 2 |
pu_symbols = punctuation + ["SP", "UNK"]
|
| 3 |
-
pad =
|
| 4 |
|
| 5 |
# chinese
|
| 6 |
-
zh_symbols = [
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
num_zh_tones = 6
|
| 15 |
|
| 16 |
# japanese
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
num_ja_tones = 1
|
| 20 |
|
| 21 |
# English
|
| 22 |
-
en_symbols = [
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
num_en_tones = 4
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
}
|
| 41 |
-
num_languages = len(language_id_map.keys())
|
| 42 |
-
|
| 43 |
-
language_tone_start_map = {
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
punctuation = ["!", "?", "…", ",", ".", "'", "-"]
|
| 2 |
pu_symbols = punctuation + ["SP", "UNK"]
|
| 3 |
+
pad = "_"
|
| 4 |
|
| 5 |
# chinese
|
| 6 |
+
zh_symbols = [
|
| 7 |
+
"E",
|
| 8 |
+
"En",
|
| 9 |
+
"a",
|
| 10 |
+
"ai",
|
| 11 |
+
"an",
|
| 12 |
+
"ang",
|
| 13 |
+
"ao",
|
| 14 |
+
"b",
|
| 15 |
+
"c",
|
| 16 |
+
"ch",
|
| 17 |
+
"d",
|
| 18 |
+
"e",
|
| 19 |
+
"ei",
|
| 20 |
+
"en",
|
| 21 |
+
"eng",
|
| 22 |
+
"er",
|
| 23 |
+
"f",
|
| 24 |
+
"g",
|
| 25 |
+
"h",
|
| 26 |
+
"i",
|
| 27 |
+
"i0",
|
| 28 |
+
"ia",
|
| 29 |
+
"ian",
|
| 30 |
+
"iang",
|
| 31 |
+
"iao",
|
| 32 |
+
"ie",
|
| 33 |
+
"in",
|
| 34 |
+
"ing",
|
| 35 |
+
"iong",
|
| 36 |
+
"ir",
|
| 37 |
+
"iu",
|
| 38 |
+
"j",
|
| 39 |
+
"k",
|
| 40 |
+
"l",
|
| 41 |
+
"m",
|
| 42 |
+
"n",
|
| 43 |
+
"o",
|
| 44 |
+
"ong",
|
| 45 |
+
"ou",
|
| 46 |
+
"p",
|
| 47 |
+
"q",
|
| 48 |
+
"r",
|
| 49 |
+
"s",
|
| 50 |
+
"sh",
|
| 51 |
+
"t",
|
| 52 |
+
"u",
|
| 53 |
+
"ua",
|
| 54 |
+
"uai",
|
| 55 |
+
"uan",
|
| 56 |
+
"uang",
|
| 57 |
+
"ui",
|
| 58 |
+
"un",
|
| 59 |
+
"uo",
|
| 60 |
+
"v",
|
| 61 |
+
"van",
|
| 62 |
+
"ve",
|
| 63 |
+
"vn",
|
| 64 |
+
"w",
|
| 65 |
+
"x",
|
| 66 |
+
"y",
|
| 67 |
+
"z",
|
| 68 |
+
"zh",
|
| 69 |
+
"AA",
|
| 70 |
+
"EE",
|
| 71 |
+
"OO",
|
| 72 |
+
]
|
| 73 |
num_zh_tones = 6
|
| 74 |
|
| 75 |
# japanese
|
| 76 |
+
ja_symbols_legacy = ['I', 'N', 'U', 'a', 'b', 'by', 'ch', 'cl', 'd', 'dy', 'e', 'f', 'g', 'gy', 'h', 'hy', 'i', 'j',
|
| 77 |
+
'k', 'ky',
|
| 78 |
+
'm', 'my', 'n', 'ny', 'o', 'p', 'py', 'r', 'ry', 's', 'sh', 't', 'ts', 'u', 'V', 'w', 'y', 'z']
|
| 79 |
+
ja_symbols = [
|
| 80 |
+
"N",
|
| 81 |
+
"a",
|
| 82 |
+
"a:",
|
| 83 |
+
"b",
|
| 84 |
+
"by",
|
| 85 |
+
"ch",
|
| 86 |
+
"d",
|
| 87 |
+
"dy",
|
| 88 |
+
"e",
|
| 89 |
+
"e:",
|
| 90 |
+
"f",
|
| 91 |
+
"g",
|
| 92 |
+
"gy",
|
| 93 |
+
"h",
|
| 94 |
+
"hy",
|
| 95 |
+
"i",
|
| 96 |
+
"i:",
|
| 97 |
+
"j",
|
| 98 |
+
"k",
|
| 99 |
+
"ky",
|
| 100 |
+
"m",
|
| 101 |
+
"my",
|
| 102 |
+
"n",
|
| 103 |
+
"ny",
|
| 104 |
+
"o",
|
| 105 |
+
"o:",
|
| 106 |
+
"p",
|
| 107 |
+
"py",
|
| 108 |
+
"q",
|
| 109 |
+
"r",
|
| 110 |
+
"ry",
|
| 111 |
+
"s",
|
| 112 |
+
"sh",
|
| 113 |
+
"t",
|
| 114 |
+
"ts",
|
| 115 |
+
"ty",
|
| 116 |
+
"u",
|
| 117 |
+
"u:",
|
| 118 |
+
"w",
|
| 119 |
+
"y",
|
| 120 |
+
"z",
|
| 121 |
+
"zy",
|
| 122 |
+
]
|
| 123 |
num_ja_tones = 1
|
| 124 |
|
| 125 |
# English
|
| 126 |
+
en_symbols = [
|
| 127 |
+
"aa",
|
| 128 |
+
"ae",
|
| 129 |
+
"ah",
|
| 130 |
+
"ao",
|
| 131 |
+
"aw",
|
| 132 |
+
"ay",
|
| 133 |
+
"b",
|
| 134 |
+
"ch",
|
| 135 |
+
"d",
|
| 136 |
+
"dh",
|
| 137 |
+
"eh",
|
| 138 |
+
"er",
|
| 139 |
+
"ey",
|
| 140 |
+
"f",
|
| 141 |
+
"g",
|
| 142 |
+
"hh",
|
| 143 |
+
"ih",
|
| 144 |
+
"iy",
|
| 145 |
+
"jh",
|
| 146 |
+
"k",
|
| 147 |
+
"l",
|
| 148 |
+
"m",
|
| 149 |
+
"n",
|
| 150 |
+
"ng",
|
| 151 |
+
"ow",
|
| 152 |
+
"oy",
|
| 153 |
+
"p",
|
| 154 |
+
"r",
|
| 155 |
+
"s",
|
| 156 |
+
"sh",
|
| 157 |
+
"t",
|
| 158 |
+
"th",
|
| 159 |
+
"uh",
|
| 160 |
+
"uw",
|
| 161 |
+
"V",
|
| 162 |
+
"w",
|
| 163 |
+
"y",
|
| 164 |
+
"z",
|
| 165 |
+
"zh",
|
| 166 |
+
]
|
| 167 |
num_en_tones = 4
|
| 168 |
|
| 169 |
+
|
| 170 |
+
def get_symbols(legacy=False):
|
| 171 |
+
if legacy:
|
| 172 |
+
ja_symbols = ja_symbols_legacy
|
| 173 |
+
# combine all symbols
|
| 174 |
+
normal_symbols = sorted(set(zh_symbols + ja_symbols + en_symbols))
|
| 175 |
+
symbols = [pad] + normal_symbols + pu_symbols
|
| 176 |
+
sil_phonemes_ids = [symbols.index(i) for i in pu_symbols]
|
| 177 |
+
|
| 178 |
+
# combine all tones
|
| 179 |
+
num_tones = num_zh_tones + num_ja_tones + num_en_tones
|
| 180 |
+
|
| 181 |
+
# language maps
|
| 182 |
+
language_id_map = {"ZH": 0, "JP": 1, "EN": 2}
|
| 183 |
+
num_languages = len(language_id_map.keys())
|
| 184 |
+
|
| 185 |
+
language_tone_start_map = {
|
| 186 |
+
"ZH": 0,
|
| 187 |
+
"JP": num_zh_tones,
|
| 188 |
+
"EN": num_zh_tones + num_ja_tones,
|
| 189 |
+
}
|
| 190 |
+
return symbols, num_tones, language_id_map, num_languages, language_tone_start_map
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
if __name__ == "__main__":
|
| 194 |
+
zh = set(zh_symbols)
|
| 195 |
+
en = set(en_symbols)
|
| 196 |
+
jp = set(ja_symbols)
|
| 197 |
+
print(zh)
|
| 198 |
+
print(en)
|
| 199 |
+
print(jp)
|
| 200 |
+
print(sorted(zh & en))
|
bert_vits2/text/tone_sandhi.py
CHANGED
|
@@ -19,51 +19,442 @@ from pypinyin import lazy_pinyin
|
|
| 19 |
from pypinyin import Style
|
| 20 |
|
| 21 |
|
| 22 |
-
class ToneSandhi
|
| 23 |
def __init__(self):
|
| 24 |
self.must_neural_tone_words = {
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
}
|
| 65 |
self.must_not_neural_tone_words = {
|
| 66 |
-
"男子",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
}
|
| 68 |
self.punc = ":,;。?!“”‘’':,;.?!"
|
| 69 |
|
|
@@ -72,14 +463,15 @@ class ToneSandhi():
|
|
| 72 |
# word: "家里"
|
| 73 |
# pos: "s"
|
| 74 |
# finals: ['ia1', 'i3']
|
| 75 |
-
def _neural_sandhi(self, word: str, pos: str,
|
| 76 |
-
finals: List[str]) -> List[str]:
|
| 77 |
-
|
| 78 |
# reduplication words for n. and v. e.g. 奶奶, 试试, 旺旺
|
| 79 |
for j, item in enumerate(word):
|
| 80 |
-
if
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
| 83 |
finals[j] = finals[j][:-1] + "5"
|
| 84 |
ge_idx = word.find("个")
|
| 85 |
if len(word) >= 1 and word[-1] in "吧呢啊呐噻嘛吖嗨呐哦哒额滴哩哟喽啰耶喔诶":
|
|
@@ -89,9 +481,12 @@ class ToneSandhi():
|
|
| 89 |
# e.g. 走了, 看着, 去过
|
| 90 |
# elif len(word) == 1 and word in "了着过" and pos in {"ul", "uz", "ug"}:
|
| 91 |
# finals[-1] = finals[-1][:-1] + "5"
|
| 92 |
-
elif
|
| 93 |
-
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
| 95 |
finals[-1] = finals[-1][:-1] + "5"
|
| 96 |
# e.g. 桌上, 地下, 家里
|
| 97 |
elif len(word) > 1 and word[-1] in "上下里" and pos in {"s", "l", "f"}:
|
|
@@ -100,21 +495,26 @@ class ToneSandhi():
|
|
| 100 |
elif len(word) > 1 and word[-1] in "来去" and word[-2] in "上下进出回过起开":
|
| 101 |
finals[-1] = finals[-1][:-1] + "5"
|
| 102 |
# 个做量词
|
| 103 |
-
elif (
|
| 104 |
-
|
| 105 |
-
|
|
|
|
| 106 |
finals[ge_idx] = finals[ge_idx][:-1] + "5"
|
| 107 |
else:
|
| 108 |
-
if
|
| 109 |
-
|
|
|
|
|
|
|
| 110 |
finals[-1] = finals[-1][:-1] + "5"
|
| 111 |
|
| 112 |
word_list = self._split_word(word)
|
| 113 |
-
finals_list = [finals[:len(word_list[0])], finals[len(word_list[0]):]]
|
| 114 |
for i, word in enumerate(word_list):
|
| 115 |
# conventional neural in Chinese
|
| 116 |
-
if
|
| 117 |
-
|
|
|
|
|
|
|
| 118 |
finals_list[i][-1] = finals_list[i][-1][:-1] + "5"
|
| 119 |
finals = sum(finals_list, [])
|
| 120 |
return finals
|
|
@@ -126,17 +526,17 @@ class ToneSandhi():
|
|
| 126 |
else:
|
| 127 |
for i, char in enumerate(word):
|
| 128 |
# "不" before tone4 should be bu2, e.g. 不怕
|
| 129 |
-
if char == "不" and i + 1 < len(word) and finals[i +
|
| 130 |
-
1][-1] == "4":
|
| 131 |
finals[i] = finals[i][:-1] + "2"
|
| 132 |
return finals
|
| 133 |
|
| 134 |
def _yi_sandhi(self, word: str, finals: List[str]) -> List[str]:
|
| 135 |
# "一" in number sequences, e.g. 一零零, 二一零
|
| 136 |
if word.find("一") != -1 and all(
|
| 137 |
-
|
|
|
|
| 138 |
return finals
|
| 139 |
-
# "一" between reduplication words
|
| 140 |
elif len(word) == 3 and word[1] == "一" and word[0] == word[-1]:
|
| 141 |
finals[1] = finals[1][:-1] + "5"
|
| 142 |
# when "一" is ordinal word, it should be yi1
|
|
@@ -161,10 +561,10 @@ class ToneSandhi():
|
|
| 161 |
first_subword = word_list[0]
|
| 162 |
first_begin_idx = word.find(first_subword)
|
| 163 |
if first_begin_idx == 0:
|
| 164 |
-
second_subword = word[len(first_subword):]
|
| 165 |
new_word_list = [first_subword, second_subword]
|
| 166 |
else:
|
| 167 |
-
second_subword = word[:-len(first_subword)]
|
| 168 |
new_word_list = [second_subword, first_subword]
|
| 169 |
return new_word_list
|
| 170 |
|
|
@@ -182,18 +582,19 @@ class ToneSandhi():
|
|
| 182 |
elif len(word_list[0]) == 1:
|
| 183 |
finals[1] = finals[1][:-1] + "2"
|
| 184 |
else:
|
| 185 |
-
finals_list = [
|
| 186 |
-
finals[:len(word_list[0])], finals[len(word_list[0]):]
|
| 187 |
-
]
|
| 188 |
if len(finals_list) == 2:
|
| 189 |
for i, sub in enumerate(finals_list):
|
| 190 |
# e.g. 所有/人
|
| 191 |
if self._all_tone_three(sub) and len(sub) == 2:
|
| 192 |
finals_list[i][0] = finals_list[i][0][:-1] + "2"
|
| 193 |
# e.g. 好/喜欢
|
| 194 |
-
elif
|
| 195 |
-
|
| 196 |
-
|
|
|
|
|
|
|
|
|
|
| 197 |
finals_list[0][-1] = finals_list[0][-1][:-1] + "2"
|
| 198 |
finals = sum(finals_list, [])
|
| 199 |
# split idiom into two words who's length is 2
|
|
@@ -222,7 +623,7 @@ class ToneSandhi():
|
|
| 222 |
new_seg.append((word, pos))
|
| 223 |
last_word = word[:]
|
| 224 |
if last_word == "不":
|
| 225 |
-
new_seg.append((last_word,
|
| 226 |
last_word = ""
|
| 227 |
return new_seg
|
| 228 |
|
|
@@ -236,12 +637,21 @@ class ToneSandhi():
|
|
| 236 |
new_seg = []
|
| 237 |
# function 1
|
| 238 |
for i, (word, pos) in enumerate(seg):
|
| 239 |
-
if
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
new_seg[i - 1][0] = new_seg[i - 1][0] + "一" + new_seg[i - 1][0]
|
| 242 |
else:
|
| 243 |
-
if
|
| 244 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
continue
|
| 246 |
else:
|
| 247 |
new_seg.append([word, pos])
|
|
@@ -257,22 +667,27 @@ class ToneSandhi():
|
|
| 257 |
|
| 258 |
# the first and the second words are all_tone_three
|
| 259 |
def _merge_continuous_three_tones(
|
| 260 |
-
|
|
|
|
| 261 |
new_seg = []
|
| 262 |
sub_finals_list = [
|
| 263 |
-
lazy_pinyin(
|
| 264 |
-
word, neutral_tone_with_five=True, style=Style.FINALS_TONE3)
|
| 265 |
for (word, pos) in seg
|
| 266 |
]
|
| 267 |
assert len(sub_finals_list) == len(seg)
|
| 268 |
merge_last = [False] * len(seg)
|
| 269 |
for i, (word, pos) in enumerate(seg):
|
| 270 |
-
if
|
| 271 |
-
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
| 273 |
# if the last word is reduplication, not merge, because reduplication need to be _neural_sandhi
|
| 274 |
-
if
|
| 275 |
-
|
|
|
|
|
|
|
| 276 |
new_seg[-1][0] = new_seg[-1][0] + seg[i][0]
|
| 277 |
merge_last[i] = True
|
| 278 |
else:
|
|
@@ -287,21 +702,27 @@ class ToneSandhi():
|
|
| 287 |
|
| 288 |
# the last char of first word and the first char of second word is tone_three
|
| 289 |
def _merge_continuous_three_tones_2(
|
| 290 |
-
|
|
|
|
| 291 |
new_seg = []
|
| 292 |
sub_finals_list = [
|
| 293 |
-
lazy_pinyin(
|
| 294 |
-
word, neutral_tone_with_five=True, style=Style.FINALS_TONE3)
|
| 295 |
for (word, pos) in seg
|
| 296 |
]
|
| 297 |
assert len(sub_finals_list) == len(seg)
|
| 298 |
merge_last = [False] * len(seg)
|
| 299 |
for i, (word, pos) in enumerate(seg):
|
| 300 |
-
if
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
# if the last word is reduplication, not merge, because reduplication need to be _neural_sandhi
|
| 303 |
-
if
|
| 304 |
-
|
|
|
|
|
|
|
| 305 |
new_seg[-1][0] = new_seg[-1][0] + seg[i][0]
|
| 306 |
merge_last[i] = True
|
| 307 |
else:
|
|
@@ -319,8 +740,7 @@ class ToneSandhi():
|
|
| 319 |
new_seg.append([word, pos])
|
| 320 |
return new_seg
|
| 321 |
|
| 322 |
-
def _merge_reduplication(
|
| 323 |
-
self, seg: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
|
| 324 |
new_seg = []
|
| 325 |
for i, (word, pos) in enumerate(seg):
|
| 326 |
if new_seg and word == new_seg[-1][0]:
|
|
@@ -329,8 +749,7 @@ class ToneSandhi():
|
|
| 329 |
new_seg.append([word, pos])
|
| 330 |
return new_seg
|
| 331 |
|
| 332 |
-
def pre_merge_for_modify(
|
| 333 |
-
self, seg: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
|
| 334 |
seg = self._merge_bu(seg)
|
| 335 |
try:
|
| 336 |
seg = self._merge_yi(seg)
|
|
@@ -342,8 +761,7 @@ class ToneSandhi():
|
|
| 342 |
seg = self._merge_er(seg)
|
| 343 |
return seg
|
| 344 |
|
| 345 |
-
def modified_tone(self, word: str, pos: str,
|
| 346 |
-
finals: List[str]) -> List[str]:
|
| 347 |
finals = self._bu_sandhi(word, finals)
|
| 348 |
finals = self._yi_sandhi(word, finals)
|
| 349 |
finals = self._neural_sandhi(word, pos, finals)
|
|
|
|
| 19 |
from pypinyin import Style
|
| 20 |
|
| 21 |
|
| 22 |
+
class ToneSandhi:
|
| 23 |
def __init__(self):
|
| 24 |
self.must_neural_tone_words = {
|
| 25 |
+
"麻烦",
|
| 26 |
+
"麻利",
|
| 27 |
+
"鸳鸯",
|
| 28 |
+
"高粱",
|
| 29 |
+
"骨头",
|
| 30 |
+
"骆驼",
|
| 31 |
+
"马虎",
|
| 32 |
+
"首饰",
|
| 33 |
+
"馒头",
|
| 34 |
+
"���饨",
|
| 35 |
+
"风筝",
|
| 36 |
+
"难为",
|
| 37 |
+
"队伍",
|
| 38 |
+
"阔气",
|
| 39 |
+
"闺女",
|
| 40 |
+
"门道",
|
| 41 |
+
"锄头",
|
| 42 |
+
"铺盖",
|
| 43 |
+
"铃铛",
|
| 44 |
+
"铁匠",
|
| 45 |
+
"钥匙",
|
| 46 |
+
"里脊",
|
| 47 |
+
"里头",
|
| 48 |
+
"部分",
|
| 49 |
+
"那么",
|
| 50 |
+
"道士",
|
| 51 |
+
"造化",
|
| 52 |
+
"迷糊",
|
| 53 |
+
"连累",
|
| 54 |
+
"这么",
|
| 55 |
+
"这个",
|
| 56 |
+
"运气",
|
| 57 |
+
"过去",
|
| 58 |
+
"软和",
|
| 59 |
+
"转悠",
|
| 60 |
+
"踏实",
|
| 61 |
+
"跳蚤",
|
| 62 |
+
"跟头",
|
| 63 |
+
"趔趄",
|
| 64 |
+
"财主",
|
| 65 |
+
"豆腐",
|
| 66 |
+
"讲究",
|
| 67 |
+
"记性",
|
| 68 |
+
"记号",
|
| 69 |
+
"认识",
|
| 70 |
+
"规矩",
|
| 71 |
+
"见识",
|
| 72 |
+
"裁缝",
|
| 73 |
+
"补丁",
|
| 74 |
+
"衣裳",
|
| 75 |
+
"衣服",
|
| 76 |
+
"衙门",
|
| 77 |
+
"街坊",
|
| 78 |
+
"行李",
|
| 79 |
+
"行当",
|
| 80 |
+
"蛤蟆",
|
| 81 |
+
"蘑菇",
|
| 82 |
+
"薄荷",
|
| 83 |
+
"葫芦",
|
| 84 |
+
"葡萄",
|
| 85 |
+
"萝卜",
|
| 86 |
+
"荸荠",
|
| 87 |
+
"苗条",
|
| 88 |
+
"苗头",
|
| 89 |
+
"苍蝇",
|
| 90 |
+
"芝麻",
|
| 91 |
+
"舒服",
|
| 92 |
+
"舒坦",
|
| 93 |
+
"舌头",
|
| 94 |
+
"自在",
|
| 95 |
+
"膏药",
|
| 96 |
+
"脾气",
|
| 97 |
+
"脑袋",
|
| 98 |
+
"脊梁",
|
| 99 |
+
"能耐",
|
| 100 |
+
"胳膊",
|
| 101 |
+
"胭脂",
|
| 102 |
+
"胡萝",
|
| 103 |
+
"胡琴",
|
| 104 |
+
"胡同",
|
| 105 |
+
"聪明",
|
| 106 |
+
"耽误",
|
| 107 |
+
"耽搁",
|
| 108 |
+
"耷拉",
|
| 109 |
+
"耳朵",
|
| 110 |
+
"老爷",
|
| 111 |
+
"老实",
|
| 112 |
+
"老婆",
|
| 113 |
+
"老头",
|
| 114 |
+
"老太",
|
| 115 |
+
"翻腾",
|
| 116 |
+
"罗嗦",
|
| 117 |
+
"罐头",
|
| 118 |
+
"编辑",
|
| 119 |
+
"结实",
|
| 120 |
+
"红火",
|
| 121 |
+
"累赘",
|
| 122 |
+
"糨糊",
|
| 123 |
+
"糊涂",
|
| 124 |
+
"精神",
|
| 125 |
+
"粮食",
|
| 126 |
+
"簸箕",
|
| 127 |
+
"篱笆",
|
| 128 |
+
"算计",
|
| 129 |
+
"算盘",
|
| 130 |
+
"答应",
|
| 131 |
+
"笤帚",
|
| 132 |
+
"笑语",
|
| 133 |
+
"笑话",
|
| 134 |
+
"窟窿",
|
| 135 |
+
"窝囊",
|
| 136 |
+
"窗户",
|
| 137 |
+
"稳当",
|
| 138 |
+
"稀罕",
|
| 139 |
+
"称呼",
|
| 140 |
+
"秧歌",
|
| 141 |
+
"秀气",
|
| 142 |
+
"秀才",
|
| 143 |
+
"福气",
|
| 144 |
+
"祖宗",
|
| 145 |
+
"砚台",
|
| 146 |
+
"码头",
|
| 147 |
+
"石榴",
|
| 148 |
+
"石头",
|
| 149 |
+
"石匠",
|
| 150 |
+
"知识",
|
| 151 |
+
"眼睛",
|
| 152 |
+
"眯缝",
|
| 153 |
+
"眨巴",
|
| 154 |
+
"眉毛",
|
| 155 |
+
"相声",
|
| 156 |
+
"盘算",
|
| 157 |
+
"白净",
|
| 158 |
+
"痢疾",
|
| 159 |
+
"痛快",
|
| 160 |
+
"疟疾",
|
| 161 |
+
"疙瘩",
|
| 162 |
+
"疏忽",
|
| 163 |
+
"畜生",
|
| 164 |
+
"生意",
|
| 165 |
+
"甘蔗",
|
| 166 |
+
"琵琶",
|
| 167 |
+
"琢磨",
|
| 168 |
+
"琉璃",
|
| 169 |
+
"玻璃",
|
| 170 |
+
"玫瑰",
|
| 171 |
+
"玄乎",
|
| 172 |
+
"狐狸",
|
| 173 |
+
"状元",
|
| 174 |
+
"特务",
|
| 175 |
+
"牲口",
|
| 176 |
+
"牙碜",
|
| 177 |
+
"牌楼",
|
| 178 |
+
"爽快",
|
| 179 |
+
"爱人",
|
| 180 |
+
"热闹",
|
| 181 |
+
"烧饼",
|
| 182 |
+
"烟筒",
|
| 183 |
+
"烂糊",
|
| 184 |
+
"点心",
|
| 185 |
+
"炊帚",
|
| 186 |
+
"灯笼",
|
| 187 |
+
"火候",
|
| 188 |
+
"漂亮",
|
| 189 |
+
"滑溜",
|
| 190 |
+
"溜达",
|
| 191 |
+
"温和",
|
| 192 |
+
"清楚",
|
| 193 |
+
"消息",
|
| 194 |
+
"浪头",
|
| 195 |
+
"活泼",
|
| 196 |
+
"比方",
|
| 197 |
+
"正经",
|
| 198 |
+
"欺负",
|
| 199 |
+
"模糊",
|
| 200 |
+
"槟榔",
|
| 201 |
+
"棺材",
|
| 202 |
+
"棒槌",
|
| 203 |
+
"棉花",
|
| 204 |
+
"核桃",
|
| 205 |
+
"栅栏",
|
| 206 |
+
"柴火",
|
| 207 |
+
"架势",
|
| 208 |
+
"枕头",
|
| 209 |
+
"枇杷",
|
| 210 |
+
"机灵",
|
| 211 |
+
"本事",
|
| 212 |
+
"木头",
|
| 213 |
+
"木匠",
|
| 214 |
+
"朋友",
|
| 215 |
+
"月饼",
|
| 216 |
+
"月亮",
|
| 217 |
+
"暖和",
|
| 218 |
+
"明白",
|
| 219 |
+
"时候",
|
| 220 |
+
"新鲜",
|
| 221 |
+
"故事",
|
| 222 |
+
"收拾",
|
| 223 |
+
"收成",
|
| 224 |
+
"提防",
|
| 225 |
+
"挖苦",
|
| 226 |
+
"挑剔",
|
| 227 |
+
"指甲",
|
| 228 |
+
"指头",
|
| 229 |
+
"拾掇",
|
| 230 |
+
"拳头",
|
| 231 |
+
"拨弄",
|
| 232 |
+
"招牌",
|
| 233 |
+
"招呼",
|
| 234 |
+
"抬举",
|
| 235 |
+
"护士",
|
| 236 |
+
"折腾",
|
| 237 |
+
"扫帚",
|
| 238 |
+
"打量",
|
| 239 |
+
"打算",
|
| 240 |
+
"打点",
|
| 241 |
+
"打扮",
|
| 242 |
+
"打听",
|
| 243 |
+
"打发",
|
| 244 |
+
"扎实",
|
| 245 |
+
"扁担",
|
| 246 |
+
"戒指",
|
| 247 |
+
"懒得",
|
| 248 |
+
"意识",
|
| 249 |
+
"意思",
|
| 250 |
+
"情形",
|
| 251 |
+
"悟性",
|
| 252 |
+
"怪物",
|
| 253 |
+
"思量",
|
| 254 |
+
"怎么",
|
| 255 |
+
"念头",
|
| 256 |
+
"念叨",
|
| 257 |
+
"快活",
|
| 258 |
+
"忙活",
|
| 259 |
+
"志气",
|
| 260 |
+
"心思",
|
| 261 |
+
"得罪",
|
| 262 |
+
"张罗",
|
| 263 |
+
"弟兄",
|
| 264 |
+
"开通",
|
| 265 |
+
"应酬",
|
| 266 |
+
"庄稼",
|
| 267 |
+
"干事",
|
| 268 |
+
"帮手",
|
| 269 |
+
"帐篷",
|
| 270 |
+
"希罕",
|
| 271 |
+
"师父",
|
| 272 |
+
"师傅",
|
| 273 |
+
"巴结",
|
| 274 |
+
"巴掌",
|
| 275 |
+
"差事",
|
| 276 |
+
"工夫",
|
| 277 |
+
"岁数",
|
| 278 |
+
"屁股",
|
| 279 |
+
"尾巴",
|
| 280 |
+
"少爷",
|
| 281 |
+
"小气",
|
| 282 |
+
"小伙",
|
| 283 |
+
"将就",
|
| 284 |
+
"对头",
|
| 285 |
+
"对付",
|
| 286 |
+
"寡妇",
|
| 287 |
+
"家伙",
|
| 288 |
+
"客气",
|
| 289 |
+
"实在",
|
| 290 |
+
"官司",
|
| 291 |
+
"学问",
|
| 292 |
+
"学生",
|
| 293 |
+
"字号",
|
| 294 |
+
"嫁妆",
|
| 295 |
+
"媳妇",
|
| 296 |
+
"媒人",
|
| 297 |
+
"婆家",
|
| 298 |
+
"娘家",
|
| 299 |
+
"委屈",
|
| 300 |
+
"姑娘",
|
| 301 |
+
"姐夫",
|
| 302 |
+
"妯娌",
|
| 303 |
+
"妥当",
|
| 304 |
+
"妖精",
|
| 305 |
+
"奴才",
|
| 306 |
+
"女婿",
|
| 307 |
+
"头发",
|
| 308 |
+
"太阳",
|
| 309 |
+
"大爷",
|
| 310 |
+
"大方",
|
| 311 |
+
"大意",
|
| 312 |
+
"大夫",
|
| 313 |
+
"多少",
|
| 314 |
+
"多么",
|
| 315 |
+
"外甥",
|
| 316 |
+
"壮实",
|
| 317 |
+
"地道",
|
| 318 |
+
"地方",
|
| 319 |
+
"在乎",
|
| 320 |
+
"困难",
|
| 321 |
+
"嘴巴",
|
| 322 |
+
"嘱咐",
|
| 323 |
+
"嘟囔",
|
| 324 |
+
"嘀咕",
|
| 325 |
+
"喜欢",
|
| 326 |
+
"喇嘛",
|
| 327 |
+
"喇叭",
|
| 328 |
+
"商量",
|
| 329 |
+
"唾沫",
|
| 330 |
+
"哑巴",
|
| 331 |
+
"哈欠",
|
| 332 |
+
"哆嗦",
|
| 333 |
+
"咳嗽",
|
| 334 |
+
"和尚",
|
| 335 |
+
"告诉",
|
| 336 |
+
"告示",
|
| 337 |
+
"含糊",
|
| 338 |
+
"吓唬",
|
| 339 |
+
"后头",
|
| 340 |
+
"名字",
|
| 341 |
+
"名堂",
|
| 342 |
+
"合同",
|
| 343 |
+
"吆喝",
|
| 344 |
+
"叫唤",
|
| 345 |
+
"口袋",
|
| 346 |
+
"厚道",
|
| 347 |
+
"厉害",
|
| 348 |
+
"千斤",
|
| 349 |
+
"包袱",
|
| 350 |
+
"包涵",
|
| 351 |
+
"匀称",
|
| 352 |
+
"勤快",
|
| 353 |
+
"动静",
|
| 354 |
+
"动弹",
|
| 355 |
+
"功夫",
|
| 356 |
+
"力气",
|
| 357 |
+
"前头",
|
| 358 |
+
"刺猬",
|
| 359 |
+
"刺激",
|
| 360 |
+
"别扭",
|
| 361 |
+
"利落",
|
| 362 |
+
"利索",
|
| 363 |
+
"利害",
|
| 364 |
+
"分析",
|
| 365 |
+
"出息",
|
| 366 |
+
"凑合",
|
| 367 |
+
"凉快",
|
| 368 |
+
"冷战",
|
| 369 |
+
"冤枉",
|
| 370 |
+
"冒失",
|
| 371 |
+
"养活",
|
| 372 |
+
"关系",
|
| 373 |
+
"先生",
|
| 374 |
+
"兄弟",
|
| 375 |
+
"便宜",
|
| 376 |
+
"使唤",
|
| 377 |
+
"佩服",
|
| 378 |
+
"作坊",
|
| 379 |
+
"体面",
|
| 380 |
+
"位置",
|
| 381 |
+
"似的",
|
| 382 |
+
"伙计",
|
| 383 |
+
"休息",
|
| 384 |
+
"什么",
|
| 385 |
+
"人家",
|
| 386 |
+
"亲戚",
|
| 387 |
+
"亲家",
|
| 388 |
+
"交情",
|
| 389 |
+
"云彩",
|
| 390 |
+
"事情",
|
| 391 |
+
"买卖",
|
| 392 |
+
"主意",
|
| 393 |
+
"丫头",
|
| 394 |
+
"丧气",
|
| 395 |
+
"两口",
|
| 396 |
+
"东西",
|
| 397 |
+
"东家",
|
| 398 |
+
"世故",
|
| 399 |
+
"不由",
|
| 400 |
+
"不在",
|
| 401 |
+
"下水",
|
| 402 |
+
"下巴",
|
| 403 |
+
"上头",
|
| 404 |
+
"上司",
|
| 405 |
+
"丈夫",
|
| 406 |
+
"丈人",
|
| 407 |
+
"一辈",
|
| 408 |
+
"那个",
|
| 409 |
+
"菩萨",
|
| 410 |
+
"父亲",
|
| 411 |
+
"母亲",
|
| 412 |
+
"咕噜",
|
| 413 |
+
"邋遢",
|
| 414 |
+
"费用",
|
| 415 |
+
"冤家",
|
| 416 |
+
"甜头",
|
| 417 |
+
"介绍",
|
| 418 |
+
"荒唐",
|
| 419 |
+
"大人",
|
| 420 |
+
"泥鳅",
|
| 421 |
+
"幸福",
|
| 422 |
+
"熟悉",
|
| 423 |
+
"计划",
|
| 424 |
+
"扑腾",
|
| 425 |
+
"蜡烛",
|
| 426 |
+
"姥爷",
|
| 427 |
+
"照顾",
|
| 428 |
+
"喉咙",
|
| 429 |
+
"吉他",
|
| 430 |
+
"弄堂",
|
| 431 |
+
"蚂蚱",
|
| 432 |
+
"凤凰",
|
| 433 |
+
"拖沓",
|
| 434 |
+
"寒碜",
|
| 435 |
+
"糟蹋",
|
| 436 |
+
"倒腾",
|
| 437 |
+
"报复",
|
| 438 |
+
"逻辑",
|
| 439 |
+
"盘缠",
|
| 440 |
+
"喽啰",
|
| 441 |
+
"牢骚",
|
| 442 |
+
"咖喱",
|
| 443 |
+
"扫把",
|
| 444 |
+
"惦记",
|
| 445 |
}
|
| 446 |
self.must_not_neural_tone_words = {
|
| 447 |
+
"男子",
|
| 448 |
+
"女子",
|
| 449 |
+
"分子",
|
| 450 |
+
"原子",
|
| 451 |
+
"量子",
|
| 452 |
+
"莲子",
|
| 453 |
+
"石子",
|
| 454 |
+
"瓜子",
|
| 455 |
+
"电子",
|
| 456 |
+
"人人",
|
| 457 |
+
"虎虎",
|
| 458 |
}
|
| 459 |
self.punc = ":,;。?!“”‘’':,;.?!"
|
| 460 |
|
|
|
|
| 463 |
# word: "家里"
|
| 464 |
# pos: "s"
|
| 465 |
# finals: ['ia1', 'i3']
|
| 466 |
+
def _neural_sandhi(self, word: str, pos: str, finals: List[str]) -> List[str]:
|
|
|
|
|
|
|
| 467 |
# reduplication words for n. and v. e.g. 奶奶, 试试, 旺旺
|
| 468 |
for j, item in enumerate(word):
|
| 469 |
+
if (
|
| 470 |
+
j - 1 >= 0
|
| 471 |
+
and item == word[j - 1]
|
| 472 |
+
and pos[0] in {"n", "v", "a"}
|
| 473 |
+
and word not in self.must_not_neural_tone_words
|
| 474 |
+
):
|
| 475 |
finals[j] = finals[j][:-1] + "5"
|
| 476 |
ge_idx = word.find("个")
|
| 477 |
if len(word) >= 1 and word[-1] in "吧呢啊呐噻嘛吖嗨呐哦哒额滴哩哟喽啰耶喔诶":
|
|
|
|
| 481 |
# e.g. 走了, 看着, 去过
|
| 482 |
# elif len(word) == 1 and word in "了着过" and pos in {"ul", "uz", "ug"}:
|
| 483 |
# finals[-1] = finals[-1][:-1] + "5"
|
| 484 |
+
elif (
|
| 485 |
+
len(word) > 1
|
| 486 |
+
and word[-1] in "们子"
|
| 487 |
+
and pos in {"r", "n"}
|
| 488 |
+
and word not in self.must_not_neural_tone_words
|
| 489 |
+
):
|
| 490 |
finals[-1] = finals[-1][:-1] + "5"
|
| 491 |
# e.g. 桌上, 地下, 家里
|
| 492 |
elif len(word) > 1 and word[-1] in "上下里" and pos in {"s", "l", "f"}:
|
|
|
|
| 495 |
elif len(word) > 1 and word[-1] in "来去" and word[-2] in "上下进出回过起开":
|
| 496 |
finals[-1] = finals[-1][:-1] + "5"
|
| 497 |
# 个做量词
|
| 498 |
+
elif (
|
| 499 |
+
ge_idx >= 1
|
| 500 |
+
and (word[ge_idx - 1].isnumeric() or word[ge_idx - 1] in "几有两半多各整每做是")
|
| 501 |
+
) or word == "个":
|
| 502 |
finals[ge_idx] = finals[ge_idx][:-1] + "5"
|
| 503 |
else:
|
| 504 |
+
if (
|
| 505 |
+
word in self.must_neural_tone_words
|
| 506 |
+
or word[-2:] in self.must_neural_tone_words
|
| 507 |
+
):
|
| 508 |
finals[-1] = finals[-1][:-1] + "5"
|
| 509 |
|
| 510 |
word_list = self._split_word(word)
|
| 511 |
+
finals_list = [finals[: len(word_list[0])], finals[len(word_list[0]) :]]
|
| 512 |
for i, word in enumerate(word_list):
|
| 513 |
# conventional neural in Chinese
|
| 514 |
+
if (
|
| 515 |
+
word in self.must_neural_tone_words
|
| 516 |
+
or word[-2:] in self.must_neural_tone_words
|
| 517 |
+
):
|
| 518 |
finals_list[i][-1] = finals_list[i][-1][:-1] + "5"
|
| 519 |
finals = sum(finals_list, [])
|
| 520 |
return finals
|
|
|
|
| 526 |
else:
|
| 527 |
for i, char in enumerate(word):
|
| 528 |
# "不" before tone4 should be bu2, e.g. 不怕
|
| 529 |
+
if char == "不" and i + 1 < len(word) and finals[i + 1][-1] == "4":
|
|
|
|
| 530 |
finals[i] = finals[i][:-1] + "2"
|
| 531 |
return finals
|
| 532 |
|
| 533 |
def _yi_sandhi(self, word: str, finals: List[str]) -> List[str]:
|
| 534 |
# "一" in number sequences, e.g. 一零零, 二一零
|
| 535 |
if word.find("一") != -1 and all(
|
| 536 |
+
[item.isnumeric() for item in word if item != "一"]
|
| 537 |
+
):
|
| 538 |
return finals
|
| 539 |
+
# "一" between reduplication words should be yi5, e.g. 看一看
|
| 540 |
elif len(word) == 3 and word[1] == "一" and word[0] == word[-1]:
|
| 541 |
finals[1] = finals[1][:-1] + "5"
|
| 542 |
# when "一" is ordinal word, it should be yi1
|
|
|
|
| 561 |
first_subword = word_list[0]
|
| 562 |
first_begin_idx = word.find(first_subword)
|
| 563 |
if first_begin_idx == 0:
|
| 564 |
+
second_subword = word[len(first_subword) :]
|
| 565 |
new_word_list = [first_subword, second_subword]
|
| 566 |
else:
|
| 567 |
+
second_subword = word[: -len(first_subword)]
|
| 568 |
new_word_list = [second_subword, first_subword]
|
| 569 |
return new_word_list
|
| 570 |
|
|
|
|
| 582 |
elif len(word_list[0]) == 1:
|
| 583 |
finals[1] = finals[1][:-1] + "2"
|
| 584 |
else:
|
| 585 |
+
finals_list = [finals[: len(word_list[0])], finals[len(word_list[0]) :]]
|
|
|
|
|
|
|
| 586 |
if len(finals_list) == 2:
|
| 587 |
for i, sub in enumerate(finals_list):
|
| 588 |
# e.g. 所有/人
|
| 589 |
if self._all_tone_three(sub) and len(sub) == 2:
|
| 590 |
finals_list[i][0] = finals_list[i][0][:-1] + "2"
|
| 591 |
# e.g. 好/喜欢
|
| 592 |
+
elif (
|
| 593 |
+
i == 1
|
| 594 |
+
and not self._all_tone_three(sub)
|
| 595 |
+
and finals_list[i][0][-1] == "3"
|
| 596 |
+
and finals_list[0][-1][-1] == "3"
|
| 597 |
+
):
|
| 598 |
finals_list[0][-1] = finals_list[0][-1][:-1] + "2"
|
| 599 |
finals = sum(finals_list, [])
|
| 600 |
# split idiom into two words who's length is 2
|
|
|
|
| 623 |
new_seg.append((word, pos))
|
| 624 |
last_word = word[:]
|
| 625 |
if last_word == "不":
|
| 626 |
+
new_seg.append((last_word, "d"))
|
| 627 |
last_word = ""
|
| 628 |
return new_seg
|
| 629 |
|
|
|
|
| 637 |
new_seg = []
|
| 638 |
# function 1
|
| 639 |
for i, (word, pos) in enumerate(seg):
|
| 640 |
+
if (
|
| 641 |
+
i - 1 >= 0
|
| 642 |
+
and word == "一"
|
| 643 |
+
and i + 1 < len(seg)
|
| 644 |
+
and seg[i - 1][0] == seg[i + 1][0]
|
| 645 |
+
and seg[i - 1][1] == "v"
|
| 646 |
+
):
|
| 647 |
new_seg[i - 1][0] = new_seg[i - 1][0] + "一" + new_seg[i - 1][0]
|
| 648 |
else:
|
| 649 |
+
if (
|
| 650 |
+
i - 2 >= 0
|
| 651 |
+
and seg[i - 1][0] == "一"
|
| 652 |
+
and seg[i - 2][0] == word
|
| 653 |
+
and pos == "v"
|
| 654 |
+
):
|
| 655 |
continue
|
| 656 |
else:
|
| 657 |
new_seg.append([word, pos])
|
|
|
|
| 667 |
|
| 668 |
# the first and the second words are all_tone_three
|
| 669 |
def _merge_continuous_three_tones(
|
| 670 |
+
self, seg: List[Tuple[str, str]]
|
| 671 |
+
) -> List[Tuple[str, str]]:
|
| 672 |
new_seg = []
|
| 673 |
sub_finals_list = [
|
| 674 |
+
lazy_pinyin(word, neutral_tone_with_five=True, style=Style.FINALS_TONE3)
|
|
|
|
| 675 |
for (word, pos) in seg
|
| 676 |
]
|
| 677 |
assert len(sub_finals_list) == len(seg)
|
| 678 |
merge_last = [False] * len(seg)
|
| 679 |
for i, (word, pos) in enumerate(seg):
|
| 680 |
+
if (
|
| 681 |
+
i - 1 >= 0
|
| 682 |
+
and self._all_tone_three(sub_finals_list[i - 1])
|
| 683 |
+
and self._all_tone_three(sub_finals_list[i])
|
| 684 |
+
and not merge_last[i - 1]
|
| 685 |
+
):
|
| 686 |
# if the last word is reduplication, not merge, because reduplication need to be _neural_sandhi
|
| 687 |
+
if (
|
| 688 |
+
not self._is_reduplication(seg[i - 1][0])
|
| 689 |
+
and len(seg[i - 1][0]) + len(seg[i][0]) <= 3
|
| 690 |
+
):
|
| 691 |
new_seg[-1][0] = new_seg[-1][0] + seg[i][0]
|
| 692 |
merge_last[i] = True
|
| 693 |
else:
|
|
|
|
| 702 |
|
| 703 |
# the last char of first word and the first char of second word is tone_three
|
| 704 |
def _merge_continuous_three_tones_2(
|
| 705 |
+
self, seg: List[Tuple[str, str]]
|
| 706 |
+
) -> List[Tuple[str, str]]:
|
| 707 |
new_seg = []
|
| 708 |
sub_finals_list = [
|
| 709 |
+
lazy_pinyin(word, neutral_tone_with_five=True, style=Style.FINALS_TONE3)
|
|
|
|
| 710 |
for (word, pos) in seg
|
| 711 |
]
|
| 712 |
assert len(sub_finals_list) == len(seg)
|
| 713 |
merge_last = [False] * len(seg)
|
| 714 |
for i, (word, pos) in enumerate(seg):
|
| 715 |
+
if (
|
| 716 |
+
i - 1 >= 0
|
| 717 |
+
and sub_finals_list[i - 1][-1][-1] == "3"
|
| 718 |
+
and sub_finals_list[i][0][-1] == "3"
|
| 719 |
+
and not merge_last[i - 1]
|
| 720 |
+
):
|
| 721 |
# if the last word is reduplication, not merge, because reduplication need to be _neural_sandhi
|
| 722 |
+
if (
|
| 723 |
+
not self._is_reduplication(seg[i - 1][0])
|
| 724 |
+
and len(seg[i - 1][0]) + len(seg[i][0]) <= 3
|
| 725 |
+
):
|
| 726 |
new_seg[-1][0] = new_seg[-1][0] + seg[i][0]
|
| 727 |
merge_last[i] = True
|
| 728 |
else:
|
|
|
|
| 740 |
new_seg.append([word, pos])
|
| 741 |
return new_seg
|
| 742 |
|
| 743 |
+
def _merge_reduplication(self, seg: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
|
|
|
|
| 744 |
new_seg = []
|
| 745 |
for i, (word, pos) in enumerate(seg):
|
| 746 |
if new_seg and word == new_seg[-1][0]:
|
|
|
|
| 749 |
new_seg.append([word, pos])
|
| 750 |
return new_seg
|
| 751 |
|
| 752 |
+
def pre_merge_for_modify(self, seg: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
|
|
|
|
| 753 |
seg = self._merge_bu(seg)
|
| 754 |
try:
|
| 755 |
seg = self._merge_yi(seg)
|
|
|
|
| 761 |
seg = self._merge_er(seg)
|
| 762 |
return seg
|
| 763 |
|
| 764 |
+
def modified_tone(self, word: str, pos: str, finals: List[str]) -> List[str]:
|
|
|
|
| 765 |
finals = self._bu_sandhi(word, finals)
|
| 766 |
finals = self._yi_sandhi(word, finals)
|
| 767 |
finals = self._neural_sandhi(word, pos, finals)
|
config.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
import os
|
| 2 |
import sys
|
| 3 |
|
|
|
|
|
|
|
| 4 |
JSON_AS_ASCII = False
|
| 5 |
|
| 6 |
MAX_CONTENT_LENGTH = 5242880
|
|
@@ -79,6 +81,8 @@ DIMENSIONAL_EMOTION_NPY = ABS_PATH + "/Model/npy"
|
|
| 79 |
# w2v2-vits: Need to have both `model.onnx` and `model.yaml` files in the same path.
|
| 80 |
# DIMENSIONAL_EMOTION_MODEL = ABS_PATH + "/Model/model.yaml"
|
| 81 |
|
|
|
|
|
|
|
| 82 |
"""
|
| 83 |
Default parameter
|
| 84 |
"""
|
|
|
|
| 1 |
import os
|
| 2 |
import sys
|
| 3 |
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
JSON_AS_ASCII = False
|
| 7 |
|
| 8 |
MAX_CONTENT_LENGTH = 5242880
|
|
|
|
| 81 |
# w2v2-vits: Need to have both `model.onnx` and `model.yaml` files in the same path.
|
| 82 |
# DIMENSIONAL_EMOTION_MODEL = ABS_PATH + "/Model/model.yaml"
|
| 83 |
|
| 84 |
+
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 85 |
+
|
| 86 |
"""
|
| 87 |
Default parameter
|
| 88 |
"""
|
requirements.txt
CHANGED
|
@@ -28,4 +28,5 @@ fastlid
|
|
| 28 |
langid
|
| 29 |
phonemizer==3.2.1
|
| 30 |
transformers
|
| 31 |
-
pydantic==2.3.0
|
|
|
|
|
|
| 28 |
langid
|
| 29 |
phonemizer==3.2.1
|
| 30 |
transformers
|
| 31 |
+
pydantic==2.3.0
|
| 32 |
+
num2words
|
utils/{merge.py → load_model.py}
RENAMED
|
File without changes
|
utils/{nlp.py → sentence.py}
RENAMED
|
File without changes
|
vits/vits.py
CHANGED
|
@@ -4,7 +4,7 @@ import re
|
|
| 4 |
import numpy as np
|
| 5 |
import torch
|
| 6 |
from torch import no_grad, LongTensor, inference_mode, FloatTensor
|
| 7 |
-
from utils.
|
| 8 |
from vits.mel_processing import spectrogram_torch
|
| 9 |
from vits.text import text_to_sequence
|
| 10 |
from vits.models import SynthesizerTrn
|
|
|
|
| 4 |
import numpy as np
|
| 5 |
import torch
|
| 6 |
from torch import no_grad, LongTensor, inference_mode, FloatTensor
|
| 7 |
+
from utils.sentence import sentence_split
|
| 8 |
from vits.mel_processing import spectrogram_torch
|
| 9 |
from vits.text import text_to_sequence
|
| 10 |
from vits.models import SynthesizerTrn
|