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

浏览器播放录制的PCM音频数据遇编码错误求助

问题:浏览器中PCM音频播放报错的原因与解决方法

背景

我尝试在浏览器中使用JavaScript从麦克风获取原始PCM格式的音频数据,并播放录制好的PCM格式音频数据。为此编写了两个简单的类:

  • AudioRecorder.startRecording()
  • AudioRecorder.stopRecording():返回录制的音频数据为PCM BufferArray
  • AudioPlayer.play(pcmAudioDataBufferArray)
  • AudioPlayer.stop()

AudioRecorder类运行正常,但AudioPlayer.play()方法存在问题。

问题代码

AudioPlayer.play() 方法

async play(pcmAudioDataBufferArray) {
  if (this.playing) {
    throw new Error("Audio is already playing!");
  }

  if (!pcmAudioDataBufferArray) {
    throw new Error("No audio data provided!");
  }

  this.playing = true;
  const audioBuffer = await this.audioContext.decodeAudioData(pcmAudioDataBufferArray);
  const source = this.audioContext.createBufferSource();
  source.buffer = audioBuffer;
  source.connect(this.audioContext.destination);

  const endedPromise = new Promise((resolve, reject) => {
    source.addEventListener("ended", resolve, { once: true });
    source.addEventListener('error', reject, { once: true });
  });

  source.start();

  await endedPromise;
  this.playing = false;
}

报错信息

Error: EncodingError: The buffer passed to decodeAudioData contains an unknown content type.

音频转换代码

在Firefox中测试时,浏览器默认以ogg/opus格式录制为Blob,之后通过以下代码将其转换为PCM BufferArray:

async audioBlobToPcmBuffer(blob) {
  const fileReader = new FileReader();
  const arrayBuffer = await new Promise((resolve, reject) => {
    fileReader.onload = () => resolve(fileReader.result);
    fileReader.onerror = reject;
    fileReader.readAsArrayBuffer(blob);
  });
  const audioCtx = new AudioContext({ sampleRate: this.sampleRate });
  const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
  const pcmBuffer = audioBuffer.getChannelData(0).buffer;
  return pcmBuffer;
}

错误原因

decodeAudioData方法的作用是解码带有容器格式的音频数据(如OGG、MP3、WAV等),这类数据包含文件头和格式描述信息。但你传入的是原始PCM数据——它没有任何格式标识,浏览器无法识别其编码规则,因此抛出编码错误。

另外,audioBlobToPcmBuffer方法中,audioBuffer.getChannelData(0).buffer返回的是仅包含原始采样值的ArrayBuffer,缺少采样率、声道数、位深这些关键播放参数,直接传给decodeAudioData必然无法解析。

解决方法

播放原始PCM数据不能用decodeAudioData,需要手动创建AudioBuffer并填充数据,再通过BufferSourceNode播放。修改AudioPlayer.play()方法如下:

async play(pcmAudioDataBufferArray) {
  if (this.playing) {
    throw new Error("Audio is already playing!");
  }

  if (!pcmAudioDataBufferArray) {
    throw new Error("No audio data provided!");
  }

  this.playing = true;
  // 创建AudioBuffer:参数依次为声道数、采样点数、采样率
  // 这里假设是单声道、32位浮点型PCM(和getChannelData返回的格式一致)
  const audioBuffer = this.audioContext.createBuffer(
    1,
    pcmAudioDataBufferArray.byteLength / Float32Array.BYTES_PER_ELEMENT,
    this.audioContext.sampleRate
  );
  // 将原始PCM数据写入AudioBuffer的声道中
  const channelData = audioBuffer.getChannelData(0);
  channelData.set(new Float32Array(pcmAudioDataBufferArray));

  const source = this.audioContext.createBufferSource();
  source.buffer = audioBuffer;
  source.connect(this.audioContext.destination);

  const endedPromise = new Promise((resolve, reject) => {
    source.addEventListener("ended", resolve, { once: true });
    source.addEventListener('error', reject, { once: true });
  });

  source.start();

  await endedPromise;
  this.playing = false;
}

关键注意点

  1. 确保this.audioContext.sampleRate与录制时的采样率完全一致,否则播放速度会出现失真。
  2. 如果你的PCM数据是其他格式(如16位整型),需要将Float32Array替换为对应类型(如Int16Array),并在写入AudioBuffer时完成格式转换(比如将整型转成浮点型)。
  3. 若为多声道录制,需修改createBuffer的第一个参数为对应声道数,并分别给每个声道写入数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:48:08