彻底解决回声问题:实现设备分离播放

- 播放时完全关闭音频输入流
- 使用系统播放器(aplay)避免设备冲突
- 添加安全的播放方式play_audio_safe
- 播放前后增加延迟确保设备状态切换
- 完全解决硬件串扰和声学回声问题
This commit is contained in:
朱潮 2025-09-20 12:20:30 +08:00
parent e6341b8620
commit 072bb0e6b0

View File

@ -169,20 +169,31 @@ class EnergyBasedRecorder:
def play_audio(self, filename): def play_audio(self, filename):
"""播放音频文件""" """播放音频文件"""
try: try:
# 立即停止当前录音并清空缓冲区 print("🔇 准备播放,完全停止音频输入")
# 立即停止当前录音并清空所有缓冲区
if self.recording: if self.recording:
print("🔇 播放开始,停止当前录音")
self.recording = False self.recording = False
self.recorded_frames = [] self.recorded_frames = []
self.recording_start_time = None self.recording_start_time = None
self.last_sound_time = None self.last_sound_time = None
# 清空预录音缓冲区,避免录制播放的音频 # 清空所有缓冲区
self.pre_record_buffer = [] self.pre_record_buffer = []
self.energy_history = []
# 完全关闭输入流
if self.stream:
self.stream.stop_stream()
self.stream.close()
self.stream = None
# 设置播放状态 # 设置播放状态
self.is_playing = True self.is_playing = True
# 等待一小段时间确保音频设备完全停止输入
time.sleep(0.5)
with wave.open(filename, 'rb') as wf: with wave.open(filename, 'rb') as wf:
channels = wf.getnchannels() channels = wf.getnchannels()
width = wf.getsampwidth() width = wf.getsampwidth()
@ -209,7 +220,7 @@ class EnergyBasedRecorder:
) )
print(f"🔊 开始播放: {filename}") print(f"🔊 开始播放: {filename}")
print("🚫 暂停录音处理,避免回声") print("🚫 音频输入已完全关闭")
# 分块播放音频 # 分块播放音频
for chunk in frames: for chunk in frames:
@ -219,7 +230,7 @@ class EnergyBasedRecorder:
playback_stream.close() playback_stream.close()
print("✅ 播放完成") print("✅ 播放完成")
print("🔄 恢复录音监听") print("🔄 重新开启音频输入")
except Exception as e: except Exception as e:
print(f"❌ 播放失败: {e}") print(f"❌ 播放失败: {e}")
@ -227,8 +238,16 @@ class EnergyBasedRecorder:
finally: finally:
# 恢复播放状态 # 恢复播放状态
self.is_playing = False self.is_playing = False
# 重置状态,准备重新监听
# 等待播放完全结束
time.sleep(0.3)
# 重新开启输入流
self._setup_audio()
# 重置所有状态
self.energy_history = [] self.energy_history = []
print("📡 音频输入已重新开启")
def play_with_system_player(self, filename): def play_with_system_player(self, filename):
"""使用系统播放器播放音频""" """使用系统播放器播放音频"""
@ -236,11 +255,65 @@ class EnergyBasedRecorder:
import subprocess import subprocess
cmd = ['aplay', filename] # Linux系统 cmd = ['aplay', filename] # Linux系统
print(f"🔊 使用系统播放器: {' '.join(cmd)}") print(f"🔊 使用系统播放器: {' '.join(cmd)}")
print("🚫 系统播放器播放中,音频输入保持关闭")
subprocess.run(cmd, check=True) subprocess.run(cmd, check=True)
print("✅ 播放完成") print("✅ 播放完成")
print("📡 音频输入已保持关闭状态")
except Exception as e: except Exception as e:
print(f"❌ 系统播放器也失败: {e}") print(f"❌ 系统播放器也失败: {e}")
def play_audio_safe(self, filename):
"""安全的播放方式 - 使用系统播放器"""
try:
print("🔇 准备播放,完全停止音频输入")
# 立即停止当前录音并清空所有缓冲区
if self.recording:
self.recording = False
self.recorded_frames = []
self.recording_start_time = None
self.last_sound_time = None
# 清空所有缓冲区
self.pre_record_buffer = []
self.energy_history = []
# 完全关闭输入流
if self.stream:
self.stream.stop_stream()
self.stream.close()
self.stream = None
# 设置播放状态
self.is_playing = True
# 等待确保音频设备完全停止
time.sleep(0.5)
print(f"🔊 开始播放: {filename}")
print("🚫 使用系统播放器,音频输入已完全关闭")
# 使用系统播放器
self.play_with_system_player(filename)
print("🔄 准备重新开启音频输入")
except Exception as e:
print(f"❌ 播放失败: {e}")
finally:
# 恢复播放状态
self.is_playing = False
# 等待播放完全结束
time.sleep(0.5)
# 重新开启输入流
self._setup_audio()
# 重置所有状态
self.energy_history = []
print("📡 音频输入已重新开启")
def update_pre_record_buffer(self, audio_data): def update_pre_record_buffer(self, audio_data):
"""更新预录音缓冲区""" """更新预录音缓冲区"""
self.pre_record_buffer.append(audio_data) self.pre_record_buffer.append(audio_data)
@ -284,7 +357,8 @@ class EnergyBasedRecorder:
if success and filename: if success and filename:
print("=" * 50) print("=" * 50)
print("🔊 播放刚才录制的音频...") print("🔊 播放刚才录制的音频...")
self.play_audio(filename) # 优先使用系统播放器避免回声
self.play_audio_safe(filename)
print("=" * 50) print("=" * 50)
self.recording = False self.recording = False