mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 01:53:53 +08:00
refactor: 优化非流式音频过滤机制
fix: 修复等待中被打断队列被停止造成的死锁
This commit is contained in:
@@ -17,7 +17,11 @@ AUDIO_FRAME_DURATION = 60
|
|||||||
PRE_BUFFER_COUNT = 5
|
PRE_BUFFER_COUNT = 5
|
||||||
|
|
||||||
|
|
||||||
async def sendAudioMessage(conn: "ConnectionHandler", sentenceType, audios, text):
|
async def sendAudioMessage(conn: "ConnectionHandler", sentenceType, audios, text, sentence_id=None):
|
||||||
|
# 跳过旧句子残留音频
|
||||||
|
if sentence_id is not None and sentence_id != conn.sentence_id:
|
||||||
|
return
|
||||||
|
|
||||||
if conn.tts.tts_audio_first_sentence:
|
if conn.tts.tts_audio_first_sentence:
|
||||||
conn.logger.bind(tag=TAG).info(f"发送第一段语音: {text}")
|
conn.logger.bind(tag=TAG).info(f"发送第一段语音: {text}")
|
||||||
conn.tts.tts_audio_first_sentence = False
|
conn.tts.tts_audio_first_sentence = False
|
||||||
@@ -281,12 +285,14 @@ async def send_tts_message(conn: "ConnectionHandler", state, text=None):
|
|||||||
await sendAudio(conn, audios)
|
await sendAudio(conn, audios)
|
||||||
# 等待所有音频包发送完成
|
# 等待所有音频包发送完成
|
||||||
await _wait_for_audio_completion(conn)
|
await _wait_for_audio_completion(conn)
|
||||||
# 停止音频发送循环(仅在流控器已初始化时调用)
|
|
||||||
if hasattr(conn, "audio_rate_controller") and conn.audio_rate_controller:
|
|
||||||
conn.audio_rate_controller.stop_sending()
|
|
||||||
# 检查是否是当前轮次
|
# 检查是否是当前轮次
|
||||||
if current_sentence_id != conn.sentence_id:
|
if current_sentence_id != conn.sentence_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 停止音频发送循环(仅在流控器已初始化时调用)
|
||||||
|
if hasattr(conn, "audio_rate_controller") and conn.audio_rate_controller:
|
||||||
|
conn.audio_rate_controller.stop_sending()
|
||||||
conn.clearSpeakStatus()
|
conn.clearSpeakStatus()
|
||||||
|
|
||||||
# 发送消息到客户端
|
# 发送消息到客户端
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class TTSProviderBase(ABC):
|
|||||||
|
|
||||||
def handle_opus(self, opus_data: bytes):
|
def handle_opus(self, opus_data: bytes):
|
||||||
logger.bind(tag=TAG).debug(f"推送数据到队列里面帧数~~ {len(opus_data)}")
|
logger.bind(tag=TAG).debug(f"推送数据到队列里面帧数~~ {len(opus_data)}")
|
||||||
self.tts_audio_queue.put((SentenceType.MIDDLE, opus_data, None))
|
self.tts_audio_queue.put((SentenceType.MIDDLE, opus_data, None, getattr(self, 'current_sentence_id', None)))
|
||||||
|
|
||||||
def handle_audio_file(self, file_audio: bytes, text):
|
def handle_audio_file(self, file_audio: bytes, text):
|
||||||
self.before_stop_play_files.append((file_audio, text))
|
self.before_stop_play_files.append((file_audio, text))
|
||||||
@@ -97,7 +97,7 @@ class TTSProviderBase(ABC):
|
|||||||
try:
|
try:
|
||||||
audio_bytes = asyncio.run(self.text_to_speak(text, None))
|
audio_bytes = asyncio.run(self.text_to_speak(text, None))
|
||||||
if audio_bytes:
|
if audio_bytes:
|
||||||
self.tts_audio_queue.put((SentenceType.FIRST, None, text))
|
self.tts_audio_queue.put((SentenceType.FIRST, None, text, getattr(self, 'current_sentence_id', None)))
|
||||||
audio_bytes_to_data_stream(
|
audio_bytes_to_data_stream(
|
||||||
audio_bytes,
|
audio_bytes,
|
||||||
file_type=self.audio_file_type,
|
file_type=self.audio_file_type,
|
||||||
@@ -146,7 +146,7 @@ class TTSProviderBase(ABC):
|
|||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
self.tts_audio_queue.put((SentenceType.FIRST, None, text))
|
self.tts_audio_queue.put((SentenceType.FIRST, None, text, getattr(self, 'current_sentence_id', None)))
|
||||||
self._process_audio_file_stream(tmp_file, callback=opus_handler)
|
self._process_audio_file_stream(tmp_file, callback=opus_handler)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
||||||
@@ -327,7 +327,7 @@ class TTSProviderBase(ABC):
|
|||||||
logger.bind(tag=TAG).info("收到打断信息,终止TTS文本处理线程")
|
logger.bind(tag=TAG).info("收到打断信息,终止TTS文本处理线程")
|
||||||
continue
|
continue
|
||||||
if message.sentence_type == SentenceType.FIRST:
|
if message.sentence_type == SentenceType.FIRST:
|
||||||
# 初始化参数
|
self.current_sentence_id = message.sentence_id
|
||||||
self.tts_stop_request = False
|
self.tts_stop_request = False
|
||||||
self.processed_chars = 0
|
self.processed_chars = 0
|
||||||
self.tts_text_buff = []
|
self.tts_text_buff = []
|
||||||
@@ -348,7 +348,7 @@ class TTSProviderBase(ABC):
|
|||||||
if message.sentence_type == SentenceType.LAST:
|
if message.sentence_type == SentenceType.LAST:
|
||||||
self._process_remaining_text_stream(opus_handler=self.handle_opus)
|
self._process_remaining_text_stream(opus_handler=self.handle_opus)
|
||||||
self.tts_audio_queue.put(
|
self.tts_audio_queue.put(
|
||||||
(message.sentence_type, [], message.content_detail)
|
(message.sentence_type, [], message.content_detail, message.sentence_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
@@ -367,9 +367,12 @@ class TTSProviderBase(ABC):
|
|||||||
text = None
|
text = None
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
sentence_type, audio_datas, text = self.tts_audio_queue.get(
|
item = self.tts_audio_queue.get(timeout=0.1)
|
||||||
timeout=0.1
|
if len(item) == 4:
|
||||||
)
|
sentence_type, audio_datas, text, sentence_id = item
|
||||||
|
else:
|
||||||
|
sentence_type, audio_datas, text = item
|
||||||
|
sentence_id = None
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
if self.conn.stop_event.is_set():
|
if self.conn.stop_event.is_set():
|
||||||
break
|
break
|
||||||
@@ -404,7 +407,7 @@ class TTSProviderBase(ABC):
|
|||||||
|
|
||||||
# 发送音频
|
# 发送音频
|
||||||
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, sentence_id),
|
||||||
self.conn.loop,
|
self.conn.loop,
|
||||||
)
|
)
|
||||||
future.result()
|
future.result()
|
||||||
@@ -493,9 +496,9 @@ class TTSProviderBase(ABC):
|
|||||||
|
|
||||||
def _process_before_stop_play_files(self):
|
def _process_before_stop_play_files(self):
|
||||||
for audio_datas, text in self.before_stop_play_files:
|
for audio_datas, text in self.before_stop_play_files:
|
||||||
self.tts_audio_queue.put((SentenceType.MIDDLE, audio_datas, text))
|
self.tts_audio_queue.put((SentenceType.MIDDLE, audio_datas, text, getattr(self, 'current_sentence_id', None)))
|
||||||
self.before_stop_play_files.clear()
|
self.before_stop_play_files.clear()
|
||||||
self.tts_audio_queue.put((SentenceType.LAST, [], None))
|
self.tts_audio_queue.put((SentenceType.LAST, [], None, getattr(self, 'current_sentence_id', None)))
|
||||||
|
|
||||||
def _process_remaining_text_stream(
|
def _process_remaining_text_stream(
|
||||||
self, opus_handler: Callable[[bytes], None] = None
|
self, opus_handler: Callable[[bytes], None] = None
|
||||||
|
|||||||
Reference in New Issue
Block a user