Skip to content

Commit

Permalink
Merge pull request #700 from Ikaros-521/owner
Browse files Browse the repository at this point in the history
gpt-sovits新增模型配置项,支持动态加载模型
  • Loading branch information
Ikaros-521 committed Mar 14, 2024
2 parents e9e41ed + df6a35c commit 55fbd42
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 16 deletions.
4 changes: 3 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -541,14 +541,16 @@
"request_parameters": "{{\"url\": \"https://xzjosh-nana7mi-bert-vits2.hf.space/--replicas/b9be4/\", \"fn_index\": 0, \"data_analysis\": 1, \"text_input\": \"{content}\", \"speaker_option\": \"Nana7mi\", \"sdp_ratio\": 0.5, \"noise\": 0.6, \"noise_w\": 0.9, \"length\": 1}}"
},
"gpt_sovits": {
"type": "gradio",
"type": "api",
"ws_ip_port": "ws://localhost:9872/queue/join",
"api_ip_port": "http://127.0.0.1:9880",
"ref_audio_path": "F:\\GPT-SoVITS\\raws\\ikaros\\21.wav",
"prompt_text": "マスター、どうりょくろか、いいえ、なんでもありません",
"prompt_language": "日文",
"language": "自动识别",
"cut": "凑四句一切",
"gpt_model_path": "F:\\GPT-SoVITS\\GPT_weights\\ikaros-e15.ckpt",
"sovits_model_path": "F:\\GPT-SoVITS\\SoVITS_weights\\ikaros_e8_s280.pth",
"webtts": {
"api_ip_port": "http://127.0.0.1:8080",
"spk": "sanyueqi",
Expand Down
4 changes: 3 additions & 1 deletion config.json.bak
Original file line number Diff line number Diff line change
Expand Up @@ -541,14 +541,16 @@
"request_parameters": "{{\"url\": \"https://xzjosh-nana7mi-bert-vits2.hf.space/--replicas/b9be4/\", \"fn_index\": 0, \"data_analysis\": 1, \"text_input\": \"{content}\", \"speaker_option\": \"Nana7mi\", \"sdp_ratio\": 0.5, \"noise\": 0.6, \"noise_w\": 0.9, \"length\": 1}}"
},
"gpt_sovits": {
"type": "gradio",
"type": "api",
"ws_ip_port": "ws://localhost:9872/queue/join",
"api_ip_port": "http://127.0.0.1:9880",
"ref_audio_path": "F:\\GPT-SoVITS\\raws\\ikaros\\21.wav",
"prompt_text": "マスター、どうりょくろか、いいえ、なんでもありません",
"prompt_language": "日文",
"language": "自动识别",
"cut": "凑四句一切",
"gpt_model_path": "F:\\GPT-SoVITS\\GPT_weights\\ikaros-e15.ckpt",
"sovits_model_path": "F:\\GPT-SoVITS\\SoVITS_weights\\ikaros_e8_s280.pth",
"webtts": {
"api_ip_port": "http://127.0.0.1:8080",
"spk": "sanyueqi",
Expand Down
20 changes: 15 additions & 5 deletions docs/投资人/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_230923093032_QQ%E5%9B%BE%E7%89%8720230923172945.jpg",
amount: "¥468"
},
{
name: "/:)★XSIN",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_240314150105_QQ%E5%9B%BE%E7%89%8720240310195619.jpg",
amount: "¥400"
},
{
name: "Kingsss",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_231201155829_QQ%E5%9B%BE%E7%89%8720231201235446.jpg",
Expand All @@ -86,6 +91,11 @@
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_230725123351_QQ%E5%9B%BE%E7%89%8720230725203325.jpg",
amount: "¥266.6"
},
{
name: "曹某",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_240314140043_image3.png",
amount: "¥266.33"
},
{
name: "真的要多喝热水",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_231122140807_QQ%E5%9B%BE%E7%89%8720231122220722.jpg",
Expand Down Expand Up @@ -146,6 +156,11 @@
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_230722165246_QQ%E5%9B%BE%E7%89%8720230723005230.jpg",
amount: "¥146.98"
},
{
name: "灵吾玄志",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_240303075801_imag2e.png",
amount: "¥135"
},
{
name: "kunfox-",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_230902161301_otaku.jpg",
Expand All @@ -166,11 +181,6 @@
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_230820095537_QQ%E5%9B%BE%E7%89%8720230820175506.jpg",
amount: "¥120"
},
{
name: "灵吾玄志",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_240303075801_imag2e.png",
amount: "¥105"
},
{
name: "一如既往",
avatar: "https://images.cnblogs.com/cnblogs_com/ikaros-521/2328032/o_230719123315_%E4%B8%80%E5%A6%82%E6%97%A2%E5%BE%80.jpg",
Expand Down
Binary file modified docs/投资人/invest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
198 changes: 198 additions & 0 deletions tests/test_gpt_sovits/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import logging, json, aiohttp, os, traceback
import base64
import mimetypes
import websockets
import asyncio

async def gpt_sovits_api(data):


def file_to_data_url(file_path):
# 根据文件扩展名确定 MIME 类型
mime_type, _ = mimetypes.guess_type(file_path)

# 读取文件内容
with open(file_path, "rb") as file:
file_content = file.read()

# 转换为 Base64 编码
base64_encoded_data = base64.b64encode(file_content).decode('utf-8')

# 构造完整的 Data URL
return f"data:{mime_type};base64,{base64_encoded_data}"

async def websocket_client(data_json):
try:
async with websockets.connect(data["ws_ip_port"]) as websocket:
# 设置最大连接时长(例如 30 秒)
return await asyncio.wait_for(websocket_client_logic(websocket, data_json), timeout=30)
except asyncio.TimeoutError:
logging.error("gpt_sovits WebSocket连接超时")
return None

async def websocket_client_logic(websocket, data_json):
async for message in websocket:
logging.debug(f"Received message: {message}")

# 解析收到的消息
data = json.loads(message)
# 检查是否是预期的消息
if "msg" in data:
if data["msg"] == "send_hash":
# 发送响应消息
response = json.dumps({"session_hash":"3obpzfqql7f","fn_index":3})
await websocket.send(response)
logging.debug(f"Sent message: {response}")
elif data["msg"] == "send_data":
# audio_path = "F:\\GPT-SoVITS\\raws\\ikaros\\1.wav"
audio_path = data_json["ref_audio_path"]

# 发送响应消息
response = json.dumps(
{
"session_hash":"3obpzfqql7f",
"fn_index":3,
"data":[
{
"data": file_to_data_url(audio_path),
"name": os.path.basename(audio_path)
},
data_json["prompt_text"],
data_json["prompt_language"],
data_json["content"],
data_json["language"],
data_json["cut"]
]
}
)
await websocket.send(response)
logging.debug(f"Sent message: {response}")
elif data["msg"] == "process_completed":
return data["output"]["data"][0]["name"]

try:
logging.debug(f"data={data}")

if data["type"] == "gradio":
# 调用函数并等待结果
voice_tmp_path = await websocket_client(data)

# if voice_tmp_path:
# new_file_path = self.common.move_file(voice_tmp_path, os.path.join(self.audio_out_path, 'gpt_sovits_' + self.common.get_bj_time(4)), 'gpt_sovits_' + self.common.get_bj_time(4))

new_file_path = 'gpt_sovits_.wav'

return new_file_path
elif data["type"] == "api":
try:
data_json = {
"refer_wav_path": data["ref_audio_path"],
"prompt_text": data["prompt_text"],
"prompt_language": data["prompt_language"],
"text": data["content"],
"text_language": data["language"]
}

async with aiohttp.ClientSession() as session:
async with session.post(data["api_ip_port"], json=data_json, timeout=30) as response:
response = await response.read()

file_name = 'gpt_sovits_.wav'

voice_tmp_path = file_name

# voice_tmp_path = self.common.get_new_audio_path(self.audio_out_path, file_name)

with open(voice_tmp_path, 'wb') as f:
f.write(response)

return voice_tmp_path
except aiohttp.ClientError as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits请求失败: {e}')
except Exception as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits未知错误: {e}')
elif data["type"] == "webtts":
try:
# 使用字典推导式构建 params 字典,只包含非空字符串的值
params = {
key: value
for key, value in data["webtts"].items()
if value != ""
if key != "api_ip_port"
}

# params["speed"] = self.get_random_float(params["speed"])
params["text"] = data["content"]

async with aiohttp.ClientSession() as session:
async with session.get(data["webtts"]["api_ip_port"], params=params, timeout=30) as response:
response = await response.read()

file_name = 'gpt_sovits_.wav'

voice_tmp_path = file_name

# voice_tmp_path = self.common.get_new_audio_path(self.audio_out_path, file_name)

with open(voice_tmp_path, 'wb') as f:
f.write(response)

return voice_tmp_path
except aiohttp.ClientError as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits请求失败: {e}')
except Exception as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits未知错误: {e}')
except Exception as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits未知错误,请检查您的gpt_sovits推理是否启动/配置是否正确,报错内容: {e}')

return None


async def gpt_sovits_set_model(data):
from urllib.parse import urljoin

if data["type"] == "api":
try:
data_json = {
"gpt_model_path": data["gpt_model_path"],
"sovits_model_path": data["sovits_model_path"]
}

API_URL = urljoin(data["api_ip_port"], '/set_model')

async with aiohttp.ClientSession() as session:
async with session.post(API_URL, json=data_json, timeout=30) as response:
response = await response.read()

print(response)

return response
except aiohttp.ClientError as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits请求失败: {e}')
except Exception as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits未知错误: {e}')


if __name__ == '__main__':
# 配置日志输出格式
logging.basicConfig(
level=logging.DEBUG, # 设置日志级别,可以根据需求调整
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

data = {
"type": "api",
"api_ip_port": "http://127.0.0.1:9880",
"gpt_model_path": "F:\GPT-SoVITS\GPT_weights\ikaros-e15.ckpt",
"sovits_model_path": "F:\GPT-SoVITS\SoVITS_weights\ikaros_e8_s280.pth"
}

asyncio.run(gpt_sovits_set_model(data))
17 changes: 11 additions & 6 deletions utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,33 +882,38 @@ def get_all_audio_device_info(self, type):
.@/. =@@@ ,@@@@@@@. =@@@@@@`
"""
def send_request(self, url, method='GET', json_data=None):
def send_request(self, url, method='GET', json_data=None, resp_data_type="json", timeout=60):
"""
发送 HTTP 请求并返回结果
Parameters:
url (str): 请求的 URL
method (str): 请求方法,'GET' 或 'POST'
json_data (dict): JSON 数据,用于 POST 请求
resp_data_type (str): 返回数据的类型(json | content)
timeout (int): 请求超时时间
Returns:
dict: 包含响应的 JSON 数据
dict|str: 包含响应的 JSON数据 | 字符串数据
"""
headers = {'Content-Type': 'application/json'}

try:
if method == 'GET':
response = requests.get(url, headers=headers)
response = requests.get(url, headers=headers, timeout=timeout)
elif method == 'POST':
response = requests.post(url, headers=headers, data=json.dumps(json_data))
response = requests.post(url, headers=headers, data=json.dumps(json_data), timeout=timeout)
else:
raise ValueError('无效 method. 支持的 methods 为 GET 和 POST.')

# 检查请求是否成功
response.raise_for_status()

# 解析响应的 JSON 数据
result = response.json()
if resp_data_type == "json":
# 解析响应的 JSON 数据
result = response.json()
else:
result = response.content

return result

Expand Down
38 changes: 35 additions & 3 deletions webui.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,33 @@ def test_openai_key():
else:
ui.notify(position="top", type="negative", message=f"测试失败!")

# GPT-SoVITS加载模型
def gpt_sovits_set_model():
try:
from urllib.parse import urljoin

API_URL = urljoin(input_gpt_sovits_api_ip_port.value, '/set_model')

data_json = {
"gpt_model_path": input_gpt_sovits_gpt_model_path.value,
"sovits_model_path": input_gpt_sovits_sovits_model_path.value
}

resp_data = common.send_request(API_URL, "POST", data_json, resp_data_type="content")

if resp_data is None:
content = "gpt_sovits加载模型失败,请查看双方日志排查问题"
logging.error(content)
ui.notify(position="top", type="negative", message=content)
else:
content = "gpt_sovits加载模型成功"
logging.info(content)
ui.notify(position="top", type="positive", message=content)
except Exception as e:
logging.error(traceback.format_exc())
logging.error(f'gpt_sovits未知错误: {e}')
ui.notify(position="top", type="negative", message=f'gpt_sovits未知错误: {e}')

# 页面滑到顶部
def scroll_to_top():
# 这段JavaScript代码将页面滚动到顶部
Expand Down Expand Up @@ -3148,18 +3175,18 @@ def clear_tts_common_audio_card(file_path):
input_gpt_sovits_ws_ip_port = ui.input(label='WS地址(gradio)', value=config.get("gpt_sovits", "ws_ip_port"), placeholder='启动TTS推理后,ws的接口地址').style("width:200px;")
input_gpt_sovits_api_ip_port = ui.input(label='API地址(http)', value=config.get("gpt_sovits", "api_ip_port"), placeholder='官方API程序启动后监听的地址').style("width:200px;")
with ui.row():
input_gpt_sovits_ref_audio_path = ui.input(label='参考音频路径', value=config.get("gpt_sovits", "ref_audio_path"), placeholder='参考音频路径,建议填绝对路径').style("width:200px;")
input_gpt_sovits_ref_audio_path = ui.input(label='参考音频路径', value=config.get("gpt_sovits", "ref_audio_path"), placeholder='参考音频路径,建议填绝对路径').style("width:300px;")
input_gpt_sovits_prompt_text = ui.input(label='参考音频的文本', value=config.get("gpt_sovits", "prompt_text"), placeholder='参考音频的文本').style("width:200px;")
select_gpt_sovits_prompt_language = ui.select(
label='参考音频的语种',
options={'中文':'中文', '日文':'日文', '英文':'英文'},
value=config.get("gpt_sovits", "prompt_language")
).style("width:200px;")
).style("width:150px;")
select_gpt_sovits_language = ui.select(
label='需要合成的语种',
options={'自动识别':'自动识别', '中文':'中文', '日文':'日文', '英文':'英文'},
value=config.get("gpt_sovits", "language")
).style("width:200px;")
).style("width:150px;")
select_gpt_sovits_cut = ui.select(
label='语句切分',
options={
Expand All @@ -3172,6 +3199,11 @@ def clear_tts_common_audio_card(file_path):
},
value=config.get("gpt_sovits", "cut")
).style("width:200px;")
with ui.row():
input_gpt_sovits_gpt_model_path = ui.input(label='GPT模型路径', value=config.get("gpt_sovits", "gpt_model_path"), placeholder='GPT模型路径,填绝对路径').style("width:300px;")
input_gpt_sovits_sovits_model_path = ui.input(label='SOVITS模型路径', value=config.get("gpt_sovits", "sovits_model_path"), placeholder='SOVITS模型路径,填绝对路径').style("width:300px;")
button_gpt_sovits_set_model = ui.button('加载模型', on_click=gpt_sovits_set_model, color=button_internal_color).style(button_internal_css)

with ui.card().style(card_css):
ui.label("WebTTS相关配置")
with ui.row():
Expand Down

0 comments on commit 55fbd42

Please sign in to comment.