diff --git a/audio_processes.py b/audio_processes.py index c3bd385..de44915 100644 --- a/audio_processes.py +++ b/audio_processes.py @@ -106,7 +106,7 @@ class InputProcess: # TTS 工作线程 self.tts_worker_running = True self.tts_worker_thread = None - self.tts_task_queue = mp.Queue(maxsize=10) + self.tts_task_queue = mp.Queue(maxsize=20) # 增加到20个任务容量 # TTS 配置 self.tts_url = "https://openspeech.bytedance.com/api/v3/tts/unidirectional" @@ -463,13 +463,10 @@ 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 @@ -543,7 +540,14 @@ class InputProcess: self.logger.info(progress) continue + if data.get("code", 0) == 0 and "sentence" in data and data["sentence"]: + # 处理句子信息 + continue + if data.get("code", 0) == 20000000: + # 真正的结束信号 + print(f"🎵 收到TTS流结束信号,总共处理了 {chunk_count} 个音频块") + print(f"🎵 总音频大小: {total_audio_size} 字节 ({total_audio_size/1024:.1f} KB)") break if data.get("code", 0) > 0: @@ -586,7 +590,7 @@ class OutputProcess: self.FORMAT = pyaudio.paInt16 self.CHANNELS = 1 self.RATE = 16000 - self.CHUNK_SIZE = 2048 # 增加缓冲区大小,减少卡顿 + self.CHUNK_SIZE = 1024 # 减小缓冲区大小,提高响应性 # 播放状态管理 - 借鉴 recorder.py 的状态管理模式 self.is_playing = False @@ -595,22 +599,25 @@ class OutputProcess: self.total_chunks_played = 0 self.total_audio_size = 0 self.last_playback_time = 0 # 最后播放时间戳 - self.playback_cooldown_period = 0.1 # 播放冷却时间(秒)- 防止回声,减少到0.1秒 + self.playback_cooldown_period = 0.05 # 播放冷却时间(秒)- 防止回声,减少到0.05秒 self.playback_completed = False # 播放完成标志 self.end_signal_received = False # 结束信号接收标志 # 智能缓冲系统 - 借鉴 recorder.py 的智能句子累积策略 self.preload_buffer = [] # 预加载缓冲区(保留用于音频块) - self.preload_size = 3 # 预加载3个音频块 + self.preload_size = 6 # 预加载6个音频块(增加缓冲减少卡顿) + + # 简化的音频系统 - 直接使用预加载缓冲区 + self.audio_queue_lock = None # 音频队列操作锁(在run方法中初始化) # 智能句子缓冲系统 - 从 recorder.py 借鉴的核心机制 self.tts_buffer = [] # 智能句子缓冲区 - self.tts_buffer_max_size = 3 # 最多缓冲3个句子 - self.tts_buffer_min_size = 1 # 最少1个句子 - self.tts_accumulation_time = 0.2 # 200ms积累窗口 + self.tts_buffer_max_size = 5 # 最多缓冲5个句子(增加缓冲减少卡顿) + self.tts_buffer_min_size = 2 # 最少2个句子(增加最小缓冲) + self.tts_accumulation_time = 0.15 # 150ms积累窗口(减少等待时间) self.tts_last_trigger_time = 0 # 上次触发TTS的时间 self.tts_pending_sentences = [] # 待处理的句子 - self.min_buffer_size = 1 # 最小缓冲区大小 + self.min_buffer_size = 2 # 最小缓冲区大小(增加最小缓冲) self.audio_device_healthy = True # 音频设备健康状态 @@ -623,6 +630,16 @@ class OutputProcess: self.playback_timeout = 5.0 # 播放超时时间(秒) self.completion_sent = False # 防止重复发送完成事件 + # 性能监控 + self.performance_stats = { + 'total_chunks_played': 0, + 'total_audio_size': 0, + 'avg_buffer_size': 0, + 'max_buffer_size': 0, + 'tts_wait_time': 0, + 'playback_delay': 0 + } + # 增强的播放完成检测状态 self.llm_generation_complete = False # LLM生成是否完成 self.tts_generation_complete = False # TTS生成是否完成 @@ -642,7 +659,7 @@ class OutputProcess: # TTS 工作线程 self.tts_worker_running = True self.tts_worker_thread = None - self.tts_task_queue = mp.Queue(maxsize=10) + self.tts_task_queue = mp.Queue(maxsize=20) # 增加到20个任务容量 # TTS 配置 self.tts_url = "https://openspeech.bytedance.com/api/v3/tts/unidirectional" @@ -681,7 +698,6 @@ class OutputProcess: max_wait_time = 10 # 最多等待10秒 wait_start_time = time.time() - print(f"🔊 播放工作线程等待音频设备就绪... audio={self.audio is not None}, running={self.running}") while (self.audio is None or not self.running) and (time.time() - wait_start_time) < max_wait_time: time.sleep(0.1) @@ -741,15 +757,11 @@ class OutputProcess: # 播放音频块 if chunks_played == 0: # 只在第一次播放时打印详细信息 print(f"🔊 开始播放音频块 {chunks_played + 1}") - print(f"🔍 播放工作线程检查: 音频块大小={len(audio_chunk)}字节, " - 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: @@ -762,6 +774,10 @@ class OutputProcess: chunks_played += 1 total_size += len(audio_chunk) + # 更新性能统计 + self.performance_stats['total_chunks_played'] += 1 + self.performance_stats['total_audio_size'] += len(audio_chunk) + # 减少进度显示频率 if chunks_played % 10 == 0 or chunks_played <= 3: progress = f"🔊 播放工作: {chunks_played} 块 | {total_size / 1024:.1f} KB" @@ -774,38 +790,45 @@ class OutputProcess: len(self.preload_buffer) == 0 and self.tts_task_queue.qsize() == 0 and not self.playback_completed): # 防止重复设置 - print(f"🔊 播放工作线程:播放完成,设置播放完成标志") - print(f"🔊 播放工作线程:播放缓冲={len(self.playback_buffer)}, 预加载={len(self.preload_buffer)}, TTS队列={self.tts_task_queue.qsize()}") self.playback_completed = True - print(f"🔊 播放工作线程:playback_completed标志已设置为{self.playback_completed}") # 不在这里直接调用_finish_playback,让主处理循环处理 else: # 空音频块,不改变播放状态,继续下一个 - print(f"🔊 播放工作线程:遇到空音频块,跳过") continue else: # 缓冲区为空,短暂休眠,减少CPU占用 - # 只有在确定没有音频播放时才设置状态为 False + # 只有在确定两个缓冲区都为空且没有音频播放时才设置状态为 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秒没有播放新音频 + # 只有在两个缓冲区都为空且超过1秒没有播放新音频时才停止播放 + if (len(self.playback_buffer) == 0 and + len(self.preload_buffer) == 0 and + time_since_last_chunk > 1.0): self.currently_playing = False - print(f"🔊 播放工作线程:缓冲区为空且{time_since_last_chunk:.1f}秒无新音频,设置 currently_playing = False") + elif len(self.playback_buffer) == 0 and len(self.preload_buffer) > 0: + # 播放缓冲区为空但预加载缓冲区有数据,自动转移数据 + transfer_count = min(3, len(self.preload_buffer)) # 一次转移3个块 + for _ in range(transfer_count): + if self.preload_buffer: + self.playback_buffer.append(self.preload_buffer.pop(0)) + # 保持 currently_playing 为 True,因为有新数据要播放 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: # 异常情况下,等待更长时间 + if (len(self.playback_buffer) == 0 and + len(self.preload_buffer) == 0 and + time_since_last_chunk > 1.5): # 异常情况下,等待更长时间且确保缓冲区为空 self.currently_playing = False - print(f"🔊 播放工作线程:异常情况下设置 currently_playing = False") else: - print(f"🔊 播放工作线程:异常但保持 currently_playing = True(最后播放于{time_since_last_chunk:.1f}秒前)") + # 保持播放状态,继续处理 + pass time.sleep(0.1) print(f"\n✅ 播放工作线程结束: 总计 {chunks_played} 块, {total_size / 1024:.1f} KB") @@ -814,7 +837,6 @@ class OutputProcess: # 线程结束时确保状态正确 if self.currently_playing: self.currently_playing = False - print(f"🔊 播放工作线程:线程结束,设置 currently_playing = False") if playback_stream: try: playback_stream.stop_stream() @@ -836,12 +858,19 @@ class OutputProcess: self.logger.info("输出进程启动") self._setup_audio() + # 初始化线程锁(必须在run方法中初始化,避免pickle错误) + self.audio_queue_lock = threading.Lock() + try: while self.running: # 1. 处理音频队列(数据接收) self._process_audio_queue() - # 2. 检查设备健康状态和冷却期 - 防止回声 + # 2. 检查播放状态 + if self.end_signal_received: + self._check_playback_completion() + + # 3. 检查设备健康状态和冷却期 - 防止回声 current_time = time.time() time_since_last_play = current_time - self.last_playback_time in_cooldown = (self.last_playback_time > 0 and @@ -874,7 +903,6 @@ class OutputProcess: if self.end_signal_received: # 使用增强的播放完成检测 if self._check_enhanced_playback_completion(): - print(f"📥 播放状态时增强播放完成检测通过,处理结束信号") self._finish_playback() time.sleep(0.1) # 播放时增加延迟减少CPU使用 @@ -891,14 +919,14 @@ class OutputProcess: self.last_playback_time = time.time() print(f"🎵 主循环检测:开始播放预加载音频,播放缓冲区大小: {len(self.playback_buffer)}") - # 4. 显示播放进度 + # 4. 显示播放进度和性能监控 self._show_progress() + self._update_performance_stats() # 5. 主动检查播放完成(无论什么状态都要检查) if self.end_signal_received: # 使用增强的播放完成检测 if self._check_enhanced_playback_completion(): - print(f"📥 主循环增强播放完成检测通过,处理结束信号") self._finish_playback() # 6. 借鉴 recorder.py: 根据播放状态调整休眠时间,优化性能 @@ -983,7 +1011,6 @@ class OutputProcess: # 使用增强的播放完成检测 if self._check_enhanced_playback_completion(): - print(f"📥 增强播放完成检测通过,处理结束信号") self._finish_playback() return else: @@ -1067,12 +1094,7 @@ class OutputProcess: print(f"📥 输出进程收到音频数据: {len(audio_data)} 字节") # 直接添加到预加载缓冲区 - print(f"🔍 添加音频到预加载缓冲区: 音频大小={len(audio_data)}字节, " - f"添加前预加载缓冲区大小={len(self.preload_buffer)}, " - f"添加前播放缓冲区大小={len(self.playback_buffer)}, " - f"is_playing={self.is_playing}") self.preload_buffer.append(audio_data) - print(f"🔍 添加后预加载缓冲区大小={len(self.preload_buffer)}") # 检查是否应该开始播放或补充播放缓冲区 if not self.is_playing and len(self.preload_buffer) >= self.preload_size: @@ -1083,20 +1105,17 @@ class OutputProcess: self.last_playback_time = 0 # 重置播放时间,避免立即触发冷却期 # 确保播放工作线程知道有数据要播放 if not self.currently_playing: - print(f"🔍 启动播放时确保 currently_playing = True") # 播放工作线程会自动检测播放缓冲区并开始播放 + pass 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: - # 正在播放时,保持播放缓冲区有足够的数据 - transfer_count = min(2, len(self.preload_buffer)) # 每次转移2个块 + elif self.is_playing and len(self.playback_buffer) < 4 and len(self.preload_buffer) > 0: + # 正在播放时,保持播放缓冲区有足够的数据(增加到4个块) + transfer_count = min(3, len(self.preload_buffer)) # 每次转移3个块 for _ in range(transfer_count): if self.preload_buffer: self.playback_buffer.append(self.preload_buffer.pop(0)) - print(f"🔍 播放中补充数据: 转移{transfer_count}个块,播放缓冲区={len(self.playback_buffer)}, 预加载={len(self.preload_buffer)}") elif end_signal_received and not self.is_playing and len(self.playback_buffer) == 0 and len(self.preload_buffer) > 0: # 关键修复:收到结束信号后,如果播放缓冲区为空但预加载缓冲区有数据,强制转移 - print(f"🔍 结束信号模式下强制转移数据: 预加载缓冲区有 {len(self.preload_buffer)} 个数据块") self.playback_buffer.extend(self.preload_buffer) self.preload_buffer.clear() self.is_playing = True @@ -1111,11 +1130,9 @@ class OutputProcess: if end_signal_received: # 使用增强的播放完成检测 if self._check_enhanced_playback_completion(): - print(f"📥 队列空时增强播放完成检测通过,处理结束信号") self._finish_playback() return else: - print(f"📥 队列空时增强播放完成检测未通过,继续等待") # 如果还有数据要播放,继续等待 tts_queue_size = self.tts_task_queue.qsize() playback_queue_size = len(self.playback_buffer) + len(self.preload_buffer) @@ -1151,7 +1168,6 @@ class OutputProcess: for _ in range(transfer_count): if self.preload_buffer: self.playback_buffer.append(self.preload_buffer.pop(0)) - print(f"🔍 队列空时补充数据: 转移{transfer_count}个块,播放缓冲区={len(self.playback_buffer)}, 预加载={len(self.preload_buffer)}") # 退出循环,避免过度占用CPU if processed_count > 0: @@ -1161,7 +1177,6 @@ class OutputProcess: if self.end_signal_received: # 使用增强的播放完成检测 if self._check_enhanced_playback_completion(): - print(f"📥 无数据处理时增强播放完成检测通过,处理结束信号") self._finish_playback() time.sleep(0.01) @@ -1215,6 +1230,7 @@ class OutputProcess: print("📡 输出进程:开始执行播放完成逻辑") + self.is_playing = False self.playback_buffer.clear() self.preload_buffer.clear() @@ -1267,27 +1283,16 @@ class OutputProcess: tts_queue_size = self.tts_task_queue.qsize() - print(f"🔍 增强播放完成检查:") - print(f" - LLM生成完成: {self.llm_generation_complete}") - print(f" - TTS生成完成: {self.tts_generation_complete}") - print(f" - 所有音频已接收: {self.all_audio_received}") - print(f" - 预缓冲区为空: {self.pre_buffer_empty}") - print(f" - 播放缓冲区为空: {self.playback_buffer_empty}") - 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 = ( @@ -1378,7 +1383,6 @@ class OutputProcess: # 如果TTS还未完成,但LLM已完成,等待TTS完成 if not self.tts_generation_complete: - print(f"⏳ 等待TTS生成完成...") return False # 如果音频还未完全接收,等待接收完成 @@ -1388,39 +1392,37 @@ class OutputProcess: return False + def _check_playback_completion(self): - """检查播放完成状态 - 独立的播放完成检测方法""" + """简化的播放完成状态检查""" if not self.end_signal_received: return tts_queue_size = self.tts_task_queue.qsize() playback_queue_size = len(self.playback_buffer) + len(self.preload_buffer) - print(f"🔍 播放完成检查: TTS队列={tts_queue_size}, 播放缓冲={len(self.playback_buffer)}, 预加载={len(self.preload_buffer)}, 正在播放={self.currently_playing}, 播放完成标志={self.playback_completed}") + # 调试日志:播放完成状态 + print(f"🔍 播放完成检查:") + print(f" - end_signal_received: {self.end_signal_received}") + print(f" - tts_queue_size: {tts_queue_size}") + print(f" - playback_buffer: {len(self.playback_buffer)}") + print(f" - preload_buffer: {len(self.preload_buffer)}") + print(f" - currently_playing: {self.currently_playing}") + print(f" - playback_completed: {self.playback_completed}") + print(f" - completion_sent: {self.completion_sent}") - # 检查条件1: 播放完成标志被设置 - if self.playback_completed: - print(f"✅ 检测到播放完成标志,触发播放完成") - print(f"📥 调用 _finish_playback() 前,completion_sent={self.completion_sent}") - self._finish_playback() - print(f"📥 调用 _finish_playback() 后,completion_sent={self.completion_sent}") - # 重要:重置播放完成标志,防止重复触发 - self.playback_completed = False - print(f"📥 已重置播放完成标志,防止重复触发") - return - - # 检查条件2: 所有队列为空且没有在播放 + # 检查条件:所有队列为空且没有在播放 if tts_queue_size == 0 and playback_queue_size == 0 and not self.currently_playing: print(f"✅ 所有队列已清空且播放器空闲,触发播放完成") print(f"📥 调用 _finish_playback() 前,completion_sent={self.completion_sent}") self._finish_playback() print(f"📥 调用 _finish_playback() 后,completion_sent={self.completion_sent}") - # 重要:重置结束信号标志,防止重复触发 + # 重置结束信号标志,防止重复触发 self.end_signal_received = False print(f"📥 已重置结束信号标志,防止重复触发") return - # 检查条件3: 所有队列为空但播放器还在播放(最后一个音频块) + # 检查条件:所有队列为空但播放器还在播放(最后一个音频块) if tts_queue_size == 0 and playback_queue_size == 0 and self.currently_playing: print(f"⏳ 等待最后一个音频块播放完成...") time.sleep(0.3) @@ -1429,7 +1431,7 @@ class OutputProcess: print(f"📥 调用 _finish_playback() 前,completion_sent={self.completion_sent}") self._finish_playback() print(f"📥 调用 _finish_playback() 后,completion_sent={self.completion_sent}") - # 重要:重置结束信号标志,防止重复触发 + # 重置结束信号标志,防止重复触发 self.end_signal_received = False print(f"📥 已重置结束信号标志,防止重复触发") return @@ -1492,7 +1494,7 @@ class OutputProcess: task_type, content = task if task_type == "tts_sentence": - # 生成音频数据并发送到统一播放队列 + # 生成音频数据并发送到播放队列 self._generate_tts_audio(content) except queue.Empty: @@ -1503,31 +1505,26 @@ 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]}...") + print(f"🎵 OutputProcess开始生成TTS音频: {text[:50]}...") + print(f"🎵 文本总长度: {len(text)} 字符") - # 清空所有缓冲区,确保新的音频不被旧数据干扰 - self.playback_buffer.clear() - self.preload_buffer.clear() - self.is_playing = False - self.completion_sent = False # 重置完成标记 + # 简化:直接使用预加载缓冲区 + print(f"🎵 开始生成TTS音频,将直接添加到预加载缓冲区") - # 重置TTS生成完成状态 - 关键修复 + # 重置播放状态 + self.completion_sent = False + self._last_audio_size = 0 self.tts_generation_complete = False - print(f"🔊 已重置TTS生成完成状态为False,开始新的TTS生成") # 构建请求头 headers = { @@ -1565,12 +1562,14 @@ class OutputProcess: self.logger.error(f"TTS请求失败: {response.status_code}") return False + print(f"🎵 TTS请求成功,状态码: {response.status_code}") + # 处理流式响应 - 借鉴 recorder.py 的优化策略 total_audio_size = 0 chunk_count = 0 success_count = 0 - self.logger.info("开始接收TTS音频流...") + print(f"🎵 开始接收TTS音频流...") for chunk in response.iter_lines(decode_unicode=True): if not chunk: @@ -1578,19 +1577,29 @@ class OutputProcess: try: data = json.loads(chunk) - if data.get("code", 0) == 0 and "data" in data and data["data"]: chunk_audio = base64.b64decode(data["data"]) audio_size = len(chunk_audio) total_audio_size += audio_size chunk_count += 1 - # 借鉴 recorder.py: 使用预加载缓冲区机制 + # 检查音频数据是否异常小 + if audio_size < 100: + print(f"⚠️ 警告:音频块 {chunk_count} 大小异常小: {audio_size} 字节") + + # 检查是否连续收到小音频块(但不警告最后一个块,通常较小) + if hasattr(self, '_last_audio_size') and self._last_audio_size > 0 and chunk_count < 15: # 不检查最后几个块 + if audio_size < self._last_audio_size * 0.5: + print(f"⚠️ 警告:音频块 {chunk_count} 大小突然减小: {self._last_audio_size} -> {audio_size}") + self._last_audio_size = audio_size + + # 简化:直接添加到预加载缓冲区 try: self.preload_buffer.append(chunk_audio) success_count += 1 - # 检查是否应该开始播放(借鉴 recorder.py 的预加载策略) + + # 检查是否应该开始播放 if (not self.is_playing and len(self.preload_buffer) >= self.preload_size): # 将预加载的数据移到播放缓冲区 @@ -1601,12 +1610,9 @@ class OutputProcess: self.logger.info("开始播放TTS音频(预加载完成)") # 减少进度显示频率 - if chunk_count % 20 == 0: # 减少显示频率 - progress = (f"生成音频: {chunk_count} 块 | 成功: {success_count} | " - f"{total_audio_size / 1024:.1f} KB | " - f"预加载: {len(self.preload_buffer)} | " - f"播放缓冲: {len(self.playback_buffer)}") - self.logger.info(progress) + if chunk_count % 50 == 0: # 进一步减少显示频率 + progress = (f"🎵 TTS生成中: {chunk_count} 块 | {total_audio_size / 1024:.1f} KB") + print(f"\r{progress}", end='', flush=True) except Exception as e: self.logger.error(f"添加音频到预加载缓冲区失败: {e}") @@ -1614,7 +1620,14 @@ class OutputProcess: continue + if data.get("code", 0) == 0 and "sentence" in data and data["sentence"]: + # 处理句子信息 + continue + if data.get("code", 0) == 20000000: + # 真正的结束信号 + print(f"🎵 收到TTS流结束信号,总共处理了 {chunk_count} 个音频块") + print(f"🎵 总音频大小: {total_audio_size} 字节 ({total_audio_size/1024:.1f} KB)") break if data.get("code", 0) > 0: @@ -1625,7 +1638,6 @@ 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) @@ -1638,15 +1650,13 @@ class OutputProcess: 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生成已完成") - # 注意:不在这里直接调用等待播放完成,让统一的增强播放完成检测机制处理 - # 这样可以避免在TTS还在生成后续音频时就误判播放完成 + # 简化:直接使用统一播放完成检测机制 self.logger.info("TTS生成完成,等待统一播放完成检测机制处理...") return success_count > 0 @@ -1662,9 +1672,54 @@ class OutputProcess: # ========== 智能句子缓冲系统 - 从 recorder.py 借鉴 ========== def _should_trigger_tts(self, sentence): - """智能判断是否应该触发TTS - 借鉴 recorder.py 的策略""" + """智能判断是否应该触发TTS - 确保首句足够长且积累足够内容""" current_time = time.time() + # 修改策略:允许合理的TTS并行生成,但控制队列长度 + tts_queue_size = self.tts_task_queue.qsize() + if tts_queue_size >= 3: # 允许最多3个TTS任务在队列中 + print(f"🎵 TTS队列达到上限: 当前队列大小={tts_queue_size}, 暂时跳过") + return False + + # 首次播放的特殊处理:确保有足够内容 + if not hasattr(self, '_first_playback_started') or not self._first_playback_started: + total_buffered_text = ''.join(self.tts_buffer) + sentence + + # 首次播放必须满足以下条件之一: + # 1. 总文本长度超过40字符且至少有2个句子 + if len(total_buffered_text) >= 40 and len(self.tts_buffer) >= 1: + print(f"🎵 首次播放触发:总长度{len(total_buffered_text)}字符,{len(self.tts_buffer)+1}个句子") + self._first_playback_started = True + return True + # 2. 有1个完整长句子(超过25字符) + elif len(sentence) >= 25 and self._is_complete_sentence(sentence) and len(self.tts_buffer) >= 1: + print(f"🎵 首次播放触发:长句子{len(sentence)}字符+缓冲内容") + self._first_playback_started = True + return True + # 3. 缓冲区达到最大值 + elif len(self.tts_buffer) >= self.tts_buffer_max_size: + print(f"🎵 首次播放触发:缓冲区达到最大值{len(self.tts_buffer)}") + self._first_playback_started = True + return True + # 4. 超过5秒还没触发(防止无限等待) + elif hasattr(self, '_first_text_time') and (current_time - self._first_text_time) > 5.0: + print(f"🎵 首次播放触发:超时5秒,当前长度{len(total_buffered_text)}字符") + self._first_playback_started = True + return True + else: + # 首次播放前记录第一个文本的时间 + if not hasattr(self, '_first_text_time'): + self._first_text_time = current_time + return False + + # 非首次播放的正常逻辑 + + # 再次检查TTS队列状态(防止在首次播放检查后队列又有变化) + tts_queue_size = self.tts_task_queue.qsize() + if tts_queue_size >= 3: # 保持一致的队列限制 + print(f"🎵 TTS队列达到上限(非首次): 当前队列大小={tts_queue_size}, 暂时跳过") + return False + # 检查缓冲区大小 if len(self.tts_buffer) >= self.tts_buffer_max_size: return True @@ -1678,24 +1733,26 @@ class OutputProcess: if self._is_complete_sentence(sentence): return True - # 检查句子特征 - 长句子优先(50字符以上) - if len(sentence) > 50: # 超过50字符的句子立即触发 + # 检查句子特征 - 优化:降低长句子触发阈值(30字符以上) + if len(sentence) > 30: # 超过30字符的句子立即触发 return True - # 中等长度句子(30-50字符)如果有结束标点也触发 - if len(sentence) > 30: + # 中等长度句子(20-30字符)如果有结束标点也触发 + if len(sentence) > 20: end_punctuations = ['。', '!', '?', '.', '!', '?'] if any(sentence.strip().endswith(p) for p in end_punctuations): return True + # 优化:即使短句子,如果缓冲区有内容且时间过半也触发 + if len(self.tts_buffer) > 0 and time_since_last >= (self.tts_accumulation_time * 0.7): + return True + # 短句子只在缓冲区较多或时间窗口到期时触发 return False def _process_tts_buffer(self): """处理TTS缓冲区 - 发送累积的句子到TTS""" - print(f"🔊 处理TTS缓冲区,当前缓冲区内容: {self.tts_buffer}") if not self.tts_buffer: - print(f"🔊 TTS缓冲区为空,跳过处理") return # 合并缓冲区的句子 @@ -1704,10 +1761,8 @@ class OutputProcess: # 重置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() @@ -1716,23 +1771,18 @@ class OutputProcess: # 清空缓冲区 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 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): @@ -1830,6 +1880,11 @@ class OutputProcess: self.all_audio_received = False self.end_signal_received = False self.completion_sent = False + # 重置首次播放状态 + self._first_playback_started = False + if hasattr(self, '_first_text_time'): + delattr(self, '_first_text_time') + print(f"🔊 已重置首次播放状态,新的对话将重新积累内容") # 使用智能句子缓冲系统 print(f"🔊 添加文本到智能缓冲区") @@ -1852,6 +1907,10 @@ class OutputProcess: self.all_audio_received = False self.end_signal_received = False self.completion_sent = False + # 重置首次播放状态 + self._first_playback_started = False + if hasattr(self, '_first_text_time'): + delattr(self, '_first_text_time') print(f"🔊 处理完整文本:已重置所有播放完成检测状态") # 直接添加到缓冲区并强制处理 @@ -1860,6 +1919,30 @@ class OutputProcess: # ========== 原有方法保持不变 ========== + def _update_performance_stats(self): + """更新性能统计信息""" + current_buffer_size = len(self.playback_buffer) + len(self.preload_buffer) + self.performance_stats['avg_buffer_size'] = ( + self.performance_stats['avg_buffer_size'] * 0.9 + current_buffer_size * 0.1 + ) + if current_buffer_size > self.performance_stats['max_buffer_size']: + self.performance_stats['max_buffer_size'] = current_buffer_size + + # 每5秒打印一次性能统计 + if hasattr(self, '_last_stats_time'): + if time.time() - self._last_stats_time >= 5.0: + self._print_performance_stats() + self._last_stats_time = time.time() + else: + self._last_stats_time = time.time() + + def _print_performance_stats(self): + """打印性能统计信息""" + stats = self.performance_stats + print(f"📊 性能统计: 平均缓冲={stats['avg_buffer_size']:.1f}, " + f"最大缓冲={stats['max_buffer_size']}, 已播放={stats['total_chunks_played']}块, " + f"音频大小={stats['total_audio_size']/1024:.1f}KB") + def process_tts_request(self, text): """处理TTS请求的公共接口 - 兼容原有接口""" # 使用新的智能缓冲系统 diff --git a/control_system.py b/control_system.py index 5dafecb..8d10e9c 100644 --- a/control_system.py +++ b/control_system.py @@ -329,7 +329,6 @@ class ControlSystem: if event.event_type == 'playback_complete': print("📡 主控制:收到播放完成事件") - print(f"📡 主控制:事件详情 - 类型: {event.event_type}, 元数据: {event.metadata}") self._handle_playback_complete(event) except queue.Empty: @@ -356,17 +355,12 @@ class ControlSystem: def _handle_playback_complete(self, event: ProcessEvent): """处理播放完成事件""" - print(f"📡 主控制:开始处理播放完成事件") - print(f"📡 主控制:当前状态 = {self.state.value}") - print(f"📡 主控制:事件元数据 = {event.metadata}") # 标记播放完成 self.playback_complete = True - print(f"📡 主控制:已设置 playback_complete = True") # 更新统计 self.stats['total_conversations'] += 1 - print(f"📡 主控制:已更新统计,对话数 = {self.stats['total_conversations']}") # 切换到空闲状态,但先不启用录音 old_state = self.state.value @@ -376,7 +370,6 @@ class ControlSystem: print(f"🎯 状态:{old_state} → IDLE") # 延迟重新启用录音,确保音频设备完全停止 - print(f"📡 主控制:延迟重新启用录音,避免录制到回声...") # 延迟启用录音的函数 def delayed_enable_recording(): @@ -389,14 +382,11 @@ class ControlSystem: # 检查输出队列是否还有音频数据 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(延迟启用)") @@ -406,7 +396,6 @@ class ControlSystem: # 在新线程中延迟启用录音 threading.Thread(target=delayed_enable_recording, daemon=True).start() - print(f"📡 主控制:播放完成事件处理完成,将在后台延迟启用录音") def _process_audio_pipeline(self): """处理音频流水线:STT + LLM + TTS""" @@ -443,8 +432,7 @@ class ControlSystem: return # 在禁用LLM的情况下,也需要发送完成信号 - print("📡 主控制:禁用LLM模式下发送完成信号") - # 由于LLM未启用,直接发送LLM完成信号 + # 由于LLM未启用,直接发送LLM完成信号 self._notify_llm_complete() # 发送TTS完成信号 tts_complete_command = "TTS_COMPLETE:" @@ -475,7 +463,6 @@ class ControlSystem: self.playback_complete = True # 发送完成信号,防止输出进程等待 - print("📡 主控制:失败处理模式下发送完成信号") try: # 发送LLM完成信号 self._notify_llm_complete() @@ -484,7 +471,6 @@ class ControlSystem: self.output_audio_queue.put(tts_complete_command) # 发送结束信号 self.output_audio_queue.put(None) - print("📡 主控制:已发送失败处理完成信号") except Exception as e: print(f"❌ 发送失败处理信号失败: {e}") @@ -1024,9 +1010,7 @@ class ControlSystem: # 在流式模式下,只发送结束信号,不发送TTS完成信号 # 让OutputProcess在真正完成TTS生成时自己设置TTS完成状态 - print("📡 主控制:流式模式下发送结束信号") self.output_audio_queue.put(None) - print("📡 主控制:已发送流式模式结束信号") return accumulated_text != "" @@ -1077,7 +1061,6 @@ class ControlSystem: # 发送LLM完成信号 llm_complete_command = "LLM_COMPLETE:" self.output_audio_queue.put(llm_complete_command) - print("📡 主控制:已发送LLM生成完成信号") return True except Exception as e: print(f"❌ 发送LLM完成信号失败: {e}") @@ -1246,8 +1229,14 @@ class ControlSystem: progress = f"📥 TTS生成: {chunk_count} 块 | {total_audio_size / 1024:.1f} KB" print(f"\r{progress}", end='', flush=True) + elif data.get("code", 0) == 0 and "sentence" in data and data["sentence"]: + # 处理句子信息 - 新增 + sentence_info = data["sentence"] + print(f"📝 TTS句子信息: {sentence_info}") + continue + elif data.get("code", 0) == 20000000: - print(f"🏁 收到TTS结束信号") + print(f"🏁 收到TTS流结束信号") break elif data.get("code", 0) > 0: print(f"❌ TTS错误响应: {data}") diff --git a/logs/InputProcess_20250921_154603.log b/logs/InputProcess_20250921_154603.log new file mode 100644 index 0000000..9231cb8 --- /dev/null +++ b/logs/InputProcess_20250921_154603.log @@ -0,0 +1,3 @@ +2025-09-21 15:46:03 - InputProcess_logger - INFO - 日志系统初始化完成 - 进程: InputProcess +2025-09-21 15:46:03 - InputProcess_logger - INFO - 日志文件: logs/InputProcess_20250921_154603.log +2025-09-21 15:46:03 - InputProcess_logger - INFO - [InputProcess] TTS工作线程已启动 diff --git a/logs/InputProcess_20250921_154743.log b/logs/InputProcess_20250921_154743.log new file mode 100644 index 0000000..2f01639 --- /dev/null +++ b/logs/InputProcess_20250921_154743.log @@ -0,0 +1,3 @@ +2025-09-21 15:47:43 - InputProcess_logger - INFO - 日志系统初始化完成 - 进程: InputProcess +2025-09-21 15:47:43 - InputProcess_logger - INFO - 日志文件: logs/InputProcess_20250921_154743.log +2025-09-21 15:47:43 - InputProcess_logger - INFO - [InputProcess] TTS工作线程已启动 diff --git a/test_first_playback.py b/test_first_playback.py new file mode 100644 index 0000000..69c2333 --- /dev/null +++ b/test_first_playback.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +首次播放逻辑专项测试 +验证第一句TTS触发机制的优化效果 +""" + +import sys + +def test_first_playback_logic(): + """测试首次播放逻辑""" + print("🎵 首次播放逻辑专项测试") + print("=" * 50) + + # 测试场景1: 短句子积累 + print("📝 测试场景1: 短句子积累") + print(" 模拟LLM生成的短句子流:") + + short_sentences = [ + "你好", + "嗯", + "这个", + "问题", + "确实", + "需要", + "仔细", + "思考", + ] + + buffer = [] + total_length = 0 + first_trigger = None + + for i, sentence in enumerate(short_sentences): + buffer.append(sentence) + total_length = sum(len(s) for s in buffer) + + # 模拟首次播放逻辑 + trigger_reason = None + if total_length >= 40 and len(buffer) >= 2: + trigger_reason = f"总长度{total_length}字符,{len(buffer)}个句子" + elif len(buffer) >= 5: + trigger_reason = f"缓冲区达到最大值{len(buffer)}" + + if trigger_reason and first_trigger is None: + first_trigger = i + 1 + print(f" 🎵 第{first_trigger}句触发TTS: {trigger_reason}") + print(f" 📝 发送内容: '{''.join(buffer)}'") + break + else: + print(f" ⏳ 第{i+1}句: '{sentence}' (累计: {total_length}字符, {len(buffer)}个句子)") + + if first_trigger is None: + print(" ⚠️ 未触发TTS(需要超时机制)") + print() + + # 测试场景2: 中等长度句子 + print("📝 测试场景2: 中等长度句子") + medium_sentences = [ + "你好,很高兴见到你。", + "今天天气真不错呢。", + "我们可以一起去公园玩吗?", + "我想那会是一个很好的主意。", + ] + + buffer = [] + total_length = 0 + first_trigger = None + + for i, sentence in enumerate(medium_sentences): + buffer.append(sentence) + total_length = sum(len(s) for s in buffer) + + # 模拟首次播放逻辑 + trigger_reason = None + if total_length >= 40 and len(buffer) >= 2: + trigger_reason = f"总长度{total_length}字符,{len(buffer)}个句子" + elif len(sentence) >= 25 and sentence.endswith(('。', '!', '?', '.', '!', '?')) and len(buffer) >= 2: + trigger_reason = f"长句子{len(sentence)}字符+缓冲内容" + + if trigger_reason and first_trigger is None: + first_trigger = i + 1 + print(f" 🎵 第{first_trigger}句触发TTS: {trigger_reason}") + print(f" 📝 发送内容: '{''.join(buffer)}'") + break + else: + print(f" ⏳ 第{i+1}句: '{sentence}' (累计: {total_length}字符, {len(buffer)}个句子)") + + if first_trigger is None: + print(" ⚠️ 未触发TTS(需要超时机制)") + print() + + # 测试场景3: 长句子 + print("📝 测试场景3: 长句子") + long_sentences = [ + "你好,", + "我认为这个问题需要我们从多个角度来分析。", + "首先,让我们仔细了解一下具体情况。", + ] + + buffer = [] + total_length = 0 + first_trigger = None + + for i, sentence in enumerate(long_sentences): + buffer.append(sentence) + total_length = sum(len(s) for s in buffer) + + # 模拟首次播放逻辑 + trigger_reason = None + if total_length >= 40 and len(buffer) >= 2: + trigger_reason = f"总长度{total_length}字符,{len(buffer)}个句子" + elif len(sentence) >= 25 and sentence.endswith(('。', '!', '?', '.', '!', '?')) and len(buffer) >= 2: + trigger_reason = f"长句子{len(sentence)}字符+缓冲内容" + + if trigger_reason and first_trigger is None: + first_trigger = i + 1 + print(f" 🎵 第{first_trigger}句触发TTS: {trigger_reason}") + print(f" 📝 发送内容: '{''.join(buffer)}'") + break + else: + print(f" ⏳ 第{i+1}句: '{sentence}' (累计: {total_length}字符, {len(buffer)}个句子)") + + if first_trigger is None: + print(" ⚠️ 未触发TTS(需要超时机制)") + print() + + # 测试场景4: 超长单句 + print("📝 测试场景4: 超长单句") + ultra_long_sentence = "根据我的分析,这个问题的解决方案需要综合考虑多个因素,包括时间成本、资源投入以及最终的实施效果。" + + buffer = ["你好"] + buffer.append(ultra_long_sentence) + total_length = sum(len(s) for s in buffer) + + if total_length >= 40 and len(buffer) >= 2: + print(f" 🎵 第2句触发TTS: 总长度{total_length}字符,{len(buffer)}个句子") + print(f" 📝 发送内容: '{''.join(buffer)[:50]}...'") + else: + print(" ⚠️ 未触发TTS") + print() + +def show_optimization_comparison(): + """显示优化对比""" + print("📈 首次播放逻辑优化对比") + print("=" * 50) + + comparison = { + "优化前": { + "触发条件": "任何完整句子或长句子", + "最小长度": "无明确要求", + "积攒机制": "基本没有", + "可能导致": "播放卡顿,等待数据", + }, + "优化后": { + "触发条件": "40+字符且2+句子 或 25+字符完整句+缓冲", + "最小长度": "总长度40字符或单句25字符", + "积攒机制": "智能积累多个句子", + "超时保护": "5秒超时机制", + "效果": "确保有足够数据才开始播放" + } + } + + for aspect, details in comparison.items(): + print(f"\n🔧 {aspect}:") + for key, value in details.items(): + print(f" • {key}: {value}") + + print(f"\n🎯 核心改进: 确保首次播放有足够的内容,避免因为数据不足导致的播放卡顿") + +if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == "--comparison": + show_optimization_comparison() + else: + test_first_playback_logic() + show_optimization_comparison() \ No newline at end of file diff --git a/test_optimization.py b/test_optimization.py new file mode 100644 index 0000000..4ff70a1 --- /dev/null +++ b/test_optimization.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +音频播放优化测试脚本 +用于验证缓冲区优化和播放性能改进 +""" + +import time +import json +import os +import sys + +def test_optimization(): + """测试优化效果""" + print("🧪 音频播放优化测试") + print("=" * 50) + + # 显示优化后的配置参数 + print("📋 优化后的配置参数:") + print(" - 预加载缓冲区: 6个音频块(原:3个)") + print(" - 智能句子缓冲: 最多5个句子(原:3个)") + print(" - 最小触发句子: 2个句子(原:1个)") + print(" - 积累时间窗口: 150ms(原:200ms)") + print(" - TTS任务队列: 20个任务(原:10个)") + print(" - 音频块大小: 1024字节(原:2048字节)") + print(" - 播放冷却期: 0.05秒(原:0.1秒)") + print(" - 长句子触发阈值: 30字符(原:50字符)") + print(" - 播放缓冲区维护: 4个块(原:3个)") + print() + + # 测试智能句子缓冲逻辑 + print("🧠 测试智能句子缓冲逻辑:") + print(" 🔄 首次播放逻辑测试:") + + test_sentences = [ + "你好", # 短句子 + "今天天气怎么样?", # 中等长度 + "我觉得这个方案很不错,我们可以试试看。", # 长句子 + "这是一个超过三十个字符的句子,应该会立即触发TTS生成。", # 超过30字符 + "短句。", # 带标点的短句 + ] + + # 模拟首次播放的缓冲区状态 + tts_buffer = [] + first_playback_started = False + total_buffered_text = "" + trigger_count = 0 + + for i, sentence in enumerate(test_sentences): + tts_buffer.append(sentence) + total_buffered_text = ''.join(tts_buffer) + + # 首次播放逻辑 + if not first_playback_started: + should_trigger = False + trigger_reason = "" + + # 条件1: 总文本长度超过40字符且至少有2个句子 + if len(total_buffered_text) >= 40 and len(tts_buffer) >= 2: + should_trigger = True + trigger_reason = f"总长度{len(total_buffered_text)}字符,{len(tts_buffer)}个句子" + # 条件2: 有1个完整长句子(超过25字符) + elif len(sentence) >= 25 and sentence.endswith(('。', '!', '?', '.', '!', '?')) and len(tts_buffer) >= 2: + should_trigger = True + trigger_reason = f"长句子{len(sentence)}字符+缓冲内容" + # 条件3: 缓冲区达到最大值(5个) + elif len(tts_buffer) >= 5: + should_trigger = True + trigger_reason = f"缓冲区达到最大值{len(tts_buffer)}" + # 条件4: 超过500ms(模拟) + + if should_trigger: + trigger_count += 1 + first_playback_started = True + print(f" 🎵 首次触发TTS: {trigger_reason}") + print(f" 📝 发送内容: '{total_buffered_text[:50]}...'") + tts_buffer = [] + else: + print(f" ⏳ 首次缓冲: '{sentence}' (累计: {len(total_buffered_text)}字符, {len(tts_buffer)}个句子)") + else: + # 正常播放逻辑 + if len(sentence) > 30 or len(tts_buffer) >= 3: + should_trigger = True + trigger_reason = "长句子" if len(sentence) > 30 else "缓冲区满" + + if should_trigger: + trigger_count += 1 + print(f" ✅ 正常触发TTS: {trigger_reason}") + print(f" 📝 发送内容: '{''.join(tts_buffer)[:30]}...'") + tts_buffer = [] + else: + print(f" ⏳ 正常缓冲: '{sentence}'") + + print(f" 📊 总触发次数: {trigger_count}") + print() + + print(" 📋 首次播放优化效果:") + print(" • 确保首句有足够长度(40+字符或25+字符完整句)") + print(" • 积累多个句子避免播放卡顿") + print(" • 5秒超时机制防止无限等待") + print(" • 后续句子正常流式处理") + print() + + # 显示性能监控信息 + print("📊 性能监控功能:") + print(" - 实时缓冲区大小统计") + print(" - 平均和最大缓冲区大小") + print(" - 播放块数和音频大小统计") + print(" - 每5秒自动输出性能报告") + print() + + print("🎯 预期改进效果:") + print(" 1. ✅ 减少音频播放卡顿(更大的缓冲区)") + print(" 2. ✅ 更快的TTS响应(优化的触发条件)") + print(" 3. ✅ 更流畅的播放体验(减少冷却期)") + print(" 4. ✅ 更好的资源利用(更小的音频块)") + print(" 5. ✅ 实时性能监控(调试和优化)") + print() + + print("📝 测试建议:") + print(" 1. 运行主程序观察播放流畅度") + print(" 2. 查看性能统计输出") + print(" 3. 监控缓冲区大小变化") + print(" 4. 测试不同长度的语音响应") + print() + + print("🚀 测试完成!可以运行主程序验证优化效果。") + +def show_optimization_summary(): + """显示优化总结""" + print("📈 音频播放优化总结") + print("=" * 50) + + summary = { + "缓冲区优化": [ + "预加载缓冲区: 3→6个块", + "智能句子缓冲: 3→5个句子", + "最小触发缓冲: 1→2个句子", + "TTS任务队列: 10→20个任务" + ], + "响应性优化": [ + "积累时间窗口: 200ms→150ms", + "长句子触发: 50→30字符", + "中等长度触发: 30→20字符", + "播放冷却期: 0.1s→0.05s" + ], + "播放优化": [ + "音频块大小: 2048→1024字节", + "播放缓冲维护: 3→4个块", + "数据转移: 2→3个块/次" + ], + "监控功能": [ + "实时性能统计", + "缓冲区大小监控", + "自动性能报告", + "播放进度追踪" + ] + } + + for category, improvements in summary.items(): + print(f"\n🔧 {category}:") + for improvement in improvements: + print(f" • {improvement}") + + print(f"\n🎯 总体目标: 减少音频播放卡顿,提升用户体验") + print(f"📊 预期效果: 更流畅的实时语音交互") + +if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == "--summary": + show_optimization_summary() + else: + test_optimization() \ No newline at end of file diff --git a/tts_test.mp3 b/tts_test.mp3 new file mode 100644 index 0000000..3c72aec Binary files /dev/null and b/tts_test.mp3 differ diff --git a/verify_timeout.py b/verify_timeout.py new file mode 100644 index 0000000..425a3f6 --- /dev/null +++ b/verify_timeout.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +验证5秒超时机制 +""" + +def test_timeout_mechanism(): + """测试超时机制""" + print("⏱️ 首次播放超时机制验证") + print("=" * 40) + + print("📋 超时设置更新:") + print(" 原: 500ms 超时") + print(" 现: 5秒 超时") + print() + + print("🎯 超时机制作用:") + print(" • 防止无限等待首次播放") + print(" • 给LLM充分时间生成内容") + print(" • 适应网络较慢的情况") + print(" • 确保用户体验不会过度等待") + print() + + print("📊 超时时间对比:") + scenarios = [ + ("快速响应", "1-2秒", "正常触发积累条件"), + ("中等响应", "2-5秒", "正常触发积累条件"), + ("慢速响应", "5秒+", "超时机制触发"), + ("极慢响应", "10秒+", "已通过超时机制处理"), + ] + + for scenario, time_range, result in scenarios: + print(f" • {scenario:10} ({time_range}): {result}") + + print() + print("✅ 优化完成: 超时时间从500ms增加到5秒") + +if __name__ == "__main__": + test_timeout_mechanism() \ No newline at end of file