Artrajz commited on
Commit
0988497
·
1 Parent(s): 0ef431b

update: bert_vits2 japanese

Browse files
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.merge import merge_model
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 utils.nlp import sentence_split, cut
 
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
- _symbol_to_id = {s: i for i, s in enumerate(symbols)}
6
-
7
-
8
- def cleaned_text_to_sequence(cleaned_text, tones, language):
9
- '''Converts a string of text to a sequence of IDs corresponding to the symbols in the text.
10
- Args:
11
- text: string to convert to a sequence
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
- lang_bert_func_map = {
25
- 'ZH': zh_bert,
26
- 'EN': en_bert
27
- }
28
- bert = lang_bert_func_map[language](norm_text, word2ph)
 
 
 
 
 
 
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
- device)
13
  logger.info("Loading finished.")
14
  except Exception as e:
15
  logger.error(e)
16
- logger.error(f"Please download model from hfl/chinese-roberta-wwm-ext-large.")
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
- # modified from https://github.com/CjangCjengh/vits/blob/main/text/japanese.py
 
2
  import re
3
- import sys
4
-
5
- import pyopenjtalk
6
-
7
- from bert_vits2.text import symbols
8
-
9
- # Regular expression matching Japanese without punctuation marks:
10
- _japanese_characters = re.compile(
11
- r'[A-Za-z\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d]')
12
-
13
- # Regular expression matching non-Japanese characters or punctuation marks:
14
- _japanese_marks = re.compile(
15
- r'[^A-Za-z\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d]')
16
-
17
- # List of (symbol, Japanese) pairs for marks:
18
- _symbols_to_japanese = [(re.compile('%s' % x[0]), x[1]) for x in [
19
- ('%', 'パーセント')
20
- ]]
21
-
22
- # List of (consonant, sokuon) pairs:
23
- _real_sokuon = [(re.compile('%s' % x[0]), x[1]) for x in [
24
- (r'Q([↑↓]*[kg])', r'k#\1'),
25
- (r'Q([↑↓]*[tdjʧ])', r't#\1'),
26
- (r'Q([↑↓]*[sʃ])', r's\1'),
27
- (r'Q([↑↓]*[pb])', r'p#\1')
28
- ]]
29
-
30
- # List of (consonant, hatsuon) pairs:
31
- _real_hatsuon = [(re.compile('%s' % x[0]), x[1]) for x in [
32
- (r'N([↑↓]*[pbm])', r'm\1'),
33
- (r'N([↑↓]*[ʧʥj])', r'n^\1'),
34
- (r'N([↑↓]*[tdn])', r'n\1'),
35
- (r'N([↑↓]*[kg])', r'ŋ\1')
36
- ]]
37
-
38
-
39
- def post_replace_ph(ph):
40
- rep_map = {
41
- ':': ',',
42
- ';': ',',
43
- ',': ',',
44
- '。': '.',
45
- '!': '!',
46
- '?': '?',
47
- '\n': '.',
48
- "·": ",",
49
- '、': ",",
50
- '...': '…',
51
- 'v': "V"
52
- }
53
- if ph in rep_map.keys():
54
- ph = rep_map[ph]
55
- if ph in symbols:
56
- return ph
57
- if ph not in symbols:
58
- ph = 'UNK'
59
- return ph
60
-
61
-
62
- def symbols_to_japanese(text):
63
- for regex, replacement in _symbols_to_japanese:
64
- text = re.sub(regex, replacement, text)
65
- return text
66
-
67
-
68
- def preprocess_jap(text):
69
- '''Reference https://r9y9.github.io/ttslearn/latest/notebooks/ch10_Recipe-Tacotron.html'''
70
- text = symbols_to_japanese(text)
71
- sentences = re.split(_japanese_marks, text)
72
- marks = re.findall(_japanese_marks, text)
73
- text = []
74
- for i, sentence in enumerate(sentences):
75
- if re.match(_japanese_characters, sentence):
76
- p = pyopenjtalk.g2p(sentence)
77
- text += p.split(" ")
78
-
79
- if i < len(marks):
80
- text += [marks[i].replace(' ', '')]
81
- return text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
 
84
  def text_normalize(text):
85
- return text
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
 
88
  def g2p(norm_text):
89
- phones = preprocess_jap(norm_text)
90
- phones = [post_replace_ph(i) for i in phones]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
 
 
92
  tones = [0 for i in phones]
93
- word2ph = [1 for i in phones]
94
  return phones, tones, word2ph
95
 
96
 
