如何PCM采样转换成字节数组作为浮点数范围为-1.0至1.0和回?(How to convert p

2019-07-20 13:18发布

我使用重新采样算法预计含有输入样品中的范围-1.0到1.0阵列。 音频数据是16位 PCM与采样率22kHz的

我想从下采样到22kHz的8kHz的音频,如何表示在字节数组的样品作为浮点数> = -1和<= 1并返回到字节数组?

Answer 1:

你问两个问题:

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

  2. 如何从浮[-1,1]转换为16位int和回?

请注意,这个问题已被更新,以表明#1是照顾其他地方,但我会离开的情况下我的回答的那部分它可以帮助别人。

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

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

重新采样信号的一种非常好的方式是用多相滤波器 。 然而,这是相当复杂的,甚至有人在信号处理经验。 您有其他几个选项:

  • 使用实现高品质重采样库,像libsamplerate
  • 做一些快速和肮脏

这听起来像你已经与第一种方法,这是伟大的了。

一个快速和肮脏的解决方案不会听起来不错,但因为你会下降到8千赫,我猜音质不是你的首要任务。 一个快速和肮脏的方法是:

  • 应用低通滤波器应用于所述信号。 尝试,你可以摆脱尽可能多的音频4kHz以上。 您可以使用所描述的过滤器在这里 (虽然理想的你想要的东西比这些滤波器更陡峭,他们至少聊胜于无)。
  • 从原始信号中选择每2.75th样品以产生新的,重新采样信号。 当你需要一个非整数样本,使用线性插值。 如果你需要用线性插值问题,请尝试在这里 。

这种技术应该比语音应用足够好了。 但是,我还没有尝试过,所以我不肯定知道,所以我强烈建议使用别人的库。

如果你真的想实现自己的高品质的采样率转换,比如多相滤波器,你应该研究它,然后问你的任何问题上https://dsp.stackexchange.com/ ,不在这里。

2.如何从浮[-1,1] 16位int和转换回?

这是已经开始通过c.fogelklou,但让我美化。

要下手,16位整数的范围为-32768至32767(通常是16位音频签署)。 由Int转换为浮动你这样做:

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://博客.bjornroche.com / 2011/11 /载玻片从-基本面的-audio.html



Answer 2:

16位PCM具有范围 - 32768〜32767。因此,通过(1.0F / 32768.0f)乘以每个PCM样本的在float一个新的数组,并传递到您的重采样。

让我们再回到漂浮重采样后,乘以32768.0,饱和(夹任何范围之外 - 32768〜32767),圆形(或抖动作为比约恩提到),然后转换回短。

测试代码,显示前进和后退转换使用乘以无位误差:

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


文章来源: How to convert pcm samples in byte array as floating point numbers in the range -1.0 to 1.0 and back?