使用 WebAudio API 从 getChannelData 方法获取麦克风 PCM 数据不起作用
Obtaining microphone PCM data from getChannelData method using WebAudio API doesn't work
我正在尝试使用网络音频从麦克风获取原始 PCM 样本 API。经过一些研究,我似乎应该得到 "a Float32Array containing the PCM data associated with the channel"。该流程适用于我,但是当我尝试将 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 文件只是随机噪声。如果你想自己复制这个,我正在使用 this NodeJS library 来编写 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 位整数,但我仍然遇到同样的问题,并且我尝试将样本乘以某个常数以防录音音量过低,但也无济于事。
问题(至少在 Chrome 中)是 ScriptProcessorNode 不断重复使用相同的底层音频缓冲区。这意味着 recBuffers
数组中的每个 Float32Array
都指向相同的内存。您可以通过复制数据来避免这种情况。
行...
const inputBuffer = e.inputBuffer.getChannelData(0);
... 变成...
const inputBuffer = new Float32Array(bufferLen);
e.inputBuffer.copyFromChannel(inputBuffer, 0);
请记住,这在 Safari 中不起作用,因为它还没有 copyFromChannel
方法。缓冲区将需要手动复制。
如果您只想录制 wav 文件,那么重用 extendable-media-recorder 等现有库可能会更容易。但我猜你只是创建了一个wav文件来调试问题。
我正在尝试使用网络音频从麦克风获取原始 PCM 样本 API。经过一些研究,我似乎应该得到 "a Float32Array containing the PCM data associated with the channel"。该流程适用于我,但是当我尝试将 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 文件只是随机噪声。如果你想自己复制这个,我正在使用 this NodeJS library 来编写 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 位整数,但我仍然遇到同样的问题,并且我尝试将样本乘以某个常数以防录音音量过低,但也无济于事。
问题(至少在 Chrome 中)是 ScriptProcessorNode 不断重复使用相同的底层音频缓冲区。这意味着 recBuffers
数组中的每个 Float32Array
都指向相同的内存。您可以通过复制数据来避免这种情况。
行...
const inputBuffer = e.inputBuffer.getChannelData(0);
... 变成...
const inputBuffer = new Float32Array(bufferLen);
e.inputBuffer.copyFromChannel(inputBuffer, 0);
请记住,这在 Safari 中不起作用,因为它还没有 copyFromChannel
方法。缓冲区将需要手动复制。
如果您只想录制 wav 文件,那么重用 extendable-media-recorder 等现有库可能会更容易。但我猜你只是创建了一个wav文件来调试问题。