Spaces:
Build error
Build error
| from datetime import datetime | |
| import gradio as gr | |
| import json, os | |
| import requests | |
| import numpy as np | |
| from string import Template | |
| import wave | |
| # 在开头加入路径 | |
| import os, sys | |
| now_dir = os.getcwd() | |
| sys.path.append(now_dir) | |
| # 尝试清空含有GPT_SoVITS的路径 | |
| for path in sys.path: | |
| if path.find(r"GPT_SoVITS") != -1: | |
| sys.path.remove(path) | |
| # 取得模型文件夹路径 | |
| from src.config_manager import Inference_Config | |
| from src.config_manager import __version__ as frontend_version | |
| inference_config = Inference_Config() | |
| default_word_count = inference_config.default_word_count | |
| max_text_length = inference_config.max_text_length | |
| from tools.i18n.i18n import I18nAuto | |
| i18n = I18nAuto(locale_path="i18n/locale") | |
| from Synthesizers.base import Base_TTS_Synthesizer, Base_TTS_Task, get_wave_header_chunk | |
| from importlib import import_module | |
| synthesizer_name = inference_config.synthesizer | |
| # 动态导入合成器模块, 此处可写成 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 nltk | |
| nltk.data.path.append(os.path.abspath(os.path.join(now_dir,"nltk_data"))) | |
| language_list = ["auto", "zh", "en", "ja", "all_zh", "all_ja"] | |
| translated_language_list = [i18n("auto"), i18n("zh"), i18n("en"), i18n("ja"), i18n("all_zh"), i18n("all_ja")] # 由于i18n库的特性,这里需要全部手输一遍 | |
| language_dict = dict(zip(translated_language_list, language_list)) | |
| cut_method_list = ["auto_cut", "cut0", "cut1", "cut2", "cut3", "cut4", "cut5"] | |
| translated_cut_method_list = [i18n("auto_cut"), i18n("cut0"), i18n("cut1"), i18n("cut2"), i18n("cut3"), i18n("cut4"), i18n("cut5")] | |
| cut_method_dict = dict(zip(translated_cut_method_list, cut_method_list)) | |
| 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") | |
| from Adapters.gsv_fast import GSV_Instance as TTS_instance | |
| tts_instance = TTS_instance() | |
| import soundfile as sf | |
| def get_audio( | |
| text, | |
| cha_name, | |
| text_language, | |
| batch_size, | |
| speed_factor, | |
| top_k, | |
| top_p, | |
| temperature, | |
| character_emotion, | |
| cut_method, | |
| word_count, | |
| seed, | |
| stream="False", | |
| ): | |
| text_language = language_dict[text_language] | |
| cut_method = cut_method_dict[cut_method] | |
| if cut_method == "auto_cut": | |
| cut_method = f"{cut_method}_{word_count}" | |
| # Using Template to fill in variables | |
| stream = stream.lower() in ('true', '1', 't', 'y', 'yes') | |
| params = { | |
| "text": text, | |
| "text_language": text_language, | |
| "character": cha_name, | |
| "emotion": character_emotion, | |
| "top_k": top_k, | |
| "top_p": top_p, | |
| "temperature": temperature, | |
| "cut_method": cut_method, | |
| "stream": stream, | |
| "seed": seed, | |
| "speed_factor": speed_factor, | |
| "batch_size": batch_size, | |
| } | |
| # 如果不是经典模式,则添加额外的参数 | |
| try: | |
| task = tts_instance.params_analyser(params) | |
| gen = tts_instance.generate(task) | |
| sampling_rate, audio_data = next(gen) | |
| except Exception as e: | |
| gr.Warning(f"Error: {e}") | |
| return sampling_rate, np.array(audio_data,dtype=np.int16) | |
| 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_instance.get_characters() | |
| print(characters_and_emotions_dict) | |
| return characters_and_emotions_dict | |
| def change_character_list( | |
| cha_name="", auto_emotion=False, 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 cha_name in character_names: | |
| character_name_value = cha_name | |
| else: | |
| character_name_value = character_names[0] | |
| else: | |
| character_name_value = "" | |
| emotions = characters_and_emotions.get(character_name_value, ["default"]) | |
| emotion_value = character_emotion | |
| if auto_emotion == False and emotion_value not in emotions: | |
| emotion_value = "default" | |
| except: | |
| character_names = [] | |
| character_name_value = "" | |
| emotions = ["default"] | |
| emotion_value = "default" | |
| characters_and_emotions = {} | |
| if auto_emotion: | |
| return ( | |
| gr.Dropdown(character_names, value=character_name_value, label=i18n("选择角色")), | |
| gr.Checkbox(auto_emotion, label=i18n("是否自动匹配情感"), visible=False, interactive=False), | |
| gr.Dropdown(["auto"], value="auto", label=i18n("情感列表"), interactive=False), | |
| characters_and_emotions, | |
| ) | |
| return ( | |
| gr.Dropdown(character_names, value=character_name_value, label=i18n("选择角色")), | |
| gr.Checkbox(auto_emotion, label=i18n("是否自动匹配情感"),visible=False, interactive=False), | |
| gr.Dropdown(emotions, value=emotion_value, label=i18n("情感列表"), interactive=True), | |
| characters_and_emotions, | |
| ) | |
| def change_endpoint(url): | |
| url = url.strip() | |
| return gr.Textbox(f"{url}/tts"), gr.Textbox(f"{url}/character_list") | |
| 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("我是一个粉刷匠,粉刷本领强。我要把那新房子,刷得更漂亮。刷了房顶又刷墙,刷子像飞一样。哎呀我的小鼻子,变呀变了样。") | |
| if "。" not in default_text: | |
| _sentence_list = default_text.split(".") | |
| default_text = ".".join(_sentence_list[:1]) + "." | |
| else: | |
| _sentence_list = default_text.split("。") | |
| default_text = "。".join(_sentence_list[:2]) + "。" | |
| information = "" | |
| try: | |
| with open("Information.md", "r", encoding="utf-8") as f: | |
| information = f.read() | |
| except: | |
| pass | |
| 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]) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| with gr.Tabs(): | |
| with gr.Tab(label=i18n("基础选项")): | |
| with gr.Group(): | |
| text_language = gr.Dropdown( | |
| translated_language_list, | |
| value=translated_language_list[0], | |
| label=i18n("文本语言"), | |
| ) | |
| with gr.Group(): | |
| ( | |
| cha_name, | |
| auto_emotion_checkbox, | |
| character_emotion, | |
| characters_and_emotions_, | |
| ) = change_character_list() | |
| characters_and_emotions = gr.State(characters_and_emotions_) | |
| scan_character_list = gr.Button(i18n("扫描人物列表"), variant="secondary") | |
| with gr.Column(scale=2): | |
| with gr.Tabs(): | |
| with gr.Tab(label=i18n("基础选项")): | |
| with gr.Group(): | |
| speed_factor = gr.Slider( | |
| minimum=0.25, | |
| maximum=4, | |
| value=1, | |
| label=i18n("语速"), | |
| step=0.05, | |
| ) | |
| with gr.Group(): | |
| cut_method = gr.Dropdown( | |
| translated_cut_method_list, | |
| value=translated_cut_method_list[0], | |
| label=i18n("切句方式"), | |
| ) | |
| batch_size = gr.Slider( | |
| minimum=1, | |
| maximum=35, | |
| value=1, | |
| label=i18n("batch_size,1代表不并行,越大越快,但是越可能出问题"), | |
| step=1, | |
| ) | |
| word_count = gr.Slider( | |
| minimum=5,maximum=500,value=default_word_count,label=i18n("每句允许最大切分字词数"),step=1, | |
| ) | |
| with gr.Column(scale=2): | |
| with gr.Tabs(): | |
| with gr.Tab(label=i18n("高级选项")): | |
| with gr.Group(): | |
| seed = gr.Number( | |
| -1, | |
| label=i18n("种子"), | |
| interactive=True, | |
| ) | |
| with gr.Group(): | |
| top_k = gr.Slider(minimum=1, maximum=30, value=3, label=i18n("Top K"), step=1) | |
| top_p = gr.Slider(minimum=0, maximum=1, value=0.8, label=i18n("Top P")) | |
| temperature = gr.Slider( | |
| minimum=0, maximum=1, value=0.8, label=i18n("Temperature") | |
| ) | |
| cut_method.input(lambda x: gr.update(visible=(cut_method_dict[x]=="auto_cut")), [cut_method], [word_count]) | |
| with gr.Tabs(): | |
| with gr.Tab(label=i18n("请求完整音频")): | |
| with gr.Row(): | |
| sendRequest = gr.Button(i18n("发送请求"), variant="primary") | |
| audioRecieve = gr.Audio( | |
| None, label=i18n("音频输出"), type="filepath", streaming=False | |
| ) | |
| with gr.Tab(label=i18n("流式音频"),interactive=False,visible=False): | |
| with gr.Row(): | |
| sendStreamRequest = gr.Button( | |
| i18n("发送并开始播放"), variant="primary", interactive=True | |
| ) | |
| stopStreamButton = gr.Button(i18n("停止播放"), variant="secondary") | |
| with gr.Row(): | |
| audioStreamRecieve = gr.Audio(None, label=i18n("音频输出"), interactive=False) | |
| gr.HTML("<hr style='border-top: 1px solid #ccc; margin: 20px 0;' />") | |
| gr.HTML( | |
| f"""<p>{i18n("这是一个由")} <a href="{i18n("https://space.bilibili.com/66633770")}">XTer</a> {i18n("提供的推理特化包,当前版本:")}<a href="https://www.yuque.com/xter/zibxlp/awo29n8m6e6soru9">{frontend_version}</a> {i18n("项目开源地址:")} <a href="https://github.com/X-T-E-R/TTS-for-GPT-soVITS">Github</a></p> | |
| <p>{i18n("吞字漏字属于正常现象,太严重可尝试换行、加句号或调节batch size滑条。")}</p> | |
| <p>{i18n("若有疑问或需要进一步了解,可参考文档:")}<a href="{i18n("https://www.yuque.com/xter/zibxlp")}">{i18n("点击查看详细文档")}</a>。</p>""" | |
| ) | |
| # 以下是事件绑定 | |
| app.load( | |
| change_character_list, | |
| inputs=[cha_name, auto_emotion_checkbox, character_emotion], | |
| outputs=[ | |
| cha_name, | |
| auto_emotion_checkbox, | |
| character_emotion, | |
| characters_and_emotions, | |
| ] | |
| ) | |
| sendRequest.click(lambda: gr.update(interactive=False), None, [sendRequest]).then( | |
| get_audio, | |
| inputs=[ | |
| text, | |
| cha_name, | |
| text_language, | |
| batch_size, | |
| speed_factor, | |
| top_k, | |
| top_p, | |
| temperature, | |
| character_emotion, | |
| cut_method, | |
| word_count, | |
| seed, | |
| gr.State("False"), | |
| ], | |
| outputs=[audioRecieve], | |
| ).then(lambda: gr.update(interactive=True), None, [sendRequest]) | |
| sendStreamRequest.click( | |
| lambda: gr.update(interactive=False), None, [sendStreamRequest] | |
| ).then( | |
| get_audio, | |
| inputs=[ | |
| text, | |
| cha_name, | |
| text_language, | |
| batch_size, | |
| speed_factor, | |
| top_k, | |
| top_p, | |
| temperature, | |
| character_emotion, | |
| cut_method, | |
| word_count, | |
| seed, | |
| gr.State("True"), | |
| ], | |
| outputs=[audioStreamRecieve], | |
| ).then( | |
| lambda: gr.update(interactive=True), None, [sendStreamRequest] | |
| ) | |
| stopStreamButton.click(stopAudioPlay, inputs=[]) | |
| cha_name.change( | |
| load_character_emotions, | |
| inputs=[cha_name, characters_and_emotions], | |
| outputs=[character_emotion], | |
| ) | |
| scan_character_list.click( | |
| change_character_list, | |
| inputs=[cha_name, auto_emotion_checkbox, character_emotion], | |
| outputs=[ | |
| cha_name, | |
| auto_emotion_checkbox, | |
| character_emotion, | |
| characters_and_emotions, | |
| ], | |
| ) | |
| auto_emotion_checkbox.input( | |
| change_character_list, | |
| inputs=[cha_name, auto_emotion_checkbox, character_emotion], | |
| outputs=[ | |
| cha_name, | |
| auto_emotion_checkbox, | |
| character_emotion, | |
| characters_and_emotions, | |
| ], | |
| ) | |
| import uvicorn | |
| from pure_api import tts, character_list, set_tts_synthesizer | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| 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=["*"], | |
| ) | |
| fastapi_app = gr.mount_gradio_app(fastapi_app, app, path="/") | |
| uvicorn.run(fastapi_app, host=inference_config.tts_host, port=inference_config.tts_port) | |