From b82aaf4867fc45a54801b9759f7d908c2d3416b7 Mon Sep 17 00:00:00 2001 From: HonestQiao Date: Tue, 18 Feb 2025 22:58:35 +0800 Subject: [PATCH] =?UTF-8?q?minimax=E8=AF=AD=E9=9F=B3tts=E5=92=8C=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91=E6=99=BA=E8=83=BD=E8=AF=AD=E9=9F=B3tts?= =?UTF-8?q?=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yaml | 55 +++++++++++++++++++++++++ core/providers/tts/aliyun.py | 57 ++++++++++++++++++++++++++ core/providers/tts/minimax.py | 77 +++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 core/providers/tts/aliyun.py create mode 100644 core/providers/tts/minimax.py diff --git a/config.yaml b/config.yaml index 2e616e13..fd48b7a7 100644 --- a/config.yaml +++ b/config.yaml @@ -217,3 +217,58 @@ TTS: parallel_infer: true repetition_penalty: 1.35 aux_ref_audio_paths: [] + MinimaxTTS: + # Minimax语音合成服务,需要先在minimax平台创建账户充值,并获取登录信息 + # 平台地址:https://platform.minimaxi.com/ + # 充值地址:https://platform.minimaxi.com/user-center/payment/balance + # group_id地址:https://platform.minimaxi.com/user-center/basic-information + # api_key地址:https://platform.minimaxi.com/user-center/basic-information/interface-key + # 定义TTS API类型 + type: minimax + output_file: tmp/ + group_id: 你的minimax平台groupID + api_key: 你的minimax平台接口密钥 + model: "speech-01-turbo" + # 此处设置将优先于voice_setting中voice_id的设置;如都不设置,默认为 female-shaonv + voice_id: "female-shaonv" + # 以下可不用设置,使用默认设置 + # voice_setting: + # voice_id: "male-qn-qingse" + # speed: 1 + # vol: 1 + # pitch: 0 + # emotion: "happy" + # pronunciation_dict: + # tone: + # - "处理/(chu3)(li3)" + # - "危险/dangerous" + # audio_setting: + # sample_rate: 32000 + # bitrate: 128000 + # format: "mp3" + # channel: 1 + # timber_weights: + # - + # voice_id: male-qn-qingse + # weight: 1 + # - + # voice_id: female-shaonv + # weight: 1 + # language_boost: auto + AliyunTTS: + # 阿里云智能语音交互服务,需要先在阿里云平台开通服务,然后获取验证信息 + # 平台地址:https://nls-portal.console.aliyun.com/ + # appkey地址:https://nls-portal.console.aliyun.com/applist + # token地址:https://nls-portal.console.aliyun.com/overview + # 定义TTS API类型 + type: aliyun + output_file: tmp/ + appkey: 你的阿里云智能语音交互服务项目Appkey + token: 你的阿里云智能语音交互服务AccessToken + voice: xiaoyun + # 以下可不用设置,使用默认设置 + # format: wav + # sample_rate: 16000 + # volume: 50 + # speech_rate: 0 + # pitch_rate: 0 \ No newline at end of file diff --git a/core/providers/tts/aliyun.py b/core/providers/tts/aliyun.py new file mode 100644 index 00000000..854e6128 --- /dev/null +++ b/core/providers/tts/aliyun.py @@ -0,0 +1,57 @@ +import os +import uuid +import json +import requests +from datetime import datetime +from core.providers.tts.base import TTSProviderBase + +import http.client +import urllib.parse + + +class TTSProvider(TTSProviderBase): + def __init__(self, config, delete_audio_file): + super().__init__(config, delete_audio_file) + self.appkey = config.get("appkey") + self.token = config.get("token") + self.format = config.get("format", "wav") + self.sample_rate = config.get("sample_rate", 16000) + self.voice = config.get("voice", "xiaoyun") + self.volume = config.get("volume", 50) + self.speech_rate = config.get("speech_rate", 0) + self.pitch_rate = config.get("pitch_rate", 0) + + self.host = config.get("host", "nls-gateway-cn-shanghai.aliyuncs.com") + self.api_url = f"https://{self.host}/stream/v1/tts" + self.header = { + "Content-Type": "application/json" + } + + def generate_filename(self, extension=".wav"): + return os.path.join(self.output_file, f"tts-{__name__}{datetime.now().date()}@{uuid.uuid4().hex}{extension}") + + async def text_to_speak(self, text, output_file): + request_json = { + "appkey": self.appkey, + "token": self.token, + "text": text, + "format": self.format, + "sample_rate": self.sample_rate, + "voice": self.voice, + "volume": self.volume, + "speech_rate": self.speech_rate, + "pitch_rate": self.pitch_rate + } + + print(self.api_url, json.dumps(request_json, ensure_ascii=False)) + try: + resp = requests.post(self.api_url, json.dumps(request_json), headers=self.header) + # 检查返回请求数据的mime类型是否是audio/***,是则保存到指定路径下;返回的是binary格式的 + if resp.headers['Content-Type'].startswith('audio/'): + with open(output_file, 'wb') as f: + f.write(resp.content) + return output_file + else: + raise Exception(f"{__name__} status_code: {resp.status_code} response: {resp.content}") + except Exception as e: + raise Exception(f"{__name__} error: {e}") diff --git a/core/providers/tts/minimax.py b/core/providers/tts/minimax.py new file mode 100644 index 00000000..1fe7be50 --- /dev/null +++ b/core/providers/tts/minimax.py @@ -0,0 +1,77 @@ +import os +import uuid +import json +import requests +from datetime import datetime +from core.providers.tts.base import TTSProviderBase + + +class TTSProvider(TTSProviderBase): + def __init__(self, config, delete_audio_file): + super().__init__(config, delete_audio_file) + self.group_id = config.get("group_id") + self.api_key = config.get("api_key") + self.model = config.get("model") + self.voice_id = config.get("voice_id") + + default_voice_setting = { + "voice_id": "female-shaonv", + "speed": 1, + "vol": 1, + "pitch": 0, + "emotion": "happy" + } + default_pronunciation_dict = { + "tone": [ + "处理/(chu3)(li3)", "危险/dangerous" + ] + } + defult_audio_setting = { + "sample_rate": 32000, + "bitrate": 128000, + "format": "mp3", + "channel": 1 + } + self.voice_setting = {**default_voice_setting, **config.get("voice_setting", {})} + self.pronunciation_dict = {**default_pronunciation_dict, **config.get("pronunciation_dict", {})} + self.audio_setting = {**defult_audio_setting, **config.get("audio_setting", {})} + self.timber_weights = config.get("timber_weights", []) + + if self.voice_id: + self.voice_setting["voice_id"] = self.voice_id + + self.host = "api.minimax.chat" + self.api_url = f"https://{self.host}/v1/t2a_v2?GroupId={self.group_id}" + self.header = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}" + } + + def generate_filename(self, extension=".mp3"): + return os.path.join(self.output_file, f"tts-{__name__}{datetime.now().date()}@{uuid.uuid4().hex}{extension}") + + async def text_to_speak(self, text, output_file): + request_json = { + "model": self.model, + "text": text, + "stream": False, + "voice_setting": self.voice_setting, + "pronunciation_dict": self.pronunciation_dict, + "audio_setting": self.audio_setting, + } + + if type(self.timber_weights) is list and len(self.timber_weights) > 0: + request_json["timber_weights"] = self.timber_weights + request_json["voice_setting"]["voice_id"] = "" + + try: + resp = requests.post(self.api_url, json.dumps(request_json), headers=self.header) + # 检查返回请求数据的status_code是否为0 + if resp.json()["base_resp"]["status_code"] == 0: + data = resp.json()['data']['audio'] + file_to_save = open(output_file, "wb") + file_to_save.write(bytes.fromhex(data)) + else: + raise Exception(f"{__name__} status_code: {resp.status_code} response: {resp.content}") + except Exception as e: + raise Exception(f"{__name__} error: {e}") \ No newline at end of file