回声待处理

This commit is contained in:
朱潮 2025-09-21 10:48:51 +08:00
parent 9523176d60
commit e1d634af1f
3 changed files with 313 additions and 58 deletions

View File

@ -522,12 +522,14 @@ class OutputProcess:
"""输出进程 - 借鉴 recorder.py 的优秀架构"""
def __init__(self, audio_queue: mp.Queue, config: Dict[str, Any] = None, event_queue: mp.Queue = None):
print("🔊 OutputProcess __init__ 开始执行...")
self.audio_queue = audio_queue # 主进程 → 输出进程(统一音频播放队列)
self.event_queue = event_queue # 输出进程 → 主进程(事件通知)
self.config = config or self._get_default_config()
# 初始化日志记录器
self.logger = ProcessLogger("OutputProcess")
print("🔊 OutputProcess 基本初始化完成")
# 音频播放参数 - 完全借鉴 recorder.py 的优化参数
self.FORMAT = pyaudio.paInt16
@ -542,7 +544,9 @@ class OutputProcess:
self.total_chunks_played = 0
self.total_audio_size = 0
self.last_playback_time = 0 # 最后播放时间戳
self.playback_cooldown_period = 1.5 # 播放冷却时间(秒)- 防止回声
self.playback_cooldown_period = 0.1 # 播放冷却时间(秒)- 防止回声减少到0.1秒
self.playback_completed = False # 播放完成标志
self.end_signal_received = False # 结束信号接收标志
# 智能缓冲系统 - 借鉴 recorder.py 的缓冲策略
self.preload_buffer = [] # 预加载缓冲区
@ -580,13 +584,24 @@ class OutputProcess:
self.tts_speaker = "zh_female_wanqudashu_moon_bigtts"
# 启动工作线程 - 先启动播放线程再启动TTS线程
self._start_playback_worker()
self._start_tts_worker()
print("🔊 准备启动播放工作线程...")
try:
self._start_playback_worker()
print("🔊 播放工作线程已启动准备启动TTS工作线程...")
self._start_tts_worker()
print("🔊 TTS工作线程已启动")
except Exception as e:
print(f"❌ 工作线程启动失败: {e}")
import traceback
traceback.print_exc()
def _start_playback_worker(self):
"""启动播放工作线程 - 借鉴 recorder.py 的播放线程模式"""
print("🔊 创建播放工作线程...")
self.playback_worker_thread = threading.Thread(target=self._playback_worker, daemon=True)
print("🔊 启动播放工作线程...")
self.playback_worker_thread.start()
print("🔊 播放工作线程已启动")
self.logger.info("播放工作线程已启动")
def _playback_worker(self):
@ -597,14 +612,20 @@ 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)
if (time.time() - wait_start_time) % 1 < 0.1: # 每秒打印一次状态
print(f"🔊 仍在等待音频设备就绪... audio={self.audio is not None}, running={self.running}")
if self.audio is None:
print("❌ 音频设备未就绪,播放工作线程退出")
self.logger.error("音频设备未就绪,播放工作线程退出")
return
print(f"✅ 音频设备已就绪,耗时: {time.time() - wait_start_time:.1f}")
print("🔊 音频设备已就绪,开始创建播放流")
# 创建音频播放流
@ -643,27 +664,55 @@ class OutputProcess:
if in_cooldown:
# 在冷却期内,跳过播放
print(f"🔊 播放冷却中,跳过播放 ({time_since_last_play:.1f}s < {self.playback_cooldown_period}s)")
if chunks_played == 0: # 只在第一次遇到冷却期时打印
print(f"🔊 播放冷却中,跳过播放 ({time_since_last_play:.1f}s < {self.playback_cooldown_period}s)")
self.logger.debug(f"播放冷却中,跳过播放 ({time_since_last_play:.1f}s < {self.playback_cooldown_period}s)")
continue
# 播放音频块
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")
# 标记正在播放
self.currently_playing = True
self.last_playback_time = current_time # 更新最后播放时间
# 如果是第一次播放,不设置冷却期
if chunks_played == 0:
self.last_playback_time = 0 # 第一次播放不触发冷却期
else:
self.last_playback_time = current_time # 更新最后播放时间
# 播放音频(同步阻塞,直到播放完成)
playback_stream.write(audio_chunk)
chunks_played += 1
total_size += len(audio_chunk)
# 减少进度显示频率
if chunks_played % 10 == 0:
if chunks_played % 10 == 0 or chunks_played <= 3:
progress = f"🔊 播放工作: {chunks_played} 块 | {total_size / 1024:.1f} KB"
print(f"\r{progress}", end='', flush=True)
# 播放完成后更新状态
self.currently_playing = False
# 如果这是最后一个音频块,主动检查播放完成
if (len(self.playback_buffer) == 0 and
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:
self.currently_playing = False
else:
# 缓冲区为空,检查是否还在接收数据
# 缓冲区为空,短暂休眠减少CPU占用
self.currently_playing = False
time.sleep(0.01) # 短暂休眠减少CPU占用
time.sleep(0.01)
continue
except Exception as e:
@ -712,7 +761,7 @@ class OutputProcess:
tts_active = not self.tts_task_queue.empty()
playback_active = self.currently_playing or len(self.playback_buffer) > 0
# 如果设备不健康、正在播放、TTS生成中、或在冷却期内显示状态并跳过音频处理
# 如果设备不健康、正在播放、TTS生成中、或在冷却期内显示状态但继续处理播放完成检测
if not self.audio_device_healthy or tts_active or playback_active or in_cooldown:
# 显示播放状态
tts_queue_size = self.tts_task_queue.qsize()
@ -730,6 +779,11 @@ class OutputProcess:
status = f"🔊 {playing_status}... TTS: {tts_queue_size} 播放: {queue_size}"
print(f"\r{status}", end='', flush=True)
# 关键修复:即使正在播放,也要检查播放完成
if self.end_signal_received:
self._check_playback_completion()
time.sleep(0.1) # 播放时增加延迟减少CPU使用
continue
@ -747,7 +801,11 @@ class OutputProcess:
# 4. 显示播放进度
self._show_progress()
# 5. 借鉴 recorder.py: 根据播放状态调整休眠时间,优化性能
# 5. 主动检查播放完成(无论什么状态都要检查)
if self.end_signal_received:
self._check_playback_completion()
# 6. 借鉴 recorder.py: 根据播放状态调整休眠时间,优化性能
if self.is_playing and (self.playback_buffer or self.preload_buffer):
time.sleep(0.005) # 播放时极短休眠,提高响应性
else:
@ -766,8 +824,9 @@ class OutputProcess:
def _setup_audio(self):
"""设置音频输出设备"""
try:
print("🔊 开始初始化音频设备...")
self.audio = pyaudio.PyAudio()
print("🔊 PyAudio实例已创建")
print(f"🔊 PyAudio实例已创建: {self.audio}")
# 主进程不需要创建输出流,由播放工作线程负责
# 这里只创建PyAudio实例供播放工作线程使用
@ -798,22 +857,32 @@ class OutputProcess:
if audio_data is None:
# 结束信号处理
if end_signal_received:
print(f"📥 输出进程已收到过结束信号,忽略重复信号")
continue
print(f"📥 输出进程收到结束信号")
end_signal_received = True
self.end_signal_received = True
# 重置完成事件标记
self.completion_sent = False
# 重置播放完成标志
self.playback_completed = False
print(f"📥 已重置所有播放完成相关标志")
# 检查是否应该立即结束
tts_queue_size = self.tts_task_queue.qsize()
playback_queue_size = len(self.playback_buffer) + len(self.preload_buffer)
if tts_queue_size == 0 and playback_queue_size == 0 and not self.currently_playing:
print(f"📥 所有队列已清空且未在播放,处理结束信号")
# 双重确认机制:所有队列已清空且播放工作线程已停止
print(f"📥 双重确认通过TTS队列={tts_queue_size}, 播放缓冲={len(self.playback_buffer)}, 预加载={len(self.preload_buffer)}, 正在播放={self.currently_playing}")
print(f"📥 所有音频已播放完成,处理结束信号")
self._finish_playback()
return
else:
print(f"📥 延迟处理结束信号 - TTS队列: {tts_queue_size}, 播放缓冲: {playback_queue_size}")
print(f"📥 延迟处理结束信号 - TTS队列: {tts_queue_size}, 播放缓冲: {playback_queue_size}, 正在播放: {self.currently_playing}")
# 重新放回队列,稍后重试
self.audio_queue.put(None)
time.sleep(0.05)
@ -835,17 +904,37 @@ 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):
# 将预加载的数据移到播放缓冲区
# 检查是否应该开始播放或补充播放缓冲区
if not self.is_playing and len(self.preload_buffer) >= self.preload_size:
# 首次启动播放
self.playback_buffer.extend(self.preload_buffer)
self.preload_buffer.clear()
self.is_playing = True
self.last_playback_time = time.time()
self.last_playback_time = 0 # 重置播放时间,避免立即触发冷却期
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个块
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
self.last_playback_time = 0
print(f"🎵 强制开始播放音频,播放缓冲区大小: {len(self.playback_buffer)}")
else:
print(f"📥 输出进程收到未知类型数据: {type(audio_data)}")
@ -856,24 +945,67 @@ class OutputProcess:
tts_queue_size = self.tts_task_queue.qsize()
playback_queue_size = len(self.playback_buffer) + len(self.preload_buffer)
if tts_queue_size == 0 and playback_queue_size == 0 and not self.currently_playing:
print(f"📥 播放完成,所有工作已完成")
self._finish_playback()
return
print(f"📥 队列空时检查播放完成: TTS={tts_queue_size}, 播放缓冲={len(self.playback_buffer)}, 预加载={len(self.preload_buffer)}, 正在播放={self.currently_playing}")
if tts_queue_size == 0 and playback_queue_size == 0:
# 三重确认机制:所有缓冲区都空了,检查播放状态
if not self.currently_playing:
print(f"📥 三重确认通过TTS队列=0, 播放缓冲=0, 预加载=0, 播放状态=False")
print(f"📥 调用 _finish_playback() 前completion_sent={self.completion_sent}")
self._finish_playback()
print(f"📥 调用 _finish_playback() 后completion_sent={self.completion_sent}")
return
else:
# 播放工作线程可能还在播放最后一个块,等待一下
print(f"📥 等待播放工作线程完成...")
time.sleep(0.3) # 增加等待时间,确保播放完成
if not self.currently_playing:
print(f"📥 三重确认通过等待后TTS队列=0, 播放缓冲=0, 预加载=0, 播放状态=False")
print(f"📥 调用 _finish_playback() 前completion_sent={self.completion_sent}")
self._finish_playback()
print(f"📥 调用 _finish_playback() 后completion_sent={self.completion_sent}")
return
else:
print(f"📥 播放工作线程仍在播放,继续等待...")
elif tts_queue_size == 0 and playback_queue_size > 0:
# 还有数据要播放,继续等待
print(f"📥 还有 {playback_queue_size} 个音频块待播放,等待播放完成")
time.sleep(0.2) # 增加等待时间
elif tts_queue_size == 0 and len(self.playback_buffer) == 0 and len(self.preload_buffer) > 0:
# 关键修复:播放缓冲区为空但预加载缓冲区还有数据,需要转移数据
print(f"📥 播放缓冲区为空但预加载缓冲区有 {len(self.preload_buffer)} 个数据块,转移数据到播放缓冲区")
transfer_count = min(len(self.preload_buffer), 3) # 一次转移最多3个块
for _ in range(transfer_count):
if self.preload_buffer:
self.playback_buffer.append(self.preload_buffer.pop(0))
print(f"📥 已转移 {transfer_count} 个数据块到播放缓冲区")
time.sleep(0.2) # 增加等待时间
# 检查是否应该将预加载缓冲区的数据移到播放缓冲区
if (not self.is_playing and
len(self.preload_buffer) >= self.min_buffer_size):
# 检查是否应该补充播放缓冲区的数据
if not self.is_playing and len(self.preload_buffer) >= self.min_buffer_size:
# 首次启动播放(最小缓冲区模式)
self.playback_buffer.extend(self.preload_buffer)
self.preload_buffer.clear()
self.is_playing = True
self.last_playback_time = time.time()
self.last_playback_time = 0 # 重置播放时间,避免立即触发冷却期
print(f"🎵 开始播放音频(最小缓冲区满足)")
print(f"🔍 已重置last_playback_time避免立即触发冷却期")
elif self.is_playing and len(self.playback_buffer) < 2 and len(self.preload_buffer) > 0:
# 正在播放时补充数据,避免播放缓冲区耗尽
transfer_count = min(1, len(self.preload_buffer)) # 每次转移1个块
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:
break
else:
# 关键修复:即使没有处理数据,也要检查播放完成
if self.end_signal_received:
self._check_playback_completion()
time.sleep(0.01)
except Exception as e:
@ -920,15 +1052,18 @@ class OutputProcess:
"""完成播放 - 借鉴 recorder.py 的优雅完成机制"""
# 防止重复发送完成事件
if self.completion_sent:
print("📡 输出进程:完成事件已发送过,跳过重复发送")
return
print("📡 输出进程:开始执行播放完成逻辑")
self.is_playing = False
self.playback_buffer.clear()
self.preload_buffer.clear()
self.last_playback_time = 0
# 通知主进程播放完成
if self.event_queue and not self.completion_sent:
if self.event_queue:
try:
# 发送播放完成事件到主进程
completion_event = ProcessEvent(
@ -941,13 +1076,65 @@ class OutputProcess:
print("📡 输出进程:已发送播放完成事件到主进程")
self.completion_sent = True
except Exception as e:
print(f"❌ 输出进程:发送播放完成事件失败: {e}")
self.logger.error(f"发送播放完成事件失败: {e}")
else:
if not self.event_queue:
print("⚠️ 输出进程:未设置事件队列,无法通知主进程播放完成")
print("⚠️ 输出进程:未设置事件队列,无法通知主进程播放完成")
# 额外等待确保音频设备完全停止
time.sleep(0.5)
print("📡 输出进程:播放完成逻辑执行完毕")
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}")
# 检查条件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)
if 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
else:
print(f"⚠️ 最后一个音频块播放超时")
# 即使超时也要重置标志,防止重复检测
self.end_signal_received = False
print(f"📥 已重置结束信号标志(超时情况)")
def _cleanup(self):
"""清理资源"""

