向Azure Speech-To-Text API发送字节数组时遇NoMatch问题求助
问题分析与解决
核心问题
浏览器的MediaRecorder即使指定type: 'audio/wav',也无法保证生成标准的PCM 16kHz单声道WAV文件——大部分现代浏览器(如Chrome)默认会用WebM格式编码音频,而非Azure Speech-To-Text要求的未压缩PCM WAV格式。这就是录制的音频发送后触发NoMatch错误的原因,而测试数据是符合要求的格式,所以能正常工作。
解决方案步骤
1. 替换MediaRecorder,用AudioContext处理原始音频数据
通过AudioContext获取原始PCM音频样本,手动封装成标准WAV格式,确保匹配Azure的要求。修改后的录制逻辑如下:
let languageCode = "da-DK"; let audioContext; let scriptProcessor; let inputNode; let audioBuffer = []; let isRecording = false; function startRecording() { const constraints = { audio: { sampleRate: 16000, channelCount: 1, echoCancellation: false } }; navigator.mediaDevices.getUserMedia(constraints) .then(function (stream) { audioContext = new AudioContext({ sampleRate: 16000 }); inputNode = audioContext.createMediaStreamSource(stream); // 创建ScriptProcessor处理音频数据 scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1); inputNode.connect(scriptProcessor); scriptProcessor.connect(audioContext.destination); scriptProcessor.onaudioprocess = function(e) { // 获取单声道PCM数据并转换为16位整数 const inputData = e.inputBuffer.getChannelData(0); const int16Array = new Int16Array(inputData.length); for (let i = 0; i < inputData.length; i++) { let sample = inputData[i] * 32767; int16Array[i] = sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample); } audioBuffer.push(int16Array); }; recordButton.disabled = true; stopButton.disabled = false; playButton.disabled = true; }) .catch(function (err) { console.error('录音错误: ' + err); }); } function stopRecording() { inputNode.disconnect(); scriptProcessor.disconnect(); audioContext.close(); // 合并所有音频数据 const totalLength = audioBuffer.reduce((sum, buffer) => sum + buffer.length, 0); const combinedBuffer = new Int16Array(totalLength); let offset = 0; for (const buffer of audioBuffer) { combinedBuffer.set(buffer, offset); offset += buffer.length; } // 封装成标准WAV文件 const wavBlob = createWavBlob(combinedBuffer, 16000, 1); const reader = new FileReader(); reader.onload = function () { const audioBytes = new Uint8Array(reader.result); const base64String = btoa(String.fromCharCode.apply(null, audioBytes)); voiceToText(languageCode, base64String); }; reader.readAsArrayBuffer(wavBlob); // 重置缓冲区 audioBuffer = []; recordButton.disabled = false; stopButton.disabled = true; playButton.disabled = false; } // 手动生成标准WAV格式Blob function createWavBlob(int16Array, sampleRate, channels) { const buffer = new ArrayBuffer(44 + int16Array.length * 2); const view = new DataView(buffer); // 写入WAV文件头 view.setUint8(0, 0x52); view.setUint8(1, 0x49); view.setUint8(2, 0x46); view.setUint8(3, 0x46); view.setUint32(4, 44 + int16Array.length * 2, true); view.setUint8(8, 0x57); view.setUint8(9, 0x41); view.setUint8(10, 0x56); view.setUint8(11, 0x45); view.setUint8(12, 0x66); view.setUint8(13, 0x6D); view.setUint8(14, 0x74); view.setUint8(15, 0x20); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, channels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, sampleRate * channels * 2, true); view.setUint16(32, channels * 2, true); view.setUint16(34, 16, true); view.setUint8(36, 0x64); view.setUint8(37, 0x61); view.setUint8(38, 0x74); view.setUint8(39, 0x61); view.setUint32(40, int16Array.length * 2, true); // 写入PCM数据 const dataView = new Uint8Array(buffer, 44); dataView.set(new Uint8Array(int16Array.buffer)); return new Blob([buffer], { type: 'audio/wav' }); } // 保留原voiceToText函数 function voiceToText(myLanguageCode, wavData) { $.ajax({ type: "POST", url: "https://localhost:7058/api/Conversation/postVoiceToString", contentType: "application/json", data: JSON.stringify({ LanguageCode: myLanguageCode, WAV: wavData }), success: function (result) { console.log(result); }, error: function (req, status, error) { console.log(status); } }); }
2. 验证音频格式
录制完成后,可通过以下方式确认格式合规:
- 将生成的Blob保存为本地文件,用Audacity等工具打开,检查是否为16kHz、单声道、16位PCM格式。
- 查看自研API日志,确认转发给Azure的音频数据格式与测试数据一致。
3. 额外注意事项
- 确保浏览器支持
AudioContext和ScriptProcessorNode(现代浏览器均兼容)。 - 关闭回声消除(
echoCancellation: false),避免音频失真影响识别精度。
内容的提问来源于stack exchange,提问作者Abbas Anwar
相关产品推荐
相关产品推荐

