You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Flask实时语音转文本故障求助:Azure Speech SDK调用异常

实时语音转文本(React+Flask+Azure Speech SDK)可行方案

一、前端(React)音频采集与Socket传输优化

  • 强制音频格式匹配Azure要求:必须是16kHz采样率、16位深度、单声道PCM格式,直接传输原始PCM字节,避免浏览器自带的格式转换干扰识别。
  • 依赖安装:
    npm install socket.io-client
    
  • 核心代码示例:
import { useEffect, useRef } from 'react';
import io from 'socket.io-client';

const SpeechToText = () => {
  const audioContextRef = useRef(null);
  const scriptProcessorRef = useRef(null);
  const socketRef = useRef(null);

  const startRecording = async () => {
    // 请求麦克风权限,指定音频参数
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: { sampleRate: 16000, channelCount: 1, echoCancellation: true }
    });

    // 初始化音频上下文
    audioContextRef.current = new AudioContext({ sampleRate: 16000 });
    const mediaSource = audioContextRef.current.createMediaStreamSource(stream);
    
    // 创建脚本处理器,每帧处理4096个样本(平衡实时性与传输效率)
    scriptProcessorRef.current = audioContextRef.current.createScriptProcessor(4096, 1, 1);
    mediaSource.connect(scriptProcessorRef.current);
    scriptProcessorRef.current.connect(audioContextRef.current.destination);

    // 初始化Socket连接
    socketRef.current = io('http://your-flask-server:5000', { transports: ['websocket'] });
    socketRef.current.on('transcript', (data) => {
      // 更新UI展示实时转录文本
      console.log('实时结果:', data.text);
    });

    // 处理音频数据并发送
    scriptProcessorRef.current.onaudioprocess = (e) => {
      const channelData = e.inputBuffer.getChannelData(0);
      // 将Float32格式转换为16位PCM字节数组
      const pcmData = new Int16Array(channelData.length);
      for (let i = 0; i < channelData.length; i++) {
        const sample = Math.max(-1, Math.min(1, channelData[i]));
        pcmData[i] = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
      }
      socketRef.current.emit('audio_data', pcmData.buffer);
    };
  };

  const stopRecording = () => {
    if (scriptProcessorRef.current) {
      scriptProcessorRef.current.disconnect();
    }
    if (audioContextRef.current) {
      audioContextRef.current.close();
    }
    if (socketRef.current) {
      socketRef.current.emit('stop_recording');
      socketRef.current.disconnect();
    }
  };

  return (
    <div>
      <button onClick={startRecording}>开始录音</button>
      <button onClick={stopRecording}>停止录音</button>
    </div>
  );
};

export default SpeechToText;

二、后端(Flask)Azure Speech SDK正确配置

  • 依赖安装:
    pip install flask-socketio azure-cognitiveservices-speech eventlet
    
  • 核心代码示例:
from flask import Flask, request
from flask_socketio import SocketIO, emit
import azure.cognitiveservices.speech as speechsdk
import threading
from queue import Queue

app = Flask(__name__)
# 强制使用WebSocket传输,避免轮询导致的延迟
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet', transports=['websocket'])

# Azure Speech服务配置,替换为你的密钥和区域
speech_config = speechsdk.SpeechConfig(
    subscription="YOUR_AZURE_SPEECH_KEY",
    region="YOUR_AZURE_REGION"
)
# 绑定音频格式,与前端完全一致
audio_format = speechsdk.audio.AudioFormat(
    sample_rate_hz=16000,
    bits_per_sample=16,
    channels=1
)

# 自定义Push流,用队列缓存前端传来的音频数据
class CustomPushStream(speechsdk.audio.PushAudioInputStream):
    def __init__(self, audio_format):
        super().__init__(audio_format)
        self.data_queue = Queue()
        self.is_closed = False

    def write(self, data):
        if not self.is_closed:
            self.data_queue.put(data)

    def close(self):
        self.is_closed = True
        self.data_queue.put(None)

    def read(self, size):
        data_chunk = b''
        while len(data_chunk) < size:
            chunk = self.data_queue.get()
            if chunk is None:
                return data_chunk
            data_chunk += chunk
        return data_chunk[:size]

# 启动实时识别线程
def run_recognition(stream, sid):
    audio_config = speechsdk.audio.AudioConfig(stream=stream)
    # 单用户识别用SpeechRecognizer即可,无需ConversationTranscriber
    recognizer = speechsdk.SpeechRecognizer(
        speech_config=speech_config,
        audio_config=audio_config,
        language="zh-CN"  # 根据需求切换语言,如"en-US"
    )

    # 绑定实时识别事件
    recognizer.recognizing.connect(lambda evt: emit('transcript', {'text': evt.result.text}, room=sid))
    recognizer.recognized.connect(lambda evt: emit('transcript', {'text': evt.result.text}, room=sid))
    recognizer.canceled.connect(lambda evt: print(f"识别错误: {evt.reason} - {evt.error_details}"))

    recognizer.start_continuous_recognition_async()

# 接收前端音频数据
@socketio.on('audio_data')
def handle_audio(data):
    sid = request.sid
    # 为每个连接创建独立的音频流和识别实例
    if 'audio_stream' not in socketio.server.environ[sid]:
        stream = CustomPushStream(audio_format)
        socketio.server.environ[sid]['audio_stream'] = stream
        threading.Thread(target=run_recognition, args=(stream, sid), daemon=True).start()
    stream = socketio.server.environ[sid]['audio_stream']
    stream.write(data)

# 处理停止录音请求
@socketio.on('stop_recording')
def handle_stop():
    sid = request.sid
    if 'audio_stream' in socketio.server.environ[sid]:
        stream = socketio.server.environ[sid]['audio_stream']
        stream.close()
        del socketio.server.environ[sid]['audio_stream']

if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

三、常见问题排查

  • 音频格式不匹配:用Audacity等工具检查前端传输的PCM数据,确认采样率、声道数、位深度与Azure配置完全一致。
  • 无识别输出:查看Azure SDK错误日志,若提示认证失败,检查密钥和区域是否正确;若提示格式不支持,核对音频参数。
  • 结果不准确:开启前端回声消除,或在Azure配置中添加噪音抑制参数:
    speech_config.set_property(speechsdk.PropertyId.SpeechServiceConnection_EndSilenceTimeoutMs, "3000")
    speech_config.set_property(speechsdk.PropertyId.SpeechServiceConnection_InitialSilenceTimeoutMs, "1000")
    
  • 数据丢失:强制使用WebSocket传输(前端transports: ['websocket']),避免轮询导致的数据包丢失。

内容的提问来源于stack exchange,提问作者Sandeep pradeep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:07:51