我使用的重采样算法需要包含-1.0 到 1.0范围内的输入样本的浮点数组。音频数据是16 位PCM,采样率为 22khz。
我想将音频从 22khz 下采样到 8khz,如何将字节数组中的样本表示为浮点数>= -1 和 <= 1并返回到字节数组?
我使用的重采样算法需要包含-1.0 到 1.0范围内的输入样本的浮点数组。音频数据是16 位PCM,采样率为 22khz。
我想将音频从 22khz 下采样到 8khz,如何将字节数组中的样本表示为浮点数>= -1 和 <= 1并返回到字节数组?
你问两个问题:
如何从 22kHz 下采样到 8kHz?
如何从 float [-1,1] 转换为 16 位 int 并返回?
请注意,问题已更新以表明 #1 已在其他地方得到处理,但我将保留我的答案的那一部分,以防它帮助其他人。
一位评论者暗示这可以通过 FFT 解决。这是不正确的(重采样的一个步骤是过滤。我提到为什么不在这里使用 FFT 进行过滤,以防您感兴趣:http ://blog.bjornroche.com/2012/08/when-to-not-use -fft.html )。
对信号重新采样的一种非常好的方法是使用多相滤波器。然而,这相当复杂,即使对于有信号处理经验的人来说也是如此。您还有其他几种选择:
听起来您已经采用了第一种方法,这很棒。
快速而肮脏的解决方案听起来不会那么好,但是由于您要降低到 8 kHz,我猜音质不是您的首要任务。一种快速而肮脏的选择是:
这种技术对于语音应用来说应该绰绰有余。但是,我没有尝试过,所以我不确定,所以我强烈建议使用别人的库。
如果您真的想实现自己的高质量采样率转换,例如多相滤波器,您应该研究它,然后在https://dsp.stackexchange.com/上提出任何问题,而不是在这里。
这已经由 c.fogelklou 开始了,但让我来润色一下。
首先,16 位整数的范围是 -32768 到 32767(通常 16 位音频是有符号的)。要从 int 转换为 float,请执行以下操作:
float f;
int16 i = ...;
f = ((float) i) / (float) 32768
if( f > 1 ) f = 1;
if( f < -1 ) f = -1;
您通常不需要做额外的“边界”(事实上,如果您真的使用 16 位整数,则不需要),但如果您出于某种原因有一些 >16 位整数,则可以这样做。
要转换回来,请执行以下操作:
float f = ...;
int16 i;
f = f * 32768 ;
if( f > 32767 ) f = 32767;
if( f < -32768 ) f = -32768;
i = (int16) f;
在这种情况下,通常需要注意超出范围的值,尤其是大于 32767 的值。您可能会抱怨这会在 f = 1 时引入一些失真。这个问题引起了激烈的争论。有关对此的一些(不完整)讨论,请参阅此博客文章。
这不仅仅是“足以胜任政府工作”。换句话说,它可以正常工作,除非您担心最终音质。由于您要使用 8kHz,我认为我们已经确定情况并非如此,所以这个答案很好。
但是,为了完整起见,我必须补充一点:如果您试图保持绝对原始,请记住这种转换会引入失真。为什么?因为从 float 转换为 int 时的错误与信号相关。事实证明,该错误的相关性很糟糕,您实际上可以听到它,即使它非常小。(幸运的是,它足够小,对于语音和低动态范围音乐之类的东西并不重要)要消除此错误,您必须在从浮点数到整数的转换中使用称为抖动的东西。同样,如果这是您关心的事情,请研究它并在https://dsp.stackexchange.com/上提出相关的具体问题,而不是在这里。
您可能还对我关于数字音频编程基础的演讲中的幻灯片感兴趣,其中有一张关于这个主题的幻灯片,尽管它基本上说的是同样的事情(甚至可能比我刚才说的要少):http://blog .bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html
16 位 PCM 有一个范围 - 32768 到 32767。因此,将每个 PCM 样本乘以 (1.0f/32768.0f) 到一个新的浮点数组中,并将其传递给您的重新采样。
重新采样后返回浮动,乘以 32768.0,饱和(剪切范围之外的任何内容 - 32768 到 32767),舍入(或 Björn 提到的抖动),然后再转换回短。
使用没有位错误的乘法显示正向和反向转换的测试代码:
// PcmConvertTest.cpp : Defines the entry point for the console application.
//
#include <assert.h>
#include <string.h>
#include <stdint.h>
#define SZ 65536
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)
int main(int argc, char* argv[])
{
int16_t *pIntBuf1 = new int16_t[SZ];
int16_t *pIntBuf2 = new int16_t[SZ];
float *pFloatBuf = new float[SZ];
// Create an initial short buffer for testing
for( int i = 0; i < SZ; i++) {
pIntBuf1[i] = (int16_t)(-32768 + i);
}
// Convert the buffer to floats. (before resampling)
const float div = (1.0f/32768.0f);
for( int i = 0; i < SZ; i++) {
pFloatBuf[i] = div * (float)pIntBuf1[i];
}
// Convert back to shorts
const float mul = (32768.0f);
for( int i = 0; i < SZ; i++) {
int32_t tmp = (int32_t)(mul * pFloatBuf[i]);
tmp = MAX( tmp, -32768 ); // CLIP < 32768
tmp = MIN( tmp, 32767 ); // CLIP > 32767
pIntBuf2[i] = tmp;
}
// Check that the conversion went int16_t to float and back to int for every PCM value without any errors.
assert( 0 == memcmp( pIntBuf1, pIntBuf2, sizeof(int16_t) * SZ) );
delete pIntBuf1;
delete pIntBuf2;
delete pFloatBuf;
return 0;
}