Compare commits
3 Commits
9871b445f0
...
a451fd096d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a451fd096d | ||
|
|
85a67aa1fa | ||
|
|
92c5e0b9e4 |
@ -208,6 +208,13 @@ class InputProcess:
|
||||
# 从禁用到启用,需要重新初始化音频流
|
||||
print("🎙️ 输入进程:重新启用录音功能,重新初始化音频流")
|
||||
self._cleanup_audio_stream()
|
||||
# 清空所有音频缓冲区,防止旧数据被录制
|
||||
self.recording_buffer = []
|
||||
self.pre_record_buffer = []
|
||||
self.is_recording = False
|
||||
self.silence_start_time = None
|
||||
self.consecutive_silence_count = 0
|
||||
print("🎙️ 输入进程:已清空所有音频缓冲区")
|
||||
self._setup_audio()
|
||||
self.recording_enabled = True
|
||||
self.logger.info("录音功能已启用")
|
||||
@ -456,10 +463,13 @@ class InputProcess:
|
||||
|
||||
def _add_tts_task(self, content):
|
||||
"""添加TTS任务到队列"""
|
||||
print(f"🔊 OutputProcess添加TTS任务到队列: '{content[:30]}...' (队列大小: {self.tts_task_queue.qsize()})")
|
||||
try:
|
||||
self.tts_task_queue.put_nowait(("tts_sentence", content))
|
||||
print(f"✅ OutputProcess TTS任务添加成功,队列大小: {self.tts_task_queue.qsize()}")
|
||||
return True
|
||||
except queue.Full:
|
||||
print(f"❌ OutputProcess TTS任务队列已满,丢弃任务")
|
||||
self.logger.warning("TTS任务队列已满,丢弃任务")
|
||||
return False
|
||||
|
||||
@ -620,6 +630,7 @@ class OutputProcess:
|
||||
self.pre_buffer_empty = False # 预缓冲区是否为空
|
||||
self.playback_buffer_empty = False # 播放缓冲区是否为空
|
||||
self.no_active_playback = False # 是否没有活跃的播放
|
||||
self.last_audio_chunk_time = 0 # 最后一个音频块开始播放的时间,初始化为0表示尚未播放
|
||||
|
||||
# PyAudio实例
|
||||
self.audio = None
|
||||
@ -734,8 +745,12 @@ class OutputProcess:
|
||||
f"冷却期检查={in_cooldown}, 距离上次播放={time_since_last_play:.2f}s, "
|
||||
f"冷却阈值={self.playback_cooldown_period}s")
|
||||
|
||||
# 标记正在播放
|
||||
# 确保播放状态正确
|
||||
if not self.currently_playing:
|
||||
self.currently_playing = True
|
||||
self.last_audio_chunk_time = time.time() # 记录最后播放时间
|
||||
print(f"🔊 播放工作线程:开始播放,设置 currently_playing = True")
|
||||
|
||||
# 如果是第一次播放,不设置冷却期
|
||||
if chunks_played == 0:
|
||||
self.last_playback_time = 0 # 第一次播放不触发冷却期
|
||||
@ -752,8 +767,7 @@ class OutputProcess:
|
||||
progress = f"🔊 播放工作: {chunks_played} 块 | {total_size / 1024:.1f} KB"
|
||||
print(f"\r{progress}", end='', flush=True)
|
||||
|
||||
# 播放完成后更新状态
|
||||
self.currently_playing = False
|
||||
# 注意:不在这里重置 currently_playing 状态,保持为 True 直到真正确定播放完成
|
||||
|
||||
# 如果这是最后一个音频块,主动检查播放完成
|
||||
if (len(self.playback_buffer) == 0 and
|
||||
@ -766,23 +780,41 @@ class OutputProcess:
|
||||
print(f"🔊 播放工作线程:playback_completed标志已设置为{self.playback_completed}")
|
||||
# 不在这里直接调用_finish_playback,让主处理循环处理
|
||||
else:
|
||||
self.currently_playing = False
|
||||
# 空音频块,不改变播放状态,继续下一个
|
||||
print(f"🔊 播放工作线程:遇到空音频块,跳过")
|
||||
continue
|
||||
else:
|
||||
# 缓冲区为空,短暂休眠,减少CPU占用
|
||||
# 只有在确定没有音频播放时才设置状态为 False
|
||||
if self.currently_playing:
|
||||
# 检查是否真的没有音频在播放
|
||||
time_since_last_chunk = time.time() - self.last_audio_chunk_time
|
||||
if time_since_last_chunk > 0.5: # 超过0.5秒没有播放新音频
|
||||
self.currently_playing = False
|
||||
print(f"🔊 播放工作线程:缓冲区为空且{time_since_last_chunk:.1f}秒无新音频,设置 currently_playing = False")
|
||||
time.sleep(0.01)
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 播放工作线程错误: {e}")
|
||||
self.logger.error(f"播放工作线程错误: {e}")
|
||||
# 异常情况下,只有在确定音频停止播放时才重置状态
|
||||
if self.currently_playing:
|
||||
time_since_last_chunk = time.time() - self.last_audio_chunk_time
|
||||
if time_since_last_chunk > 1.0: # 异常情况下,等待更长时间
|
||||
self.currently_playing = False
|
||||
print(f"🔊 播放工作线程:异常情况下设置 currently_playing = False")
|
||||
else:
|
||||
print(f"🔊 播放工作线程:异常但保持 currently_playing = True(最后播放于{time_since_last_chunk:.1f}秒前)")
|
||||
time.sleep(0.1)
|
||||
|
||||
print(f"\n✅ 播放工作线程结束: 总计 {chunks_played} 块, {total_size / 1024:.1f} KB")
|
||||
|
||||
finally:
|
||||
# 线程结束时确保状态正确
|
||||
if self.currently_playing:
|
||||
self.currently_playing = False
|
||||
print(f"🔊 播放工作线程:线程结束,设置 currently_playing = False")
|
||||
if playback_stream:
|
||||
try:
|
||||
playback_stream.stop_stream()
|
||||
@ -928,7 +960,20 @@ class OutputProcess:
|
||||
print(f"📥 输出进程收到结束信号")
|
||||
end_signal_received = True
|
||||
self.end_signal_received = True
|
||||
|
||||
# 只有在有音频数据或者TTS缓冲区有内容时,才设置all_audio_received
|
||||
# 这样可以避免在没有音频的情况下误判播放完成
|
||||
if (len(self.preload_buffer) > 0 or
|
||||
len(self.playback_buffer) > 0 or
|
||||
len(self.tts_buffer) > 0 or
|
||||
self.tts_task_queue.qsize() > 0):
|
||||
self.all_audio_received = True
|
||||
print(f"📥 设置all_audio_received=True,检测到有待处理的数据")
|
||||
else:
|
||||
# 如果没有任何数据,可能是LLM响应为空或其他问题
|
||||
# 也设置为True,让播放完成检测处理这种情况
|
||||
self.all_audio_received = True
|
||||
print(f"📥 警告:收到结束信号但没有检测到任何音频数据,可能存在问题")
|
||||
|
||||
# 重置完成事件标记
|
||||
self.completion_sent = False
|
||||
@ -959,6 +1004,14 @@ class OutputProcess:
|
||||
# 流式文本处理 - 智能缓冲
|
||||
streaming_text = audio_data[15:] # 移除 "STREAMING_TEXT:" 前缀
|
||||
print(f"📥 输出进程收到流式文本: {streaming_text}")
|
||||
|
||||
# 检查是否需要重置状态(新的对话开始)
|
||||
if self.end_signal_received:
|
||||
print(f"📥 检测到新对话开始,重置end_signal_received状态")
|
||||
self.end_signal_received = False
|
||||
self.all_audio_received = False
|
||||
self.completion_sent = False
|
||||
|
||||
self.process_streaming_text(streaming_text)
|
||||
continue
|
||||
|
||||
@ -966,6 +1019,14 @@ class OutputProcess:
|
||||
# 完整文本处理 - 强制刷新缓冲区
|
||||
complete_text = audio_data[14:] # 移除 "COMPLETE_TEXT:" 前缀
|
||||
print(f"📥 输出进程收到完整文本: {complete_text}")
|
||||
|
||||
# 检查是否需要重置状态(新的对话开始)
|
||||
if self.end_signal_received:
|
||||
print(f"📥 检测到新对话开始,重置end_signal_received状态")
|
||||
self.end_signal_received = False
|
||||
self.all_audio_received = False
|
||||
self.completion_sent = False
|
||||
|
||||
self.process_complete_text(complete_text)
|
||||
continue
|
||||
|
||||
@ -979,6 +1040,10 @@ class OutputProcess:
|
||||
# LLM生成完成信号
|
||||
print(f"📥 输出进程收到LLM生成完成信号")
|
||||
self.llm_generation_complete = True
|
||||
# LLM完成后,如果还没有开始TTS,重置TTS完成状态
|
||||
if self.tts_generation_complete and self.tts_task_queue.qsize() == 0:
|
||||
print(f"📥 LLM完成但TTS队列为空,重置TTS完成状态为False")
|
||||
self.tts_generation_complete = False
|
||||
continue
|
||||
|
||||
if isinstance(audio_data, str) and audio_data.startswith("TTS_COMPLETE:"):
|
||||
@ -992,6 +1057,11 @@ class OutputProcess:
|
||||
# 更新最后收到音频数据的时间
|
||||
self.last_audio_time = time.time()
|
||||
|
||||
# 如果之前已经收到结束信号,现在又收到音频数据,重置状态
|
||||
if self.all_audio_received:
|
||||
print(f"📥 收到新音频数据,重置all_audio_received状态为False")
|
||||
self.all_audio_received = False
|
||||
|
||||
# 减少日志输出,提高性能
|
||||
if processed_count <= 3 or processed_count % 50 == 0:
|
||||
print(f"📥 输出进程收到音频数据: {len(audio_data)} 字节")
|
||||
@ -1011,6 +1081,10 @@ class OutputProcess:
|
||||
self.preload_buffer.clear()
|
||||
self.is_playing = True
|
||||
self.last_playback_time = 0 # 重置播放时间,避免立即触发冷却期
|
||||
# 确保播放工作线程知道有数据要播放
|
||||
if not self.currently_playing:
|
||||
print(f"🔍 启动播放时确保 currently_playing = True")
|
||||
# 播放工作线程会自动检测播放缓冲区并开始播放
|
||||
print(f"🎵 开始播放音频(预加载完成),播放缓冲区大小: {len(self.playback_buffer)}")
|
||||
print(f"🔍 已重置last_playback_time,避免立即触发冷却期")
|
||||
elif self.is_playing and len(self.playback_buffer) < 3 and len(self.preload_buffer) > 0:
|
||||
@ -1056,6 +1130,10 @@ class OutputProcess:
|
||||
if self.preload_buffer:
|
||||
self.playback_buffer.append(self.preload_buffer.pop(0))
|
||||
print(f"📥 已转移 {transfer_count} 个数据块到播放缓冲区")
|
||||
# 如果播放工作线程没有在播放,需要确保状态正确
|
||||
if not self.currently_playing and len(self.playback_buffer) > 0:
|
||||
print(f"📥 转移数据后,确保播放状态正确(播放缓冲区有数据但currently_playing=False)")
|
||||
# 播放工作线程会自动检测并开始播放
|
||||
time.sleep(0.2) # 增加等待时间
|
||||
|
||||
# 检查是否应该补充播放缓冲区的数据
|
||||
@ -1195,40 +1273,100 @@ class OutputProcess:
|
||||
print(f" - 所有音频已接收: {self.all_audio_received}")
|
||||
print(f" - 预缓冲区为空: {self.pre_buffer_empty}")
|
||||
print(f" - 播放缓冲区为空: {self.playback_buffer_empty}")
|
||||
print(f" - 无活跃播放: {self.no_active_playback}")
|
||||
print(f" - 无活跃播放: {self.no_active_playback} (currently_playing={self.currently_playing})")
|
||||
print(f" - TTS队列大小: {tts_queue_size}")
|
||||
|
||||
# 检查是否所有条件都满足
|
||||
# 添加时间维度检查
|
||||
if self.last_audio_chunk_time > 0:
|
||||
time_since_last_chunk = time.time() - self.last_audio_chunk_time
|
||||
print(f" - 最后播放时间: {time_since_last_chunk:.2f}秒前")
|
||||
else:
|
||||
time_since_last_chunk = 0
|
||||
print(f" - 最后播放时间: 尚未开始播放")
|
||||
|
||||
# 检查TTS是否正在生成 - 新增条件
|
||||
# TTS正在生成的条件:队列中有任务 或 TTS生成未完成 或 还有待处理的缓冲区内容
|
||||
tts_is_generating = (tts_queue_size > 0 or not self.tts_generation_complete or len(self.tts_buffer) > 0)
|
||||
print(f" - TTS正在生成: {tts_is_generating} (队列:{tts_queue_size}, 完成:{self.tts_generation_complete}, 缓冲:{len(self.tts_buffer)})")
|
||||
|
||||
# 检查是否所有条件都满足 - 使用更新的状态变量,添加TTS生成状态检查
|
||||
all_conditions_met = (
|
||||
self.llm_generation_complete and
|
||||
self.tts_generation_complete and
|
||||
self.all_audio_received and
|
||||
self.pre_buffer_empty and
|
||||
self.playback_buffer_empty and
|
||||
not self.currently_playing and # 直接检查当前播放状态
|
||||
tts_queue_size == 0
|
||||
self.no_active_playback and # 使用状态变量
|
||||
tts_queue_size == 0 and
|
||||
not tts_is_generating # 新增:确保TTS不在生成中
|
||||
)
|
||||
|
||||
if all_conditions_met:
|
||||
print(f"✅ 所有播放完成条件已满足,可以结束播放")
|
||||
# 额外时间检查:确保音频真正播放完成
|
||||
if self.last_audio_chunk_time > 0 and time_since_last_chunk > 0.3: # 至少0.3秒没有新音频播放
|
||||
print(f"✅ 所有播放完成条件已满足,且{time_since_last_chunk:.2f}秒无新音频,可以结束播放")
|
||||
return True
|
||||
elif self.last_audio_chunk_time == 0:
|
||||
# 如果从未开始播放,检查是否有音频数据
|
||||
if len(self.playback_buffer) == 0 and len(self.preload_buffer) == 0:
|
||||
# 检查是否有TTS任务在排队
|
||||
if self.tts_task_queue.qsize() == 0 and len(self.tts_buffer) == 0:
|
||||
# 真的没有任何数据,可能是LLM响应为空或TTS失败
|
||||
print(f"⚠️ 从未开始播放且无任何数据,可能LLM响应为空或TTS失败,强制结束")
|
||||
return True
|
||||
else:
|
||||
# 还有TTS任务待处理
|
||||
print(f"⏳ 从未开始播放但还有TTS任务待处理,等待TTS生成...")
|
||||
return False
|
||||
else:
|
||||
print(f"⏳ 从未开始播放但还有音频数据,等待播放开始...")
|
||||
return False
|
||||
else:
|
||||
print(f"⏳ 所有条件满足但等待音频完全播放(最后播放于{time_since_last_chunk:.2f}秒前)...")
|
||||
return False
|
||||
|
||||
# 如果LLM和TTS都完成了,但还有音频数据,等待播放完成
|
||||
if (self.llm_generation_complete and
|
||||
self.tts_generation_complete and
|
||||
self.all_audio_received and
|
||||
tts_queue_size == 0):
|
||||
tts_queue_size == 0 and
|
||||
not tts_is_generating): # 新增:确保TTS不在生成中
|
||||
|
||||
if self.pre_buffer_empty and self.playback_buffer_empty:
|
||||
if not self.currently_playing: # 直接检查当前播放状态
|
||||
print(f"✅ LLM和TTS完成,所有缓冲区已清空,播放器空闲")
|
||||
if self.no_active_playback: # 使用状态变量
|
||||
# 额外检查:确保最后播放的音频已经完成播放
|
||||
if self.last_audio_chunk_time > 0:
|
||||
time_since_last_chunk = time.time() - self.last_audio_chunk_time
|
||||
if time_since_last_chunk > 0.8: # 增加到0.8秒确保音频完全播放
|
||||
print(f"✅ LLM和TTS完成,所有缓冲区已清空,播放器空闲,最后播放于{time_since_last_chunk:.1f}秒前")
|
||||
return True
|
||||
else:
|
||||
print(f"⏳ 等待最后的音频播放完成...")
|
||||
time.sleep(0.5)
|
||||
if not self.currently_playing:
|
||||
print(f"✅ 最后的音频播放完成")
|
||||
print(f"⏳ 等待最后音频播放完成(最后播放于{time_since_last_chunk:.1f}秒前,需要0.8秒)...")
|
||||
return False
|
||||
else:
|
||||
# 从未开始播放的情况
|
||||
print(f"⚠️ LLM和TTS完成,缓冲区清空,但从未开始播放,可能播放失败")
|
||||
return True
|
||||
else:
|
||||
print(f"⏳ 等待最后的音频播放完成(currently_playing={self.currently_playing})...")
|
||||
time.sleep(0.3)
|
||||
# 重新更新状态
|
||||
self.no_active_playback = (not self.currently_playing)
|
||||
if self.no_active_playback:
|
||||
if self.last_audio_chunk_time > 0:
|
||||
time_since_last_chunk = time.time() - self.last_audio_chunk_time
|
||||
if time_since_last_chunk > 0.5:
|
||||
print(f"✅ 最后的音频播放完成(最后播放于{time_since_last_chunk:.1f}秒前)")
|
||||
return True
|
||||
else:
|
||||
print(f"⏳ 仍在等待音频完全播放完成(最后播放于{time_since_last_chunk:.1f}秒前)...")
|
||||
return False
|
||||
else:
|
||||
print(f"⚠️ 播放器空闲但从未开始播放,可能播放失败")
|
||||
return True
|
||||
else:
|
||||
print(f"⏳ 播放器仍在活跃状态,继续等待...")
|
||||
return False
|
||||
else:
|
||||
print(f"⏳ 等待缓冲区数据播放完成 - 预缓冲: {len(self.preload_buffer)}, 播放缓冲: {len(self.playback_buffer)}")
|
||||
return False
|
||||
@ -1365,16 +1503,20 @@ class OutputProcess:
|
||||
|
||||
def _add_tts_task(self, content):
|
||||
"""添加TTS任务到队列"""
|
||||
print(f"🔊 OutputProcess添加TTS任务到队列: '{content[:30]}...' (队列大小: {self.tts_task_queue.qsize()})")
|
||||
try:
|
||||
self.tts_task_queue.put_nowait(("tts_sentence", content))
|
||||
print(f"✅ OutputProcess TTS任务添加成功,队列大小: {self.tts_task_queue.qsize()}")
|
||||
return True
|
||||
except queue.Full:
|
||||
print(f"❌ OutputProcess TTS任务队列已满,丢弃任务")
|
||||
self.logger.warning("TTS任务队列已满,丢弃任务")
|
||||
return False
|
||||
|
||||
def _generate_tts_audio(self, text):
|
||||
"""生成TTS音频数据并发送到统一播放队列 - 借鉴 recorder.py 的流式处理"""
|
||||
try:
|
||||
print(f"🔊 TTS开始生成音频,文本长度: {len(text)} 文本内容: {text[:50]}...")
|
||||
self.logger.info(f"生成TTS音频: {text[:50]}...")
|
||||
|
||||
# 清空所有缓冲区,确保新的音频不被旧数据干扰
|
||||
@ -1383,6 +1525,10 @@ class OutputProcess:
|
||||
self.is_playing = False
|
||||
self.completion_sent = False # 重置完成标记
|
||||
|
||||
# 重置TTS生成完成状态 - 关键修复
|
||||
self.tts_generation_complete = False
|
||||
print(f"🔊 已重置TTS生成完成状态为False,开始新的TTS生成")
|
||||
|
||||
# 构建请求头
|
||||
headers = {
|
||||
"X-Api-App-Id": self.tts_app_id,
|
||||
@ -1479,25 +1625,29 @@ class OutputProcess:
|
||||
continue
|
||||
|
||||
# 处理剩余的预加载数据
|
||||
print(f"🔊 TTS生成结束,检查剩余预加载数据: preload_buffer={len(self.preload_buffer)} 块")
|
||||
if self.preload_buffer:
|
||||
print(f"🔊 将剩余的 {len(self.preload_buffer)} 个音频块转移到播放缓冲区")
|
||||
self.playback_buffer.extend(self.preload_buffer)
|
||||
self.preload_buffer.clear()
|
||||
if not self.is_playing:
|
||||
self.is_playing = True
|
||||
self.last_playback_time = time.time()
|
||||
self.logger.info("开始播放TTS音频(处理剩余预加载)")
|
||||
else:
|
||||
print(f"⚠️ TTS生成完成但预加载缓冲区为空!")
|
||||
|
||||
success_rate = (success_count / chunk_count * 100) if chunk_count > 0 else 0
|
||||
print(f"🔊 TTS音频生成完成统计: chunk_count={chunk_count}, success_count={success_count}, success_rate={success_rate:.1f}%, total_size={total_audio_size / 1024:.1f} KB")
|
||||
self.logger.info(f"TTS音频生成完成: {chunk_count} 块, 成功率 {success_rate:.1f}% | 总大小: {total_audio_size / 1024:.1f} KB")
|
||||
|
||||
# 通知自己TTS生成已完成
|
||||
self.tts_generation_complete = True
|
||||
print(f"🎵 OutputProcess TTS生成已完成")
|
||||
|
||||
# 等待播放完成
|
||||
if success_count > 0:
|
||||
self.logger.info("等待TTS音频播放完成...")
|
||||
self._wait_for_playback_complete()
|
||||
# 注意:不在这里直接调用等待播放完成,让统一的增强播放完成检测机制处理
|
||||
# 这样可以避免在TTS还在生成后续音频时就误判播放完成
|
||||
self.logger.info("TTS生成完成,等待统一播放完成检测机制处理...")
|
||||
|
||||
return success_count > 0
|
||||
|
||||
@ -1509,27 +1659,6 @@ class OutputProcess:
|
||||
self.logger.error(f"TTS音频生成失败: {e}")
|
||||
return False
|
||||
|
||||
def _wait_for_playback_complete(self):
|
||||
"""等待播放完成"""
|
||||
max_wait_time = 30 # 最多等待30秒
|
||||
wait_start_time = time.time()
|
||||
|
||||
while (len(self.playback_buffer) > 0 or self.currently_playing) and (time.time() - wait_start_time) < max_wait_time:
|
||||
# 等待播放缓冲区清空且当前播放完成
|
||||
time.sleep(0.1)
|
||||
|
||||
if len(self.playback_buffer) == 0 and not self.currently_playing:
|
||||
self.logger.info("TTS音频播放完成")
|
||||
# 调用播放完成处理,发送完成事件
|
||||
self._finish_playback()
|
||||
else:
|
||||
self.logger.warning(f"TTS音频播放超时,剩余 {len(self.playback_buffer)} 块未播放")
|
||||
# 清空缓冲区
|
||||
self.playback_buffer.clear()
|
||||
self.preload_buffer.clear()
|
||||
# 即使超时也要调用播放完成处理
|
||||
self._finish_playback()
|
||||
|
||||
# ========== 智能句子缓冲系统 - 从 recorder.py 借鉴 ==========
|
||||
|
||||
def _should_trigger_tts(self, sentence):
|
||||
@ -1564,29 +1693,46 @@ class OutputProcess:
|
||||
|
||||
def _process_tts_buffer(self):
|
||||
"""处理TTS缓冲区 - 发送累积的句子到TTS"""
|
||||
print(f"🔊 处理TTS缓冲区,当前缓冲区内容: {self.tts_buffer}")
|
||||
if not self.tts_buffer:
|
||||
print(f"🔊 TTS缓冲区为空,跳过处理")
|
||||
return
|
||||
|
||||
# 合并缓冲区的句子
|
||||
combined_text = ''.join(self.tts_buffer)
|
||||
print(f"🔊 合并后的文本: '{combined_text}' (长度: {len(combined_text)})")
|
||||
|
||||
# 重置TTS生成完成状态 - 关键修复
|
||||
self.tts_generation_complete = False
|
||||
print(f"🔊 触发新TTS任务,已重置TTS生成完成状态为False")
|
||||
|
||||
# 添加到TTS任务队列
|
||||
print(f"🔊 尝试添加TTS任务到队列")
|
||||
if self._add_tts_task(combined_text):
|
||||
print(f"🎵 触发TTS: {combined_text[:50]}...")
|
||||
self.tts_last_trigger_time = time.time()
|
||||
else:
|
||||
print(f"❌ 添加TTS任务失败")
|
||||
|
||||
# 清空缓冲区
|
||||
self.tts_buffer.clear()
|
||||
print(f"🔊 TTS缓冲区已清空")
|
||||
|
||||
def _add_sentence_to_buffer(self, sentence):
|
||||
"""添加句子到智能缓冲区 - 核心方法"""
|
||||
print(f"🔊 添加句子到TTS缓冲区: '{sentence}' (缓冲区大小: {len(self.tts_buffer)} -> {len(self.tts_buffer)+1})")
|
||||
if not sentence.strip():
|
||||
print(f"🔊 句子为空,不添加到缓冲区")
|
||||
return
|
||||
|
||||
self.tts_buffer.append(sentence)
|
||||
print(f"🔊 已添加到TTS缓冲区,当前缓冲区: {self.tts_buffer}")
|
||||
|
||||
# 检查是否应该触发TTS
|
||||
if self._should_trigger_tts(sentence):
|
||||
should_trigger = self._should_trigger_tts(sentence)
|
||||
print(f"🔊 是否应该触发TTS: {should_trigger}")
|
||||
if should_trigger:
|
||||
print(f"🔊 触发TTS缓冲区处理")
|
||||
self._process_tts_buffer()
|
||||
|
||||
def _flush_tts_buffer(self):
|
||||
@ -1664,15 +1810,32 @@ class OutputProcess:
|
||||
|
||||
def process_streaming_text(self, text):
|
||||
"""处理流式文本 - 新增的公共接口,用于与LLM流式输出集成"""
|
||||
print(f"🔊 收到流式文本: '{text}' (长度: {len(text)})")
|
||||
if not text.strip():
|
||||
print(f"🔊 流式文本为空,跳过处理")
|
||||
return
|
||||
|
||||
# 过滤括号内容
|
||||
filtered_text = self._filter_parentheses_content(text.strip())
|
||||
print(f"🔊 过滤后的文本: '{filtered_text}' (长度: {len(filtered_text)})")
|
||||
|
||||
if filtered_text:
|
||||
# 检查是否是第一次收到流式文本(即所有状态都还是完成状态)
|
||||
if (self.llm_generation_complete and
|
||||
self.tts_generation_complete and
|
||||
self.all_audio_received):
|
||||
print(f"🔊 首次收到流式文本,重置所有播放完成检测状态")
|
||||
self.llm_generation_complete = False
|
||||
self.tts_generation_complete = False
|
||||
self.all_audio_received = False
|
||||
self.end_signal_received = False
|
||||
self.completion_sent = False
|
||||
|
||||
# 使用智能句子缓冲系统
|
||||
print(f"🔊 添加文本到智能缓冲区")
|
||||
self._add_sentence_to_buffer(filtered_text)
|
||||
else:
|
||||
print(f"🔊 过滤后文本为空,不添加到缓冲区")
|
||||
|
||||
def process_complete_text(self, text):
|
||||
"""处理完整文本 - 强制刷新缓冲区"""
|
||||
@ -1683,6 +1846,14 @@ class OutputProcess:
|
||||
filtered_text = self._filter_parentheses_content(text.strip())
|
||||
|
||||
if filtered_text:
|
||||
# 重置所有播放完成检测状态 - 开始新的对话
|
||||
self.llm_generation_complete = False
|
||||
self.tts_generation_complete = False
|
||||
self.all_audio_received = False
|
||||
self.end_signal_received = False
|
||||
self.completion_sent = False
|
||||
print(f"🔊 处理完整文本:已重置所有播放完成检测状态")
|
||||
|
||||
# 直接添加到缓冲区并强制处理
|
||||
self.tts_buffer.append(filtered_text)
|
||||
self._process_tts_buffer()
|
||||
|
||||
@ -282,6 +282,12 @@ class ControlSystem:
|
||||
def _handle_idle_state(self):
|
||||
"""处理空闲状态"""
|
||||
if self.state == RecordingState.IDLE:
|
||||
# 检查是否应该立即启用录音
|
||||
# 如果刚刚从播放完成状态切换过来,不要立即启用录音
|
||||
if hasattr(self, '_just_finished_playing') and self._just_finished_playing:
|
||||
print("🎯 状态:IDLE(播放刚完成,等待延迟启用录音)")
|
||||
self._just_finished_playing = False # 重置标志
|
||||
else:
|
||||
# 启用输入进程录音功能
|
||||
self.input_command_queue.put(ControlCommand('enable_recording'))
|
||||
self.state = RecordingState.RECORDING
|
||||
@ -362,24 +368,45 @@ class ControlSystem:
|
||||
self.stats['total_conversations'] += 1
|
||||
print(f"📡 主控制:已更新统计,对话数 = {self.stats['total_conversations']}")
|
||||
|
||||
# 等待一段时间确保音频设备完全停止播放
|
||||
print(f"📡 主控制:等待音频设备完全停止...")
|
||||
time.sleep(1.0) # 增加1秒等待时间
|
||||
|
||||
# 切换到空闲状态
|
||||
# 切换到空闲状态,但先不启用录音
|
||||
old_state = self.state.value
|
||||
self.state = RecordingState.IDLE
|
||||
# 设置标志,表示刚从播放状态切换过来
|
||||
self._just_finished_playing = True
|
||||
print(f"🎯 状态:{old_state} → IDLE")
|
||||
|
||||
# 延迟重新启用录音,确保音频设备完全停止
|
||||
print(f"📡 主控制:延迟重新启用录音,避免录制到回声...")
|
||||
|
||||
# 延迟启用录音的函数
|
||||
def delayed_enable_recording():
|
||||
import threading
|
||||
import time
|
||||
|
||||
# 等待更长时间确保音频完全停止
|
||||
time.sleep(2.0) # 增加到2秒
|
||||
|
||||
# 检查输出队列是否还有音频数据
|
||||
output_queue_size = self.output_audio_queue.qsize()
|
||||
if output_queue_size > 0:
|
||||
print(f"📡 主控制:检测到输出队列仍有 {output_queue_size} 个音频块,继续等待...")
|
||||
time.sleep(1.0)
|
||||
|
||||
# 重新启用输入进程录音功能
|
||||
try:
|
||||
self.input_command_queue.put(ControlCommand('enable_recording'))
|
||||
print(f"📡 主控制:已发送 enable_recording 命令到输入进程")
|
||||
print(f"📡 主控制:延迟发送 enable_recording 命令到输入进程")
|
||||
print(f"📡 主控制:输入进程已重新启用,可以开始新的录音")
|
||||
# 更新状态为录音状态
|
||||
self.state = RecordingState.RECORDING
|
||||
print(f"🎯 状态:IDLE → RECORDING(延迟启用)")
|
||||
except Exception as e:
|
||||
print(f"❌ 主控制:发送 enable_recording 命令失败: {e}")
|
||||
print(f"❌ 主控制:延迟发送 enable_recording 命令失败: {e}")
|
||||
|
||||
print(f"📡 主控制:播放完成事件处理完成")
|
||||
# 在新线程中延迟启用录音
|
||||
threading.Thread(target=delayed_enable_recording, daemon=True).start()
|
||||
|
||||
print(f"📡 主控制:播放完成事件处理完成,将在后台延迟启用录音")
|
||||
|
||||
def _process_audio_pipeline(self):
|
||||
"""处理音频流水线:STT + LLM + TTS"""
|
||||
@ -414,6 +441,16 @@ class ControlSystem:
|
||||
print("❌ 文本转语音失败")
|
||||
self._handle_processing_failure()
|
||||
return
|
||||
|
||||
# 在禁用LLM的情况下,也需要发送完成信号
|
||||
print("📡 主控制:禁用LLM模式下发送完成信号")
|
||||
# 由于LLM未启用,直接发送LLM完成信号
|
||||
self._notify_llm_complete()
|
||||
# 发送TTS完成信号
|
||||
tts_complete_command = "TTS_COMPLETE:"
|
||||
self.output_audio_queue.put(tts_complete_command)
|
||||
# 发送结束信号
|
||||
self.output_audio_queue.put(None)
|
||||
else:
|
||||
print("ℹ️ 文本转语音功能已禁用")
|
||||
# 直接发送结束信号
|
||||
@ -436,6 +473,21 @@ class ControlSystem:
|
||||
self.state = RecordingState.IDLE
|
||||
self.processing_complete = True
|
||||
self.playback_complete = True
|
||||
|
||||
# 发送完成信号,防止输出进程等待
|
||||
print("📡 主控制:失败处理模式下发送完成信号")
|
||||
try:
|
||||
# 发送LLM完成信号
|
||||
self._notify_llm_complete()
|
||||
# 发送TTS完成信号
|
||||
tts_complete_command = "TTS_COMPLETE:"
|
||||
self.output_audio_queue.put(tts_complete_command)
|
||||
# 发送结束信号
|
||||
self.output_audio_queue.put(None)
|
||||
print("📡 主控制:已发送失败处理完成信号")
|
||||
except Exception as e:
|
||||
print(f"❌ 发送失败处理信号失败: {e}")
|
||||
|
||||
print("🎯 状态:PROCESSING → IDLE (失败)")
|
||||
|
||||
def _speech_to_text(self, audio_data: bytes) -> Optional[str]:
|
||||
@ -970,6 +1022,12 @@ class ControlSystem:
|
||||
# 通知输出进程所有文本已发送完成
|
||||
self._flush_output_process_tts_buffer()
|
||||
|
||||
# 在流式模式下,只发送结束信号,不发送TTS完成信号
|
||||
# 让OutputProcess在真正完成TTS生成时自己设置TTS完成状态
|
||||
print("📡 主控制:流式模式下发送结束信号")
|
||||
self.output_audio_queue.put(None)
|
||||
print("📡 主控制:已发送流式模式结束信号")
|
||||
|
||||
return accumulated_text != ""
|
||||
|
||||
else:
|
||||
@ -1202,31 +1260,10 @@ class ControlSystem:
|
||||
print(f"\n✅ TTS音频生成完成: {chunk_count} 块, {total_audio_size / 1024:.1f} KB")
|
||||
print(f"📊 队列大小变化: {queue_size_before} -> {self.output_audio_queue.qsize()}")
|
||||
|
||||
# 等待音频数据被输出进程完全处理
|
||||
print(f"📦 TTS音频数据已全部发送,等待输出进程处理...")
|
||||
max_wait_time = 30 # 最多等待30秒
|
||||
wait_start_time = time.time()
|
||||
last_queue_size = self.output_audio_queue.qsize()
|
||||
|
||||
while time.time() - wait_start_time < max_wait_time:
|
||||
current_queue_size = self.output_audio_queue.qsize()
|
||||
|
||||
# 如果队列为空或队列大小不再变化,说明音频数据已被处理
|
||||
if current_queue_size == 0:
|
||||
print(f"✅ 音频队列已清空,可以发送结束信号")
|
||||
break
|
||||
elif current_queue_size == last_queue_size:
|
||||
# 队列大小不再变化,可能处理完成
|
||||
print(f"📦 队列大小稳定在 {current_queue_size},等待确认...")
|
||||
time.sleep(1)
|
||||
if self.output_audio_queue.qsize() == current_queue_size:
|
||||
print(f"✅ 队列大小稳定,可以发送结束信号")
|
||||
break
|
||||
else:
|
||||
print(f"📦 等待队列处理: {current_queue_size} 个项目待处理")
|
||||
last_queue_size = current_queue_size
|
||||
|
||||
time.sleep(0.5)
|
||||
# 注意:TTS音频数据已经全部发送到队列,不需要等待队列清空
|
||||
# 立即发送结束信号,让OutputProcess的增强播放完成检测机制处理
|
||||
print(f"📦 TTS音频数据已全部发送到队列,立即发送结束信号")
|
||||
print(f"📦 让OutputProcess的增强播放完成检测机制来等待真正的播放完成")
|
||||
|
||||
# 发送TTS完成信号
|
||||
print(f"📦 发送TTS完成信号到输出进程")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user