From 344006fb07254eccf4e5c80d1fed1967b774da33 Mon Sep 17 00:00:00 2001 From: ikaros <327209194@qq.com> Date: Wed, 13 Mar 2024 00:17:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=20fish-speech?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.json | 34 ++++++++++- config.json.bak | 34 ++++++++++- tests/test_fish_speech/1.wav | Bin 0 -> 41004 bytes tests/test_fish_speech/api.py | 106 ++++++++++++++++++++++++++++++++++ utils/audio.py | 13 +++++ utils/audio_handle/my_tts.py | 65 ++++++++++++++++++++- webui.py | 67 ++++++++++++++++++++- 7 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 tests/test_fish_speech/1.wav create mode 100644 tests/test_fish_speech/api.py diff --git a/config.json b/config.json index 9eb7e1cc..9c767fc6 100644 --- a/config.json +++ b/config.json @@ -569,6 +569,37 @@ "region": "japanwest", "voice_name": "zh-CN-XiaoyanNeural" }, + "fish_speech": { + "api_ip_port": "http://127.0.0.1:8000", + "model_name": "default", + "model_config": { + "device": "cuda", + "llama": { + "config_name": "text2semantic_finetune", + "checkpoint_path": "checkpoints/text2semantic-400m-v0.2-4k.pth", + "precision": "bfloat16", + "tokenizer": "fishaudio/speech-lm-v1", + "compile": true + }, + "vqgan": { + "config_name": "vqgan_pretrain", + "checkpoint_path": "checkpoints/vqgan-v1.pth" + } + }, + "tts_config": { + "prompt_text": "", + "prompt_tokens": "", + "max_new_tokens": 0, + "top_k": 3, + "top_p": 0.5, + "repetition_penalty": 1.5, + "temperature": 0.7, + "order": "zh,jp,en", + "use_g2p": true, + "seed": 1, + "speaker": "" + } + }, "choose_song": { "enable": true, "similarity": 0.5, @@ -1324,7 +1355,8 @@ "gradio_tts": true, "gpt_sovits": true, "clone_voice": true, - "azure_tts": true + "azure_tts": true, + "fish_speech": true }, "svc": { "ddsp_svc": true, diff --git a/config.json.bak b/config.json.bak index 9eb7e1cc..9c767fc6 100644 --- a/config.json.bak +++ b/config.json.bak @@ -569,6 +569,37 @@ "region": "japanwest", "voice_name": "zh-CN-XiaoyanNeural" }, + "fish_speech": { + "api_ip_port": "http://127.0.0.1:8000", + "model_name": "default", + "model_config": { + "device": "cuda", + "llama": { + "config_name": "text2semantic_finetune", + "checkpoint_path": "checkpoints/text2semantic-400m-v0.2-4k.pth", + "precision": "bfloat16", + "tokenizer": "fishaudio/speech-lm-v1", + "compile": true + }, + "vqgan": { + "config_name": "vqgan_pretrain", + "checkpoint_path": "checkpoints/vqgan-v1.pth" + } + }, + "tts_config": { + "prompt_text": "", + "prompt_tokens": "", + "max_new_tokens": 0, + "top_k": 3, + "top_p": 0.5, + "repetition_penalty": 1.5, + "temperature": 0.7, + "order": "zh,jp,en", + "use_g2p": true, + "seed": 1, + "speaker": "" + } + }, "choose_song": { "enable": true, "similarity": 0.5, @@ -1324,7 +1355,8 @@ "gradio_tts": true, "gpt_sovits": true, "clone_voice": true, - "azure_tts": true + "azure_tts": true, + "fish_speech": true }, "svc": { "ddsp_svc": true, diff --git a/tests/test_fish_speech/1.wav b/tests/test_fish_speech/1.wav new file mode 100644 index 0000000000000000000000000000000000000000..ee4eeb0fa3ab00af9dbe755a40e21781f846e5af GIT binary patch literal 41004 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 r{UA<>nPrXrjG6Y0-4iqBV%kp)Yx`-w! literal 0 HcmV?d00001 diff --git a/tests/test_fish_speech/api.py b/tests/test_fish_speech/api.py new file mode 100644 index 00000000..b166ca9c --- /dev/null +++ b/tests/test_fish_speech/api.py @@ -0,0 +1,106 @@ +import json, logging +import aiohttp, asyncio +from urllib.parse import urljoin + +async def fish_speech_load_model(data): + API_URL = urljoin(data["api_ip_port"], f'/v1/models/{data["model_name"]}') + + try: + async with aiohttp.ClientSession() as session: + async with session.put(API_URL, json=data["model_config"]) as response: + if response.status == 200: + ret = await response.json() + print(ret) + + if ret["name"] == data["model_name"]: + print(f'fish_speech模型加载成功: {ret["name"]}') + return ret + else: + return None + + except aiohttp.ClientError as e: + print(f'fish_speech请求失败: {e}') + except Exception as e: + print(f'fish_speech未知错误: {e}') + + return None + +async def fish_speech_api(data): + API_URL = urljoin(data["api_ip_port"], f'/v1/models/{data["model_name"]}/invoke') + + print(f"data={data}") + + def replace_empty_strings_with_none(input_dict): + for key, value in input_dict.items(): + if value == "": + input_dict[key] = None + return input_dict + + data["tts_config"] = replace_empty_strings_with_none(data["tts_config"]) + + print(f"data={data}") + + try: + async with aiohttp.ClientSession() as session: + async with session.post(API_URL, json=data["tts_config"]) as response: + if response.status == 200: + content = await response.read() + + # voice_tmp_path = os.path.join(self.audio_out_path, 'reecho_ai_' + self.common.get_bj_time(4) + '.wav') + # file_name = 'fish_speech_' + self.common.get_bj_time(4) + '.wav' + + # voice_tmp_path = self.common.get_new_audio_path(self.audio_out_path, file_name) + voice_tmp_path = "1.wav" + with open(voice_tmp_path, 'wb') as file: + file.write(content) + + return voice_tmp_path + else: + print(f'fish_speech下载音频失败: {response.status}') + return None + except aiohttp.ClientError as e: + print(f'fish_speech请求失败: {e}') + except Exception as e: + print(f'fish_speech未知错误: {e}') + + return None + + +data = { + "fish_speech": { + "api_ip_port": "http://127.0.0.1:8000", + "model_name": "default", + "model_config": { + "device": "cuda", + "llama": { + "config_name": "text2semantic_finetune", + "checkpoint_path": "checkpoints/text2semantic-400m-v0.2-4k.pth", + "precision": "bfloat16", + "tokenizer": "fishaudio/speech-lm-v1", + "compile": True + }, + "vqgan": { + "config_name": "vqgan_pretrain", + "checkpoint_path": "checkpoints/vqgan-v1.pth" + } + }, + "tts_config": { + "prompt_text": "", + "prompt_tokens": "", + "max_new_tokens": 0, + "top_k": 3, + "top_p": 0.5, + "repetition_penalty": 1.5, + "temperature": 0.7, + "order": "zh,jp,en", + "use_g2p": True, + "seed": 1, + "speaker": "" + } + } +} + +asyncio.run(fish_speech_load_model(data["fish_speech"])) + +data["fish_speech"]["tts_config"]["text"] = "你好" +asyncio.run(fish_speech_api(data["fish_speech"])) \ No newline at end of file diff --git a/utils/audio.py b/utils/audio.py index 26089565..c7fc47f2 100644 --- a/utils/audio.py +++ b/utils/audio.py @@ -863,6 +863,11 @@ async def voice_change_and_put_to_queue(message, voice_tmp_path): } voice_tmp_path = self.my_tts.azure_tts_api(data) + elif message["tts_type"] == "fish_speech": + data = message["data"] + data["tts_config"]["text"] = message["content"] + + voice_tmp_path = await self.my_tts.fish_speech_api(data) elif message["tts_type"] == "none": pass except Exception as e: @@ -1596,6 +1601,14 @@ async def audio_synthesis_use_local_config(self, content, audio_synthesis_type=" logging.debug(f"data={data}") voice_tmp_path = self.my_tts.azure_tts_api(data) + elif audio_synthesis_type == "fish_speech": + data = self.config.get("fish_speech") + data["tts_config"]["text"] = content + + logging.debug(f"data={data}") + + voice_tmp_path = await self.my_tts.fish_speech_api(data) + return voice_tmp_path diff --git a/utils/audio_handle/my_tts.py b/utils/audio_handle/my_tts.py index 0ba0f14a..5c88ba9c 100644 --- a/utils/audio_handle/my_tts.py +++ b/utils/audio_handle/my_tts.py @@ -746,4 +746,67 @@ def azure_tts_api(self, data): logging.error(traceback.format_exc()) logging.error(f'azure_tts未知错误: {e}') - return None \ No newline at end of file + return None + + + async def fish_speech_load_model(self, data): + API_URL = urljoin(data["api_ip_port"], f'/v1/models/{data["model_name"]}') + + try: + async with aiohttp.ClientSession() as session: + async with session.put(API_URL, json=data["model_config"]) as response: + if response.status == 200: + ret = await response.json() + logging.debug(ret) + + if ret["name"] == data["model_name"]: + logging.info(f'fish_speech模型加载成功: {ret["name"]}') + return ret + else: + return None + + except aiohttp.ClientError as e: + logging.error(f'fish_speech请求失败: {e}') + except Exception as e: + logging.error(f'fish_speech未知错误: {e}') + + return None + + async def fish_speech_api(self, data): + API_URL = urljoin(data["api_ip_port"], f'/v1/models/{data["model_name"]}/invoke') + + def replace_empty_strings_with_none(input_dict): + for key, value in input_dict.items(): + if value == "": + input_dict[key] = None + return input_dict + + data["tts_config"] = replace_empty_strings_with_none(data["tts_config"]) + + logging.debug(f"data={data}") + + try: + async with aiohttp.ClientSession() as session: + async with session.post(API_URL, json=data["tts_config"]) as response: + if response.status == 200: + content = await response.read() + + voice_tmp_path = os.path.join(self.audio_out_path, 'fish_speech_' + self.common.get_bj_time(4) + '.wav') + file_name = 'fish_speech_' + self.common.get_bj_time(4) + '.wav' + + voice_tmp_path = self.common.get_new_audio_path(self.audio_out_path, file_name) + + with open(voice_tmp_path, 'wb') as file: + file.write(content) + + return voice_tmp_path + else: + logging.error(f'fish_speech下载音频失败: {response.status}') + return None + except aiohttp.ClientError as e: + logging.error(f'fish_speech请求失败: {e}') + except Exception as e: + logging.error(f'fish_speech未知错误: {e}') + + return None + diff --git a/webui.py b/webui.py index 7b3dd81b..2d47a20f 100644 --- a/webui.py +++ b/webui.py @@ -1376,6 +1376,29 @@ def common_textarea_handle(content): config_data["azure_tts"]["region"] = input_azure_tts_region.value config_data["azure_tts"]["voice_name"] = input_azure_tts_voice_name.value + if config.get("webui", "show_card", "tts", "fish_speech"): + config_data["fish_speech"]["api_ip_port"] = input_fish_speech_api_ip_port.value + config_data["fish_speech"]["model_name"] = input_fish_speech_model_name.value + config_data["fish_speech"]["model_config"]["device"] = input_fish_speech_model_config_device.value + config_data["fish_speech"]["model_config"]["llama"]["config_name"] = input_fish_speech_model_config_llama_config_name.value + config_data["fish_speech"]["model_config"]["llama"]["checkpoint_path"] = input_fish_speech_model_config_llama_checkpoint_path.value + config_data["fish_speech"]["model_config"]["llama"]["precision"] = input_fish_speech_model_config_llama_precision.value + config_data["fish_speech"]["model_config"]["llama"]["tokenizer"] = input_fish_speech_model_config_llama_tokenizer.value + config_data["fish_speech"]["model_config"]["llama"]["compile"] = switch_fish_speech_model_config_llama_compile.value + config_data["fish_speech"]["model_config"]["vqgan"]["config_name"] = input_fish_speech_model_config_vqgan_config_name.value + config_data["fish_speech"]["model_config"]["vqgan"]["checkpoint_path"] = input_fish_speech_model_config_vqgan_checkpoint_path.value + config_data["fish_speech"]["tts_config"]["prompt_text"] = input_fish_speech_tts_config_prompt_text.value + config_data["fish_speech"]["tts_config"]["prompt_tokens"] = input_fish_speech_tts_config_prompt_tokens.value + config_data["fish_speech"]["tts_config"]["max_new_tokens"] = int(input_fish_speech_tts_config_max_new_tokens.value) + config_data["fish_speech"]["tts_config"]["top_k"] = int(input_fish_speech_tts_config_top_k.value) + config_data["fish_speech"]["tts_config"]["top_p"] = round(float(input_fish_speech_tts_config_top_p.value), 2) + config_data["fish_speech"]["tts_config"]["repetition_penalty"] = round(float(input_fish_speech_tts_config_repetition_penalty.value), 2) + config_data["fish_speech"]["tts_config"]["temperature"] = round(float(input_fish_speech_tts_config_temperature.value), 2) + config_data["fish_speech"]["tts_config"]["order"] = input_fish_speech_tts_config_order.value + config_data["fish_speech"]["tts_config"]["seed"] = int(input_fish_speech_tts_config_seed.value) + config_data["fish_speech"]["tts_config"]["speaker"] = input_fish_speech_tts_config_speaker.value + config_data["fish_speech"]["tts_config"]["use_g2p"] = switch_fish_speech_tts_config_use_g2p.value + """ SVC """ @@ -1657,6 +1680,7 @@ def common_textarea_handle(content): config_data["webui"]["show_card"]["tts"]["gpt_sovits"] = switch_webui_show_card_tts_gpt_sovits.value config_data["webui"]["show_card"]["tts"]["clone_voice"] = switch_webui_show_card_tts_clone_voice.value config_data["webui"]["show_card"]["tts"]["azure_tts"] = switch_webui_show_card_tts_azure_tts.value + config_data["webui"]["show_card"]["tts"]["fish_speech"] = switch_webui_show_card_tts_fish_speech.value config_data["webui"]["show_card"]["svc"]["ddsp_svc"] = switch_webui_show_card_svc_ddsp_svc.value config_data["webui"]["show_card"]["svc"]["so_vits_svc"] = switch_webui_show_card_svc_so_vits_svc.value @@ -1745,7 +1769,8 @@ def common_textarea_handle(content): 'gradio_tts': 'Gradio', 'gpt_sovits': 'GPT_SoVITS', 'clone_voice': 'clone-voice', - 'azure_tts': 'azure_tts' + 'azure_tts': 'azure_tts', + 'fish_speech': 'fish_speech' } # 聊天类型所有配置项 @@ -3191,7 +3216,43 @@ def clear_tts_common_audio_card(file_path): input_azure_tts_subscription_key = ui.input(label='密钥', value=config.get("azure_tts", "subscription_key"), placeholder='申请开通服务后,自然就看见了').style("width:200px;") input_azure_tts_region = ui.input(label='区域', value=config.get("azure_tts", "region"), placeholder='申请开通服务后,自然就看见了').style("width:200px;") input_azure_tts_voice_name = ui.input(label='说话人名', value=config.get("azure_tts", "voice_name"), placeholder='Speech Studio平台试听获取说话人名').style("width:200px;") - + + if config.get("webui", "show_card", "tts", "fish_speech"): + with ui.card().style(card_css): + ui.label("fish_speech") + with ui.row(): + input_fish_speech_api_ip_port = ui.input(label='API地址', value=config.get("fish_speech", "api_ip_port"), placeholder='程序启动后监听的地址').style("width:200px;") + input_fish_speech_model_name = ui.input(label='模型名', value=config.get("fish_speech", "model_name"), placeholder='需要加载的模型名').style("width:200px;") + + with ui.card().style(card_css): + ui.label("模型配置") + with ui.row(): + input_fish_speech_model_config_device = ui.input(label='device', value=config.get("fish_speech", "model_config", "device"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_model_config_llama_config_name = ui.input(label='config_name', value=config.get("fish_speech", "model_config", "llama", "config_name"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_model_config_llama_checkpoint_path = ui.input(label='checkpoint_path', value=config.get("fish_speech", "model_config", "llama", "checkpoint_path"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_model_config_llama_precision = ui.input(label='precision', value=config.get("fish_speech", "model_config", "llama", "precision"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_model_config_llama_tokenizer = ui.input(label='tokenizer', value=config.get("fish_speech", "model_config", "llama", "tokenizer"), placeholder='自行查阅').style("width:200px;") + switch_fish_speech_model_config_llama_compile = ui.switch('compile', value=config.get("fish_speech", "model_config", "llama", "compile")).style(switch_internal_css) + + input_fish_speech_model_config_vqgan_config_name = ui.input(label='config_name', value=config.get("fish_speech", "model_config", "vqgan", "config_name"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_model_config_vqgan_checkpoint_path = ui.input(label='checkpoint_path', value=config.get("fish_speech", "model_config", "vqgan", "checkpoint_path"), placeholder='自行查阅').style("width:200px;") + + with ui.card().style(card_css): + ui.label("TTS配置") + with ui.row(): + input_fish_speech_tts_config_prompt_text = ui.input(label='prompt_text', value=config.get("fish_speech", "tts_config", "prompt_text"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_prompt_tokens = ui.input(label='prompt_tokens', value=config.get("fish_speech", "tts_config", "prompt_tokens"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_max_new_tokens = ui.input(label='max_new_tokens', value=config.get("fish_speech", "tts_config", "max_new_tokens"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_top_k = ui.input(label='top_k', value=config.get("fish_speech", "tts_config", "top_k"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_top_p = ui.input(label='top_p', value=config.get("fish_speech", "tts_config", "top_p"), placeholder='自行查阅').style("width:200px;") + with ui.row(): + input_fish_speech_tts_config_repetition_penalty = ui.input(label='repetition_penalty', value=config.get("fish_speech", "tts_config", "repetition_penalty"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_temperature = ui.input(label='temperature', value=config.get("fish_speech", "tts_config", "temperature"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_order = ui.input(label='order', value=config.get("fish_speech", "tts_config", "order"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_seed = ui.input(label='seed', value=config.get("fish_speech", "tts_config", "seed"), placeholder='自行查阅').style("width:200px;") + input_fish_speech_tts_config_speaker = ui.input(label='speaker', value=config.get("fish_speech", "tts_config", "speaker"), placeholder='自行查阅').style("width:200px;") + switch_fish_speech_tts_config_use_g2p = ui.switch('use_g2p', value=config.get("fish_speech", "tts_config", "use_g2p")).style(switch_internal_css) + with ui.tab_panel(svc_page).style(tab_panel_css): if config.get("webui", "show_card", "svc", "ddsp_svc"): with ui.card().style(card_css): @@ -3791,7 +3852,7 @@ def update_echart_gift(): switch_webui_show_card_tts_gpt_sovits = ui.switch('gpt_sovits', value=config.get("webui", "show_card", "tts", "gpt_sovits")).style(switch_internal_css) switch_webui_show_card_tts_clone_voice = ui.switch('clone_voice', value=config.get("webui", "show_card", "tts", "clone_voice")).style(switch_internal_css) switch_webui_show_card_tts_azure_tts = ui.switch('azure_tts', value=config.get("webui", "show_card", "tts", "azure_tts")).style(switch_internal_css) - + switch_webui_show_card_tts_fish_speech = ui.switch('fish_speech', value=config.get("webui", "show_card", "tts", "fish_speech")).style(switch_internal_css) with ui.card().style(card_css): ui.label("变声") with ui.row():