Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
import os | |
import json | |
import gradio as gr | |
from gradio_client import Client | |
voice_models = [ | |
("👨🦳 #6671", "ccby_nvidia_hifi_6671_M"), | |
("👱♀️ 🇬🇧 #92", "ccby_nvidia_hifi_92_F"), | |
] | |
voice_models_more = [ | |
("🧔 #6670", "ccby_nvidia_hifi_6670_M"), | |
("Male #9017", "ccby_nvidia_hifi_9017_M"), | |
("Male #6097", "ccby_nvidia_hifi_6097_M"), | |
("👩🦱 #12787", "ccby_nvidia_hifi_12787_F"), | |
("👵 #11614", "ccby_nv_hifi_11614_F"), | |
("Female #8051", "ccby_nvidia_hifi_8051_F"), | |
("👩🦳 #11697", "ccby_nvidia_hifi_11697_F"), | |
("Female #9136", "ccby_nvidia_hifi_9136_F"), | |
] | |
# order ranked by similarity to English due to the xVASynth's use of ARPAbet instead of IPA | |
languages = [ | |
("🇺🇸 EN", "en"), | |
("🇩🇪 DE", "de"), | |
("🇪🇸 ES", "es"), | |
("🇮🇳 HI", "hi"), | |
("🇨🇳 ZH", "zh"), | |
] | |
languages_more = [ | |
("🇳🇱 NL", "nl"), | |
("🇧🇷 PT", "pt"), | |
("🇮🇹 IT", "it"), | |
("🇵🇱 PL", "pl"), | |
("🇷🇴 RO", "ro"), | |
("🇸🇪 SV", "sv"), | |
("🇩🇰 DA", "da"), | |
("🇫🇮 FI", "fi"), | |
("🇭🇺 HU", "hu"), | |
("🇬🇷 EL", "el"), | |
("🇫🇷 FR", "fr"), | |
("🇷🇺 RU", "ru"), | |
("🇺🇦 UA", "uk"), | |
("🇹🇷 TR", "tr"), | |
("🇸🇦 AR", "ar"), | |
("🇯🇵 JP", "jp"), | |
("🇰🇷 KO", "ko"), | |
("🇻🇳 VI", "vi"), | |
("🇻🇦 LA", "la"), | |
("🇳🇬 YO", "yo"), | |
("Swahili", "sw"), | |
("Hausa", "ha"), | |
("Wolof", "wo"), | |
] | |
# Translated from English by DeepMind's Gemini Pro | |
default_text = { | |
"ar": "هذا هو صوتي.", | |
"da": "Sådan lyder min stemme.", | |
"de": "So klingt meine Stimme.", | |
"el": "Έτσι ακούγεται η φωνή μου.", | |
"en": "This is what my voice sounds like.", | |
"es": "Así suena mi voz.", | |
"fi": "Näin ääneni kuulostaa.", | |
"fr": "Voici à quoi ressemble ma voix.", | |
"ha": "Wannan ne muryata ke.", | |
"hi": "यह मेरी आवाज़ कैसी लगती है।", | |
"hu": "Így hangzik a hangom.", | |
"it": "Così suona la mia voce.", | |
"jp": "これが私の声です。", | |
"ko": "여기 제 목소리가 어떤지 들어보세요.", | |
"la": "Haec est vox mea sonans.", | |
"nl": "Dit is hoe mijn stem klinkt.", | |
"pl": "Tak brzmi mój głos.", | |
"pt": "É assim que minha voz soa.", | |
"ro": "Așa sună vocea mea.", | |
"ru": "Вот как звучит мой голос.", | |
"sv": "Såhär låter min röst.", | |
"sw": "Sauti yangu inasikika hivi.", | |
"tr": "Benim sesimin sesi böyle.", | |
"uk": "Ось як звучить мій голос.", | |
"vi": "Đây là giọng nói của tôi.", | |
"wo": "Ndox li neen xewnaal ma.", | |
"yo": "Ìyí ni ohùn mi ńlá.", | |
"zh": "这是我的声音。", | |
} | |
# Component defaults | |
input_textbox_init = { | |
'label': "Input Text", | |
'value': "This is what my voice sounds like.", | |
'info': "Also accepts ARPAbet symbols placed within {} brackets.", | |
'lines': 1, | |
'max_lines': 5, | |
'autofocus': True, | |
} | |
pacing_slider_init = { | |
'value': 1.0, | |
'minimum': 0.5, | |
'maximum': 2.0, | |
'step': 0.1, | |
'label': "Duration", | |
} | |
pitch_slider_init = { | |
'minimum': 0, | |
'maximum': 1.0, | |
'value': 0.5, | |
'step': 0.05, | |
'label': "Pitch", | |
'visible': False, | |
} | |
energy_slider_init = { | |
'minimum': 0.1, | |
'maximum': 1.0, | |
'value': 1.0, | |
'step': 0.05, | |
'label': "Energy", | |
'visible': False, | |
} | |
anger_slider_init = { | |
'minimum': 0, | |
'maximum': 1.0, | |
'value': 0, | |
'step': 0.05, | |
'label': "😠 Anger", | |
'info': "Tread lightly beyond 0.9", | |
} | |
happy_slider_init = { | |
'minimum': 0, | |
'maximum': 1.0, | |
'value': 0, | |
'step': 0.05, | |
'label': "😃 Happiness", | |
'info': "Tread lightly beyond 0.7", | |
} | |
sad_slider_init = { | |
'minimum': 0, | |
'maximum': 1.0, | |
'value': 0, | |
'step': 0.05, | |
'label': "😭 Sadness", | |
'info': "Duration increased when beyond 0.2", | |
} | |
surprise_slider_init = { | |
'minimum': 0, | |
'maximum': 1.0, | |
'value': 0, | |
'step': 0.05, | |
'label': "😮 Surprise", | |
'info': "Does not play well with Happiness with either being beyond 0.3", | |
} | |
voice_radio_init = { | |
'choices': [*voice_models, (f'+{len(voice_models_more)}', 'more')], | |
'value': "ccby_nvidia_hifi_6671_M", | |
'label': "Voice", | |
'info': "NVIDIA HIFI CC-BY-4.0 xVAPitch voice model" | |
} | |
deepmoji_checkbox_init = { | |
'label': "Use DeepMoji", | |
'info': "Auto adjust emotional values for English", | |
'value': True, | |
'interactive': True | |
} | |
def more_lang_options(lang): | |
# print('more_lang_options') | |
if lang != 'more': | |
return lang | |
radio_init = {**language_radio_init} | |
radio_init['choices'] = [*languages, *languages_more] | |
return gr.Radio(**radio_init) | |
def set_default_text(lang, deepmoji_checked): | |
# print('set_default_text') | |
textbox_init = {**input_textbox_init} | |
if lang == 'more': | |
textbox_init['value'] = default_text['en'] | |
# return default_text['en'], deepmoji_checked | |
return gr.Textbox(**textbox_init), deepmoji_checked | |
textbox_init['value'] = default_text[lang] | |
# DeepMoji only works on English Text | |
checkbox_init = {**deepmoji_checkbox_init} | |
if lang == 'en': | |
checkbox_init['value'] = deepmoji_checked, | |
# checkbox_init['interactive'] = True | |
else: | |
deepmoji_checked = False | |
# FIXME: event listener conflict with toggle_deepmoji | |
# checkbox_init['info'] = "Works only with English!", | |
# checkbox_init['value'] = False, | |
# checkbox_init['interactive'] = False | |
# gr.Checkbox(**checkbox_init) | |
return gr.Textbox(**textbox_init), deepmoji_checked | |
# examples component | |
en_examples = [ | |
"This is what my voice sounds like.", | |
"If there is anything else you need, feel free to ask.", | |
"Amazing! Could you do that again?", | |
"Why, I would be more than happy to help you!", | |
"That was unexpected.", | |
"How dare you! . You have no right.", | |
"Ahh, well, you see. There is more to it.", | |
"I can't believe she is gone.", | |
"Stay out of my way!!!", | |
# ARPAbet example | |
"This { IH1 Z } { W AH1 T } { M AY1 } { V OY1 S } { S AW1 N D Z } like.", | |
] | |
en_examples_dropdown_init = { | |
'choices': en_examples, | |
'value': en_examples[0], | |
'label': "Example dropdown", | |
'show_label': False, | |
'info': "English Examples", | |
'visible': True | |
} | |
def set_example_as_input(example_text): | |
# print('set_example_as_input') | |
return example_text | |
def toggle_example_dropdown(lang): | |
# print('toggle_example_dropdown') | |
dropdown_init = {**en_examples_dropdown_init} | |
if lang == 'en': | |
dropdown_init['visible'] = True | |
else: | |
dropdown_init['visible'] = False | |
return gr.Dropdown(**dropdown_init) | |
def more_voice_options(voice): | |
# print('more_voice_options') | |
if voice != 'more': | |
return voice | |
radio_init = {**voice_radio_init} | |
radio_init['choices'] = [*voice_models, *voice_models_more] | |
return gr.Radio(**radio_init) | |
def reset_em_sliders( | |
deepmoji_enabled, | |
anger, | |
happy, | |
sad, | |
surprise | |
): | |
# print('reset_em_sliders') | |
if (deepmoji_enabled): | |
return (0, 0, 0, 0) | |
else: | |
return ( | |
anger, | |
happy, | |
sad, | |
surprise | |
) | |
def toggle_deepmoji( | |
checked, | |
anger, | |
happy, | |
sad, | |
surprise | |
): | |
# print('toggle_deepmoji') | |
if checked: | |
return (0, 0, 0, 0) | |
else: | |
return ( | |
anger, | |
happy, | |
sad, | |
surprise | |
) | |
# languages component | |
language_radio_init = { | |
'choices': [*languages, *[(f'+{len(languages_more)}', 'more')]], | |
'value': "en", | |
'label': "Language", | |
'info': "Will be more monotone and have an English accent." | |
} | |
_DESCRIPTION = ''' | |
<div> | |
<a style="display:inline-block;" href="https://github.com/DanRuta/xVA-Synth"><img src='https://img.shields.io/github/stars/DanRuta/xVA-Synth?style=social'/></a> | |
<a style="display:inline-block;" href="https://www.nexusmods.com/skyrimspecialedition/mods/44184"><img src='https://img.shields.io/badge/Endorsements-3.4k-blue?logo=nexusmods'/></a> | |
<a style="display:inline-block; margin-left: .5em" href="https://discord.gg/nv7c6E2TzV"><img src='https://img.shields.io/discord/794590496202293278.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2'/></a> | |
<span style="display: inline-block;margin-left: .5em;vertical-align: top;"><a href="https://huggingface.co/spaces/Pendrokar/xVASynth?duplicate=true" style="" target="_blank"><img style="margin-bottom: 0em;display: inline;" src="https://bit.ly/3gLdBN6" alt="Duplicate Space"></a> for a personal CPU-run one</span> | |
</div> | |
''' | |
class BlocksDemo: | |
def __init__(self): | |
with gr.Blocks(css=".arpabet {background-color: gray; border-radius: 5px; font-size: 120%; padding: 0 0.1em; margin: 0 0.1em; text-align: center}") as demo: | |
gr.Markdown("# xVASynth TTS") | |
gr.HTML(label="description", value=_DESCRIPTION) | |
with gr.Row(): # Main row for inputs and language selection | |
with gr.Column(): # Input column | |
input_textbox = gr.Textbox(**input_textbox_init) | |
language_radio = gr.Radio(**language_radio_init) | |
# remove autofocus | |
input_textbox_init['autofocus'] = False | |
with gr.Row(): | |
with gr.Column(): | |
en_examples_dropdown = gr.Dropdown(**en_examples_dropdown_init) | |
with gr.Column(): | |
pacing_slider = gr.Slider(**pacing_slider_init) | |
with gr.Column(): # Control column | |
voice_radio = gr.Radio(**voice_radio_init) | |
pitch_slider = gr.Slider(**pitch_slider_init) | |
energy_slider = gr.Slider(**energy_slider_init) | |
with gr.Row(): # Main row for inputs and language selection | |
with gr.Column(): # Input column | |
anger_slider = gr.Slider(**anger_slider_init) | |
sad_slider = gr.Slider(**sad_slider_init) | |
with gr.Column(): # Input column | |
happy_slider = gr.Slider(**happy_slider_init) | |
surprise_slider = gr.Slider(**surprise_slider_init) | |
deepmoji_checkbox = gr.Checkbox(**deepmoji_checkbox_init) | |
# Event handling using click | |
btn = gr.Button("Generate", variant="primary") | |
# with gr.Row(): # Main row for inputs and language selection | |
# with gr.Column(): # Input column | |
output_wav = gr.Audio( | |
label="22kHz audio output", | |
type="filepath", | |
editable=False, | |
autoplay=True | |
) | |
# with gr.Column(): # Input column | |
output_arpabet = gr.HTML(label="ARPAbet") | |
btn.click( | |
fn=self.predict, | |
inputs=[ | |
input_textbox, | |
voice_radio, | |
language_radio, | |
pacing_slider, | |
pitch_slider, | |
energy_slider, | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider, | |
deepmoji_checkbox | |
], | |
outputs=[ | |
output_wav, | |
output_arpabet, | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
] | |
) | |
# more languages option | |
language_radio.change( | |
more_lang_options, | |
inputs=language_radio, | |
outputs=language_radio, | |
trigger_mode='once', | |
show_progress='hidden', | |
) | |
# more voices option | |
voice_radio.change( | |
more_voice_options, | |
inputs=voice_radio, | |
outputs=voice_radio, | |
trigger_mode='once', | |
show_progress='hidden', | |
queue=False, | |
) | |
# set default text | |
language_radio.change( | |
set_default_text, | |
inputs=[language_radio, deepmoji_checkbox], | |
outputs=[input_textbox, deepmoji_checkbox], | |
show_progress='hidden', | |
queue=False, | |
) | |
# toggle en examples | |
language_radio.change( | |
toggle_example_dropdown, | |
inputs=language_radio, | |
outputs=en_examples_dropdown, | |
show_progress='hidden', | |
queue=False, | |
) | |
en_examples_dropdown.change( | |
set_example_as_input, | |
inputs=[en_examples_dropdown], | |
outputs=[input_textbox], | |
show_progress='hidden', | |
queue=False, | |
) | |
deepmoji_checkbox.change( | |
toggle_deepmoji, | |
inputs=[ | |
deepmoji_checkbox, | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
], | |
outputs=[ | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
], | |
show_progress='hidden', | |
queue=False, | |
) | |
input_textbox.change( | |
reset_em_sliders, | |
inputs=[ | |
deepmoji_checkbox, | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
], | |
outputs=[ | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
], | |
show_progress='hidden', | |
queue=False, | |
) | |
voice_radio.change( | |
reset_em_sliders, | |
inputs=[ | |
deepmoji_checkbox, | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
], | |
outputs=[ | |
anger_slider, | |
happy_slider, | |
sad_slider, | |
surprise_slider | |
], | |
show_progress='hidden', | |
queue=False, | |
) | |
self.block = demo | |
def predict( | |
self, | |
input_text, | |
voice, | |
lang, | |
pacing, | |
pitch, | |
energy, | |
anger, | |
happy, | |
sad, | |
surprise, | |
deepmoji_checked | |
): | |
wav_path, arpabet_html, angry, happy, sad, surprise, response = client.predict( | |
input_text, # str in 'Input Text' Textbox component | |
voice, # Literal['ccby_nvidia_hifi_6670_M', 'ccby_nv_hifi_11614_F', 'ccby_nvidia_hifi_11697_F', 'ccby_nvidia_hifi_12787_F', 'ccby_nvidia_hifi_6097_M', 'ccby_nvidia_hifi_6671_M', 'ccby_nvidia_hifi_8051_F', 'ccby_nvidia_hifi_9017_M', 'ccby_nvidia_hifi_9136_F', 'ccby_nvidia_hifi_92_F'] in 'Voice' Radio component | |
lang, # Literal['en', 'de', 'es', 'it', 'fr', 'ru', 'tr', 'la', 'ro', 'da', 'vi', 'ha', 'nl', 'zh', 'ar', 'uk', 'hi', 'ko', 'pl', 'sw', 'fi', 'hu', 'pt', 'yo', 'sv', 'el', 'wo', 'jp'] in 'Language' Radio component | |
pacing, # float (numeric value between 0.5 and 2.0) in 'Duration' Slider component | |
pitch, # float (numeric value between 0 and 1.0) in 'Pitch' Slider component | |
energy, # float (numeric value between 0.1 and 1.0) in 'Energy' Slider component | |
anger, # float (numeric value between 0 and 1.0) in '😠 Anger' Slider component | |
happy, # float (numeric value between 0 and 1.0) in '😃 Happiness' Slider component | |
sad, # float (numeric value between 0 and 1.0) in '😭 Sadness' Slider component | |
surprise, # float (numeric value between 0 and 1.0) in '😮 Surprise' Slider component | |
deepmoji_checked, # bool | |
api_name="/predict" | |
) | |
json_data = json.loads(response.replace("'", '"')) | |
arpabet_html = '<h6>ARPAbet & Durations</h6>' | |
arpabet_html += '<table style="margin: 0 var(--size-2)"><tbody><tr>' | |
arpabet_nopad = json_data['arpabet'].split('|PAD|') | |
arpabet_symbols = json_data['arpabet'].split('|') | |
wpad_len = len(arpabet_symbols) | |
nopad_len = len(arpabet_nopad) | |
total_dur_length = 0 | |
for symb_i in range(wpad_len): | |
if (arpabet_symbols[symb_i] == '<PAD>'): | |
continue | |
total_dur_length += float(json_data['durations'][symb_i]) | |
for symb_i in range(wpad_len): | |
if (arpabet_symbols[symb_i] == '<PAD>'): | |
continue | |
arpabet_length = float(json_data['durations'][symb_i]) | |
cell_width = round(arpabet_length / total_dur_length * 100, 2) | |
arpabet_html += '<td class="arpabet" style="width: '\ | |
+ str(cell_width)\ | |
+'%">'\ | |
+ arpabet_symbols[symb_i]\ | |
+ '</td> ' | |
arpabet_html += '<tr></tbody></table>' | |
return [ | |
wav_path, | |
arpabet_html, | |
round(json_data['em_angry'][0], 2), | |
round(json_data['em_happy'][0], 2), | |
round(json_data['em_sad'][0], 2), | |
round(json_data['em_surprise'][0], 2) | |
] | |
if __name__ == "__main__": | |
print('running Gradio interface') | |
client = Client("Pendrokar/xVASynth") | |
demo = BlocksDemo() | |
demo.block.launch() | |