如何从 Blob 创建 AudioBuffer?

23

我有一个使用MediaRecorder api创建的音频文件/ Blob:

let recorder = new MediaRecorder(this.stream)
let data = [];
recorder.ondataavailable = event => data.push(event.data);

然后当录音完成时:

let superBlob = new Blob(data, { type: "video/webm" });
如何使用这个blob创建一个 AudioBuffer?我需要做以下两个操作之一:
  • Blob 对象转换为 ArrayBuffer,然后我就可以用它和 AudioContext.decodeAudioData 一起使用(返回一个 AudioBuffer);或者
  • Blob 对象转换为 Float32Array,然后我可以使用 AudioBuffer.copyToChannel() 将其复制到 AudioBuffer 中。
欢迎提供任何有关如何实现这些操作的提示。谢谢!
5个回答

17

要将一个Blob对象转换成ArrayBuffer,使用FileReader.readAsArrayBuffer方法。

let fileReader = new FileReader();
let arrayBuffer;

fileReader.onloadend = () => {
    arrayBuffer = fileReader.result;
}

fileReader.readAsArrayBuffer(superBlob);

我在我的实现中使用了这个,但是fileReader.result总是返回一个空的ArrayBuffer - 你知道可能发生了什么吗?更多细节可以在我提出的这个问题中找到。 - Alistair Hughes
你确定你在onloadend事件中检查了fileReader.result吗? - Maxime Dupré
2
是的,我已经成功地让它工作了,这是我的一个微不足道的错误,对此我感到非常抱歉。非常感谢您发布这个答案,它非常有帮助! - Alistair Hughes
不确定错误有多微小。我发现您无法直接访问生成的数组缓冲区。我使用了 data = new Uint8Array(reader.result); - bobbdelsol
1
正如PAT-O-MATION所述,这将返回一个arrayBuffer,而不是audioBuffer。 - Will59

12
接受的答案很好,但只提供了一个数组缓冲区,而不是音频缓冲区。您需要使用音频上下文将数组缓冲区转换为音频缓冲区。
const audioContext = new AudioContext();
const fileReader = new FileReader();

// Set up file reader on loaded end event
fileReader.onloadend = () => {

    const arrayBuffer = fileReader.result as ArrayBuffer

    // Convert array buffer into audio buffer
    audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => {
    
      // Do something with audioBuffer
      console.log(audioBuffer)
    
    })
      
}

//Load blob
fileReader.readAsArrayBuffer(blob)

我希望答案中包含了一个使用decodeAudioData的示例。我不得不在其他地方找到它,我想既然这是“Blob转Audio Buffer”的热门搜索结果,我可以为下一个遇到这个问题的人添加一些有用的信息。

1
decodeAudioData 只能用于文件。在 iOS 14 Safari 上,它会出现空值错误。 - Weilory

6

所有答案都是正确的。然而,在像Chrome 76和Firefox 69这样的现代Web浏览器中,有一种更简单的方法:使用Blob.arrayBuffer()

由于Blob.arrayBuffer()返回一个Promise,你可以执行以下任一操作:

superBlob.arrayBuffer().then(arrayBuffer => {
  // Do something with arrayBuffer
});

或者

async function doSomethingWithAudioBuffer(blob) {
  var arrayBuffer = await blob.arrayBuffer();
  // Do something with arrayBuffer;
}

1
刚刚尝试了一下,你得到的是一个数组缓冲区,而不是音频缓冲区。 - Will59
@Will59 你能仔细阅读问题吗?提问者已经知道如何将ArrayBuffer转换为AudioBuffer。问题不是关于从Blob获取AudioBuffer。 - soundlake
1
仔细阅读您的代码,不要在实际获取的数组缓冲区处写入“audioBuffer”。 - Will59
感谢您抽出时间。 - Will59

3

使用异步函数的简化版本:

async function blobToAudioBuffer(audioContext, blob) {
  const arrayBuffer = await blob.arrayBuffer();
  return await audioContext.decodeAudioData(arrayBuffer);
}

我将 audioContext 作为参数,因为我建议重复使用实例。


https://dev59.com/KMLra4cB1Zd3GeqPG1TW - Weilory

2

两个答案都是正确的,只有一些细微的变化。这是我最终使用的函数:

function convertBlobToAudioBuffer(myBlob) {

  const audioContext = new AudioContext();
  const fileReader = new FileReader();

  fileReader.onloadend = () => {

    let myArrayBuffer = fileReader.result;

    audioContext.decodeAudioData(myArrayBuffer, (audioBuffer) => {

      // Do something with audioBuffer

    });
  };

  //Load blob
  fileReader.readAsArrayBuffer(myBlob);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接