zlwu vor 1 Jahr
Ursprung
Commit
b68254d04b

+ 2 - 5
backend/websocket_handler.py

@@ -422,16 +422,13 @@ class StreamingSession:
         # 记录录音完成信息
         logger.info(f"🎤 [录音完成] 时长: {duration}ms, 音频: {self.audio_received_bytes / 1024:.1f}KB")
         
-        # 等待一下让ASR完成识别
-        await asyncio.sleep(0.3)
+        # 等待ASR完成识别和队列处理(优化:减少等待时间)
+        await asyncio.sleep(0.25)
         
         # 打印ASR识别的完整文本
         if self.asr_text:
             logger.info(f"📝 [用户说] {self.asr_text}")
         
-        # 等待队列中的音频数据全部发送完成
-        await asyncio.sleep(0.15)
-        
         # 发送静音帧让豆包VAD检测到结束
         # 豆包配置了 end_smooth_window_ms: 1200,需要1.2秒静音
         # 发送1300ms静音确保触发(16kHz 16bit = 32000 bytes/s)

+ 8 - 4
frontend/src/components/MainPage.tsx

@@ -198,6 +198,8 @@ export default function MainPage() {
         console.log('🎬 AI开始回复 - 清理播放器状态')
         // 清理播放器状态,准备接收新的音频
         playerRef.current?.clear()
+        // 不要在这里切换到SPEAKING状态
+        // 保持PROCESSING状态,直到真正开始播放
         break
 
       case 'ai_response_end':
@@ -208,7 +210,8 @@ export default function MainPage() {
 
       case 'state_change':
         const newState = message.payload?.state
-        console.log('状态变更:', newState)
+        const previousState = message.payload?.previousState
+        console.log('状态变更:', newState, '← 从', previousState)
         
         // 同步后端状态到前端
         if (newState === 'listening') {
@@ -216,11 +219,13 @@ export default function MainPage() {
         } else if (newState === 'processing') {
           setAnimationState('PROCESSING')
         } else if (newState === 'speaking') {
-          setAnimationState('SPEAKING')
+          // 不要立即切换到SPEAKING
+          // 等待播放器真正开始播放(onplaying事件)
+          console.log('⏳ 后端准备好音频,等待播放器开始播放...')
+          // 保持PROCESSING状态
         } else if (newState === 'idle') {
           // 如果是从speaking切换到idle,不要立即切换
           // 让播放器的onEnded回调来控制最终状态
-          const previousState = message.payload?.previousState
           if (previousState === 'speaking') {
             console.log('忽略过早的idle状态,等待播放完成')
             // 不切换状态,保持SPEAKING直到播放完成
@@ -262,7 +267,6 @@ export default function MainPage() {
       return
     }
     
-    console.log('收到音频数据:', audioData.byteLength, 'bytes')
     performanceMonitor.recordAudioChunkReceived()
     playerRef.current?.appendAudioChunk(audioData)
   }

+ 11 - 5
frontend/src/utils/audioPlayerFallback.ts

@@ -23,15 +23,21 @@ export class FallbackAudioPlayer {
   }
 
   appendAudioChunk(chunk: ArrayBuffer) {
-    console.log(`📦 收集音频块: ${chunk.byteLength} bytes (总计: ${this.audioChunks.length + 1} 块)`)
+    const totalChunks = this.audioChunks.length + 1
+    console.log(`📦 收集音频块: ${chunk.byteLength} bytes (总计: ${totalChunks} 块)`)
     
     // 收集音频块
     this.audioChunks.push(chunk)
     
-    // 不使用超时自动播放!
-    // 等待后端发送 ai_response_end 信号后统一播放
-    // 这样可以确保所有音频块都收集完毕再播放
-    console.log('💾 音频块已收集,等待 ai_response_end 信号...')
+    // 策略:等待 ai_response_end 信号统一播放
+    // Safari不支持真正的流式播放,必须等待完整的OGG流
+    
+    // 优化:收到一定数量的块时给予提示,让用户知道正在准备
+    if (totalChunks === 1) {
+      console.log('🎵 AI开始生成语音...')
+    } else if (totalChunks === 5) {
+      console.log('⚡ 已收集足够音频,准备播放...')
+    }
   }
 
   private async playAll() {