白菜工厂1145号员工
Automated commit from batch script
a17d0bc
frontend_version = "2.2.3 240316"
from datetime import datetime
import gradio as gr
import json, os
import requests
import numpy as np
from string import Template
import pyaudio, wave
# 在开头加入路径
import os, sys
now_dir = os.getcwd()
sys.path.append(now_dir)
# sys.path.append(os.path.join(now_dir, "tools"))
# 取得模型文件夹路径
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json")
# 读取config.json
if os.path.exists(config_path):
with open(config_path, "r", encoding="utf-8") as f:
_config = json.load(f)
locale_language = str(_config.get("locale", "auto"))
locale_language = None if locale_language.lower() == "auto" else locale_language
tts_port = _config.get("tts_port", 5000)
default_batch_size = _config.get("batch_size", 10)
default_word_count = _config.get("max_word_count", 80)
is_share = _config.get("is_share", "false").lower() == "true"
is_classic = _config.get("classic_inference", "false").lower() == "true"
enable_auth = _config.get("enable_auth", "false").lower() == "true"
users = _config.get("user", {})
try:
default_username = list(users.keys())[0]
default_password = users[default_username]
except:
default_username = "admin"
default_password = "admin123"
from tools.i18n.i18n import I18nAuto
i18n = I18nAuto(locale_language , os.path.join(os.path.dirname(os.path.dirname(__file__)), "i18n/locale"))
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))
tts_port = 5000
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")
global p, streamAudio
p = pyaudio.PyAudio()
streamAudio = None
def send_request(
endpoint,
endpoint_data,
text,
cha_name,
text_language,
batch_size,
speed_factor,
top_k,
top_p,
temperature,
character_emotion,
cut_method,
word_count,
seed,
stream="False",
):
urlencoded_text = requests.utils.quote(text)
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
params = {
"chaName": cha_name,
"speakText": urlencoded_text,
"textLanguage": text_language,
"batch_size": batch_size,
"speed_factor": speed_factor,
"topK": top_k,
"topP": top_p,
"temperature": temperature,
"characterEmotion": character_emotion,
"cut_method": cut_method,
"seed": seed,
"stream": stream,
}
endpoint_template = Template(endpoint)
final_endpoint = endpoint_template.substitute(**params)
endpoint_data_template = Template(endpoint_data)
filled_json_str = endpoint_data_template.substitute(**params)
# Parse the filled JSON string
request_data = json.loads(filled_json_str)
body = request_data["body"]
if stream.lower() == "false":
print(i18n("发送请求到") + final_endpoint)
# Sending POST request
response = requests.post(final_endpoint, json=body)
# Checking if the request was successful
if response.status_code == 200:
# Generating save path
save_path = (
f"tmp_audio/{cha_name}{datetime.now().strftime('%Y%m%d%H%M%S%f')}.wav"
)
# Checking if the save path exists
if not os.path.exists("tmp_audio"):
os.makedirs("tmp_audio")
# Saving the audio file locally
with open(save_path, "wb") as f:
f.write(response.content)
# Returning to gradio
return gr.Audio(save_path, type="filepath")
else:
gr.Warning(
i18n("请求失败,状态码:") + f"{response.status_code}" + i18n(", 返回内容:") + f"{response.content}"
)
return gr.Audio(None, type="filepath")
else:
# Sending POST request
response = requests.post(final_endpoint, json=body, stream=True)
# Checking if the request was successful
global p, streamAudio
# Opening the audio stream
streamAudio = p.open(
format=p.get_format_from_width(2), channels=1, rate=32000, output=True
)
response = requests.post(final_endpoint, json=body, stream=True)
if response.status_code == 200:
save_path = (
f"tmp_audio/{cha_name}{datetime.now().strftime('%Y%m%d%H%M%S%f')}.wav"
)
# Audio parameters
channels = 1 # Mono
sampwidth = 2 # Sample width, 2 bytes (16 bits)
framerate = 32000 # Sample rate, 32000 Hz
# Checking if the save path exists
if not os.path.exists("tmp_audio"):
os.makedirs("tmp_audio")
# Opening a new wave file to write
with wave.open(save_path, "wb") as wf:
wf.setnchannels(channels) # Setting the number of channels
wf.setsampwidth(sampwidth) # Setting the sample width
wf.setframerate(framerate) # Setting the sample rate
for data in response.iter_content(chunk_size=1024):
wf.writeframes(data)
if (streamAudio is not None) and (not streamAudio.is_stopped()):
streamAudio.write(data)
# Stopping and closing the stream
if streamAudio is not None:
streamAudio.stop_stream()
return gr.Audio(save_path, type="filepath")
else:
gr.Warning(
i18n("请求失败,状态码:") + f"{response.status_code}" + i18n(", 返回内容:") + f"{response.content}"
)
return gr.Audio(None, type="filepath")
def stopAudioPlay():
global streamAudio
if streamAudio is not None:
streamAudio.stop_stream()
streamAudio = None
def get_characters_and_emotions(character_list_url):
try:
response = requests.get(character_list_url)
if response.status_code == 200:
return response.json()
else:
raise Exception(i18n("请求失败,状态码:") + f"{response.status_code}")
except:
raise Exception(i18n("请求失败,请检查URL是否正确"))
def change_character_list(
character_list_url, cha_name="", auto_emotion=False, character_emotion="default"
):
characters_and_emotions = {}
try:
characters_and_emotions = get_characters_and_emotions(character_list_url)
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),
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),
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 change_batch_size(batch_size):
try:
with open(config_path, "r", encoding="utf-8") as f:
_config = json.load(f)
with open(config_path, "w", encoding="utf-8") as f:
_config["batch_size"] = batch_size
json.dump(_config, f, ensure_ascii=False, indent=4)
except:
pass
return
def change_word_count(word_count):
try:
with open(config_path, "r", encoding="utf-8") as f:
_config = json.load(f)
with open(config_path, "w", encoding="utf-8") as f:
_config["max_word_count"] = word_count
json.dump(_config, f, ensure_ascii=False, indent=4)
except:
pass
return
default_request_url = f"http://127.0.0.1:{tts_port}"
default_character_info_url = f"{default_request_url}/character_list"
default_endpoint = f"{default_request_url}/tts"
default_endpoint_data = """{
"method": "POST",
"body": {
"cha_name": "${chaName}",
"character_emotion": "${characterEmotion}",
"text": "${speakText}",
"text_language": "${textLanguage}",
"batch_size": ${batch_size},
"speed": ${speed_factor},
"top_k": ${topK},
"top_p": ${topP},
"temperature": ${temperature},
"stream": "${stream}",
"cut_method": "${cut_method}",
"seed": ${seed},
"save_temp": "False"
}
}"""
default_text = i18n("我是一个粉刷匠,粉刷本领强。我要把那新房子,刷得更漂亮。刷了房顶又刷墙,刷子像飞一样。哎呀我的小鼻子,变呀变了样。")
with gr.Blocks() as app:
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> {i18n("使用前,请确认后端服务已启动。")}</p>
<p>{i18n("吞字漏字属于正常现象,太严重可通过换行或加句号解决,或者更换参考音频(使用模型管理界面)、调节下方batch size滑条。")}</p>
<p>{i18n("若有疑问或需要进一步了解,可参考文档:")}<a href="{i18n("https://www.yuque.com/xter/zibxlp")}">{i18n("点击查看详细文档")}</a>。</p>"""
)
with gr.Row():
text = gr.Textbox(
value=default_text, label=i18n("输入文本"), interactive=True, lines=8
)
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("文本语言"),
)
cut_method = gr.Dropdown(
translated_cut_method_list,
value=translated_cut_method_list[0],
label=i18n("切句方式"),
)
with gr.Group():
(
cha_name,
auto_emotion_checkbox,
character_emotion,
characters_and_emotions_,
) = change_character_list(default_character_info_url)
characters_and_emotions = gr.State(characters_and_emotions_)
scan_character_list = gr.Button(i18n("扫描人物列表"), variant="secondary")
with gr.Column(scale=1):
with gr.Tabs():
with gr.Tab(label=i18n("基础选项")):
gr.Textbox(
value=i18n("您在使用经典推理模式,部分选项不可用"),
label=i18n("提示"),
interactive=False,
visible=is_classic,
)
with gr.Group():
speed_factor = gr.Slider(
minimum=0.25,
maximum=4,
value=1,
label=i18n("语速"),
step=0.05,
visible=not is_classic,
)
with gr.Group():
batch_size = gr.Slider(
minimum=1,
maximum=35,
value=default_batch_size,
label=i18n("batch_size,1代表不并行,越大越快,但是越可能出问题"),
step=1,
visible=not is_classic,
)
word_count = gr.Slider(
minimum=5,maximum=500,value=default_word_count,label=i18n("每句允许最大切分字词数"),step=1, visible=not is_classic,
)
with gr.Tab(label=i18n("高级选项")):
with gr.Group():
seed = gr.Number(
-1,
label=i18n("种子"),
visible=not is_classic,
interactive=True,
)
with gr.Group():
top_k = gr.Slider(minimum=1, maximum=30, value=6, 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")
)
batch_size.release(change_batch_size, inputs=[batch_size])
word_count.release(change_word_count, inputs=[word_count])
cut_method.input(lambda x: gr.update(visible=(cut_method_dict[x]=="auto_cut")), [cut_method], [word_count])
with gr.Column(scale=2):
with gr.Tabs():
with gr.Tab(label=i18n("网址设置")):
request_url_input = gr.Textbox(
value=default_request_url, label=i18n("请求网址"), interactive=True
)
endpoint = gr.Textbox(
value=default_endpoint, label=i18n("Endpoint"), interactive=False
)
character_list_url = gr.Textbox(
value=default_character_info_url,
label=i18n("人物情感列表网址"),
interactive=False,
)
request_url_input.blur(
change_endpoint,
inputs=[request_url_input],
outputs=[endpoint, character_list_url],
)
with gr.Tab(label=i18n("认证信息"),visible=enable_auth):
gr.Textbox(
value=i18n("认证信息已启用,您可以在config.json中关闭。\n但是这个功能还没做好,只是摆设"),
label=i18n("认证信息"),
interactive=False
)
username = gr.Textbox(
value=default_username, label=i18n("用户名"), interactive=False
)
password = gr.Textbox(
value=default_password, label=i18n("密码"), interactive=False
)
with gr.Tab(label=i18n("json设置(一般不动)")):
endpoint_data = gr.Textbox(
value=default_endpoint_data, label=i18n("发送json格式"), lines=10
)
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("流式音频")):
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)
# 以下是事件绑定
app.load(
change_character_list,
inputs=[character_list_url, 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(
send_request,
inputs=[
endpoint,
endpoint_data,
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(
send_request,
inputs=[
endpoint,
endpoint_data,
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],
)
character_list_url.change(
change_character_list,
inputs=[character_list_url, cha_name, auto_emotion_checkbox, character_emotion],
outputs=[
cha_name,
auto_emotion_checkbox,
character_emotion,
characters_and_emotions,
],
)
scan_character_list.click(
change_character_list,
inputs=[character_list_url, 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=[character_list_url, cha_name, auto_emotion_checkbox, character_emotion],
outputs=[
cha_name,
auto_emotion_checkbox,
character_emotion,
characters_and_emotions,
],
)
app.launch(server_port=9867, show_error=True, share=is_share, inbrowser=True)