如何将字节数组中的pcm样本转换为-1.0到1.0和返回范围内的浮点数



我使用的重采样算法期望float数组包含-1.0到1.0范围内的输入样本。音频数据为16位 PCM,采样率22khz

我想将音频从22khz降采样到8khz,如何将字节数组中的样本表示为浮点数>= -1和<= 1并返回字节数组?

你问两个问题:

  1. 如何从22kHz降采样到8kHz?

  2. 如何从浮点[-1,1]转换为16位整型和返回?

请注意,问题已更新,表明第1条已在其他地方处理,但我将保留我的答案的这一部分,以防它对其他人有所帮助。

1。如何从22kHz降采样到8kHz?

一位评论者暗示这可以用FFT来解决。这是不正确的(重采样的一个步骤是滤波。我在这里提到了为什么不使用FFT进行过滤,如果你感兴趣的话:http://blog.bjornroche.com/2012/08/when-to-not-use-fft.html)。

重新采样信号的一个很好的方法是使用多相滤波器。然而,即使对于有信号处理经验的人来说,这也是相当复杂的。您还有其他几个选项:

  • 使用实现高质量重采样的库,如libsamplerate
  • 做一些快速和肮脏的事情

听起来你已经采用了第一种方法,这很好。

一个快速而肮脏的解决方案听起来不会那么好,但既然你要下降到8千赫,我猜音质不是你的首要任务。一个简单快捷的方法是:

  • 对信号应用低通滤波器。尽量去掉4千赫以上的音频。你可以使用这里描述的过滤器(虽然理想情况下你想要比这些过滤器陡峭得多的东西,但它们至少比没有好)。
  • 从原始信号中选择每2.75采样一次产生新的重采样信号。当你需要一个非整数样本时,使用线性插值。如果你需要线性插值的帮助,点击这里。

对于语音应用程序来说,这项技术应该足够好了。但是,我没有尝试过,所以我不确定,所以我强烈建议使用别人的库。

如果你真的想实现你自己的高质量的采样率转换,如多相滤波器,你应该研究它,然后问你有什么问题https://dsp.stackexchange.com/,而不是这里。

2。如何从浮点[-1,1]转换为16位整型和返回?

这已经由c. fogelkou开始了,但是让我再补充一下。

首先,16位整数的范围是-32768到32767(通常16位音频是带符号的)。要将整型转换为浮点,可以这样做:

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时的误差与信号相关。事实证明,这个误差的相关性是可怕的,你实际上可以听到它,即使它非常小。(幸运的是,它足够小,对于像语音和低动态范围音乐这样的东西来说,它并不重要)为了消除这个错误,你必须在从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)成一个新的浮点数数组,并将其传递给您的样本。

重新采样后返回float,乘以32768.0,饱和(剪辑范围之外的任何东西- 32768到32767),四舍六入(或抖动如Björn提到),然后转换回short。

显示使用乘法向前和向后转换且没有位错误的测试代码:

// 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;
}

最新更新