From 0ab8e49ba55647b56b6c1dfdd85ee4dcc68957fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=BD=AE?= Date: Sat, 20 Sep 2025 22:25:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E5=A3=B0=E5=BE=85=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recorder.py | 89 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/recorder.py b/recorder.py index 665b11a..b92f10a 100644 --- a/recorder.py +++ b/recorder.py @@ -100,7 +100,8 @@ class EnergyBasedRecorder: # 状态变量 self.audio = None - self.stream = None + self.input_stream = None # 输入流(录音) + self.output_stream = None # 输出流(播放) self.running = False self.recording = False self.recorded_frames = [] @@ -484,19 +485,14 @@ class EnergyBasedRecorder: # 如果强制重置,先完全释放资源 if force_reset: self._force_close_audio_stream() - if self.audio: - try: - self.audio.terminate() - except: - pass - self.audio = None + # 不终止整个PyAudio实例,只重置输入流 - # 创建新的PyAudio实例 + # 创建新的PyAudio实例(如果不存在) if not self.audio: self.audio = pyaudio.PyAudio() # 创建音频输入流 - self.stream = self.audio.open( + self.input_stream = self.audio.open( format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, @@ -510,8 +506,8 @@ class EnergyBasedRecorder: self.audio_device_healthy = False def _force_close_audio_stream(self): - """强制关闭音频流,避免阻塞""" - if self.stream: + """强制关闭音频输入流,避免阻塞""" + if self.input_stream: try: # 尝试优雅关闭,设置超时 import signal @@ -524,23 +520,23 @@ class EnergyBasedRecorder: signal.alarm(5) try: - self.stream.stop_stream() - self.stream.close() + self.input_stream.stop_stream() + self.input_stream.close() finally: signal.alarm(0) # 取消超时 except TimeoutError: - print("⚠️ 音频流关闭超时,强制终止") - self.stream = None + print("⚠️ 音频输入流关闭超时,强制终止") + self.input_stream = None except Exception as e: - print(f"⚠️ 优雅关闭音频流失败: {e}") + print(f"⚠️ 优雅关闭音频输入流失败: {e}") try: # 强制关闭 - self.stream = None + self.input_stream = None except: pass finally: - self.stream = None + self.input_stream = None self.audio_device_healthy = False # 标记设备需要重置 def calculate_energy(self, audio_data): @@ -1192,10 +1188,23 @@ class EnergyBasedRecorder: # 等待音频播放完成后再重新开启音频输入 print("⏳ 等待音频播放完成...") - while not self.audio_playback_queue.empty() or self.currently_playing: - queue_size = self.audio_playback_queue.qsize() - playing_status = "播放中" if self.currently_playing else "等待播放" - print(f"\r🔊 {playing_status}... 队列: {queue_size}", end='', flush=True) + + # 检查多个队列的状态 + tts_queue_size = self.tts_task_queue.qsize() + playback_queue_size = self.audio_playback_queue.qsize() + + while tts_queue_size > 0 or playback_queue_size > 0 or self.currently_playing: + tts_queue_size = self.tts_task_queue.qsize() + playback_queue_size = self.audio_playback_queue.qsize() + + if tts_queue_size > 0: + status = f"🎵 TTS生成中... TTS队列: {tts_queue_size} 播放队列: {playback_queue_size}" + elif self.currently_playing: + status = f"🔊 正在播放... 播放队列: {playback_queue_size}" + else: + status = f"⏳ 等待播放... 播放队列: {playback_queue_size}" + + print(f"\r{status}", end='', flush=True) time.sleep(0.1) # 额外等待1秒,确保音频设备完全停止 @@ -1245,7 +1254,7 @@ class EnergyBasedRecorder: def run(self): """运行录音系统""" - if not self.stream: + if not self.input_stream: print("❌ 音频设备未初始化") return @@ -1275,42 +1284,50 @@ class EnergyBasedRecorder: try: while self.running: # 检查音频流是否可用 - if self.stream is None: + if self.input_stream is None: print("\n❌ 音频流已断开,尝试重新连接...") self._setup_audio(force_reset=True) - if self.stream is None: + if self.input_stream is None: print("❌ 音频流重连失败,等待...") time.sleep(1) continue # 读取音频数据 try: - data = self.stream.read(self.CHUNK_SIZE, exception_on_overflow=False) + data = self.input_stream.read(self.CHUNK_SIZE, exception_on_overflow=False) except Exception as e: print(f"\n❌ 读取音频数据失败: {e}") - self.stream = None + self.input_stream = None continue if len(data) == 0: continue - # 检查设备健康状态和播放冷却期 - 防止回声 + # 检查设备健康状态、TTS状态和播放冷却期 - 防止回声 current_time = time.time() time_since_last_play = current_time - self.last_playback_time in_cooldown = time_since_last_play < self.playback_cooldown_period - # 如果设备不健康、正在播放、播放队列不为空、或在冷却期内,完全跳过音频处理 - if not self.audio_device_healthy or self.is_playing or self.currently_playing or not self.audio_playback_queue.empty() or in_cooldown: + # 检查TTS和播放队列状态 + tts_active = not self.tts_task_queue.empty() + playback_active = not self.audio_playback_queue.empty() or self.currently_playing + + # 如果设备不健康、正在播放、TTS生成中、播放队列不为空、或在冷却期内,完全跳过音频处理 + if not self.audio_device_healthy or self.is_playing or tts_active or playback_active or in_cooldown: # 显示播放状态 + tts_queue_size = self.tts_task_queue.qsize() queue_size = self.audio_playback_queue.qsize() + if not self.audio_device_healthy: - status = f"🔧 设备重置中... 队列: {queue_size}" + status = f"🔧 设备重置中... TTS: {tts_queue_size} 播放: {queue_size}" + elif tts_active: + status = f"🎵 TTS生成中... TTS: {tts_queue_size} 播放: {queue_size}" elif in_cooldown: cooldown_time = self.playback_cooldown_period - time_since_last_play - status = f"🔊 播放冷却中... {cooldown_time:.1f}s 队列: {queue_size}" + status = f"🔊 播放冷却中... {cooldown_time:.1f}s TTS: {tts_queue_size} 播放: {queue_size}" else: playing_status = "播放中" if self.currently_playing else "等待播放" - status = f"🔊 {playing_status}... 队列: {queue_size}" + status = f"🔊 {playing_status}... TTS: {tts_queue_size} 播放: {queue_size}" print(f"\r{status}", end='', flush=True) time.sleep(0.1) # 播放时增加延迟减少CPU使用 continue @@ -1427,9 +1444,9 @@ class EnergyBasedRecorder: self.audio_playback_queue.put(None) # 发送结束信号 self.playback_worker_thread.join(timeout=3.0) - if self.stream: - self.stream.stop_stream() - self.stream.close() + if self.input_stream: + self.input_stream.stop_stream() + self.input_stream.close() if self.audio: self.audio.terminate()