add: GSV本地语音合成相关 (#266)

This commit is contained in:
香草味的纳西妲
2025-03-11 21:04:04 +08:00
committed by GitHub
parent 442eef36ba
commit f8ece2d984
2 changed files with 153 additions and 0 deletions
+30
View File
@@ -311,6 +311,36 @@ TTS:
media_type: "wav"
streaming_mode: false
threshold: 30
GSV:
# GSV原神本地语音合成
# 整合包下载:https://pan.ai-hobbyist.com/Infer%20Packs/TTS
# 国内镜像:https://alist.hancat.work/d/cdn/GPT-Sovits%E8%AF%AD%E9%9F%B3%E5%90%88%E6%88%90%E6%95%B4%E5%90%88%E5%8C%85%EF%BC%8C%E6%94%AF%E6%8C%81%E5%A4%9A%E4%BA%BA%E5%AF%B9%E8%AF%9D%E3%80%82%E6%B0%B8%E4%B9%85%E5%85%8D%E8%B4%B9%EF%BC%8C%E4%B8%A5%E7%A6%81%E5%80%92%E5%8D%96%EF%BC%81.7z
# 本TTS需要配合模型来使用,模型可以去这里下载:
# 二次元 & 游戏角色 GPT-Sovits V2 语音合成模型:https://www.modelscope.cn/models/aihobbyist/Anime_GPT-Sovits_Models
# 下载后将其解压到model文件夹
# URL需要修改批处理文件才可生效,默认WebUI端口号为8080,API端口号为WebUI端口号+1
url: "http://127.0.0.1:9881/infer_single"
app_key: ""
audio_dl_url: "http://127.0.0.1:9881"
model_name: "【原神】须弥"
speaker_name: "纳西妲"
prompt_text_lang: "中文"
emocton: "中立_neutral"
text_lang: "中文"
top_k: 10
top_p: 1
temperature: 1
text_split_method: "按标点符号切"
batch_size: 10
batch_size: 0.75
split_bucket: true
speed_facter: 1
fragment_interval: 0.3
media_type: "wav"
parallel_infer: true
repetition_penalty: 1.35
seed: -1
output_file: tmp/
MinimaxTTS:
# Minimax语音合成服务,需要先在minimax平台创建账户充值,并获取登录信息
# 平台地址:https://platform.minimaxi.com/
@@ -0,0 +1,123 @@
import os
import uuid
import requests
from config.logger import setup_logging
from datetime import datetime
from core.providers.tts.base import TTSProviderBase
TAG = __name__
logger = setup_logging()
class TTSProvider(TTSProviderBase):
def __init__(self, config, delete_audio_file):
super().__init__(config, delete_audio_file)
# POST请求地址
self.url = config.get("url")
# 从配置中加载所有参数
self.app_key = config.get("app_key")
self.audio_dl_url = config.get("audio_dl_url")
self.model_name = config.get("model_name")
self.speaker_name = config.get("speaker_name")
self.prompt_text_lang = config.get("prompt_text_lang")
self.emotion = config.get("emotion")
self.text_lang = config.get("text_lang")
self.top_k = config.get("top_k")
self.top_p = config.get("top_p")
self.temperature = config.get("temperature")
self.text_split_method = config.get("text_split_method")
self.batch_size = config.get("batch_size")
self.batch_threshold = config.get("batch_size")
self.split_bucket = config.get("split_bucket")
self.speed_facter = config.get("speed_facter")
self.fragment_interval = config.get("fragment_interval")
self.media_type = config.get("media_type")
self.parallel_infer = config.get("parallel_infer")
self.repetition_penalty = config.get("repetition_penalty")
self.seed = config.get("seed")
def generate_filename(self):
"""根据媒体类型生成文件名"""
extension = f".{self.media_type}"
return os.path.join(
self.output_file,
f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}"
)
async def text_to_speak(self, text, output_file):
"""文本转语音并保存音频文件"""
# 构造请求体
request_body = {
"app_key": self.app_key,
"audio_dl_url": self.audio_dl_url,
"model_name": self.model_name,
"speaker_name": self.speaker_name,
"prompt_text_lang": self.prompt_text_lang,
"emotion": self.emotion,
"text": text,
"text_lang": self.text_lang,
"top_k": self.top_k,
"top_p": self.top_p,
"temperature": self.temperature,
"text_split_method": self.text_split_method,
"batch_size": self.batch_size,
"batch_threshold": self.batch_threshold,
"split_bucket": self.split_bucket,
"speed_facter": self.speed_facter,
"fragment_interval": self.fragment_interval,
"media_type": self.media_type,
"parallel_infer": self.parallel_infer,
"repetition_penalty": self.repetition_penalty,
"seed": self.seed
}
body = {
"app_key": "",
"audio_dl_url": "http://127.0.0.1:9881",
"model_name": "【原神】须弥",
"speaker_name": "纳西妲",
"prompt_text_lang": "中文",
"emotion": "中立_neutral",
"text": text,
"text_lang": "中文",
"top_k": 10,
"top_p": 1,
"temperature": 1,
"text_split_method": "按标点符号切",
"batch_size": 10,
"batch_threshold": 0.75,
"split_bucket": True,
"speed_facter": 1,
"fragment_interval": 0.3,
"media_type": "wav",
"parallel_infer": True,
"repetition_penalty": 1.35,
"seed": -1
}
try:
# 发送POST请求
resp = requests.post(self.url, json=body)
resp.raise_for_status()
# 解析响应
result = resp.json()
if "audio_url" not in result:
logger.bind(tag=TAG).error("响应中缺少音频地址")
return
# 下载音频文件
audio_resp = requests.get(result["audio_url"])
audio_resp.raise_for_status()
# 保存文件
with open(output_file, "wb") as f:
f.write(audio_resp.content)
logger.bind(tag=TAG).info(f"音频文件已保存至: {output_file}")
except requests.exceptions.HTTPError as e:
logger.bind(tag=TAG).error(f"HTTP请求失败: {str(e)}")
except requests.exceptions.JSONDecodeError:
logger.bind(tag=TAG).error("响应解析失败,无效的JSON格式")
except Exception as e:
logger.bind(tag=TAG).error(f"语音合成失败: {str(e)}")