From ae228f3b8616b463c5b7e15a977e29961def1ffe Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 10:33:38 +0800 Subject: [PATCH 1/5] =?UTF-8?q?update=EF=BC=9A=E6=9B=B4=E6=96=B0paddlespee?= =?UTF-8?q?chtts=E4=BE=9B=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/paddlespeech-deploy.md | 1 - main/xiaozhi-server/config.yaml | 1 - .../core/providers/tts/paddle_speech.py | 29 +++++++------------ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/docs/paddlespeech-deploy.md b/docs/paddlespeech-deploy.md index 6584efff..6639a1b4 100644 --- a/docs/paddlespeech-deploy.md +++ b/docs/paddlespeech-deploy.md @@ -75,7 +75,6 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 - save_path: ./streaming_tts.wav # 服务器生成的语音文件保存路径 ``` ### 3.启动xiaozhi服务 ```py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 51b6a617..01e51c35 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -895,7 +895,6 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 - save_path: ./streaming_tts.wav # 服务器生成的语音文件保存路径 IndexStreamTTS: # 基于Index-TTS-vLLM项目的TTS接口服务 # 参照教程:https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 7f1c6a23..5e5999d9 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -1,3 +1,4 @@ + import asyncio import json import base64 @@ -32,7 +33,7 @@ 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", "./streaming_tts.wav") + self.save_path = config.get("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: @@ -55,22 +56,22 @@ class TTSProvider(TTSProviderBase): return wav_io.getvalue() - async def text_to_speak(self, text, output_file): + async def text_to_speak(self, text, output_file=None): if self.protocol == "websocket": - return await self.text_streaming(text, output_file) + return await self.text_streaming(text) elif self.protocol == "http": - return await self.text(text, output_file) + return await self.text(text) else: raise ValueError("Unsupported protocol. Please use 'websocket' or 'http'.") - async def text(self, text, output_file): + async def text(self, text): request_json = { "text": text, "spk_id": self.spk_id, "speed": self.speed, "volume": self.volume, "sample_rate": self.sample_rate, - "save_path": self.save_path + "save_path": None, } try: @@ -81,11 +82,7 @@ class TTSProvider(TTSProviderBase): if resp_json.get("success"): data = resp_json["result"] audio_bytes = base64.b64decode(data["audio"]) - if output_file: - with open(output_file, "wb") as file_to_save: - file_to_save.write(audio_bytes) - else: - return audio_bytes + return audio_bytes else: raise Exception( f"Error: {resp_json.get('message', 'Unknown error')} while processing text: {text}") @@ -95,7 +92,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, output_file): + async def text_streaming(self, text): try: # 使用 websockets 异步连接到 WebSocket 服务器 async with websockets.connect(self.url) as ws: @@ -151,12 +148,8 @@ class TTSProvider(TTSProviderBase): # 接收结束响应避免服务抛出异常 await ws.recv() - # 返回或保存音频数据 - if output_file: - with open(output_file, "wb") as file_to_save: - file_to_save.write(wav_data) - else: - return wav_data + # 直接返回音频数据 + return wav_data except Exception as e: raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") From 15291bfcf2dd1d71a6f171d562d9563cca18761e Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:02:57 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0paddlespeechtts=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/tts/paddle_speech.py | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) 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 From 9af77d0f473d040483ead8d6588dc4529d99db72 Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:24:58 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0paddlespeechtts=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/providers/tts/paddle_speech.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index c4e2c31f..18677705 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -34,7 +34,7 @@ class TTSProvider(TTSProviderBase): volume = config.get("volume", 1.0) self.volume = float(volume) if volume else 1.0 - self.delete_audio_file = config.get("delete_audio_file", True) + self.delete_audio_file = config.get("delete_audio", 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") From e53ba28c652914c76adf10607542de907d8bd3ac Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:47:52 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0paddlespeechtts=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/tts/paddle_speech.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 18677705..4b987b71 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -37,7 +37,16 @@ class TTSProvider(TTSProviderBase): self.delete_audio_file = config.get("delete_audio", 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") + save_path = config.get("save_path") + if save_path: + if not save_path.endswith('.wav'): + save_path = f"{save_path}_{timestamp}.wav" + else: + other_path = save_path[:-4] + save_path = f"{other_path}_{timestamp}.wav" + self.save_path = save_path + else: + self.save_path = f"./streaming_tts_{timestamp}.wav" else: self.save_path = None From 880b6698770c15cacb6ad67d1a5cc980493295d4 Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:49:08 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/paddlespeech-deploy.md | 1 + main/xiaozhi-server/config.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/paddlespeech-deploy.md b/docs/paddlespeech-deploy.md index 6639a1b4..17030fab 100644 --- a/docs/paddlespeech-deploy.md +++ b/docs/paddlespeech-deploy.md @@ -75,6 +75,7 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 + save_path: # 保存路径 ``` ### 3.启动xiaozhi服务 ```py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 01e51c35..a75a607f 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -895,6 +895,7 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 + save_path: # 保存路径 IndexStreamTTS: # 基于Index-TTS-vLLM项目的TTS接口服务 # 参照教程:https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md