使用 WebAudio API 从 getChannelData 方法获取麦克风 PCM 数据不起作用



我正在尝试使用 Web 音频 API 从麦克风获取原始 PCM 样本。经过一些研究,似乎我应该得到"一个包含与通道关联的 PCM 数据的 Float32Array"。该流程对我有用,但是当我尝试将 PCM 样本放入 WAV 文件中时,我会收到随机噪音,而不是我在录制麦克风时实际对麦克风说话的内容。下面是使用 AudioBuffer.getChannelData(( 获取 PCM 数据的代码片段,您可以将其直接粘贴到浏览器控制台中:

let recLength = 0;
let recBuffers = [];
let bufferLen = 4096;
let numChannels = 1;
let mimeType = 'audio/wav';
window.AudioContext = window.AudioContext || window.webkitAudioContext;
let audio_context = new AudioContext();
let audio_stream;
navigator.mediaDevices.getUserMedia({audio: true}).then(function (stream)
{
audio_stream = stream;
});
// wait a sec or two
let source = audio_context.createMediaStreamSource(audio_stream);
let context = source.context;
let sampleRate = context.sampleRate; // you will need to know this number for creating a WAV file.
let node = (context.createScriptProcessor || context.createJavaScriptNode).call(context, bufferLen, numChannels, numChannels);
node.onaudioprocess = function(e)
{
const inputBuffer = e.inputBuffer.getChannelData(0);
recBuffers.push(inputBuffer);
recLength += inputBuffer.length;
};
source.connect(node);
node.connect(context.destination);
// wait 10 seconds or so, while speaking into microphone
node.disconnect();
function mergeBuffers()
{
let result = new Float32Array(recLength);
let offset = 0;
for (let i = 0; i < recBuffers.length; i++) {
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
let mergedBuffer = mergeBuffers();
let normalBuffer = [];
for (let i = 0; i < mergedBuffer.length; i++)
{
normalBuffer.push(mergedBuffer[i]);
}
JSON.stringify(normalBuffer);

现在,如果我复制最后一行的字符串输出,并将其传递给生成 WAV 文件的任何库,则输出的 WAV 文件只是随机噪声。如果你想自己复制这个,我正在使用这个 NodeJS 库来编写 WAV 文件:

let arr = [1,2,3]; // replace this array with the string output of normalBuffer from above
samples = [[]];
for (let i = 0; i < arr.length; i++)
{
samples[0].push(arr[i]);
}
const fs = require("fs");
const WaveFile = require("wavefile");
let wav = new WaveFile();
wav.fromScratch(1, 44100, "32f", samples); // the sampling rate (second parameter) could be different on your machine, but you can print it out in the code snippet above to find out
fs.writeFileSync("test.wav", wav.toBuffer());

我也尝试将样本转换为无符号的 16 位 Int,但我仍然有同样的问题,我尝试将样本乘以一些常数,以防录音音量太低,但也无济于事。

问题(至少在Chrome中(是ScriptProcessorNode不断重用相同的底层音频缓冲区。这意味着recBuffers数组中的每个Float32Array都指向相同的内存。您可以通过复制数据来避免这种情况。

线...

const inputBuffer = e.inputBuffer.getChannelData(0);

。变成...

const inputBuffer = new Float32Array(bufferLen);
e.inputBuffer.copyFromChannel(inputBuffer, 0);

请记住,这在 Safari 中不起作用,因为它还没有copyFromChannel方法。需要手动复制缓冲区。

如果您只想记录 wav 文件,则重用现有库(如可扩展媒体录像机(可能会更容易。但我想你只创建了一个 wav 文件来调试问题。

最新更新