mirror of
https://github.com/BoardWare-Genius/jarvis-models.git
synced 2025-12-14 00:53:25 +00:00
feat: tts
This commit is contained in:
19
tts/vits/text/LICENSE
Normal file
19
tts/vits/text/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017 Keith Ito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
56
tts/vits/text/__init__.py
Normal file
56
tts/vits/text/__init__.py
Normal file
@ -0,0 +1,56 @@
|
||||
""" from https://github.com/keithito/tacotron """
|
||||
from text import cleaners
|
||||
from text.symbols import symbols
|
||||
|
||||
|
||||
# Mappings from symbol to numeric ID and vice versa:
|
||||
_symbol_to_id = {s: i for i, s in enumerate(symbols)}
|
||||
_id_to_symbol = {i: s for i, s in enumerate(symbols)}
|
||||
|
||||
|
||||
def text_to_sequence(text, cleaner_names):
|
||||
'''Converts a string of text to a sequence of IDs corresponding to the symbols in the text.
|
||||
Args:
|
||||
text: string to convert to a sequence
|
||||
cleaner_names: names of the cleaner functions to run the text through
|
||||
Returns:
|
||||
List of integers corresponding to the symbols in the text
|
||||
'''
|
||||
sequence = []
|
||||
|
||||
clean_text = _clean_text(text, cleaner_names)
|
||||
for symbol in clean_text:
|
||||
if symbol not in _symbol_to_id.keys():
|
||||
continue
|
||||
symbol_id = _symbol_to_id[symbol]
|
||||
sequence += [symbol_id]
|
||||
return sequence
|
||||
|
||||
|
||||
def cleaned_text_to_sequence(cleaned_text):
|
||||
'''Converts a string of text to a sequence of IDs corresponding to the symbols in the text.
|
||||
Args:
|
||||
text: string to convert to a sequence
|
||||
Returns:
|
||||
List of integers corresponding to the symbols in the text
|
||||
'''
|
||||
sequence = [_symbol_to_id[symbol] for symbol in cleaned_text if symbol in _symbol_to_id.keys()]
|
||||
return sequence
|
||||
|
||||
|
||||
def sequence_to_text(sequence):
|
||||
'''Converts a sequence of IDs back to a string'''
|
||||
result = ''
|
||||
for symbol_id in sequence:
|
||||
s = _id_to_symbol[symbol_id]
|
||||
result += s
|
||||
return result
|
||||
|
||||
|
||||
def _clean_text(text, cleaner_names):
|
||||
for name in cleaner_names:
|
||||
cleaner = getattr(cleaners, name)
|
||||
if not cleaner:
|
||||
raise Exception('Unknown cleaner: %s' % name)
|
||||
text = cleaner(text)
|
||||
return text
|
||||
59
tts/vits/text/cantonese.py
Normal file
59
tts/vits/text/cantonese.py
Normal file
@ -0,0 +1,59 @@
|
||||
import re
|
||||
import cn2an
|
||||
import opencc
|
||||
|
||||
|
||||
converter = opencc.OpenCC('jyutjyu')
|
||||
|
||||
# List of (Latin alphabet, ipa) pairs:
|
||||
_latin_to_ipa = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('A', 'ei˥'),
|
||||
('B', 'biː˥'),
|
||||
('C', 'siː˥'),
|
||||
('D', 'tiː˥'),
|
||||
('E', 'iː˥'),
|
||||
('F', 'e˥fuː˨˩'),
|
||||
('G', 'tsiː˥'),
|
||||
('H', 'ɪk̚˥tsʰyː˨˩'),
|
||||
('I', 'ɐi˥'),
|
||||
('J', 'tsei˥'),
|
||||
('K', 'kʰei˥'),
|
||||
('L', 'e˥llou˨˩'),
|
||||
('M', 'ɛːm˥'),
|
||||
('N', 'ɛːn˥'),
|
||||
('O', 'ou˥'),
|
||||
('P', 'pʰiː˥'),
|
||||
('Q', 'kʰiːu˥'),
|
||||
('R', 'aː˥lou˨˩'),
|
||||
('S', 'ɛː˥siː˨˩'),
|
||||
('T', 'tʰiː˥'),
|
||||
('U', 'juː˥'),
|
||||
('V', 'wiː˥'),
|
||||
('W', 'tʊk̚˥piː˥juː˥'),
|
||||
('X', 'ɪk̚˥siː˨˩'),
|
||||
('Y', 'waːi˥'),
|
||||
('Z', 'iː˨sɛːt̚˥')
|
||||
]]
|
||||
|
||||
|
||||
def number_to_cantonese(text):
|
||||
return re.sub(r'\d+(?:\.?\d+)?', lambda x: cn2an.an2cn(x.group()), text)
|
||||
|
||||
|
||||
def latin_to_ipa(text):
|
||||
for regex, replacement in _latin_to_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def cantonese_to_ipa(text):
|
||||
text = number_to_cantonese(text.upper())
|
||||
text = converter.convert(text).replace('-','').replace('$',' ')
|
||||
text = re.sub(r'[A-Z]', lambda x: latin_to_ipa(x.group())+' ', text)
|
||||
text = re.sub(r'[、;:]', ',', text)
|
||||
text = re.sub(r'\s*,\s*', ', ', text)
|
||||
text = re.sub(r'\s*。\s*', '. ', text)
|
||||
text = re.sub(r'\s*?\s*', '? ', text)
|
||||
text = re.sub(r'\s*!\s*', '! ', text)
|
||||
text = re.sub(r'\s*$', '', text)
|
||||
return text
|
||||
128
tts/vits/text/cleaners.py
Normal file
128
tts/vits/text/cleaners.py
Normal file
@ -0,0 +1,128 @@
|
||||
import re
|
||||
# from text.japanese import japanese_to_romaji_with_accent, japanese_to_ipa, japanese_to_ipa2, japanese_to_ipa3
|
||||
# from text.korean import latin_to_hangul, number_to_hangul, divide_hangul, korean_to_lazy_ipa, korean_to_ipa
|
||||
from text.mandarin import number_to_chinese, chinese_to_bopomofo, latin_to_bopomofo, chinese_to_romaji, chinese_to_lazy_ipa, chinese_to_ipa, chinese_to_ipa2
|
||||
# from text.sanskrit import devanagari_to_ipa
|
||||
# from text.english import english_to_lazy_ipa, english_to_ipa2, english_to_lazy_ipa2
|
||||
# from text.thai import num_to_thai, latin_to_thai
|
||||
# from text.shanghainese import shanghainese_to_ipa
|
||||
# from text.cantonese import cantonese_to_ipa
|
||||
# from text.ngu_dialect import ngu_dialect_to_ipa
|
||||
|
||||
|
||||
# def japanese_cleaners(text):
|
||||
# text = japanese_to_romaji_with_accent(text)
|
||||
# text = re.sub(r'([A-Za-z])$', r'\1.', text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def japanese_cleaners2(text):
|
||||
# return japanese_cleaners(text).replace('ts', 'ʦ').replace('...', '…')
|
||||
#
|
||||
#
|
||||
# def korean_cleaners(text):
|
||||
# '''Pipeline for Korean text'''
|
||||
# text = latin_to_hangul(text)
|
||||
# text = number_to_hangul(text)
|
||||
# text = divide_hangul(text)
|
||||
# text = re.sub(r'([\u3131-\u3163])$', r'\1.', text)
|
||||
# return text
|
||||
#
|
||||
|
||||
def chinese_cleaners(text):
|
||||
'''Pipeline for Chinese text'''
|
||||
text = number_to_chinese(text)
|
||||
text = chinese_to_bopomofo(text)
|
||||
text = latin_to_bopomofo(text)
|
||||
text = re.sub(r'([ˉˊˇˋ˙])$', r'\1。', text)
|
||||
return text
|
||||
|
||||
|
||||
def zh_ja_mixture_cleaners(text):
|
||||
text = re.sub(r'\[ZH\](.*?)\[ZH\]',
|
||||
lambda x: chinese_to_romaji(x.group(1))+' ', text)
|
||||
text = re.sub(r'\[JA\](.*?)\[JA\]', lambda x: japanese_to_romaji_with_accent(
|
||||
x.group(1)).replace('ts', 'ʦ').replace('u', 'ɯ').replace('...', '…')+' ', text)
|
||||
text = re.sub(r'\s+$', '', text)
|
||||
text = re.sub(r'([^\.,!\?\-…~])$', r'\1.', text)
|
||||
return text
|
||||
|
||||
|
||||
# def sanskrit_cleaners(text):
|
||||
# text = text.replace('॥', '।').replace('ॐ', 'ओम्')
|
||||
# text = re.sub(r'([^।])$', r'\1।', text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def cjks_cleaners(text):
|
||||
# text = re.sub(r'\[ZH\](.*?)\[ZH\]',
|
||||
# lambda x: chinese_to_lazy_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[JA\](.*?)\[JA\]',
|
||||
# lambda x: japanese_to_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[KO\](.*?)\[KO\]',
|
||||
# lambda x: korean_to_lazy_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[SA\](.*?)\[SA\]',
|
||||
# lambda x: devanagari_to_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[EN\](.*?)\[EN\]',
|
||||
# lambda x: english_to_lazy_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\s+$', '', text)
|
||||
# text = re.sub(r'([^\.,!\?\-…~])$', r'\1.', text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def cjke_cleaners(text):
|
||||
# text = re.sub(r'\[ZH\](.*?)\[ZH\]', lambda x: chinese_to_lazy_ipa(x.group(1)).replace(
|
||||
# 'ʧ', 'tʃ').replace('ʦ', 'ts').replace('ɥan', 'ɥæn')+' ', text)
|
||||
# text = re.sub(r'\[JA\](.*?)\[JA\]', lambda x: japanese_to_ipa(x.group(1)).replace('ʧ', 'tʃ').replace(
|
||||
# 'ʦ', 'ts').replace('ɥan', 'ɥæn').replace('ʥ', 'dz')+' ', text)
|
||||
# text = re.sub(r'\[KO\](.*?)\[KO\]',
|
||||
# lambda x: korean_to_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[EN\](.*?)\[EN\]', lambda x: english_to_ipa2(x.group(1)).replace('ɑ', 'a').replace(
|
||||
# 'ɔ', 'o').replace('ɛ', 'e').replace('ɪ', 'i').replace('ʊ', 'u')+' ', text)
|
||||
# text = re.sub(r'\s+$', '', text)
|
||||
# text = re.sub(r'([^\.,!\?\-…~])$', r'\1.', text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def cjke_cleaners2(text):
|
||||
# text = re.sub(r'\[ZH\](.*?)\[ZH\]',
|
||||
# lambda x: chinese_to_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[JA\](.*?)\[JA\]',
|
||||
# lambda x: japanese_to_ipa2(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[KO\](.*?)\[KO\]',
|
||||
# lambda x: korean_to_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[EN\](.*?)\[EN\]',
|
||||
# lambda x: english_to_ipa2(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\s+$', '', text)
|
||||
# text = re.sub(r'([^\.,!\?\-…~])$', r'\1.', text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def thai_cleaners(text):
|
||||
# text = num_to_thai(text)
|
||||
# text = latin_to_thai(text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def shanghainese_cleaners(text):
|
||||
# text = shanghainese_to_ipa(text)
|
||||
# text = re.sub(r'([^\.,!\?\-…~])$', r'\1.', text)
|
||||
# return text
|
||||
#
|
||||
#
|
||||
# def chinese_dialect_cleaners(text):
|
||||
# text = re.sub(r'\[ZH\](.*?)\[ZH\]',
|
||||
# lambda x: chinese_to_ipa2(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[JA\](.*?)\[JA\]',
|
||||
# lambda x: japanese_to_ipa3(x.group(1)).replace('Q', 'ʔ')+' ', text)
|
||||
# text = re.sub(r'\[SH\](.*?)\[SH\]', lambda x: shanghainese_to_ipa(x.group(1)).replace('1', '˥˧').replace('5',
|
||||
# '˧˧˦').replace('6', '˩˩˧').replace('7', '˥').replace('8', '˩˨').replace('ᴀ', 'ɐ').replace('ᴇ', 'e')+' ', text)
|
||||
# text = re.sub(r'\[GD\](.*?)\[GD\]',
|
||||
# lambda x: cantonese_to_ipa(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[EN\](.*?)\[EN\]',
|
||||
# lambda x: english_to_lazy_ipa2(x.group(1))+' ', text)
|
||||
# text = re.sub(r'\[([A-Z]{2})\](.*?)\[\1\]', lambda x: ngu_dialect_to_ipa(x.group(2), x.group(
|
||||
# 1)).replace('ʣ', 'dz').replace('ʥ', 'dʑ').replace('ʦ', 'ts').replace('ʨ', 'tɕ')+' ', text)
|
||||
# text = re.sub(r'\s+$', '', text)
|
||||
# text = re.sub(r'([^\.,!\?\-…~])$', r'\1.', text)
|
||||
# return text
|
||||
188
tts/vits/text/english.py
Normal file
188
tts/vits/text/english.py
Normal file
@ -0,0 +1,188 @@
|
||||
""" from https://github.com/keithito/tacotron """
|
||||
|
||||
'''
|
||||
Cleaners are transformations that run over the input text at both training and eval time.
|
||||
|
||||
Cleaners can be selected by passing a comma-delimited list of cleaner names as the "cleaners"
|
||||
hyperparameter. Some cleaners are English-specific. You'll typically want to use:
|
||||
1. "english_cleaners" for English text
|
||||
2. "transliteration_cleaners" for non-English text that can be transliterated to ASCII using
|
||||
the Unidecode library (https://pypi.python.org/pypi/Unidecode)
|
||||
3. "basic_cleaners" if you do not want to transliterate (in this case, you should also update
|
||||
the symbols in symbols.py to match your data).
|
||||
'''
|
||||
|
||||
|
||||
# Regular expression matching whitespace:
|
||||
|
||||
|
||||
import re
|
||||
import inflect
|
||||
from unidecode import unidecode
|
||||
import eng_to_ipa as ipa
|
||||
_inflect = inflect.engine()
|
||||
_comma_number_re = re.compile(r'([0-9][0-9\,]+[0-9])')
|
||||
_decimal_number_re = re.compile(r'([0-9]+\.[0-9]+)')
|
||||
_pounds_re = re.compile(r'£([0-9\,]*[0-9]+)')
|
||||
_dollars_re = re.compile(r'\$([0-9\.\,]*[0-9]+)')
|
||||
_ordinal_re = re.compile(r'[0-9]+(st|nd|rd|th)')
|
||||
_number_re = re.compile(r'[0-9]+')
|
||||
|
||||
# List of (regular expression, replacement) pairs for abbreviations:
|
||||
_abbreviations = [(re.compile('\\b%s\\.' % x[0], re.IGNORECASE), x[1]) for x in [
|
||||
('mrs', 'misess'),
|
||||
('mr', 'mister'),
|
||||
('dr', 'doctor'),
|
||||
('st', 'saint'),
|
||||
('co', 'company'),
|
||||
('jr', 'junior'),
|
||||
('maj', 'major'),
|
||||
('gen', 'general'),
|
||||
('drs', 'doctors'),
|
||||
('rev', 'reverend'),
|
||||
('lt', 'lieutenant'),
|
||||
('hon', 'honorable'),
|
||||
('sgt', 'sergeant'),
|
||||
('capt', 'captain'),
|
||||
('esq', 'esquire'),
|
||||
('ltd', 'limited'),
|
||||
('col', 'colonel'),
|
||||
('ft', 'fort'),
|
||||
]]
|
||||
|
||||
|
||||
# List of (ipa, lazy ipa) pairs:
|
||||
_lazy_ipa = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('r', 'ɹ'),
|
||||
('æ', 'e'),
|
||||
('ɑ', 'a'),
|
||||
('ɔ', 'o'),
|
||||
('ð', 'z'),
|
||||
('θ', 's'),
|
||||
('ɛ', 'e'),
|
||||
('ɪ', 'i'),
|
||||
('ʊ', 'u'),
|
||||
('ʒ', 'ʥ'),
|
||||
('ʤ', 'ʥ'),
|
||||
('ˈ', '↓'),
|
||||
]]
|
||||
|
||||
# List of (ipa, lazy ipa2) pairs:
|
||||
_lazy_ipa2 = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('r', 'ɹ'),
|
||||
('ð', 'z'),
|
||||
('θ', 's'),
|
||||
('ʒ', 'ʑ'),
|
||||
('ʤ', 'dʑ'),
|
||||
('ˈ', '↓'),
|
||||
]]
|
||||
|
||||
# List of (ipa, ipa2) pairs
|
||||
_ipa_to_ipa2 = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('r', 'ɹ'),
|
||||
('ʤ', 'dʒ'),
|
||||
('ʧ', 'tʃ')
|
||||
]]
|
||||
|
||||
|
||||
def expand_abbreviations(text):
|
||||
for regex, replacement in _abbreviations:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def collapse_whitespace(text):
|
||||
return re.sub(r'\s+', ' ', text)
|
||||
|
||||
|
||||
def _remove_commas(m):
|
||||
return m.group(1).replace(',', '')
|
||||
|
||||
|
||||
def _expand_decimal_point(m):
|
||||
return m.group(1).replace('.', ' point ')
|
||||
|
||||
|
||||
def _expand_dollars(m):
|
||||
match = m.group(1)
|
||||
parts = match.split('.')
|
||||
if len(parts) > 2:
|
||||
return match + ' dollars' # Unexpected format
|
||||
dollars = int(parts[0]) if parts[0] else 0
|
||||
cents = int(parts[1]) if len(parts) > 1 and parts[1] else 0
|
||||
if dollars and cents:
|
||||
dollar_unit = 'dollar' if dollars == 1 else 'dollars'
|
||||
cent_unit = 'cent' if cents == 1 else 'cents'
|
||||
return '%s %s, %s %s' % (dollars, dollar_unit, cents, cent_unit)
|
||||
elif dollars:
|
||||
dollar_unit = 'dollar' if dollars == 1 else 'dollars'
|
||||
return '%s %s' % (dollars, dollar_unit)
|
||||
elif cents:
|
||||
cent_unit = 'cent' if cents == 1 else 'cents'
|
||||
return '%s %s' % (cents, cent_unit)
|
||||
else:
|
||||
return 'zero dollars'
|
||||
|
||||
|
||||
def _expand_ordinal(m):
|
||||
return _inflect.number_to_words(m.group(0))
|
||||
|
||||
|
||||
def _expand_number(m):
|
||||
num = int(m.group(0))
|
||||
if num > 1000 and num < 3000:
|
||||
if num == 2000:
|
||||
return 'two thousand'
|
||||
elif num > 2000 and num < 2010:
|
||||
return 'two thousand ' + _inflect.number_to_words(num % 100)
|
||||
elif num % 100 == 0:
|
||||
return _inflect.number_to_words(num // 100) + ' hundred'
|
||||
else:
|
||||
return _inflect.number_to_words(num, andword='', zero='oh', group=2).replace(', ', ' ')
|
||||
else:
|
||||
return _inflect.number_to_words(num, andword='')
|
||||
|
||||
|
||||
def normalize_numbers(text):
|
||||
text = re.sub(_comma_number_re, _remove_commas, text)
|
||||
text = re.sub(_pounds_re, r'\1 pounds', text)
|
||||
text = re.sub(_dollars_re, _expand_dollars, text)
|
||||
text = re.sub(_decimal_number_re, _expand_decimal_point, text)
|
||||
text = re.sub(_ordinal_re, _expand_ordinal, text)
|
||||
text = re.sub(_number_re, _expand_number, text)
|
||||
return text
|
||||
|
||||
|
||||
def mark_dark_l(text):
|
||||
return re.sub(r'l([^aeiouæɑɔəɛɪʊ ]*(?: |$))', lambda x: 'ɫ'+x.group(1), text)
|
||||
|
||||
|
||||
def english_to_ipa(text):
|
||||
text = unidecode(text).lower()
|
||||
text = expand_abbreviations(text)
|
||||
text = normalize_numbers(text)
|
||||
phonemes = ipa.convert(text)
|
||||
phonemes = collapse_whitespace(phonemes)
|
||||
return phonemes
|
||||
|
||||
|
||||
def english_to_lazy_ipa(text):
|
||||
text = english_to_ipa(text)
|
||||
for regex, replacement in _lazy_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def english_to_ipa2(text):
|
||||
text = english_to_ipa(text)
|
||||
text = mark_dark_l(text)
|
||||
for regex, replacement in _ipa_to_ipa2:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text.replace('...', '…')
|
||||
|
||||
|
||||
def english_to_lazy_ipa2(text):
|
||||
text = english_to_ipa(text)
|
||||
for regex, replacement in _lazy_ipa2:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
153
tts/vits/text/japanese.py
Normal file
153
tts/vits/text/japanese.py
Normal file
@ -0,0 +1,153 @@
|
||||
import re
|
||||
from unidecode import unidecode
|
||||
import pyopenjtalk
|
||||
|
||||
|
||||
# Regular expression matching Japanese without punctuation marks:
|
||||
_japanese_characters = re.compile(
|
||||
r'[A-Za-z\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d]')
|
||||
|
||||
# Regular expression matching non-Japanese characters or punctuation marks:
|
||||
_japanese_marks = re.compile(
|
||||
r'[^A-Za-z\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d]')
|
||||
|
||||
# List of (symbol, Japanese) pairs for marks:
|
||||
_symbols_to_japanese = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('%', 'パーセント')
|
||||
]]
|
||||
|
||||
# List of (romaji, ipa) pairs for marks:
|
||||
_romaji_to_ipa = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('ts', 'ʦ'),
|
||||
('u', 'ɯ'),
|
||||
('j', 'ʥ'),
|
||||
('y', 'j'),
|
||||
('ni', 'n^i'),
|
||||
('nj', 'n^'),
|
||||
('hi', 'çi'),
|
||||
('hj', 'ç'),
|
||||
('f', 'ɸ'),
|
||||
('I', 'i*'),
|
||||
('U', 'ɯ*'),
|
||||
('r', 'ɾ')
|
||||
]]
|
||||
|
||||
# List of (romaji, ipa2) pairs for marks:
|
||||
_romaji_to_ipa2 = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('u', 'ɯ'),
|
||||
('ʧ', 'tʃ'),
|
||||
('j', 'dʑ'),
|
||||
('y', 'j'),
|
||||
('ni', 'n^i'),
|
||||
('nj', 'n^'),
|
||||
('hi', 'çi'),
|
||||
('hj', 'ç'),
|
||||
('f', 'ɸ'),
|
||||
('I', 'i*'),
|
||||
('U', 'ɯ*'),
|
||||
('r', 'ɾ')
|
||||
]]
|
||||
|
||||
# List of (consonant, sokuon) pairs:
|
||||
_real_sokuon = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
(r'Q([↑↓]*[kg])', r'k#\1'),
|
||||
(r'Q([↑↓]*[tdjʧ])', r't#\1'),
|
||||
(r'Q([↑↓]*[sʃ])', r's\1'),
|
||||
(r'Q([↑↓]*[pb])', r'p#\1')
|
||||
]]
|
||||
|
||||
# List of (consonant, hatsuon) pairs:
|
||||
_real_hatsuon = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
(r'N([↑↓]*[pbm])', r'm\1'),
|
||||
(r'N([↑↓]*[ʧʥj])', r'n^\1'),
|
||||
(r'N([↑↓]*[tdn])', r'n\1'),
|
||||
(r'N([↑↓]*[kg])', r'ŋ\1')
|
||||
]]
|
||||
|
||||
|
||||
def symbols_to_japanese(text):
|
||||
for regex, replacement in _symbols_to_japanese:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def japanese_to_romaji_with_accent(text):
|
||||
'''Reference https://r9y9.github.io/ttslearn/latest/notebooks/ch10_Recipe-Tacotron.html'''
|
||||
text = symbols_to_japanese(text)
|
||||
sentences = re.split(_japanese_marks, text)
|
||||
marks = re.findall(_japanese_marks, text)
|
||||
text = ''
|
||||
for i, sentence in enumerate(sentences):
|
||||
if re.match(_japanese_characters, sentence):
|
||||
if text != '':
|
||||
text += ' '
|
||||
labels = pyopenjtalk.extract_fullcontext(sentence)
|
||||
for n, label in enumerate(labels):
|
||||
phoneme = re.search(r'\-([^\+]*)\+', label).group(1)
|
||||
if phoneme not in ['sil', 'pau']:
|
||||
text += phoneme.replace('ch', 'ʧ').replace('sh',
|
||||
'ʃ').replace('cl', 'Q')
|
||||
else:
|
||||
continue
|
||||
# n_moras = int(re.search(r'/F:(\d+)_', label).group(1))
|
||||
a1 = int(re.search(r"/A:(\-?[0-9]+)\+", label).group(1))
|
||||
a2 = int(re.search(r"\+(\d+)\+", label).group(1))
|
||||
a3 = int(re.search(r"\+(\d+)/", label).group(1))
|
||||
if re.search(r'\-([^\+]*)\+', labels[n + 1]).group(1) in ['sil', 'pau']:
|
||||
a2_next = -1
|
||||
else:
|
||||
a2_next = int(
|
||||
re.search(r"\+(\d+)\+", labels[n + 1]).group(1))
|
||||
# Accent phrase boundary
|
||||
if a3 == 1 and a2_next == 1:
|
||||
text += ' '
|
||||
# Falling
|
||||
elif a1 == 0 and a2_next == a2 + 1:
|
||||
text += '↓'
|
||||
# Rising
|
||||
elif a2 == 1 and a2_next == 2:
|
||||
text += '↑'
|
||||
if i < len(marks):
|
||||
text += unidecode(marks[i]).replace(' ', '')
|
||||
return text
|
||||
|
||||
|
||||
def get_real_sokuon(text):
|
||||
for regex, replacement in _real_sokuon:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def get_real_hatsuon(text):
|
||||
for regex, replacement in _real_hatsuon:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def japanese_to_ipa(text):
|
||||
text = japanese_to_romaji_with_accent(text).replace('...', '…')
|
||||
text = re.sub(
|
||||
r'([aiueo])\1+', lambda x: x.group(0)[0]+'ː'*(len(x.group(0))-1), text)
|
||||
text = get_real_sokuon(text)
|
||||
text = get_real_hatsuon(text)
|
||||
for regex, replacement in _romaji_to_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def japanese_to_ipa2(text):
|
||||
text = japanese_to_romaji_with_accent(text).replace('...', '…')
|
||||
text = get_real_sokuon(text)
|
||||
text = get_real_hatsuon(text)
|
||||
for regex, replacement in _romaji_to_ipa2:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def japanese_to_ipa3(text):
|
||||
text = japanese_to_ipa2(text).replace('n^', 'ȵ').replace(
|
||||
'ʃ', 'ɕ').replace('*', '\u0325').replace('#', '\u031a')
|
||||
text = re.sub(
|
||||
r'([aiɯeo])\1+', lambda x: x.group(0)[0]+'ː'*(len(x.group(0))-1), text)
|
||||
text = re.sub(r'((?:^|\s)(?:ts|tɕ|[kpt]))', r'\1ʰ', text)
|
||||
return text
|
||||
210
tts/vits/text/korean.py
Normal file
210
tts/vits/text/korean.py
Normal file
@ -0,0 +1,210 @@
|
||||
import re
|
||||
from jamo import h2j, j2hcj
|
||||
import ko_pron
|
||||
|
||||
|
||||
# This is a list of Korean classifiers preceded by pure Korean numerals.
|
||||
_korean_classifiers = '군데 권 개 그루 닢 대 두 마리 모 모금 뭇 발 발짝 방 번 벌 보루 살 수 술 시 쌈 움큼 정 짝 채 척 첩 축 켤레 톨 통'
|
||||
|
||||
# List of (hangul, hangul divided) pairs:
|
||||
_hangul_divided = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('ㄳ', 'ㄱㅅ'),
|
||||
('ㄵ', 'ㄴㅈ'),
|
||||
('ㄶ', 'ㄴㅎ'),
|
||||
('ㄺ', 'ㄹㄱ'),
|
||||
('ㄻ', 'ㄹㅁ'),
|
||||
('ㄼ', 'ㄹㅂ'),
|
||||
('ㄽ', 'ㄹㅅ'),
|
||||
('ㄾ', 'ㄹㅌ'),
|
||||
('ㄿ', 'ㄹㅍ'),
|
||||
('ㅀ', 'ㄹㅎ'),
|
||||
('ㅄ', 'ㅂㅅ'),
|
||||
('ㅘ', 'ㅗㅏ'),
|
||||
('ㅙ', 'ㅗㅐ'),
|
||||
('ㅚ', 'ㅗㅣ'),
|
||||
('ㅝ', 'ㅜㅓ'),
|
||||
('ㅞ', 'ㅜㅔ'),
|
||||
('ㅟ', 'ㅜㅣ'),
|
||||
('ㅢ', 'ㅡㅣ'),
|
||||
('ㅑ', 'ㅣㅏ'),
|
||||
('ㅒ', 'ㅣㅐ'),
|
||||
('ㅕ', 'ㅣㅓ'),
|
||||
('ㅖ', 'ㅣㅔ'),
|
||||
('ㅛ', 'ㅣㅗ'),
|
||||
('ㅠ', 'ㅣㅜ')
|
||||
]]
|
||||
|
||||
# List of (Latin alphabet, hangul) pairs:
|
||||
_latin_to_hangul = [(re.compile('%s' % x[0], re.IGNORECASE), x[1]) for x in [
|
||||
('a', '에이'),
|
||||
('b', '비'),
|
||||
('c', '시'),
|
||||
('d', '디'),
|
||||
('e', '이'),
|
||||
('f', '에프'),
|
||||
('g', '지'),
|
||||
('h', '에이치'),
|
||||
('i', '아이'),
|
||||
('j', '제이'),
|
||||
('k', '케이'),
|
||||
('l', '엘'),
|
||||
('m', '엠'),
|
||||
('n', '엔'),
|
||||
('o', '오'),
|
||||
('p', '피'),
|
||||
('q', '큐'),
|
||||
('r', '아르'),
|
||||
('s', '에스'),
|
||||
('t', '티'),
|
||||
('u', '유'),
|
||||
('v', '브이'),
|
||||
('w', '더블유'),
|
||||
('x', '엑스'),
|
||||
('y', '와이'),
|
||||
('z', '제트')
|
||||
]]
|
||||
|
||||
# List of (ipa, lazy ipa) pairs:
|
||||
_ipa_to_lazy_ipa = [(re.compile('%s' % x[0], re.IGNORECASE), x[1]) for x in [
|
||||
('t͡ɕ','ʧ'),
|
||||
('d͡ʑ','ʥ'),
|
||||
('ɲ','n^'),
|
||||
('ɕ','ʃ'),
|
||||
('ʷ','w'),
|
||||
('ɭ','l`'),
|
||||
('ʎ','ɾ'),
|
||||
('ɣ','ŋ'),
|
||||
('ɰ','ɯ'),
|
||||
('ʝ','j'),
|
||||
('ʌ','ə'),
|
||||
('ɡ','g'),
|
||||
('\u031a','#'),
|
||||
('\u0348','='),
|
||||
('\u031e',''),
|
||||
('\u0320',''),
|
||||
('\u0339','')
|
||||
]]
|
||||
|
||||
|
||||
def latin_to_hangul(text):
|
||||
for regex, replacement in _latin_to_hangul:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def divide_hangul(text):
|
||||
text = j2hcj(h2j(text))
|
||||
for regex, replacement in _hangul_divided:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def hangul_number(num, sino=True):
|
||||
'''Reference https://github.com/Kyubyong/g2pK'''
|
||||
num = re.sub(',', '', num)
|
||||
|
||||
if num == '0':
|
||||
return '영'
|
||||
if not sino and num == '20':
|
||||
return '스무'
|
||||
|
||||
digits = '123456789'
|
||||
names = '일이삼사오육칠팔구'
|
||||
digit2name = {d: n for d, n in zip(digits, names)}
|
||||
|
||||
modifiers = '한 두 세 네 다섯 여섯 일곱 여덟 아홉'
|
||||
decimals = '열 스물 서른 마흔 쉰 예순 일흔 여든 아흔'
|
||||
digit2mod = {d: mod for d, mod in zip(digits, modifiers.split())}
|
||||
digit2dec = {d: dec for d, dec in zip(digits, decimals.split())}
|
||||
|
||||
spelledout = []
|
||||
for i, digit in enumerate(num):
|
||||
i = len(num) - i - 1
|
||||
if sino:
|
||||
if i == 0:
|
||||
name = digit2name.get(digit, '')
|
||||
elif i == 1:
|
||||
name = digit2name.get(digit, '') + '십'
|
||||
name = name.replace('일십', '십')
|
||||
else:
|
||||
if i == 0:
|
||||
name = digit2mod.get(digit, '')
|
||||
elif i == 1:
|
||||
name = digit2dec.get(digit, '')
|
||||
if digit == '0':
|
||||
if i % 4 == 0:
|
||||
last_three = spelledout[-min(3, len(spelledout)):]
|
||||
if ''.join(last_three) == '':
|
||||
spelledout.append('')
|
||||
continue
|
||||
else:
|
||||
spelledout.append('')
|
||||
continue
|
||||
if i == 2:
|
||||
name = digit2name.get(digit, '') + '백'
|
||||
name = name.replace('일백', '백')
|
||||
elif i == 3:
|
||||
name = digit2name.get(digit, '') + '천'
|
||||
name = name.replace('일천', '천')
|
||||
elif i == 4:
|
||||
name = digit2name.get(digit, '') + '만'
|
||||
name = name.replace('일만', '만')
|
||||
elif i == 5:
|
||||
name = digit2name.get(digit, '') + '십'
|
||||
name = name.replace('일십', '십')
|
||||
elif i == 6:
|
||||
name = digit2name.get(digit, '') + '백'
|
||||
name = name.replace('일백', '백')
|
||||
elif i == 7:
|
||||
name = digit2name.get(digit, '') + '천'
|
||||
name = name.replace('일천', '천')
|
||||
elif i == 8:
|
||||
name = digit2name.get(digit, '') + '억'
|
||||
elif i == 9:
|
||||
name = digit2name.get(digit, '') + '십'
|
||||
elif i == 10:
|
||||
name = digit2name.get(digit, '') + '백'
|
||||
elif i == 11:
|
||||
name = digit2name.get(digit, '') + '천'
|
||||
elif i == 12:
|
||||
name = digit2name.get(digit, '') + '조'
|
||||
elif i == 13:
|
||||
name = digit2name.get(digit, '') + '십'
|
||||
elif i == 14:
|
||||
name = digit2name.get(digit, '') + '백'
|
||||
elif i == 15:
|
||||
name = digit2name.get(digit, '') + '천'
|
||||
spelledout.append(name)
|
||||
return ''.join(elem for elem in spelledout)
|
||||
|
||||
|
||||
def number_to_hangul(text):
|
||||
'''Reference https://github.com/Kyubyong/g2pK'''
|
||||
tokens = set(re.findall(r'(\d[\d,]*)([\uac00-\ud71f]+)', text))
|
||||
for token in tokens:
|
||||
num, classifier = token
|
||||
if classifier[:2] in _korean_classifiers or classifier[0] in _korean_classifiers:
|
||||
spelledout = hangul_number(num, sino=False)
|
||||
else:
|
||||
spelledout = hangul_number(num, sino=True)
|
||||
text = text.replace(f'{num}{classifier}', f'{spelledout}{classifier}')
|
||||
# digit by digit for remaining digits
|
||||
digits = '0123456789'
|
||||
names = '영일이삼사오육칠팔구'
|
||||
for d, n in zip(digits, names):
|
||||
text = text.replace(d, n)
|
||||
return text
|
||||
|
||||
|
||||
def korean_to_lazy_ipa(text):
|
||||
text = latin_to_hangul(text)
|
||||
text = number_to_hangul(text)
|
||||
text=re.sub('[\uac00-\ud7af]+',lambda x:ko_pron.romanise(x.group(0),'ipa').split('] ~ [')[0],text)
|
||||
for regex, replacement in _ipa_to_lazy_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def korean_to_ipa(text):
|
||||
text = korean_to_lazy_ipa(text)
|
||||
return text.replace('ʧ','tʃ').replace('ʥ','dʑ')
|
||||
326
tts/vits/text/mandarin.py
Normal file
326
tts/vits/text/mandarin.py
Normal file
@ -0,0 +1,326 @@
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from pypinyin import lazy_pinyin, BOPOMOFO
|
||||
import jieba
|
||||
import cn2an
|
||||
import logging
|
||||
|
||||
|
||||
# List of (Latin alphabet, bopomofo) pairs:
|
||||
_latin_to_bopomofo = [(re.compile('%s' % x[0], re.IGNORECASE), x[1]) for x in [
|
||||
('a', 'ㄟˉ'),
|
||||
('b', 'ㄅㄧˋ'),
|
||||
('c', 'ㄙㄧˉ'),
|
||||
('d', 'ㄉㄧˋ'),
|
||||
('e', 'ㄧˋ'),
|
||||
('f', 'ㄝˊㄈㄨˋ'),
|
||||
('g', 'ㄐㄧˋ'),
|
||||
('h', 'ㄝˇㄑㄩˋ'),
|
||||
('i', 'ㄞˋ'),
|
||||
('j', 'ㄐㄟˋ'),
|
||||
('k', 'ㄎㄟˋ'),
|
||||
('l', 'ㄝˊㄛˋ'),
|
||||
('m', 'ㄝˊㄇㄨˋ'),
|
||||
('n', 'ㄣˉ'),
|
||||
('o', 'ㄡˉ'),
|
||||
('p', 'ㄆㄧˉ'),
|
||||
('q', 'ㄎㄧㄡˉ'),
|
||||
('r', 'ㄚˋ'),
|
||||
('s', 'ㄝˊㄙˋ'),
|
||||
('t', 'ㄊㄧˋ'),
|
||||
('u', 'ㄧㄡˉ'),
|
||||
('v', 'ㄨㄧˉ'),
|
||||
('w', 'ㄉㄚˋㄅㄨˋㄌㄧㄡˋ'),
|
||||
('x', 'ㄝˉㄎㄨˋㄙˋ'),
|
||||
('y', 'ㄨㄞˋ'),
|
||||
('z', 'ㄗㄟˋ')
|
||||
]]
|
||||
|
||||
# List of (bopomofo, romaji) pairs:
|
||||
_bopomofo_to_romaji = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('ㄅㄛ', 'p⁼wo'),
|
||||
('ㄆㄛ', 'pʰwo'),
|
||||
('ㄇㄛ', 'mwo'),
|
||||
('ㄈㄛ', 'fwo'),
|
||||
('ㄅ', 'p⁼'),
|
||||
('ㄆ', 'pʰ'),
|
||||
('ㄇ', 'm'),
|
||||
('ㄈ', 'f'),
|
||||
('ㄉ', 't⁼'),
|
||||
('ㄊ', 'tʰ'),
|
||||
('ㄋ', 'n'),
|
||||
('ㄌ', 'l'),
|
||||
('ㄍ', 'k⁼'),
|
||||
('ㄎ', 'kʰ'),
|
||||
('ㄏ', 'h'),
|
||||
('ㄐ', 'ʧ⁼'),
|
||||
('ㄑ', 'ʧʰ'),
|
||||
('ㄒ', 'ʃ'),
|
||||
('ㄓ', 'ʦ`⁼'),
|
||||
('ㄔ', 'ʦ`ʰ'),
|
||||
('ㄕ', 's`'),
|
||||
('ㄖ', 'ɹ`'),
|
||||
('ㄗ', 'ʦ⁼'),
|
||||
('ㄘ', 'ʦʰ'),
|
||||
('ㄙ', 's'),
|
||||
('ㄚ', 'a'),
|
||||
('ㄛ', 'o'),
|
||||
('ㄜ', 'ə'),
|
||||
('ㄝ', 'e'),
|
||||
('ㄞ', 'ai'),
|
||||
('ㄟ', 'ei'),
|
||||
('ㄠ', 'au'),
|
||||
('ㄡ', 'ou'),
|
||||
('ㄧㄢ', 'yeNN'),
|
||||
('ㄢ', 'aNN'),
|
||||
('ㄧㄣ', 'iNN'),
|
||||
('ㄣ', 'əNN'),
|
||||
('ㄤ', 'aNg'),
|
||||
('ㄧㄥ', 'iNg'),
|
||||
('ㄨㄥ', 'uNg'),
|
||||
('ㄩㄥ', 'yuNg'),
|
||||
('ㄥ', 'əNg'),
|
||||
('ㄦ', 'əɻ'),
|
||||
('ㄧ', 'i'),
|
||||
('ㄨ', 'u'),
|
||||
('ㄩ', 'ɥ'),
|
||||
('ˉ', '→'),
|
||||
('ˊ', '↑'),
|
||||
('ˇ', '↓↑'),
|
||||
('ˋ', '↓'),
|
||||
('˙', ''),
|
||||
(',', ','),
|
||||
('。', '.'),
|
||||
('!', '!'),
|
||||
('?', '?'),
|
||||
('—', '-')
|
||||
]]
|
||||
|
||||
# List of (romaji, ipa) pairs:
|
||||
_romaji_to_ipa = [(re.compile('%s' % x[0], re.IGNORECASE), x[1]) for x in [
|
||||
('ʃy', 'ʃ'),
|
||||
('ʧʰy', 'ʧʰ'),
|
||||
('ʧ⁼y', 'ʧ⁼'),
|
||||
('NN', 'n'),
|
||||
('Ng', 'ŋ'),
|
||||
('y', 'j'),
|
||||
('h', 'x')
|
||||
]]
|
||||
|
||||
# List of (bopomofo, ipa) pairs:
|
||||
_bopomofo_to_ipa = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('ㄅㄛ', 'p⁼wo'),
|
||||
('ㄆㄛ', 'pʰwo'),
|
||||
('ㄇㄛ', 'mwo'),
|
||||
('ㄈㄛ', 'fwo'),
|
||||
('ㄅ', 'p⁼'),
|
||||
('ㄆ', 'pʰ'),
|
||||
('ㄇ', 'm'),
|
||||
('ㄈ', 'f'),
|
||||
('ㄉ', 't⁼'),
|
||||
('ㄊ', 'tʰ'),
|
||||
('ㄋ', 'n'),
|
||||
('ㄌ', 'l'),
|
||||
('ㄍ', 'k⁼'),
|
||||
('ㄎ', 'kʰ'),
|
||||
('ㄏ', 'x'),
|
||||
('ㄐ', 'tʃ⁼'),
|
||||
('ㄑ', 'tʃʰ'),
|
||||
('ㄒ', 'ʃ'),
|
||||
('ㄓ', 'ts`⁼'),
|
||||
('ㄔ', 'ts`ʰ'),
|
||||
('ㄕ', 's`'),
|
||||
('ㄖ', 'ɹ`'),
|
||||
('ㄗ', 'ts⁼'),
|
||||
('ㄘ', 'tsʰ'),
|
||||
('ㄙ', 's'),
|
||||
('ㄚ', 'a'),
|
||||
('ㄛ', 'o'),
|
||||
('ㄜ', 'ə'),
|
||||
('ㄝ', 'ɛ'),
|
||||
('ㄞ', 'aɪ'),
|
||||
('ㄟ', 'eɪ'),
|
||||
('ㄠ', 'ɑʊ'),
|
||||
('ㄡ', 'oʊ'),
|
||||
('ㄧㄢ', 'jɛn'),
|
||||
('ㄩㄢ', 'ɥæn'),
|
||||
('ㄢ', 'an'),
|
||||
('ㄧㄣ', 'in'),
|
||||
('ㄩㄣ', 'ɥn'),
|
||||
('ㄣ', 'ən'),
|
||||
('ㄤ', 'ɑŋ'),
|
||||
('ㄧㄥ', 'iŋ'),
|
||||
('ㄨㄥ', 'ʊŋ'),
|
||||
('ㄩㄥ', 'jʊŋ'),
|
||||
('ㄥ', 'əŋ'),
|
||||
('ㄦ', 'əɻ'),
|
||||
('ㄧ', 'i'),
|
||||
('ㄨ', 'u'),
|
||||
('ㄩ', 'ɥ'),
|
||||
('ˉ', '→'),
|
||||
('ˊ', '↑'),
|
||||
('ˇ', '↓↑'),
|
||||
('ˋ', '↓'),
|
||||
('˙', ''),
|
||||
(',', ','),
|
||||
('。', '.'),
|
||||
('!', '!'),
|
||||
('?', '?'),
|
||||
('—', '-')
|
||||
]]
|
||||
|
||||
# List of (bopomofo, ipa2) pairs:
|
||||
_bopomofo_to_ipa2 = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('ㄅㄛ', 'pwo'),
|
||||
('ㄆㄛ', 'pʰwo'),
|
||||
('ㄇㄛ', 'mwo'),
|
||||
('ㄈㄛ', 'fwo'),
|
||||
('ㄅ', 'p'),
|
||||
('ㄆ', 'pʰ'),
|
||||
('ㄇ', 'm'),
|
||||
('ㄈ', 'f'),
|
||||
('ㄉ', 't'),
|
||||
('ㄊ', 'tʰ'),
|
||||
('ㄋ', 'n'),
|
||||
('ㄌ', 'l'),
|
||||
('ㄍ', 'k'),
|
||||
('ㄎ', 'kʰ'),
|
||||
('ㄏ', 'h'),
|
||||
('ㄐ', 'tɕ'),
|
||||
('ㄑ', 'tɕʰ'),
|
||||
('ㄒ', 'ɕ'),
|
||||
('ㄓ', 'tʂ'),
|
||||
('ㄔ', 'tʂʰ'),
|
||||
('ㄕ', 'ʂ'),
|
||||
('ㄖ', 'ɻ'),
|
||||
('ㄗ', 'ts'),
|
||||
('ㄘ', 'tsʰ'),
|
||||
('ㄙ', 's'),
|
||||
('ㄚ', 'a'),
|
||||
('ㄛ', 'o'),
|
||||
('ㄜ', 'ɤ'),
|
||||
('ㄝ', 'ɛ'),
|
||||
('ㄞ', 'aɪ'),
|
||||
('ㄟ', 'eɪ'),
|
||||
('ㄠ', 'ɑʊ'),
|
||||
('ㄡ', 'oʊ'),
|
||||
('ㄧㄢ', 'jɛn'),
|
||||
('ㄩㄢ', 'yæn'),
|
||||
('ㄢ', 'an'),
|
||||
('ㄧㄣ', 'in'),
|
||||
('ㄩㄣ', 'yn'),
|
||||
('ㄣ', 'ən'),
|
||||
('ㄤ', 'ɑŋ'),
|
||||
('ㄧㄥ', 'iŋ'),
|
||||
('ㄨㄥ', 'ʊŋ'),
|
||||
('ㄩㄥ', 'jʊŋ'),
|
||||
('ㄥ', 'ɤŋ'),
|
||||
('ㄦ', 'əɻ'),
|
||||
('ㄧ', 'i'),
|
||||
('ㄨ', 'u'),
|
||||
('ㄩ', 'y'),
|
||||
('ˉ', '˥'),
|
||||
('ˊ', '˧˥'),
|
||||
('ˇ', '˨˩˦'),
|
||||
('ˋ', '˥˩'),
|
||||
('˙', ''),
|
||||
(',', ','),
|
||||
('。', '.'),
|
||||
('!', '!'),
|
||||
('?', '?'),
|
||||
('—', '-')
|
||||
]]
|
||||
|
||||
|
||||
def number_to_chinese(text):
|
||||
numbers = re.findall(r'\d+(?:\.?\d+)?', text)
|
||||
for number in numbers:
|
||||
text = text.replace(number, cn2an.an2cn(number), 1)
|
||||
return text
|
||||
|
||||
|
||||
def chinese_to_bopomofo(text):
|
||||
text = text.replace('、', ',').replace(';', ',').replace(':', ',')
|
||||
words = jieba.lcut(text, cut_all=False)
|
||||
text = ''
|
||||
for word in words:
|
||||
bopomofos = lazy_pinyin(word, BOPOMOFO)
|
||||
if not re.search('[\u4e00-\u9fff]', word):
|
||||
text += word
|
||||
continue
|
||||
for i in range(len(bopomofos)):
|
||||
bopomofos[i] = re.sub(r'([\u3105-\u3129])$', r'\1ˉ', bopomofos[i])
|
||||
if text != '':
|
||||
text += ' '
|
||||
text += ''.join(bopomofos)
|
||||
return text
|
||||
|
||||
|
||||
def latin_to_bopomofo(text):
|
||||
for regex, replacement in _latin_to_bopomofo:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def bopomofo_to_romaji(text):
|
||||
for regex, replacement in _bopomofo_to_romaji:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def bopomofo_to_ipa(text):
|
||||
for regex, replacement in _bopomofo_to_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def bopomofo_to_ipa2(text):
|
||||
for regex, replacement in _bopomofo_to_ipa2:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def chinese_to_romaji(text):
|
||||
text = number_to_chinese(text)
|
||||
text = chinese_to_bopomofo(text)
|
||||
text = latin_to_bopomofo(text)
|
||||
text = bopomofo_to_romaji(text)
|
||||
text = re.sub('i([aoe])', r'y\1', text)
|
||||
text = re.sub('u([aoəe])', r'w\1', text)
|
||||
text = re.sub('([ʦsɹ]`[⁼ʰ]?)([→↓↑ ]+|$)',
|
||||
r'\1ɹ`\2', text).replace('ɻ', 'ɹ`')
|
||||
text = re.sub('([ʦs][⁼ʰ]?)([→↓↑ ]+|$)', r'\1ɹ\2', text)
|
||||
return text
|
||||
|
||||
|
||||
def chinese_to_lazy_ipa(text):
|
||||
text = chinese_to_romaji(text)
|
||||
for regex, replacement in _romaji_to_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def chinese_to_ipa(text):
|
||||
text = number_to_chinese(text)
|
||||
text = chinese_to_bopomofo(text)
|
||||
text = latin_to_bopomofo(text)
|
||||
text = bopomofo_to_ipa(text)
|
||||
text = re.sub('i([aoe])', r'j\1', text)
|
||||
text = re.sub('u([aoəe])', r'w\1', text)
|
||||
text = re.sub('([sɹ]`[⁼ʰ]?)([→↓↑ ]+|$)',
|
||||
r'\1ɹ`\2', text).replace('ɻ', 'ɹ`')
|
||||
text = re.sub('([s][⁼ʰ]?)([→↓↑ ]+|$)', r'\1ɹ\2', text)
|
||||
return text
|
||||
|
||||
|
||||
def chinese_to_ipa2(text):
|
||||
text = number_to_chinese(text)
|
||||
text = chinese_to_bopomofo(text)
|
||||
text = latin_to_bopomofo(text)
|
||||
text = bopomofo_to_ipa2(text)
|
||||
text = re.sub(r'i([aoe])', r'j\1', text)
|
||||
text = re.sub(r'u([aoəe])', r'w\1', text)
|
||||
text = re.sub(r'([ʂɹ]ʰ?)([˩˨˧˦˥ ]+|$)', r'\1ʅ\2', text)
|
||||
text = re.sub(r'(sʰ?)([˩˨˧˦˥ ]+|$)', r'\1ɿ\2', text)
|
||||
return text
|
||||
30
tts/vits/text/ngu_dialect.py
Normal file
30
tts/vits/text/ngu_dialect.py
Normal file
@ -0,0 +1,30 @@
|
||||
import re
|
||||
import opencc
|
||||
|
||||
|
||||
dialects = {'SZ': 'suzhou', 'WX': 'wuxi', 'CZ': 'changzhou', 'HZ': 'hangzhou',
|
||||
'SX': 'shaoxing', 'NB': 'ningbo', 'JJ': 'jingjiang', 'YX': 'yixing',
|
||||
'JD': 'jiading', 'ZR': 'zhenru', 'PH': 'pinghu', 'TX': 'tongxiang',
|
||||
'JS': 'jiashan', 'HN': 'xiashi', 'LP': 'linping', 'XS': 'xiaoshan',
|
||||
'FY': 'fuyang', 'RA': 'ruao', 'CX': 'cixi', 'SM': 'sanmen',
|
||||
'TT': 'tiantai', 'WZ': 'wenzhou', 'SC': 'suichang', 'YB': 'youbu'}
|
||||
|
||||
converters = {}
|
||||
|
||||
for dialect in dialects.values():
|
||||
try:
|
||||
converters[dialect] = opencc.OpenCC(dialect)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def ngu_dialect_to_ipa(text, dialect):
|
||||
dialect = dialects[dialect]
|
||||
text = converters[dialect].convert(text).replace('-','').replace('$',' ')
|
||||
text = re.sub(r'[、;:]', ',', text)
|
||||
text = re.sub(r'\s*,\s*', ', ', text)
|
||||
text = re.sub(r'\s*。\s*', '. ', text)
|
||||
text = re.sub(r'\s*?\s*', '? ', text)
|
||||
text = re.sub(r'\s*!\s*', '! ', text)
|
||||
text = re.sub(r'\s*$', '', text)
|
||||
return text
|
||||
62
tts/vits/text/sanskrit.py
Normal file
62
tts/vits/text/sanskrit.py
Normal file
@ -0,0 +1,62 @@
|
||||
import re
|
||||
from indic_transliteration import sanscript
|
||||
|
||||
|
||||
# List of (iast, ipa) pairs:
|
||||
_iast_to_ipa = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('a', 'ə'),
|
||||
('ā', 'aː'),
|
||||
('ī', 'iː'),
|
||||
('ū', 'uː'),
|
||||
('ṛ', 'ɹ`'),
|
||||
('ṝ', 'ɹ`ː'),
|
||||
('ḷ', 'l`'),
|
||||
('ḹ', 'l`ː'),
|
||||
('e', 'eː'),
|
||||
('o', 'oː'),
|
||||
('k', 'k⁼'),
|
||||
('k⁼h', 'kʰ'),
|
||||
('g', 'g⁼'),
|
||||
('g⁼h', 'gʰ'),
|
||||
('ṅ', 'ŋ'),
|
||||
('c', 'ʧ⁼'),
|
||||
('ʧ⁼h', 'ʧʰ'),
|
||||
('j', 'ʥ⁼'),
|
||||
('ʥ⁼h', 'ʥʰ'),
|
||||
('ñ', 'n^'),
|
||||
('ṭ', 't`⁼'),
|
||||
('t`⁼h', 't`ʰ'),
|
||||
('ḍ', 'd`⁼'),
|
||||
('d`⁼h', 'd`ʰ'),
|
||||
('ṇ', 'n`'),
|
||||
('t', 't⁼'),
|
||||
('t⁼h', 'tʰ'),
|
||||
('d', 'd⁼'),
|
||||
('d⁼h', 'dʰ'),
|
||||
('p', 'p⁼'),
|
||||
('p⁼h', 'pʰ'),
|
||||
('b', 'b⁼'),
|
||||
('b⁼h', 'bʰ'),
|
||||
('y', 'j'),
|
||||
('ś', 'ʃ'),
|
||||
('ṣ', 's`'),
|
||||
('r', 'ɾ'),
|
||||
('l̤', 'l`'),
|
||||
('h', 'ɦ'),
|
||||
("'", ''),
|
||||
('~', '^'),
|
||||
('ṃ', '^')
|
||||
]]
|
||||
|
||||
|
||||
def devanagari_to_ipa(text):
|
||||
text = text.replace('ॐ', 'ओम्')
|
||||
text = re.sub(r'\s*।\s*$', '.', text)
|
||||
text = re.sub(r'\s*।\s*', ', ', text)
|
||||
text = re.sub(r'\s*॥', '.', text)
|
||||
text = sanscript.transliterate(text, sanscript.DEVANAGARI, sanscript.IAST)
|
||||
for regex, replacement in _iast_to_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
text = re.sub('(.)[`ː]*ḥ', lambda x: x.group(0)
|
||||
[:-1]+'h'+x.group(1)+'*', text)
|
||||
return text
|
||||
64
tts/vits/text/shanghainese.py
Normal file
64
tts/vits/text/shanghainese.py
Normal file
@ -0,0 +1,64 @@
|
||||
import re
|
||||
import cn2an
|
||||
import opencc
|
||||
|
||||
|
||||
converter = opencc.OpenCC('zaonhe')
|
||||
|
||||
# List of (Latin alphabet, ipa) pairs:
|
||||
_latin_to_ipa = [(re.compile('%s' % x[0]), x[1]) for x in [
|
||||
('A', 'ᴇ'),
|
||||
('B', 'bi'),
|
||||
('C', 'si'),
|
||||
('D', 'di'),
|
||||
('E', 'i'),
|
||||
('F', 'ᴇf'),
|
||||
('G', 'dʑi'),
|
||||
('H', 'ᴇtɕʰ'),
|
||||
('I', 'ᴀi'),
|
||||
('J', 'dʑᴇ'),
|
||||
('K', 'kʰᴇ'),
|
||||
('L', 'ᴇl'),
|
||||
('M', 'ᴇm'),
|
||||
('N', 'ᴇn'),
|
||||
('O', 'o'),
|
||||
('P', 'pʰi'),
|
||||
('Q', 'kʰiu'),
|
||||
('R', 'ᴀl'),
|
||||
('S', 'ᴇs'),
|
||||
('T', 'tʰi'),
|
||||
('U', 'ɦiu'),
|
||||
('V', 'vi'),
|
||||
('W', 'dᴀbɤliu'),
|
||||
('X', 'ᴇks'),
|
||||
('Y', 'uᴀi'),
|
||||
('Z', 'zᴇ')
|
||||
]]
|
||||
|
||||
|
||||
def _number_to_shanghainese(num):
|
||||
num = cn2an.an2cn(num).replace('一十','十').replace('二十', '廿').replace('二', '两')
|
||||
return re.sub(r'((?:^|[^三四五六七八九])十|廿)两', r'\1二', num)
|
||||
|
||||
|
||||
def number_to_shanghainese(text):
|
||||
return re.sub(r'\d+(?:\.?\d+)?', lambda x: _number_to_shanghainese(x.group()), text)
|
||||
|
||||
|
||||
def latin_to_ipa(text):
|
||||
for regex, replacement in _latin_to_ipa:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def shanghainese_to_ipa(text):
|
||||
text = number_to_shanghainese(text.upper())
|
||||
text = converter.convert(text).replace('-','').replace('$',' ')
|
||||
text = re.sub(r'[A-Z]', lambda x: latin_to_ipa(x.group())+' ', text)
|
||||
text = re.sub(r'[、;:]', ',', text)
|
||||
text = re.sub(r'\s*,\s*', ', ', text)
|
||||
text = re.sub(r'\s*。\s*', '. ', text)
|
||||
text = re.sub(r'\s*?\s*', '? ', text)
|
||||
text = re.sub(r'\s*!\s*', '! ', text)
|
||||
text = re.sub(r'\s*$', '', text)
|
||||
return text
|
||||
75
tts/vits/text/symbols.py
Normal file
75
tts/vits/text/symbols.py
Normal file
@ -0,0 +1,75 @@
|
||||
'''
|
||||
Defines the set of symbols used in text input to the model.
|
||||
'''
|
||||
|
||||
'''# japanese_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?-'
|
||||
_letters = 'AEINOQUabdefghijkmnoprstuvwyzʃʧ↓↑ '
|
||||
'''
|
||||
#
|
||||
# # japanese_cleaners2
|
||||
# _pad = '_'
|
||||
# _punctuation = ',.!?-~…'
|
||||
# _letters = 'AEINOQUabdefghijkmnoprstuvwyzʃʧʦ↓↑ '
|
||||
|
||||
|
||||
'''# korean_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?…~'
|
||||
_letters = 'ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㄲㄸㅃㅆㅉㅏㅓㅗㅜㅡㅣㅐㅔ '
|
||||
'''
|
||||
|
||||
# chinese_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',。!?—…「」'
|
||||
_letters = 'ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩˉˊˇˋ˙ '
|
||||
|
||||
|
||||
'''# zh_ja_mixture_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?-~…'
|
||||
_letters = 'AEINOQUabdefghijklmnoprstuvwyzʃʧʦɯɹəɥ⁼ʰ`→↓↑ '
|
||||
'''
|
||||
|
||||
'''# sanskrit_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = '।'
|
||||
_letters = 'ँंःअआइईउऊऋएऐओऔकखगघङचछजझञटठडढणतथदधनपफबभमयरलळवशषसहऽािीुूृॄेैोौ्ॠॢ '
|
||||
'''
|
||||
|
||||
'''# cjks_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?-~…'
|
||||
_letters = 'NQabdefghijklmnopstuvwxyzʃʧʥʦɯɹəɥçɸɾβŋɦː⁼ʰ`^#*=→↓↑ '
|
||||
'''
|
||||
|
||||
'''# thai_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = '.!? '
|
||||
_letters = 'กขฃคฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลวศษสหฬอฮฯะัาำิีึืุูเแโใไๅๆ็่้๊๋์'
|
||||
'''
|
||||
|
||||
'''# cjke_cleaners2
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?-~…'
|
||||
_letters = 'NQabdefghijklmnopstuvwxyzɑæʃʑçɯɪɔɛɹðəɫɥɸʊɾʒθβŋɦ⁼ʰ`^#*=ˈˌ→↓↑ '
|
||||
'''
|
||||
|
||||
'''# shanghainese_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?…'
|
||||
_letters = 'abdfghiklmnopstuvyzøŋȵɑɔɕəɤɦɪɿʑʔʰ̩̃ᴀᴇ15678 '
|
||||
'''
|
||||
|
||||
'''# chinese_dialect_cleaners
|
||||
_pad = '_'
|
||||
_punctuation = ',.!?~…─'
|
||||
_letters = '#Nabdefghijklmnoprstuvwxyzæçøŋœȵɐɑɒɓɔɕɗɘəɚɛɜɣɤɦɪɭɯɵɷɸɻɾɿʂʅʊʋʌʏʑʔʦʮʰʷˀː˥˦˧˨˩̥̩̃̚ᴀᴇ↑↓∅ⱼ '
|
||||
'''
|
||||
|
||||
# Export all symbols:
|
||||
symbols = [_pad] + list(_punctuation) + list(_letters)
|
||||
|
||||
# Special symbol ids
|
||||
SPACE_ID = symbols.index(" ")
|
||||
44
tts/vits/text/thai.py
Normal file
44
tts/vits/text/thai.py
Normal file
@ -0,0 +1,44 @@
|
||||
import re
|
||||
from num_thai.thainumbers import NumThai
|
||||
|
||||
|
||||
num = NumThai()
|
||||
|
||||
# List of (Latin alphabet, Thai) pairs:
|
||||
_latin_to_thai = [(re.compile('%s' % x[0], re.IGNORECASE), x[1]) for x in [
|
||||
('a', 'เอ'),
|
||||
('b','บี'),
|
||||
('c','ซี'),
|
||||
('d','ดี'),
|
||||
('e','อี'),
|
||||
('f','เอฟ'),
|
||||
('g','จี'),
|
||||
('h','เอช'),
|
||||
('i','ไอ'),
|
||||
('j','เจ'),
|
||||
('k','เค'),
|
||||
('l','แอล'),
|
||||
('m','เอ็ม'),
|
||||
('n','เอ็น'),
|
||||
('o','โอ'),
|
||||
('p','พี'),
|
||||
('q','คิว'),
|
||||
('r','แอร์'),
|
||||
('s','เอส'),
|
||||
('t','ที'),
|
||||
('u','ยู'),
|
||||
('v','วี'),
|
||||
('w','ดับเบิลยู'),
|
||||
('x','เอ็กซ์'),
|
||||
('y','วาย'),
|
||||
('z','ซี')
|
||||
]]
|
||||
|
||||
|
||||
def num_to_thai(text):
|
||||
return re.sub(r'(?:\d+(?:,?\d+)?)+(?:\.\d+(?:,?\d+)?)?', lambda x: ''.join(num.NumberToTextThai(float(x.group(0).replace(',', '')))), text)
|
||||
|
||||
def latin_to_thai(text):
|
||||
for regex, replacement in _latin_to_thai:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
Reference in New Issue
Block a user