Spaces:
Sleeping
Sleeping
from datetime import datetime | |
import gradio as gr | |
import json, os | |
import requests | |
import numpy as np | |
from string import Template | |
import wave, io | |
# 在开头加入路径 | |
import os, sys | |
now_dir = os.getcwd() | |
sys.path.insert(0, now_dir) | |
import logging | |
logging.getLogger("markdown_it").setLevel(logging.ERROR) | |
logging.getLogger("urllib3").setLevel(logging.ERROR) | |
logging.getLogger("httpcore").setLevel(logging.ERROR) | |
logging.getLogger("httpx").setLevel(logging.ERROR) | |
logging.getLogger("asyncio").setLevel(logging.ERROR) | |
logging.getLogger("charset_normalizer").setLevel(logging.ERROR) | |
logging.getLogger("torchaudio._extension").setLevel(logging.ERROR) | |
from Synthesizers.base import Base_TTS_Synthesizer, Base_TTS_Task, get_wave_header_chunk | |
from src.common_config_manager import app_config, __version__ | |
frontend_version = __version__ | |
def load_character_emotions(character_name, characters_and_emotions): | |
emotion_options = ["default"] | |
emotion_options = characters_and_emotions.get(character_name, ["default"]) | |
return gr.Dropdown(emotion_options, value="default") | |
synthesizer_name = app_config.synthesizer | |
from importlib import import_module | |
import tools.i18n.i18n as i18n_module | |
# 设置国际化支持 | |
i18n = i18n_module.I18nAuto(language=app_config.locale, locale_path=f"Synthesizers/{synthesizer_name}/configs/i18n/locale") | |
# 动态导入合成器模块, 此处可写成 from Synthesizers.xxx import TTS_Synthesizer, TTS_Task | |
synthesizer_module = import_module(f"Synthesizers.{synthesizer_name}") | |
TTS_Synthesizer = synthesizer_module.TTS_Synthesizer | |
TTS_Task = synthesizer_module.TTS_Task | |
# 创建合成器实例 | |
tts_synthesizer:Base_TTS_Synthesizer = TTS_Synthesizer(debug_mode=True) | |
import soundfile as sf | |
all_gradio_components = {} | |
from time import time as ttime | |
def get_audio(*data, streaming=False): | |
data = dict(zip([key for key in all_gradio_components.keys()], data)) | |
data["stream"] = streaming | |
if data.get("text") in ["", None]: | |
gr.Warning(i18n("文本不能为空")) | |
return None, None | |
try: | |
task: Base_TTS_Task= tts_synthesizer.params_parser(data) | |
t2 = ttime() | |
if not streaming: | |
if synthesizer_name == "remote": | |
save_path = tts_synthesizer.generate(task, return_type="filepath") | |
yield save_path | |
else: | |
gen = tts_synthesizer.generate(task, return_type="numpy") | |
yield next(gen) | |
else: | |
gen = tts_synthesizer.generate(task, return_type="numpy") | |
sample_rate = 32000 if task.sample_rate in [None, 0] else task.sample_rate | |
yield get_wave_header_chunk(sample_rate=sample_rate) | |
for chunk in gen: | |
yield chunk | |
except Exception as e: | |
gr.Warning(f"Error: {e}") | |
from functools import partial | |
get_streaming_audio = partial(get_audio, streaming=True) | |
def stopAudioPlay(): | |
return | |
global characters_and_emotions_dict | |
characters_and_emotions_dict = {} | |
def get_characters_and_emotions(): | |
global characters_and_emotions_dict | |
# 直接检查字典是否为空,如果不是,直接返回,避免重复获取 | |
if characters_and_emotions_dict == {}: | |
characters_and_emotions_dict = tts_synthesizer.get_characters() | |
print(characters_and_emotions_dict) | |
return characters_and_emotions_dict | |
def change_character_list( | |
character="", emotion="default" | |
): | |
characters_and_emotions = {} | |
try: | |
characters_and_emotions = get_characters_and_emotions() | |
character_names = [i for i in characters_and_emotions] | |
if len(character_names) != 0: | |
if character in character_names: | |
character_name_value = character | |
else: | |
character_name_value = character_names[0] | |
else: | |
character_name_value = "" | |
emotions = characters_and_emotions.get(character_name_value, ["default"]) | |
emotion_value = emotion | |
except: | |
character_names = [] | |
character_name_value = "" | |
emotions = ["default"] | |
emotion_value = "default" | |
characters_and_emotions = {} | |
return ( | |
gr.Dropdown(character_names, value=character_name_value, label=i18n("选择角色")), | |
gr.Dropdown(emotions, value=emotion_value, label=i18n("情感列表"), interactive=True), | |
characters_and_emotions, | |
) | |
def cut_sentence_multilang(text, max_length=30): | |
if max_length == -1: | |
return text, "" | |
# 初始化计数器 | |
word_count = 0 | |
in_word = False | |
for index, char in enumerate(text): | |
if char.isspace(): # 如果当前字符是空格 | |
in_word = False | |
elif char.isascii() and not in_word: # 如果是ASCII字符(英文)并且不在单词内 | |
word_count += 1 # 新的英文单词 | |
in_word = True | |
elif not char.isascii(): # 如果字符非英文 | |
word_count += 1 # 每个非英文字符单独计为一个字 | |
if word_count > max_length: | |
return text[:index], text[index:] | |
return text, "" | |
default_text = i18n("我是一个粉刷匠,粉刷本领强。我要把那新房子,刷得更漂亮。刷了房顶又刷墙,刷子像飞一样。哎呀我的小鼻子,变呀变了样。") | |
information = "" | |
try: | |
with open("Information.md", "r", encoding="utf-8") as f: | |
information = f.read() | |
except: | |
pass | |
try: | |
max_text_length = app_config.max_text_length | |
except: | |
max_text_length = -1 | |
from webuis.builders.gradio_builder import GradioTabBuilder | |
ref_settings = tts_synthesizer.ui_config.get("ref_settings", []) | |
basic_settings = tts_synthesizer.ui_config.get("basic_settings", []) | |
advanced_settings = tts_synthesizer.ui_config.get("advanced_settings", []) | |
url_setting = tts_synthesizer.ui_config.get("url_settings", []) | |
tts_task_example : Base_TTS_Task = TTS_Task() | |
params_config = tts_task_example.params_config | |
has_character_param = True if "character" in params_config else False | |
with gr.Blocks() as app: | |
gr.Markdown(information) | |
with gr.Row(): | |
max_text_length_tip = "" if max_text_length == -1 else f"( "+i18n("最大允许长度")+ f" : {max_text_length} ) " | |
text = gr.Textbox( | |
value=default_text, label=i18n("输入文本")+max_text_length_tip, interactive=True, lines=8 | |
) | |
text.blur(lambda x: gr.update(value=cut_sentence_multilang(x,max_length=max_text_length)[0]), [text], [text]) | |
all_gradio_components["text"] = text | |
with gr.Row(): | |
with gr.Column(scale=2): | |
with gr.Tabs(): | |
with gr.Tab(label=i18n("角色选项"), visible=has_character_param): | |
with gr.Group(): | |
( | |
character, | |
emotion, | |
characters_and_emotions_, | |
) = change_character_list() | |
characters_and_emotions = gr.State(characters_and_emotions_) | |
scan_character_list = gr.Button( | |
i18n("扫描人物列表"), variant="secondary" | |
) | |
all_gradio_components["character"] = character | |
all_gradio_components["emotion"] = emotion | |
character.change( | |
load_character_emotions, | |
inputs=[character, characters_and_emotions], | |
outputs=[emotion], | |
) | |
scan_character_list.click( | |
change_character_list, | |
inputs=[character, emotion], | |
outputs=[ | |
character, | |
emotion, | |
characters_and_emotions, | |
], | |
) | |
if len(ref_settings) > 0: | |
with gr.Tab(label=i18n("参考设置")): | |
ref_settings_tab = GradioTabBuilder( | |
ref_settings, params_config | |
) | |
ref_settings_components = ref_settings_tab.build() | |
all_gradio_components.update(ref_settings_components) | |
with gr.Column(scale=2): | |
with gr.Tabs(): | |
if len(basic_settings) > 0: | |
with gr.Tab(label=i18n("基础选项")): | |
basic_settings_tab = GradioTabBuilder( | |
basic_settings, params_config | |
) | |
basic_settings_components = basic_settings_tab.build() | |
all_gradio_components.update(basic_settings_components) | |
with gr.Column(scale=2): | |
with gr.Tabs(): | |
if len(advanced_settings) > 0: | |
with gr.Tab(label=i18n("高级选项")): | |
advanced_settings_tab = GradioTabBuilder( | |
advanced_settings, params_config | |
) | |
advanced_settings_components = advanced_settings_tab.build() | |
all_gradio_components.update(advanced_settings_components) | |
if len(url_setting) > 0: | |
with gr.Tab(label=i18n("URL设置")): | |
url_setting_tab = GradioTabBuilder(url_setting, params_config) | |
url_setting_components = url_setting_tab.build() | |
all_gradio_components.update(url_setting_components) | |
with gr.Tabs(): | |
with gr.Tab(label=i18n("请求完整音频")): | |
with gr.Row(): | |
get_full_audio_button = gr.Button(i18n("生成音频"), variant="primary") | |
full_audio = gr.Audio( | |
None, label=i18n("音频输出"), type="filepath", streaming=False | |
) | |
get_full_audio_button.click(lambda: gr.update(interactive=False), None, [get_full_audio_button]).then( | |
get_audio, | |
inputs=[value for key, value in all_gradio_components.items()], | |
outputs=[full_audio], | |
).then(lambda: gr.update(interactive=True), None, [get_full_audio_button]) | |
with gr.Tab(label=i18n("流式音频")): | |
with gr.Row(): | |
get_streaming_audio_button = gr.Button(i18n("生成流式音频"), variant="primary") | |
streaming_audio = gr.Audio( | |
None, label=i18n("音频输出"), type="filepath", streaming=True, autoplay=True | |
) | |
get_streaming_audio_button.click(lambda: gr.update(interactive=False), None, [get_streaming_audio_button]).then( | |
get_streaming_audio, | |
inputs=[value for key, value in all_gradio_components.items()], | |
outputs=[streaming_audio], | |
).then(lambda: gr.update(interactive=True), None, [get_streaming_audio_button]) | |
gr.HTML("<hr style='border-top: 1px solid #ccc; margin: 20px 0;' />") | |
gr.HTML( | |
f"""<p>{i18n("这是GSVI。")}{i18n(",当前版本:")}<a href="https://www.yuque.com/xter/zibxlp/awo29n8m6e6soru9">{frontend_version}</a> {i18n("项目开源地址:")} <a href="https://github.com/X-T-E-R/GPT-SoVITS-Inference">Github</a></p> | |
<p>{i18n("若有疑问或需要进一步了解,可参考文档:")}<a href="{i18n("https://www.yuque.com/xter/zibxlp")}">{i18n("点击查看详细文档")}</a>。</p>""" | |
) | |
# 以下是事件绑定 | |
# app.load( | |
# change_character_list, | |
# inputs=[character, emotion], | |
# outputs=[ | |
# character, | |
# emotion, | |
# characters_and_emotions, | |
# ] | |
# ) | |
if app_config.also_enable_api == True: | |
import uvicorn | |
from pure_api import tts, character_list, set_tts_synthesizer | |
from fastapi import FastAPI | |
from fastapi.middleware.cors import CORSMiddleware | |
from src.api_utils import get_gradio_frp, get_localhost_ipv4_address | |
set_tts_synthesizer(tts_synthesizer) | |
fastapi_app:FastAPI = app.app | |
fastapi_app.add_api_route("/tts", tts, methods=["POST", "GET"]) | |
fastapi_app.add_api_route("/character_list", character_list, methods=["GET"]) | |
fastapi_app.add_middleware( | |
CORSMiddleware, | |
allow_origins=["*"], | |
allow_credentials=True, | |
allow_methods=["*"], | |
allow_headers=["*"], | |
) | |
local_link = f"http://127.0.0.1:{app_config.server_port}" | |
link = local_link | |
if app_config.is_share: | |
share_url = get_gradio_frp(app_config.server_name, app_config.server_port, app.share_token) | |
print("This share link expires in 72 hours.") | |
print(f"Share URL: {share_url}") | |
link = share_url | |
if app_config.inbrowser: | |
import webbrowser | |
webbrowser.open(link) | |
ipv4_address = get_localhost_ipv4_address(app_config.server_name) | |
ipv4_link = f"http://{ipv4_address}:{app_config.server_port}" | |
print(f"INFO: Local Network URL: {ipv4_link}") | |
fastapi_app = gr.mount_gradio_app(fastapi_app, app, path="/") | |
uvicorn.run(fastapi_app, host=app_config.server_name, port=app_config.server_port) | |
else: | |
app.queue().launch(share=app_config.is_share, inbrowser=app_config.inbrowser, server_name=app_config.server_name, server_port=app_config.server_port) | |