如何将字节数组中的pcm样本转换为-1.0至1.0及以后的浮点数?
我使用的重采样算法期望浮点数组包含 -1.0 到 1.0 范围内的输入样本。音频数据为 16 位 PCM,采样率为 22khz。
我想将音频从22khz下采样到8khz,如何将字节数组中的样本表示为浮点数>= -1和<= 1并返回字节数组?
我使用的重采样算法期望浮点数组包含 -1.0 到 1.0 范围内的输入样本。音频数据为 16 位 PCM,采样率为 22khz。
我想将音频从22khz下采样到8khz,如何将字节数组中的样本表示为浮点数>= -1和<= 1并返回字节数组?
你问两个问题:
如何将采样从22kHz降至8kHz?
如何从浮点数 [-1,1] 转换为 16 位整数并返回?
请注意,这个问题已经更新,表明#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,我认为我们已经确定情况并非如此,因此这个答案很好。
但是,为了完整性,我必须补充一点:如果您试图保持绝对原始状态,请记住,这种转换会引入失真。为什么?因为从浮点数转换为 int 时的误差与信号相关。事实证明,这个错误的相关性是可怕的,你实际上可以听到它,即使它非常小。(幸运的是,它足够小,对于语音和低动态范围音乐等内容,它并不重要)要消除此错误,必须在从 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;
}