shanhe12345 2025-10-21 16:19 采纳率: 25%
浏览 4

火山引擎流式语音识别大模型 做持续监听持续记录 场景怎么写逻辑好

火山引擎流式语音识别大模型 做持续监听持续记录 场景怎么写逻辑好
目前是麦克风实时说话,通过静音检测分割成临时wav文件传给模型再返回,速度慢效率低。如果用麦克风流式持续传输包,我发现这个模型会不断返回增量的语句,也就是停止后才给最终结果,发现不适合于持续监听快速持续,一次性返回结果的场景。
是我的技术路线走错了吗,还有好多不明白的地方,这个场景怎么写逻辑比较好呢?

业务场景描述:麦克风一直开启状态。随着说话,屏幕上不断快速出现识别的文字结果。

  • 写回答

2条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-10-21 16:24
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案让您满意,请采纳、点赞、关注,非常感谢!
    .
    每日一句正能量,让它带给您好心情:把生活过得热气腾腾,就是最好的状态。
    .

    针对您描述的"持续监听、实时显示"场景,我认为您的技术路线确实需要调整。火山引擎流式语音识别大模型的设计就是为了实时返回增量结果的,这恰恰符合您的需求。让我为您提供一个更优化的技术方案。

    问题分析

    您当前方案的问题:

    • 静音检测分割成文件 → 增加了延迟
    • 等待最终结果 → 不符合实时显示需求
    • 文件传输 → 效率低下

    推荐的技术方案

    1. 核心逻辑架构

    class RealtimeSpeechRecognition {
      constructor() {
        this.isListening = false;
        this.mediaRecorder = null;
        this.audioChunks = [];
        this.recognitionStream = null;
      }
    
    
      // 开始监听
      async startListening() {
        try {
          const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
          this.setupMediaRecorder(stream);
          this.connectToVolcanoEngine();
          this.isListening = true;
        } catch (error) {
          console.error('无法访问麦克风:', error);
        }
      }
    
    
      // 设置媒体录制器
      setupMediaRecorder(stream) {
        const options = {
          mimeType: 'audio/webm;codecs=opus',
          audioBitsPerSecond: 16000
        };
    
    
        this.mediaRecorder = new MediaRecorder(stream, options);
        
        this.mediaRecorder.ondataavailable = (event) => {
          if (event.data.size > 0) {
            this.sendAudioData(event.data);
          }
        };
    
    
        // 每100ms发送一次数据,保证低延迟
        this.mediaRecorder.start(100);
      }
    }
    

    2. 流式传输实现

    import websocket
    import threading
    import queue
    from collections import deque
    
    
    class VolcanoStreamingClient:
        def __init__(self):
            self.ws = None
            self.audio_queue = queue.Queue()
            self.text_buffer = deque(maxlen=10)  # 保存最近10条识别结果
            self.is_connected = False
            
        def connect(self):
            """连接火山引擎WebSocket服务"""
            websocket.enableTrace(True)
            self.ws = websocket.WebSocketApp(
                "wss://your-volcano-websocket-endpoint",
                on_message=self.on_message,
                on_error=self.on_error,
                on_close=self.on_close
            )
            
            # 在单独线程中运行WebSocket
            ws_thread = threading.Thread(target=self.ws.run_forever)
            ws_thread.daemon = True
            ws_thread.start()
            
        def on_message(self, ws, message):
            """处理识别结果"""
            try:
                result = json.loads(message)
                if result.get('type') == 'partial':  # 增量结果
                    text = result.get('text', '')
                    self.update_display(text, is_final=False)
                elif result.get('type') == 'final':  # 最终结果
                    text = result.get('text', '')
                    self.update_display(text, is_final=True)
            except Exception as e:
                print(f"解析结果错误: {e}")
                
        def send_audio_data(self, audio_chunk):
            """发送音频数据"""
            if self.ws and self.ws.sock and self.ws.sock.connected:
                # 将音频数据转换为base64或直接发送二进制
                self.ws.send(audio_chunk, opcode=websocket.ABNF.OPCODE_BINARY)
    

    3. 前端实时显示优化

    class DisplayManager {
      constructor() {
        this.displayElement = document.getElementById('speech-text');
        this.currentText = '';
        this.isUpdating = false;
      }
    
    
      // 更新显示内容
      updateDisplay(text, isFinal = false) {
        if (isFinal) {
          // 最终结果,清空当前内容,开始新行
          this.addFinalText(text);
          this.currentText = '';
        } else {
          // 增量结果,更新当前行
          this.currentText = text;
          this.renderText();
        }
      }
    
    
      // 渲染文本
      renderText() {
        const timestamp = new Date().toLocaleTimeString();
        const displayText = `${timestamp} - ${this.currentText}`;
        
        // 使用requestAnimationFrame保证流畅更新
        requestAnimationFrame(() => {
          this.displayElement.textContent = displayText;
          this.displayElement.scrollIntoView({ behavior: 'smooth' });
        });
      }
    
    
      addFinalText(text) {
        const finalElement = document.createElement('div');
        finalElement.className = 'final-text';
        finalElement.textContent = `${new Date().toLocaleTimeString()} - ${text}`;
        
        this.displayElement.parentNode.insertBefore(finalElement, this.displayElement);
      }
    }
    

    4. 完整的业务流程

    public class ContinuousSpeechRecognition {
        private volatile boolean isRunning = false;
        private AudioFormat audioFormat;
        private TargetDataLine targetDataLine;
        
        public void startContinuousRecognition() {
            try {
                // 设置音频格式
                audioFormat = new AudioFormat(16000, 16, 1, true, false);
                
                // 获取麦克风输入
                DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
                targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
                targetDataLine.open(audioFormat);
                targetDataLine.start();
                
                isRunning = true;
                
                // 启动处理线程
                new Thread(this::processAudioStream).start();
                
            } catch (LineUnavailableException e) {
                e.printStackTrace();
            }
        }
        
        private void processAudioStream() {
            byte[] buffer = new byte[4096];
            VolcanoStreamClient streamClient = new VolcanoStreamClient();
            
            while (isRunning) {
                int bytesRead = targetDataLine.read(buffer, 0, buffer.length);
                if (bytesRead > 0) {
                    // 发送到火山引擎流式识别
                    streamClient.sendAudioData(buffer, bytesRead);
                }
            }
        }
        
        // 处理识别结果回调
        public void onRecognitionResult(String text, boolean isFinal) {
            if (isFinal) {
                // 最终结果,保存到历史
                saveToHistory(text);
            }
            // 实时更新UI显示
            updateDisplay(text, isFinal);
        }
    }
    

    关键优化点

    1. 低延迟传输:使用WebSocket或gRPC流式传输,避免文件操作
    2. 增量结果显示:直接使用模型的partial结果实时更新界面
    3. 缓冲区管理:合理设置音频缓冲区大小,平衡延迟和稳定性
    4. 错误恢复:网络中断时自动重连,保持服务连续性

    配置建议

    # 音频配置
    audio_config:
      sample_rate: 16000
      channels: 1
      format: pcm_s16le
      chunk_size: 1024  # 每帧大小
    
    
    # 网络配置
    streaming_config:
      reconnect_interval: 2000  # 重连间隔(ms)
      timeout: 30000           # 超时时间(ms)
      buffer_size: 8192        # 缓冲区大小
    

    这个方案充分利用了火山引擎流式识别的特性,能够实现您要求的"随着说话,屏幕上不断快速出现识别的文字结果"的效果。关键是要正确处理增量结果,而不是等待最终结果。

    评论

报告相同问题?

问题事件

  • 修改了问题 10月21日
  • 创建了问题 10月21日