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

使用JavaScript录制音频对接微软认知服务遇SPXERR_INVALID_HEADER错误

解决SPXERR_INVALID_HEADER异常:录制音频转WAV格式问题

问题根源

你遇到的SPXERR_INVALID_HEADER异常,核心原因是前端保存的文件并非真正的WAV格式。虽然你将Blob的type设为audio/wav,但MediaRecorder实际录制的是WebM格式音频(多数浏览器不支持直接录制WAV,你代码里也注释了设置audio/wav不生效),只是强行修改了Blob的类型标识,文件内容仍为WebM编码,导致认知服务Speech SDK识别文件头时出错。

解决方案

需要在前端将录制的WebM音频解码后,重新编码为标准WAV格式,再传给后端。以下是修改后的完整前端代码:

function startRecording(btn) {
    var countdown = 5;
    var audioChunks = [];
    var mediaRecorder = null;
    var options = { mimeType: 'audio/webm' };

    navigator.mediaDevices.getUserMedia({ audio: true })
        .then(function (stream) {
            mediaRecorder = new MediaRecorder(stream, options);
            mediaRecorder.start();

            setTimeout(function () {
                mediaRecorder.stop();
                stream.getTracks().forEach(track => track.stop());
                btn.text("Waiting for the results");
            }, countdown * 1000);

            mediaRecorder.addEventListener("dataavailable", function (event) {
                audioChunks.push(event.data);
            });

            mediaRecorder.addEventListener("stop", async function () {
                // 把WebM Blob转换为AudioBuffer
                const webmBlob = new Blob(audioChunks, { type: 'audio/webm' });
                const arrayBuffer = await webmBlob.arrayBuffer();
                const audioContext = new AudioContext();
                const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
                
                // 将AudioBuffer编码为标准WAV格式
                const wavBlob = audioBufferToWav(audioBuffer);
                
                var formData = new FormData();
                formData.append('audioBlob', wavBlob, 'recording.wav');

                $.ajax({
                    url: '@Url.Action("ProcessAudio", "Home")',
                    type: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function (data) {
                        $('#modalContent').html(data);
                        $('#myModal').modal('show');
                    },
                    error: function () {
                        console.log('Error processing audio');
                    }
                });
            });
        })
        .catch(function (error) {
            console.log('Error starting recording:', error);
        });
}

// 辅助函数:将AudioBuffer转换为WAV格式Blob
function audioBufferToWav(buffer) {
    const length = buffer.length * buffer.numberOfChannels * 2 + 44;
    const arrayBuffer = new ArrayBuffer(length);
    const view = new DataView(arrayBuffer);
    const channels = [];
    let offset = 0;
    let pos = 0;

    // 写入WAV文件头
    const setUint16 = (data) => {
        view.setUint16(pos, data, true);
        pos += 2;
    };
    const setUint32 = (data) => {
        view.setUint32(pos, data, true);
        pos += 4;
    };

    // RIFF标识
    setUint32(0x46464952);
    // 文件总长度
    setUint32(length - 8);
    // WAVE标识
    setUint32(0x45564157);
    // fmt子块标识
    setUint32(0x20746d66);
    // fmt子块长度
    setUint32(16);
    // 音频格式(PCM)
    setUint16(1);
    // 声道数
    setUint16(buffer.numberOfChannels);
    // 采样率
    setUint32(buffer.sampleRate);
    // 字节率
    setUint32(buffer.sampleRate * 2 * buffer.numberOfChannels);
    // 块对齐
    setUint16(buffer.numberOfChannels * 2);
    // 位深度(16位)
    setUint16(16);
    // data子块标识
    setUint32(0x61746164);
    // data子块长度
    setUint32(length - pos - 4);

    // 写入PCM音频数据
    for (let i = 0; i < buffer.numberOfChannels; i++) {
        channels.push(buffer.getChannelData(i));
    }

    while (pos < length) {
        for (let i = 0; i < buffer.numberOfChannels; i++) {
            let sample = Math.max(-1, Math.min(1, channels[i][offset]));
            sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
            view.setInt16(pos, sample, true);
            pos += 2;
        }
        offset++;
    }

    return new Blob([arrayBuffer], { type: 'audio/wav' });
}

说明

  • 后端代码无需修改,现在前端传递的是标准PCM编码的WAV文件,符合Speech SDK的格式要求。
  • 可以用音频工具(如Audacity)打开保存的文件,验证其实际编码为PCM 16位WAV。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:53:17