View File

@ -28,6 +28,19 @@ from audio_processes import (
RecordingState, ControlCommand, ProcessEvent
)
def output_process_target(audio_queue, config, event_queue):
"""输出进程的目标函数 - 在子进程中创建OutputProcess实例"""
try:
print("🔊 输出进程目标函数开始执行...")
output_process = OutputProcess(audio_queue, config, event_queue)
print("🔊 OutputProcess实例创建成功开始运行...")
output_process.run()
print("🔊 输出进程运行完成")
except Exception as e:
print(f"❌ 输出进程出错: {e}")
import traceback
traceback.print_exc()
class ControlSystem:
"""主控制系统"""
@ -213,11 +226,8 @@ class ControlSystem:
}
self.output_process = mp.Process(
target=OutputProcess(
self.output_audio_queue,
output_config,
self.output_event_queue # 传递事件队列
).run
target=output_process_target,
args=(self.output_audio_queue, output_config, self.output_event_queue)
)
# 启动进程
@ -312,6 +322,7 @@ class ControlSystem:
if event.event_type == 'playback_complete':
print("📡 主控制:收到播放完成事件")
print(f"📡 主控制:事件详情 - 类型: {event.event_type}, 元数据: {event.metadata}")
self._handle_playback_complete(event)
except queue.Empty:
@ -338,18 +349,31 @@ 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
self.state = RecordingState.IDLE
print(f"🎯 状态:PLAYING → IDLE")
print(f"🎯 状态:{old_state} → IDLE")
# 重新启用输入进程录音功能
self.input_command_queue.put(ControlCommand('enable_recording'))
try:
self.input_command_queue.put(ControlCommand('enable_recording'))
print(f"📡 主控制:已发送 enable_recording 命令到输入进程")
except Exception as e:
print(f"❌ 主控制:发送 enable_recording 命令失败: {e}")
print(f"📡 主控制:播放完成事件处理完成")
def _process_audio_pipeline(self):
"""处理音频流水线STT + LLM + TTS"""
@ -544,7 +568,6 @@ class ControlSystem:
if serialization_method == 0b0001: # JSON
payload_msg = json.loads(str(payload_msg, "utf-8"))
print(f"📋 解析后的JSON: {json.dumps(payload_msg, indent=2, ensure_ascii=False)}")
result['payload_msg'] = payload_msg
result['payload_size'] = payload_size
@ -721,7 +744,6 @@ class ControlSystem:
return results
else:
print(f"❌ 未找到result字段可用字段: {list(payload_msg.keys())}")
print(f"完整payload: {json.dumps(payload_msg, indent=2, ensure_ascii=False)}")
else:
print(f"❌ Payload不是字典类型: {type(payload_msg)}")
else:
@ -854,11 +876,8 @@ class ControlSystem:
if not chunk:
continue
print(f"🔍 原始TTS响应块 {chunk_count + 1}: {chunk[:100]}...")
try:
data = json.loads(chunk)
print(f"🔍 解析后的TTS块 {chunk_count + 1}: {data}")
if data.get("code", 0) == 0 and "data" in data and data["data"]:
chunk_audio = base64.b64decode(data["data"])
@ -899,9 +918,41 @@ 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音频数据已全部发送等待输出进程播放完成")
# 等待音频数据被输出进程完全处理
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)
# 发送结束信号,通知输出进程所有音频已发送完成
print(f"📦 发送结束信号到输出进程")
print(f"📊 音频队列当前大小: {self.output_audio_queue.qsize()}")
print(f"📦 注意:结束信号不会立即触发播放完成,会等待所有音频播放完成")
try:
self.output_audio_queue.put(None)
print(f"📡 已发送结束信号到输出进程")
except Exception as e:
print(f"❌ 发送结束信号失败: {e}")
return chunk_count > 0

