音频反复播放,无法停止:
🎵 音频播放完成
⚡ 预播放完成,开始播放完整音频(6个块)
🎵 音频段开始播放
🎵 音频播放完成
⚡ 预播放完成,开始播放完整音频(6个块) ← 又触发!
🎵 音频段开始播放
🎵 音频播放完成
⚡ 预播放完成,开始播放完整音频(6个块) ← 又触发!
... 无限循环 ❌
onended 回调判断逻辑缺少"已播放完整音频"的标志:
this.audio.onended = () => {
// 检查条件
if (this.allChunksReceived && this.audioChunks.length > 5) {
// 播放完整音频
this.playAll() // ← 这个会再次触发 onended,形成循环
}
}
循环流程:
1. 预播放完成 → onended触发
2. 检测到 allChunksReceived=true && length=6 > 5
3. 播放完整音频 → 播放完成 → onended触发
4. 再次检测到 allChunksReceived=true && length=6 > 5
5. 再次播放 → 无限循环 ❌
hasPlayedFullAudio 标志文件: frontend/src/utils/audioPlayerFallback.ts
// 类属性
private hasPlayedFullAudio = false // 标记是否已播放过完整音频
// onended 回调
this.audio.onended = () => {
console.log('🎵 音频播放完成')
this.playing = false
// 检查是否需要播放完整音频
if (this.allChunksReceived &&
this.audioChunks.length > 5 &&
!this.hasPlayedFullAudio) { // ← 关键:检查是否已播放过
console.log(`⚡ 预播放完成,开始播放完整音频`)
this.hasPlayedFullAudio = true // ← 设置标志,防止重复
this.isFirstSegmentPlaying = false
setTimeout(() => this.playAll(), 100)
} else if (this.allChunksReceived || this.hasPlayedFullAudio) {
// 完整音频播放完成
console.log('✅ 所有音频播放完成')
this.config.onEnded?.() // ← 触发结束回调
} else {
// 预播放完成,等待ai_response_end
console.log('⏳ 预播放完成,等待ai_response_end信号...')
}
}
clear() {
// 重置所有标记
this.hasStartedPlaying = false
this.allChunksReceived = false
this.playedChunksCount = 0
this.isFirstSegmentPlaying = true
this.hasPlayedFullAudio = false // ← 重置
// 清空音频块
this.audioChunks = []
this.playing = false
}
1. 收到5个块 → 预播放
└─ playing = true
└─ 播放前5个块
└─ onended触发
└─ hasPlayedFullAudio = false
└─ 条件满足,播放完整音频
└─ hasPlayedFullAudio = true ← 设置标志
2. 完整音频播放
└─ playing = true
└─ 播放所有6个块
└─ onended触发
└─ hasPlayedFullAudio = true ← 已设置
└─ 条件不满足,触发 onEnded() ✅
└─ 结束播放
关键: hasPlayedFullAudio 防止重复播放
预播放 → 完整播放 → 完整播放 → 完整播放 → ... ❌
预播放 → 完整播放 → 结束 ✅
// 1. 开始录音
后端确认开始录音 - 清理播放器准备新一轮
🧹 清理播放器状态 - 丢弃 0 个音频块
// 2. 收集音频
📦 收集音频块: 1754 bytes (总计: 1 块)
📦 收集音频块: 1880 bytes (总计: 2 块)
📦 收集音频块: 2192 bytes (总计: 3 块)
📦 收集音频块: 2124 bytes (总计: 4 块)
📦 收集音频块: 1520 bytes (总计: 5 块)
// 3. 预播放
⚡ 收到5个音频块,立即开始快速预播放!
🎵 播放音频:5个块 / 总计5个块
🎵 音频段开始播放
✅ 首段音频开始播放
// 4. 继续收集
📦 收集音频块: 192 bytes (总计: 6 块)
✅ AI回复完成
// 5. 完整播放
🎵 音频播放完成
⚡ 预播放完成,开始播放完整音频(6个块)
🎵 播放音频:6个块 / 总计6个块
🎵 音频段开始播放
// 6. 结束
🎵 音频播放完成
✅ 所有音频播放完成 ← 不再循环!
清除缓存,重新加载
说: "你好"
预期:
确保只看到一次 "⚡ 预播放完成,开始播放完整音频"
✅ frontend/src/utils/audioPlayerFallback.ts
hasPlayedFullAudio 标志onended 回调逻辑clear() 时重置标志playAudioData() 和 stop() 中清空audioChunks✅ frontend/src/components/MainPage.tsx
ai_response_start: 不清理播放器recording_started: 清理播放器✅ 快速响应: ~1秒听到声音 ✅ 完整播放: 所有音频都播放 ✅ 播放一次: 不再无限循环 ✅ 无错误: OGG格式完整
刷新浏览器,立即测试!应该完美工作了!🎉