update:优化线程

This commit is contained in:
hrz
2025-05-26 10:44:35 +08:00
parent 24526ad206
commit 5be65216e2
6 changed files with 42 additions and 38 deletions
+2 -1
View File
@@ -15,6 +15,7 @@ from plugins_func.loadplugins import auto_import_modules
from config.logger import setup_logging from config.logger import setup_logging
from core.utils.dialogue import Message, Dialogue from core.utils.dialogue import Message, Dialogue
from core.providers.tts.dto.dto import ContentType, TTSMessageDTO, SentenceType from core.providers.tts.dto.dto import ContentType, TTSMessageDTO, SentenceType
from core.providers.tts.default import DefaultTTS
from core.handle.textHandle import handleTextMessage from core.handle.textHandle import handleTextMessage
from core.utils.util import ( from core.utils.util import (
extract_json_from_string, extract_json_from_string,
@@ -312,7 +313,7 @@ class ConnectionHandler:
if self.asr is None: if self.asr is None:
self.asr = self._asr self.asr = self._asr
if self.tts is None: if self.tts is None:
self.tts = self._tts self.tts = DefaultTTS(self.config, delete_audio_file=True)
# 使用事件循环运行异步方法 # 使用事件循环运行异步方法
asyncio.run_coroutine_threadsafe(self.tts.open_audio_channels(self), self.loop) asyncio.run_coroutine_threadsafe(self.tts.open_audio_channels(self), self.loop)
@@ -5,6 +5,7 @@ from core.handle.sendAudioHandle import send_stt_message
from core.handle.intentHandler import handle_user_intent from core.handle.intentHandler import handle_user_intent
from core.utils.output_counter import check_device_output_limit from core.utils.output_counter import check_device_output_limit
from core.handle.reportHandle import enqueue_asr_report from core.handle.reportHandle import enqueue_asr_report
from core.handle.sendAudioHandle import SentenceType
from core.utils.util import audio_to_data from core.utils.util import audio_to_data
TAG = __name__ TAG = __name__
@@ -112,7 +113,7 @@ async def max_out_size(conn):
await send_stt_message(conn, text) await send_stt_message(conn, text)
file_path = "config/assets/max_output_size.wav" file_path = "config/assets/max_output_size.wav"
opus_packets, _ = audio_to_data(file_path) opus_packets, _ = audio_to_data(file_path)
conn.tts.tts_audio_queue.put((opus_packets, text, 0)) conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text))
conn.close_after_chat = True conn.close_after_chat = True
@@ -131,7 +132,7 @@ async def check_bind_device(conn):
# 播放提示音 # 播放提示音
music_path = "config/assets/bind_code.wav" music_path = "config/assets/bind_code.wav"
opus_packets, _ = audio_to_data(music_path) opus_packets, _ = audio_to_data(music_path)
conn.tts.tts_audio_queue.put((opus_packets, text, 0)) conn.tts.tts_audio_queue.put((SentenceType.FIRST, opus_packets, text))
# 逐个播放数字 # 逐个播放数字
for i in range(6): # 确保只播放6位数字 for i in range(6): # 确保只播放6位数字
@@ -139,13 +140,14 @@ async def check_bind_device(conn):
digit = conn.bind_code[i] digit = conn.bind_code[i]
num_path = f"config/assets/bind_code/{digit}.wav" num_path = f"config/assets/bind_code/{digit}.wav"
num_packets, _ = audio_to_data(num_path) num_packets, _ = audio_to_data(num_path)
conn.tts.tts_audio_queue.put((num_packets, None, i + 1)) conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, num_packets, None))
except Exception as e: except Exception as e:
conn.logger.bind(tag=TAG).error(f"播放数字音频失败: {e}") conn.logger.bind(tag=TAG).error(f"播放数字音频失败: {e}")
continue continue
conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None))
else: else:
text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。" text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。"
await send_stt_message(conn, text) await send_stt_message(conn, text)
music_path = "config/assets/bind_not_found.wav" music_path = "config/assets/bind_not_found.wav"
opus_packets, _ = audio_to_data(music_path) opus_packets, _ = audio_to_data(music_path)
conn.tts.tts_audio_queue.put((opus_packets, text, 0)) conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text))
@@ -3,6 +3,7 @@ import asyncio
import time import time
from core.providers.tts.dto.dto import SentenceType from core.providers.tts.dto.dto import SentenceType
from core.utils.util import get_string_no_punctuation_or_emoji, analyze_emotion from core.utils.util import get_string_no_punctuation_or_emoji, analyze_emotion
from loguru import logger
TAG = __name__ TAG = __name__
@@ -49,7 +50,7 @@ async def sendAudioMessage(conn, sentenceType, audios, text):
await send_tts_message(conn, "sentence_start", text) await send_tts_message(conn, "sentence_start", text)
await sendAudio(conn, audios, True) await sendAudio(conn, audios, False)
await send_tts_message(conn, "sentence_end", text) await send_tts_message(conn, "sentence_end", text)
+26 -19
View File
@@ -11,6 +11,7 @@ from core.providers.tts.dto.dto import TTSMessageDTO, SentenceType, ContentType
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from core.utils.tts import MarkdownCleaner from core.utils.tts import MarkdownCleaner
from core.utils.util import audio_to_data from core.utils.util import audio_to_data
import traceback
TAG = __name__ TAG = __name__
logger = setup_logging() logger = setup_logging()
@@ -24,8 +25,6 @@ class TTSProviderBase(ABC):
self.output_file = config.get("output_dir") self.output_file = config.get("output_dir")
self.tts_text_queue = queue.Queue() self.tts_text_queue = queue.Queue()
self.tts_audio_queue = queue.Queue() self.tts_audio_queue = queue.Queue()
self.stop_event = threading.Event()
self.loop = asyncio.get_event_loop()
self.tts_text_buff = [] self.tts_text_buff = []
self.punctuations = ( self.punctuations = (
@@ -148,9 +147,9 @@ class TTSProviderBase(ABC):
# 这里默认是非流式的处理方式 # 这里默认是非流式的处理方式
# 流式处理方式请在子类中重写 # 流式处理方式请在子类中重写
def _tts_text_priority_thread(self): def _tts_text_priority_thread(self):
while not self.stop_event.is_set(): while not self.conn.stop_event.is_set():
try: try:
message = self.tts_text_queue.get() message = self.tts_text_queue.get(timeout=1)
if message.sentence_type == SentenceType.FIRST: if message.sentence_type == SentenceType.FIRST:
# 初始化参数 # 初始化参数
self.tts_stop_request = False self.tts_stop_request = False
@@ -162,30 +161,35 @@ class TTSProviderBase(ABC):
segment_text = self._get_segment_text() segment_text = self._get_segment_text()
if segment_text: if segment_text:
tts_file = self.to_tts(segment_text) tts_file = self.to_tts(segment_text)
audio_datas = self._process_audio_file(tts_file) if tts_file:
self.tts_audio_queue.put( audio_datas = self._process_audio_file(tts_file)
(message.sentence_type, audio_datas, segment_text) self.tts_audio_queue.put(
) (message.sentence_type, audio_datas, segment_text)
)
elif ContentType.FILE == message.content_type: elif ContentType.FILE == message.content_type:
self._process_remaining_text() self._process_remaining_text()
tts_file = message.content_file tts_file = message.content_file
audio_datas = self._process_audio_file(tts_file) if tts_file and os.path.exists(tts_file):
self.tts_audio_queue.put( audio_datas = self._process_audio_file(tts_file)
(message.sentence_type, audio_datas, message.content_detail) self.tts_audio_queue.put(
) (message.sentence_type, audio_datas, message.content_detail)
)
if message.sentence_type == SentenceType.LAST: if message.sentence_type == SentenceType.LAST:
self._process_remaining_text() self._process_remaining_text()
self.tts_audio_queue.put( self.tts_audio_queue.put(
(message.sentence_type, [], message.content_detail) (message.sentence_type, [], message.content_detail)
) )
except queue.Empty:
continue
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"Failed to process TTS text: {e}") logger.bind(tag=TAG).error(
f"处理TTS文本失败: {str(e)}, 类型: {type(e).__name__}, 堆栈: {traceback.format_exc()}"
)
def _audio_play_priority_thread(self): def _audio_play_priority_thread(self):
while not self.stop_event.is_set(): while not self.conn.stop_event.is_set():
text = None text = None
try: try:
try: try:
@@ -193,14 +197,17 @@ class TTSProviderBase(ABC):
timeout=1 timeout=1
) )
except queue.Empty: except queue.Empty:
if self.stop_event.is_set(): if self.conn.stop_event.is_set():
break break
continue continue
future = asyncio.run_coroutine_threadsafe( future = asyncio.run_coroutine_threadsafe(
sendAudioMessage(self.conn, sentence_type, audio_datas, text), sendAudioMessage(self.conn, sentence_type, audio_datas, text),
self.loop, self.conn.loop,
)
result = future.result()
logger.bind(tag=TAG).info(
f"audio_play_priority result: {text} {result}"
) )
future.result()
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error( logger.bind(tag=TAG).error(
f"audio_play_priority priority_thread: {text} {e}" f"audio_play_priority priority_thread: {text} {e}"
@@ -145,7 +145,6 @@ class TTSProvider(TTSProviderBase):
self.authorization = config.get("authorization") self.authorization = config.get("authorization")
self.speaker = config.get("speaker") self.speaker = config.get("speaker")
self.header = {"Authorization": f"{self.authorization}{self.access_token}"} self.header = {"Authorization": f"{self.authorization}{self.access_token}"}
self.stop_event_response = threading.Event()
self.enable_two_way = True self.enable_two_way = True
self.start_connection_flag = False self.start_connection_flag = False
self.tts_text = "" self.tts_text = ""
@@ -162,7 +161,7 @@ class TTSProvider(TTSProviderBase):
self.ws_url, additional_headers=ws_header, max_size=1000000000 self.ws_url, additional_headers=ws_header, max_size=1000000000
) )
tts_priority = threading.Thread( tts_priority = threading.Thread(
target=self._start_monitor_tts_response_thread(), daemon=True target=self._start_monitor_tts_response_thread, daemon=True
) )
tts_priority.start() tts_priority.start()
@@ -174,15 +173,15 @@ class TTSProvider(TTSProviderBase):
def to_tts(self, text): def to_tts(self, text):
future = asyncio.run_coroutine_threadsafe( future = asyncio.run_coroutine_threadsafe(
self.start_session(self.conn.sentence_id), loop=self.loop self.start_session(self.conn.sentence_id), loop=self.conn.loop
) )
future.result() future.result()
future = asyncio.run_coroutine_threadsafe( future = asyncio.run_coroutine_threadsafe(
self.text_to_speak(text, None), loop=self.loop self.text_to_speak(text, None), loop=self.conn.loop
) )
future.result() future.result()
future = asyncio.run_coroutine_threadsafe( future = asyncio.run_coroutine_threadsafe(
self.finish_session(self.conn.sentence_id), loop=self.loop self.finish_session(self.conn.sentence_id), loop=self.conn.loop
) )
future.result() future.result()
return self.interface_type.value return self.interface_type.value
@@ -332,7 +331,6 @@ class TTSProvider(TTSProviderBase):
return return
async def start_session(self, session_id): async def start_session(self, session_id):
self.stop_event_response.clear()
header = Header( header = Header(
message_type=FULL_CLIENT_REQUEST, message_type=FULL_CLIENT_REQUEST,
message_type_specific_flags=MsgTypeFlagWithEvent, message_type_specific_flags=MsgTypeFlagWithEvent,
@@ -345,7 +343,6 @@ class TTSProvider(TTSProviderBase):
async def finish_session(self, session_id): async def finish_session(self, session_id):
logger.bind(tag=TAG).info(f"会话结束~~{session_id}") logger.bind(tag=TAG).info(f"会话结束~~{session_id}")
self.stop_event_response.set()
header = Header( header = Header(
message_type=FULL_CLIENT_REQUEST, message_type=FULL_CLIENT_REQUEST,
message_type_specific_flags=MsgTypeFlagWithEvent, message_type_specific_flags=MsgTypeFlagWithEvent,
@@ -380,11 +377,11 @@ class TTSProvider(TTSProviderBase):
def _start_monitor_tts_response_thread(self): def _start_monitor_tts_response_thread(self):
# 初始化链接 # 初始化链接
asyncio.run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self._start_monitor_tts_response(), loop=self.loop self._start_monitor_tts_response(), loop=self.conn.loop
) )
async def _start_monitor_tts_response(self): async def _start_monitor_tts_response(self):
while not self.stop_event.is_set(): while not self.conn.stop_event.is_set():
try: try:
msg = await self.ws.recv() # 确保 `recv()` 运行在同一个 event loop msg = await self.ws.recv() # 确保 `recv()` 运行在同一个 event loop
res = self.parser_response(msg) res = self.parser_response(msg)
@@ -2,7 +2,6 @@ import asyncio
import websockets import websockets
from config.logger import setup_logging from config.logger import setup_logging
from core.connection import ConnectionHandler from core.connection import ConnectionHandler
from core.providers.tts.default import DefaultTTS
from core.utils.util import initialize_modules, check_vad_update, check_asr_update from core.utils.util import initialize_modules, check_vad_update, check_asr_update
from config.config_loader import get_config_from_api from config.config_loader import get_config_from_api
@@ -31,9 +30,6 @@ class WebSocketServer:
self._intent = modules["intent"] if "intent" in modules else None self._intent = modules["intent"] if "intent" in modules else None
self._memory = modules["memory"] if "memory" in modules else None self._memory = modules["memory"] if "memory" in modules else None
if self._tts is None:
self._tts = DefaultTTS(self.config, delete_audio_file=True)
self.active_connections = set() self.active_connections = set()
async def start(self): async def start(self):