View File

@ -401,12 +401,16 @@ class EnergyBasedRecorder:
if time_since_last >= self.tts_accumulation_time and len(self.tts_buffer) >= self.tts_buffer_min_size:
return True
# 检查句子特征 - 长句子优先调整为100字符防止回声
if len(sentence) > 100: # 超过100字符的长句子立即触发
# 检查是否为完整句子(使用新的严格检测
if self._is_complete_sentence(sentence):
return True
# 中等长度句子80-100字符如果有结束标点也触发
if len(sentence) > 80:
# 检查句子特征 - 长句子优先50字符以上
if len(sentence) > 50: # 超过50字符的句子立即触发
return True
# 中等长度句子30-50字符如果有结束标点也触发
if len(sentence) > 30:
end_punctuations = ['', '', '', '.', '!', '?']
if any(sentence.strip().endswith(p) for p in end_punctuations):
return True
@ -1808,12 +1812,18 @@ class EnergyBasedRecorder:
if not text or len(text.strip()) == 0:
return False
# 增加最小长度要求 - 至少10个字符才考虑作为完整句子
if len(text.strip()) < 10:
return False
# 句子结束标点符号
sentence_endings = r'[。!?.!?]'
# 检查是否以句子结束符结尾
if re.search(sentence_endings + r'\s*$', text):
return True
# 对于以结束符结尾的句子要求至少15个字符
if len(text.strip()) >= 15:
return True
# 检查是否包含句子结束符(可能在句子中间)
if re.search(sentence_endings, text):
@ -1821,18 +1831,25 @@ class EnergyBasedRecorder:
remaining_text = re.split(sentence_endings, text, 1)[-1]
if len(remaining_text.strip()) > 0:
return False
# 对于包含结束符的句子要求至少20个字符
if len(text.strip()) >= 20:
return True
# 对于较长的文本超过50字符即使没有结束符也可以考虑
if len(text.strip()) >= 50:
return True
# 对于较短的文本,如果包含常见完整句式模式
common_patterns = [
r'^[是的有没有来去在把被让叫请使].*[的得了吗呢吧啊呀]',
r'^(你好|谢谢|再见|是的|不是|好的|没问题)',
r'^[\u4e00-\u9fff]{2,4}[的得了]$' # 2-4个中文字+的/了/得
]
for pattern in common_patterns:
if re.match(pattern, text):
return True
# 对于中等长度的文本,如果包含常见完整句式模式
if len(text.strip()) >= 20:
common_patterns = [
r'^[是的有没有来去在把被让叫请使].*[的得了吗呢吧啊呀]',
r'^(你好|谢谢|再见|是的|不是|好的|没问题)',
r'^[\u4e00-\u9fff]{4,8}[的得了]$' # 4-8个中文字+的/了/得
]
for pattern in common_patterns:
if re.match(pattern, text):
return True
return False