diff --git a/main/xiaozhi-server/core/providers/tts/alibl_stream.py b/main/xiaozhi-server/core/providers/tts/alibl_stream.py index c636d0cb..8974124c 100644 --- a/main/xiaozhi-server/core/providers/tts/alibl_stream.py +++ b/main/xiaozhi-server/core/providers/tts/alibl_stream.py @@ -6,6 +6,7 @@ import queue import asyncio import traceback import websockets +from typing import Callable, Any from asyncio import Task from config.logger import setup_logging from core.utils import opus_encoder_utils @@ -383,6 +384,24 @@ class TTSProvider(TTSProviderBase): finally: self._monitor_task = None + def audio_to_opus_data_stream( + self, audio_file_path, callback: Callable[[Any], Any] = None + ): + """重写父类方法:使用独立的临时编码器处理音频文件,避免与TTS流式编码器并发冲突。 + 双流式TTS中,monitor任务在event loop线程接收TTS音频并使用self.opus_encoder编码, + 同时tts_text_priority_thread处理音乐文件也使用self.opus_encoder, + 共享的encoder.buffer非线程安全,并发访问会导致SILK resampler断言失败。 + """ + from core.utils.util import audio_to_data_stream + + return audio_to_data_stream( + audio_file_path, + is_opus=True, + callback=callback, + sample_rate=self.conn.sample_rate, + opus_encoder=None, + ) + def to_tts(self, text: str) -> list: """非流式生成音频数据,用于生成音频及测试场景""" try: diff --git a/main/xiaozhi-server/core/providers/tts/aliyun_stream.py b/main/xiaozhi-server/core/providers/tts/aliyun_stream.py index 6026eafa..0a09c65e 100644 --- a/main/xiaozhi-server/core/providers/tts/aliyun_stream.py +++ b/main/xiaozhi-server/core/providers/tts/aliyun_stream.py @@ -8,6 +8,7 @@ import time import queue import asyncio import traceback +from typing import Callable, Any from asyncio import Task import websockets import os @@ -463,6 +464,24 @@ class TTSProvider(TTSProviderBase): finally: self._monitor_task = None + def audio_to_opus_data_stream( + self, audio_file_path, callback: Callable[[Any], Any] = None + ): + """重写父类方法:使用独立的临时编码器处理音频文件,避免与TTS流式编码器并发冲突。 + 双流式TTS中,monitor任务在event loop线程接收TTS音频并使用self.opus_encoder编码, + 同时tts_text_priority_thread处理音乐文件也使用self.opus_encoder, + 共享的encoder.buffer非线程安全,并发访问会导致SILK resampler断言失败。 + """ + from core.utils.util import audio_to_data_stream + + return audio_to_data_stream( + audio_file_path, + is_opus=True, + callback=callback, + sample_rate=self.conn.sample_rate, + opus_encoder=None, + ) + def to_tts(self, text: str) -> list: """非流式TTS处理,用于测试及保存音频文件的场景""" try: diff --git a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py index b1ba1bed..eefee383 100644 --- a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py +++ b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py @@ -683,6 +683,20 @@ class TTSProvider(TTSProviderBase): ) ) + def audio_to_opus_data_stream( + self, audio_file_path, callback: Callable[[Any], Any] = None + ): + """重写父类方法:使用独立的临时编码器处理音频文件,避免与TTS流式编码器并发冲突。 + 双流式TTS中,monitor任务在event loop线程接收TTS音频并使用self.opus_encoder编码, + 同时tts_text_priority_thread处理音乐文件也使用self.opus_encoder, + 共享的encoder.buffer非线程安全,并发访问会导致SILK resampler断言失败。 + """ + from core.utils.util import audio_to_data_stream + return audio_to_data_stream( + audio_file_path, is_opus=True, callback=callback, + sample_rate=self.conn.sample_rate, opus_encoder=None + ) + def wav_to_opus_data_audio_raw_stream(self, raw_data_var, is_end=False, callback: Callable[[Any], Any]=None): return self.opus_encoder.encode_pcm_to_opus_stream(raw_data_var, is_end, callback=callback) diff --git a/main/xiaozhi-server/core/providers/tts/xunfei_stream.py b/main/xiaozhi-server/core/providers/tts/xunfei_stream.py index e7d4d4e9..e08c5617 100644 --- a/main/xiaozhi-server/core/providers/tts/xunfei_stream.py +++ b/main/xiaozhi-server/core/providers/tts/xunfei_stream.py @@ -9,6 +9,7 @@ import hashlib import asyncio import traceback import websockets +from typing import Callable, Any from asyncio import Task from config.logger import setup_logging from core.utils import opus_encoder_utils @@ -472,9 +473,27 @@ class TTSProvider(TTSProviderBase): return audio_data except Exception as e: logger.bind(tag=TAG).error(f"生成音频数据失败: {str(e)}") - return [] - - def _build_base_request(self, status,text=" "): + return [] + + def audio_to_opus_data_stream( + self, audio_file_path, callback: Callable[[Any], Any] = None + ): + """重写父类方法:使用独立的临时编码器处理音频文件,避免与TTS流式编码器并发冲突。 + 双流式TTS中,monitor任务在event loop线程接收TTS音频并使用self.opus_encoder编码, + 同时tts_text_priority_thread处理音乐文件也使用self.opus_encoder, + 共享的encoder.buffer非线程安全,并发访问会导致SILK resampler断言失败。 + """ + from core.utils.util import audio_to_data_stream + + return audio_to_data_stream( + audio_file_path, + is_opus=True, + callback=callback, + sample_rate=self.conn.sample_rate, + opus_encoder=None, + ) + + def _build_base_request(self, status, text=" "): """构建基础请求结构""" return { "header": {