diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 5e5999d9..c4e2c31f 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -1,4 +1,3 @@ - import asyncio import json import base64 @@ -9,6 +8,7 @@ import wave import websockets from core.providers.tts.base import TTSProviderBase from config.logger import setup_logging +from datetime import datetime TAG = __name__ logger = setup_logging() @@ -19,11 +19,12 @@ class TTSProvider(TTSProviderBase): super().__init__(config, delete_audio_file) self.url = config.get("url", "ws://192.168.1.10:8092/paddlespeech/tts/streaming") self.protocol = config.get("protocol", "websocket") + if config.get("private_voice"): self.spk_id = int(config.get("private_voice")) else: - self.spk_id = int(config.get("spk_id", "0")) - + self.spk_id = int(config.get("spk_id", "0")) + sample_rate = config.get("sample_rate", 24000) self.sample_rate = float(sample_rate) if sample_rate else 24000 @@ -33,7 +34,12 @@ class TTSProvider(TTSProviderBase): volume = config.get("volume", 1.0) self.volume = float(volume) if volume else 1.0 - self.save_path = config.get("save_path", "None") + self.delete_audio_file = config.get("delete_audio_file", True) + if not self.delete_audio_file: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + self.save_path = config.get("save_path", f"./streaming_tts_{timestamp}.wav") + else: + self.save_path = None async def pcm_to_wav(self, pcm_data: bytes, sample_rate: int = 24000, num_channels: int = 1, bits_per_sample: int = 16) -> bytes: @@ -56,22 +62,22 @@ class TTSProvider(TTSProviderBase): return wav_io.getvalue() - async def text_to_speak(self, text, output_file=None): + async def text_to_speak(self, text, output_file): if self.protocol == "websocket": - return await self.text_streaming(text) + return await self.text_streaming(text, output_file) elif self.protocol == "http": - return await self.text(text) + return await self.text(text, output_file) else: raise ValueError("Unsupported protocol. Please use 'websocket' or 'http'.") - async def text(self, text): + async def text(self, text, output_file): request_json = { "text": text, "spk_id": self.spk_id, "speed": self.speed, "volume": self.volume, "sample_rate": self.sample_rate, - "save_path": None, + "save_path": self.save_path } try: @@ -82,7 +88,11 @@ class TTSProvider(TTSProviderBase): if resp_json.get("success"): data = resp_json["result"] audio_bytes = base64.b64decode(data["audio"]) - return audio_bytes + if output_file: + with open(output_file, "wb") as file_to_save: + file_to_save.write(audio_bytes) + else: + return audio_bytes else: raise Exception( f"Error: {resp_json.get('message', 'Unknown error')} while processing text: {text}") @@ -92,7 +102,7 @@ class TTSProvider(TTSProviderBase): except Exception as e: raise Exception(f"Error during TTS HTTP request: {e} while processing text: {text}") - async def text_streaming(self, text): + async def text_streaming(self, text, output_file): try: # 使用 websockets 异步连接到 WebSocket 服务器 async with websockets.connect(self.url) as ws: @@ -148,8 +158,18 @@ class TTSProvider(TTSProviderBase): # 接收结束响应避免服务抛出异常 await ws.recv() - # 直接返回音频数据 - return wav_data + # 根据配置决定是否保存文件 + if not self.delete_audio_file and self.save_path: + with open(self.save_path, "wb") as f: + f.write(wav_data) + logger.bind(tag=TAG).info(f"音频文件已保存到: {self.save_path}") + + # 返回或保存音频数据 + if output_file: + with open(output_file, "wb") as file_to_save: + file_to_save.write(wav_data) + else: + return wav_data except Exception as e: - raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") + raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") \ No newline at end of file