diff --git a/audio_processes.py b/audio_processes.py index 5e46e2a..c3bd385 100644 --- a/audio_processes.py +++ b/audio_processes.py @@ -960,7 +960,20 @@ class OutputProcess: print(f"📥 输出进程收到结束信号") end_signal_received = True self.end_signal_received = True - self.all_audio_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 @@ -991,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 @@ -998,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 @@ -1011,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:"): @@ -1024,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)} 字节") @@ -1246,7 +1284,12 @@ class OutputProcess: 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 @@ -1254,7 +1297,8 @@ class OutputProcess: self.pre_buffer_empty and self.playback_buffer_empty and self.no_active_playback and # 使用状态变量 - tts_queue_size == 0 + tts_queue_size == 0 and + not tts_is_generating # 新增:确保TTS不在生成中 ) if all_conditions_met: @@ -1263,10 +1307,17 @@ class OutputProcess: 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: - print(f"⚠️ 从未开始播放且无音频数据,可能播放失败,强制结束") - return True + # 检查是否有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 @@ -1278,7 +1329,8 @@ class OutputProcess: 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 self.no_active_playback: # 使用状态变量 @@ -1473,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, @@ -1646,6 +1702,10 @@ class OutputProcess: 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): @@ -1760,6 +1820,17 @@ class OutputProcess: 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) @@ -1775,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() diff --git a/control_system.py b/control_system.py index f5def4b..5dafecb 100644 --- a/control_system.py +++ b/control_system.py @@ -282,10 +282,16 @@ class ControlSystem: def _handle_idle_state(self): """处理空闲状态""" if self.state == RecordingState.IDLE: - # 启用输入进程录音功能 - self.input_command_queue.put(ControlCommand('enable_recording')) - self.state = RecordingState.RECORDING - print("🎯 状态:IDLE → RECORDING") + # 检查是否应该立即启用录音 + # 如果刚刚从播放完成状态切换过来,不要立即启用录音 + 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 + print("🎯 状态:IDLE → RECORDING") def _handle_recording_state(self): """处理录音状态""" @@ -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") - # 重新启用输入进程录音功能 - try: - self.input_command_queue.put(ControlCommand('enable_recording')) - print(f"📡 主控制:已发送 enable_recording 命令到输入进程") - print(f"📡 主控制:输入进程已重新启用,可以开始新的录音") - except Exception as e: - print(f"❌ 主控制:发送 enable_recording 命令失败: {e}") + # 延迟重新启用录音,确保音频设备完全停止 + print(f"📡 主控制:延迟重新启用录音,避免录制到回声...") - 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"📡 主控制:输入进程已重新启用,可以开始新的录音") + # 更新状态为录音状态 + self.state = RecordingState.RECORDING + print(f"🎯 状态:IDLE → RECORDING(延迟启用)") + except Exception as e: + print(f"❌ 主控制:延迟发送 enable_recording 命令失败: {e}") + + # 在新线程中延迟启用录音 + 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完成信号到输出进程")