Files
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/ttson.py
T

100 lines
3.6 KiB
Python
Raw Normal View History

+2
2025-03-07 18:25:18 +08:00
import os
import uuid
import json
import requests
import shutil
from datetime import datetime
from pydub import AudioSegment
+2
2025-03-07 18:25:18 +08:00
from core.providers.tts.base import TTSProviderBase
from core.providers.tts.dto.dto import TTSMessageDTO, MsgType, SentenceType
+2
2025-03-07 18:25:18 +08:00
class TTSProvider(TTSProviderBase):
def __init__(self, config, delete_audio_file):
super().__init__(config, delete_audio_file)
2025-05-21 14:52:24 +08:00
self.url = config.get(
"url",
"https://u95167-bd74-2aef8085.westx.seetacloud.com:8443/flashsummary/tts?token=",
)
if config.get("private_voice"):
self.voice_id = int(config.get("private_voice"))
else:
self.voice_id = int(config.get("voice_id", 1695))
+2
2025-03-07 18:25:18 +08:00
self.token = config.get("token")
self.to_lang = config.get("to_lang")
2025-05-21 14:52:24 +08:00
self.volume_change_dB = int(config.get("volume_change_dB", 0))
self.speed_factor = int(config.get("speed_factor", 1))
self.stream = str(config.get("stream", False)).lower() in ("true", "1", "yes")
2025-03-18 13:25:34 +08:00
self.output_file = config.get("output_dir")
2025-05-21 14:52:24 +08:00
self.pitch_factor = int(config.get("pitch_factor", 0))
+2
2025-03-07 18:25:18 +08:00
self.format = config.get("format", "mp3")
2025-05-21 14:52:24 +08:00
self.emotion = int(config.get("emotion", 1))
self.header = {"Content-Type": "application/json"}
+2
2025-03-07 18:25:18 +08:00
def generate_filename(self, extension=".mp3"):
2025-05-21 14:52:24 +08:00
return os.path.join(
self.output_file,
f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}",
)
+2
2025-03-07 18:25:18 +08:00
async def text_to_speak(self, u_id, text, is_last_text=False, is_first_text=False):
tmp_file = self.generate_filename()
2025-05-21 14:52:24 +08:00
url = f"{self.url}{self.token}"
+2
2025-03-07 18:25:18 +08:00
result = "firefly"
2025-05-21 14:52:24 +08:00
payload = json.dumps(
{
"to_lang": self.to_lang,
"text": text,
"emotion": self.emotion,
"format": self.format,
"volume_change_dB": self.volume_change_dB,
"voice_id": self.voice_id,
"pitch_factor": self.pitch_factor,
"speed_factor": self.speed_factor,
"token": self.token,
}
)
+2
2025-03-07 18:25:18 +08:00
resp = requests.request("POST", url, data=payload)
if resp.status_code != 200:
return
+2
2025-03-07 18:25:18 +08:00
resp_json = resp.json()
try:
2025-05-21 14:52:24 +08:00
result = (
resp_json["url"]
+ ":"
+ str(resp_json["port"])
+ "/flashsummary/retrieveFileData?stream=True&token="
+ self.token
+ "&voice_audio_path="
+ resp_json["voice_path"]
)
+2
2025-03-07 18:25:18 +08:00
except Exception as e:
print("error:", e)
audio_content = requests.get(result)
with open(tmp_file, "wb") as f:
+2
2025-03-07 18:25:18 +08:00
f.write(audio_content.content)
# 使用 pydub 读取临时文件
audio = AudioSegment.from_file(tmp_file, format="mp3")
audio = audio.set_channels(1).set_frame_rate(16000)
opus_datas = self.wav_to_opus_data_audio_raw(audio.raw_data)
2025-05-21 14:52:24 +08:00
yield TTSMessageDTO(
u_id=u_id,
msg_type=MsgType.TTS_TEXT_RESPONSE,
content=opus_datas,
tts_finish_text=text,
sentence_type=SentenceType.SENTENCE_START,
)
# 用完后删除临时文件
try:
os.remove(tmp_file)
except FileNotFoundError:
# 若文件不存在,忽略该异常
pass
+2
2025-03-07 18:25:18 +08:00
voice_path = resp_json.get("voice_path")
des_path = tmp_file
+2
2025-03-07 18:25:18 +08:00
shutil.move(voice_path, des_path)