97
- if __name__ == '__main__':
98
- for line in open("../../../Downloads/transcript_utf8.txt").readlines():
99
- text = line.split(":")[1]
100
- phones, tones, word2ph = g2p(text)
101
- for p in phones:
102
- if p == "z":
103
- print(text, phones)
104
- sys.exit(0)
 
 
 
 
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 = ['E', 'En', 'a', 'ai', 'an', 'ang', 'ao', 'b', 'c', 'ch', 'd', 'e', 'ei', 'en', 'eng', 'er', 'f', 'g', 'h',
7
- 'i', 'i0', 'ia', 'ian', 'iang', 'iao', 'ie', 'in', 'ing', 'iong', 'ir', 'iu', 'j', 'k', 'l', 'm', 'n',
8
- 'o',
9
- 'ong',
10
- 'ou', 'p', 'q', 'r', 's', 'sh', 't', 'u', 'ua', 'uai', 'uan', 'uang', 'ui', 'un', 'uo', 'v', 'van', 've',
11
- 'vn',
12
- 'w', 'x', 'y', 'z', 'zh',
13
- "AA", "EE", "OO"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  num_zh_tones = 6
15
 
16
  # japanese
17
- ja_symbols = ['I', 'N', 'U', 'a', 'b', 'by', 'ch', 'cl', 'd', 'dy', 'e', 'f', 'g', 'gy', 'h', 'hy', 'i', 'j', 'k', 'ky',
18
- 'm', 'my', 'n', 'ny', 'o', 'p', 'py', 'r', 'ry', 's', 'sh', 't', 'ts', 'u', 'V', 'w', 'y', 'z']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  num_ja_tones = 1
20
 
21
  # English
22
- en_symbols = ['aa', 'ae', 'ah', 'ao', 'aw', 'ay', 'b', 'ch', 'd', 'dh', 'eh', 'er', 'ey', 'f', 'g', 'hh', 'ih', 'iy',
23
- 'jh', 'k', 'l', 'm', 'n', 'ng', 'ow', 'oy', 'p', 'r', 's',
24
- 'sh', 't', 'th', 'uh', 'uw', 'V', 'w', 'y', 'z', 'zh']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  num_en_tones = 4
26
 
27
- # combine all symbols
28
- normal_symbols = sorted(set(zh_symbols + ja_symbols + en_symbols))
29
- symbols = [pad] + normal_symbols + pu_symbols
30
- sil_phonemes_ids = [symbols.index(i) for i in pu_symbols]
31
-
32
- # combine all tones
33
- num_tones = num_zh_tones + num_ja_tones + num_en_tones
34
-
35
- # language maps
36
- language_id_map = {
37
- 'ZH': 0,
38
- "JA": 1,
39
- "EN": 2
40
- }
41
- num_languages = len(language_id_map.keys())
42
-
43
- language_tone_start_map = {
44
- 'ZH': 0,
45
- "JA": num_zh_tones,
46
- "EN": num_zh_tones + num_ja_tones
47
- }
48
-
49
- if __name__ == '__main__':
50
- a = set(zh_symbols)
51
- b = set(en_symbols)
52
- print(sorted(a & b))
 
 
 
 
 
 
 
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 j - 1 >= 0 and item == word[j - 1] and pos[0] in {
81
- "n", "v", "a"
82
- } and word not in self.must_not_neural_tone_words:
 
 
 
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 len(word) > 1 and word[-1] in "们子" and pos in {
93
- "r", "n"
94
- } and word not in self.must_not_neural_tone_words:
 
 
 
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 (ge_idx >= 1 and
104
- (word[ge_idx - 1].isnumeric() or
105
- word[ge_idx - 1] in "几有两半多各整每做是")) or word == '个':
 
106
  finals[ge_idx] = finals[ge_idx][:-1] + "5"
107
  else:
108
- if word in self.must_neural_tone_words or word[
109
- -2:] in self.must_neural_tone_words:
 
 
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 word in self.must_neural_tone_words or word[
117
- -2:] in self.must_neural_tone_words:
 
 
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
- [item.isnumeric() for item in word if item != "一"]):
 
138
  return finals
139
- # "一" between reduplication words shold be yi5, e.g. 看一看
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 i == 1 and not self._all_tone_three(sub) and finals_list[i][0][-1] == "3" and \
195
- finals_list[0][-1][-1] == "3":
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, 'd'))
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 i - 1 >= 0 and word == "一" and i + 1 < len(seg) and seg[i - 1][
240
- 0] == seg[i + 1][0] and seg[i - 1][1] == "v":
 
 
 
 
 
241
  new_seg[i - 1][0] = new_seg[i - 1][0] + "一" + new_seg[i - 1][0]
242
  else:
243
- if i - 2 >= 0 and seg[i - 1][0] == "一" and seg[i - 2][
244
- 0] == word and pos == "v":
 
 
 
 
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
- self, seg: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
 
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 i - 1 >= 0 and self._all_tone_three(
271
- sub_finals_list[i - 1]) and self._all_tone_three(
272
- sub_finals_list[i]) and not merge_last[i - 1]:
 
 
 
273
  # if the last word is reduplication, not merge, because reduplication need to be _neural_sandhi
274
- if not self._is_reduplication(seg[i - 1][0]) and len(
275
- seg[i - 1][0]) + len(seg[i][0]) <= 3:
 
 
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
- self, seg: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
 
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 i - 1 >= 0 and sub_finals_list[i - 1][-1][-1] == "3" and sub_finals_list[i][0][-1] == "3" and not \
301
- merge_last[i - 1]:
 
 
 
 
302
  # if the last word is reduplication, not merge, because reduplication need to be _neural_sandhi
303
- if not self._is_reduplication(seg[i - 1][0]) and len(
304
- seg[i - 1][0]) + len(seg[i][0]) <= 3:
 
 
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.nlp 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
 
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