tri*_*jia 7 javascript audio downsampling pcm
我一直在研究音频识别演示,而api需要我传递一个8000或16000的采样率的.wav文件,所以我必须对它进行下采样.我尝试了2种算法如下.虽然它们都没有按我的意愿解决问题,但结果存在一些差异,我希望这会使它更加清晰.
这是我的第一次尝试,当sampleRate%outputSampleRate = 0时它工作正常,但是当outputSampleRate = 8000或1600时,结果音频文件是静默的(这意味着输出数组的每个元素的值为0):
function interleave(inputL){
var compression = sampleRate / outputSampleRate;
var length = inputL.length / compression;
var result = new Float32Array(length);
var index = 0,
inputIndex = 0;
while (index < length){
result[index++] = inputL[inputIndex];
inputIndex += compression;
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
所以这是我的第二次尝试,它来自一家大公司,它也不起作用.更重要的是,当我设置sampleRate%outputSampleRate = 0时,它仍然输出一个静默文件:
function interleave(e){
var t = e.length;
var n = new Float32Array(t),
r = 0,
i;
for (i = 0; i < e.length; i++){
n[r] = e[i];
r += e[i].length;
}
sampleRate += 0.0;
outputSampleRate += 0.0;
var s = 0,
o = sampleRate / outputSampleRate,
u = Math.ceil(t * outputSampleRate / sampleRate),
a = new Float32Array(u);
for (i = 0; i < u; i++) {
a[i] = n[Math.floor(s)];
s += o;
}
return a
}
Run Code Online (Sandbox Code Playgroud)
如果我的设置错误,这里是encodeWAV函数:
function encodeWAV(samples){
var sampleBits = 16;
var dataLength = samples.length*(sampleBits/8);
var buffer = new ArrayBuffer(44 + dataLength);
var view = new DataView(buffer);
var offset = 0;
/* RIFF identifier */
writeString(view, offset, 'RIFF'); offset += 4;
/* file length */
view.setUint32(offset, 32 + dataLength, true); offset += 4;
/* RIFF type */
writeString(view, offset, 'WAVE'); offset += 4;
/* format chunk identifier */
writeString(view, offset, 'fmt '); offset += 4;
/* format chunk length */
view.setUint32(offset, 16, true); offset += 4;
/* sample format (raw) */
view.setUint16(offset, 1, true); offset += 2;
/* channel count */
view.setUint16(offset, outputChannels, true); offset += 2;
/* sample rate */
view.setUint32(offset, outputSampleRate, true); offset += 4;
/* byte rate (sample rate * block align) */
view.setUint32(offset, outputSampleRate*outputChannels*(sampleBits/8), true); offset += 4;
/* block align (channel count * bytes per sample) */
view.setUint16(offset, outputChannels*(sampleBits/8), true); offset += 2;
/* bits per sample */
view.setUint16(offset, sampleBits, true); offset += 2;
/* data chunk identifier */
writeString(view, offset, 'data'); offset += 4;
/* data chunk length */
view.setUint32(offset, dataLength, true); offset += 4;
floatTo16BitPCM(view, offset, samples);
return view;
}
Run Code Online (Sandbox Code Playgroud)
它困扰了我很长一段时间,请让我知道我错过了什么...
-----------------------------解决之后------------------ --------------
我很高兴它现在运行良好,这是正确版本的函数interleave():
function interleave(e){
var t = e.length;
sampleRate += 0.0;
outputSampleRate += 0.0;
var s = 0,
o = sampleRate / outputSampleRate,
u = Math.ceil(t * outputSampleRate / sampleRate),
a = new Float32Array(u);
for (i = 0; i < u; i++) {
a[i] = e[Math.floor(s)];
s += o;
}
return a;
}
Run Code Online (Sandbox Code Playgroud)
所以你可以看到它传递给它的变量不是正确的类型〜再次感谢亲爱的@jaket和其他朋友〜虽然我想到了myslf,但他们让我更了解原始的东西~~~: )
jak*_*ket 10
采样率转换还有很多,而不仅仅是简单地扔掉样品或插入样品.
让我们简单地将下采样的情况减少2倍(例如44100-> 22050).一种天真的方法就是扔掉所有其他样本.但想象一下,在原始的44.1kHz文件中,有一个正弦波存在于20khz.对于该采样率,它在奈奎斯特(fs/2 = 22050)内.在你扔掉所有其他样品之后,它仍然会在10kHz处存在,但现在它将高于nyquist(fs/2 = 11025)并且它将混叠到输出信号中.最终结果是你将有一个大的正弦波,频率为8975赫兹!
为了在下采样期间避免这种混叠,您需要首先设计一个低通滤波器,其截止频率根据您的抽取率选择.对于上面的示例,您将先切断11025以上的所有内容然后进行抽取.
硬币的另一面称为上采样和插值.假设您希望将采样率提高2倍.首先在每个输入样本之间插入零,然后运行插值过滤器以计算值以使用周围样本替换零.
速率变化通常涉及抽取和插值的某种组合 - 因为两者都是通过整数样本工作.以48000-> 32000为例.输出/输入比率为32000/48000或2/3.因此,您将上传48000乘以获得96000,然后将其下采样3到32000.另一件事是您可以将这些过程链接在一起.因此,如果你想从48000-> 16000上升,你会上升3,下跌2,下跌2.此外,44100特别困难.例如,从48000-> 44100移动你需要上升147,下降160,你不能将它分解为更小的术语.
我建议你找一些代码或库来为你做这件事.您需要寻找的是多相滤波器或采样率转换器.