|
import os |
|
import sys |
|
|
|
|
|
sys.path.insert(0, "./hifi-gan") |
|
sys.path.insert(0, "./weights") |
|
|
|
|
|
import collections |
|
import collections.abc |
|
for type_name in collections.abc.__all__: |
|
setattr(collections, type_name, getattr(collections.abc, type_name)) |
|
|
|
|
|
import nltk |
|
from nltk.tokenize import word_tokenize |
|
import re |
|
import librosa |
|
import yaml |
|
import gradio as gr |
|
import torch |
|
import glob |
|
from attrdict import AttrDict |
|
import phonemizer |
|
import json |
|
from vocoder import Generator |
|
from models import * |
|
from utils import * |
|
from ipa_uk.ipa_uk import ipa |
|
import unicodedata |
|
from ukrainian_word_stress import Stressifier, StressSymbol |
|
|
|
nltk.download('punkt') |
|
nltk.download('punkt_tab') |
|
|
|
device = 'cuda' if torch.cuda.is_available() else 'cpu' |
|
|
|
|
|
|
|
_pad = "$" |
|
_punctuation = ';:,.!?¡¿—…"«»“” ' |
|
_letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' |
|
_letters_ipa = "ɑɐɒæɓʙβɔɕçɗɖðʤəɘɚɛɜɝɞɟʄɡɠɢʛɦɧħɥʜɨɪʝɭɬɫɮʟɱɯɰŋɳɲɴøɵɸθœɶʘɹɺɾɻʀʁɽʂʃʈʧʉʊʋⱱʌɣɤʍχʎʏʑʐʒʔʡʕʢǀǁǂǃˈˌːˑʼʴʰʱʲʷˠˤ˞↓↑→'̩'ᵻ" |
|
|
|
|
|
|
|
symbols = [_pad] + list(_punctuation) + list(_letters) + list(_letters_ipa) |
|
|
|
dicts = {} |
|
for i in range(len((symbols))): |
|
dicts[symbols[i]] = i |
|
|
|
class TextCleaner: |
|
def __init__(self, dummy=None): |
|
self.word_index_dictionary = dicts |
|
def __call__(self, text): |
|
indexes = [] |
|
for char in text: |
|
try: |
|
indexes.append(self.word_index_dictionary[char]) |
|
except KeyError: |
|
print(char) |
|
return indexes |
|
|
|
textclenaer = TextCleaner() |
|
|
|
|
|
def load_checkpoint(filepath, device): |
|
assert os.path.isfile(filepath) |
|
print("Loading '{}'".format(filepath)) |
|
checkpoint_dict = torch.load(filepath, map_location=device) |
|
print("Complete.") |
|
return checkpoint_dict |
|
|
|
def scan_checkpoint(cp_dir, prefix): |
|
pattern = os.path.join(cp_dir, prefix + '*') |
|
cp_list = glob.glob(pattern) |
|
if len(cp_list) == 0: |
|
return '' |
|
return sorted(cp_list)[-1] |
|
|
|
|
|
def greet(name): |
|
return "Hello " + name + "!!" |
|
|
|
|
|
cp_g = scan_checkpoint("Vocoder/", 'g_') |
|
config_file = os.path.join(os.path.split(cp_g)[0], 'config.json') |
|
with open(config_file) as f: |
|
data = f.read() |
|
json_config = json.loads(data) |
|
h = AttrDict(json_config) |
|
|
|
device = torch.device(device) |
|
generator = Generator(h).to(device) |
|
|
|
state_dict_g = load_checkpoint(cp_g, device) |
|
generator.load_state_dict(state_dict_g['generator']) |
|
generator.eval() |
|
generator.remove_weight_norm() |
|
|
|
|
|
|
|
model_path = "./Models/UK/second_stage.pth" |
|
model_config_path = "./Models/UK/config_uk.yml" |
|
config = yaml.safe_load(open(model_config_path)) |
|
|
|
|
|
ASR_config = config.get('ASR_config', False) |
|
ASR_path = config.get('ASR_path', False) |
|
text_aligner = load_ASR_models(ASR_path, ASR_config) |
|
|
|
|
|
F0_path = config.get('F0_path', False) |
|
pitch_extractor = load_F0_models(F0_path) |
|
|
|
model = build_model(Munch(config['model_params']), text_aligner, pitch_extractor) |
|
params = torch.load(model_path, map_location=device) |
|
params = params['net'] |
|
for key in model: |
|
if key in params: |
|
if not "discriminator" in key: |
|
print('%s loaded' % key) |
|
model[key].load_state_dict(params[key]) |
|
_ = [model[key].eval() for key in model] |
|
_ = [model[key].to(device) for key in model] |
|
|
|
|
|
|
|
reference_embeddings = np.load("reference_embeddings.npy", allow_pickle=True).item() |
|
|
|
|
|
def remove_combining_diacritics(input_str): |
|
return ''.join(char for char in unicodedata.normalize('NFD', input_str) if unicodedata.category(char) != 'Mn') |
|
|
|
def tts(text: str): |
|
stressify = Stressifier(stress_symbol = '\u02C8') |
|
|
|
|
|
text = text.strip() |
|
text = text.replace('"', '') |
|
text = text.replace('+', 'ˈ') |
|
|
|
text = re.sub(r'[{}()_*]', '', text) |
|
text = re.sub(r'([!?.])[\./]+', r'\1', text) |
|
text = text.replace('-', '') |
|
|
|
text = re.sub(r'[᠆‐‑‒–—―⁻₋−⸺⸻]', '—', text) |
|
text = re.sub(r' - ', ': ', text) |
|
|
|
|
|
|
|
|
|
ps = [ipa(stressify(text), check_accent=False)] |
|
ps = [remove_combining_diacritics(''.join(ps))] |
|
|
|
|
|
|
|
ipa2uk_phonemes = ps[0] |
|
print(ps) |
|
|
|
ps = word_tokenize(ps[0]) |
|
ps = ' '.join(ps) |
|
tokens = textclenaer(ps) |
|
tokens.insert(0, 0) |
|
tokens.append(0) |
|
tokens = torch.LongTensor(tokens).to(device).unsqueeze(0) |
|
|
|
|
|
|
|
converted_samples = {} |
|
|
|
with torch.no_grad(): |
|
input_lengths = torch.LongTensor([tokens.shape[-1]]).to(device) |
|
m = length_to_mask(input_lengths).to(device) |
|
t_en = model.text_encoder(tokens, input_lengths, m) |
|
|
|
for key, (ref, _) in reference_embeddings.items(): |
|
|
|
s = ref.squeeze(1) |
|
style = s |
|
|
|
d = model.predictor.text_encoder(t_en, style, input_lengths, m) |
|
|
|
x, _ = model.predictor.lstm(d) |
|
duration = model.predictor.duration_proj(x) / 0.98 |
|
pred_dur = torch.round(duration.squeeze()).clamp(min=1) |
|
|
|
pred_aln_trg = torch.zeros(input_lengths, int(pred_dur.sum().data)) |
|
c_frame = 0 |
|
for i in range(pred_aln_trg.size(0)): |
|
pred_aln_trg[i, c_frame:c_frame + int(pred_dur[i].data)] = 1 |
|
c_frame += int(pred_dur[i].data) |
|
|
|
|
|
en = (d.transpose(-1, -2) @ pred_aln_trg.unsqueeze(0).to(device)) |
|
style = s.expand(en.shape[0], en.shape[1], -1) |
|
|
|
F0_pred, N_pred = model.predictor.F0Ntrain(en, s) |
|
|
|
out = model.decoder((t_en @ pred_aln_trg.unsqueeze(0).to(device)), |
|
F0_pred, N_pred, ref.squeeze().unsqueeze(0)) |
|
|
|
c = out.squeeze() |
|
y_g_hat = generator(c.unsqueeze(0)) |
|
y_out = y_g_hat.squeeze() |
|
|
|
converted_samples[key] = y_out.cpu().numpy() |
|
|
|
|
|
|
|
key = list(converted_samples.keys())[0] |
|
audio_output = converted_samples[key] |
|
audio_output = (audio_output * 32767).astype(np.int16) |
|
|
|
return (24000, audio_output), ipa2uk_phonemes |
|
|
|
|
|
with open("README.md") as file: |
|
article = file.read() |
|
article = article[article.find("---\n", 4) + 5 : :] |
|
|
|
iface = gr.Interface( |
|
fn=tts, |
|
inputs=[ |
|
|
|
gr.Text( |
|
label="Input", |
|
value='''ми кр+апельки ртуті на рівному полі, на сірій безмірній пустій площині. |
|
– рухливі, прудкі, досконалі і голі, котитись навчились, а жити, ще ні, |
|
– щенячі забави щоночі, щоднини, тваринна захланність звірячий запал, |
|
– хоч крапелька кожна це майже людина, і світло тремтливе відлите в метал. |
|
– але проступають пророцтва забуті, і стеляться світом зневіра і страх. |
|
– а ми розтік+аємось краплями ртуті, по мінних, - по мінних, по мінних полях. ''', |
|
), |
|
], |
|
outputs=[ |
|
gr.components.Audio(label="Output"), |
|
gr.components.Textbox(label="IPA Phonems"), |
|
], |
|
title="Ukrainian StyleTTS Alexis", |
|
description= f'''Ukrainian StyleTTS. If the stress is incorrect, use the + symbol before the stressed letter.''', |
|
cache_examples=False, |
|
examples=[["""Але щоб ви зрозуміли, звідки виник+ає це хибне уявлення людей, цуратись насолоди і вихваляти страждання, я розкрию перед вами всю картину і роз’ясн+ю, що саме говорив цей чоловік, який відкрив істину, якого я б назвав зодчим щасливого життя."""], |
|
["""Ми кр+апельки ртуті на рівному полі, на сірій безмірній пустій площині. |
|
– рухливі, прудкі, досконалі і голі, котитись навчились, а жити, ще ні, |
|
– щенячі забави щоночі, щоднини, тваринна захланність звірячий запал, |
|
– хоч крапелька кожна це майже людина, і світло тремтливе відлите в метал. |
|
– але проступають пророцтва забуті, і стеляться світом зневіра і страх. |
|
– а ми розтік+аємось краплями ртуті, по мінних, - по мінних, по мінних полях. """], |
|
["""спини мене, отямся і отям, така любов буває раз в нік+оли. |
|
– вона ж промчить над зламаним життям за нею ж будуть бігти видноколи. |
|
– вона ж порве нам спокій до струни, вона ж слова поспалює вустами. |
|
– спини мене, спини і схамени, ще поки можу думати востаннє. |
|
– ще поки можу, – але вже не можу. – настала черга й на мою зорю. |
|
– чи біля тебе душу відморожу чи біля тебе полум'ям згорю..."""] |
|
], |
|
article=article, |
|
) |
|
iface.launch() |