SoundTouch音频处理库源码分析及算法提取(8)

  经过前面几个小节的介绍,各位想必已经对ST有个比较初步的认识。在往后的这些章节里,我将提出ST某些重要的算法以及实现。首先谈谈数字滤波器的实现。纵观整个ST的处理流程,仅仅在对声音样本进行重采样的时候可以通过bool bUseAAFilter的值来判断是否采用数字滤波器,具体在RateTransposer类成员函数processSamples中实现。
// Transposes sample rate by applying anti-alias filter to prevent folding.
// Returns amount of samples returned in the "dest" buffer.
// The maximum amount of samples that can be returned at a time is set by
// the 'set_returnBuffer_size' function.
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
{
    uint count;
    uint sizeReq;

    if (nSamples == 0) return;
    assert(pAAFilter);

    // If anti-alias filter is turned off, simply transpose without applying
    // the filter
    if (bUseAAFilter == FALSE)
    {
        sizeReq = (uint)((float)nSamples / fRate + 1.0f);
        count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
        outputBuffer.putSamples(count);
        return;
    }

    // Transpose with anti-alias filter
    if (fRate < 1.0f)
    {
        upsample(src, nSamples);
    }
    else 
    {
        downsample(src, nSamples);
    }
}
下面是RateTransposer类成员函数upsample和downsample的实现:
// Transposes up the sample rate, causing the observed playback 'rate' of the
// sound to decrease
void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
{
    uint count, sizeTemp, num;
    sizeTemp = (uint)((float)nSamples / fRate + 16.0f);

    count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
    storeBuffer.putSamples(count);

    num = storeBuffer.numSamples();
    count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
        storeBuffer.ptrBegin(), num, (uint)numChannels);
    outputBuffer.putSamples(count);

    storeBuffer.receiveSamples(count);
}

// Transposes down the sample rate, causing the observed playback 'rate' of the
// sound to increase
void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
{
    uint count, sizeTemp;

    storeBuffer.putSamples(src, nSamples);

    assert(tempBuffer.isEmpty());
    sizeTemp = storeBuffer.numSamples();

    count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
        storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);

 if (count == 0) return;

    storeBuffer.receiveSamples(count);

    sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
    count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
    outputBuffer.putSamples(count);
}
两个函数大同小异,upsample实现跳帧来加快播放速度,因此输出数据量一定比原始数据量小,所以可以直接处理输出,至于downsample,要实现插帧来减慢播放速度,输出数据量要比原始数据量大,所以要相应的做一些处理,无非就是把现在的输入buffer送进ST的内存管理模型FIFO,直到现在的输入buffer处理完,才进行新的buffer输出。不难发现两个函数都通过pAAFilter指针,调用evaluate方法来完成数字滤波器输出数值的计算。参考了前面提到的AAFilter类(数字滤波器)的实现。截止频率和滤波器参数是数字滤波器重要部分。因此可以先定义这两个变量。
#define LENGTH 33 //设置滤波器的长度为33//一定要是奇数,才能使用滤波器在(N-1)/2对称
static double h[LENGTH] = {0}; //初始化所有的滤波参数为0
static float fCutoff = 0.0; //初始化截止频率为0
然后编写一个setRate的函数来完成截止频率的计算,参考了RateTransposer类成员函数void RateTransposer::setRate(float newRate)的实现,newRate等于新速度和原始速度的比,然后通过这个比值也就是快播慢播来使用不同的公式计算,不管哪种情况计算得到的截止频率的取值范围都在0~0.5之间。
void setRate(float newRate)
{
    if (newRate > 1.0f)
        fCutoff = 0.5f / newRate;
    else
        fCutoff = 0.5f * newRate;
}
然后定义一个void cCoeffs(uint length,float fc)函数来用计算滤波器参数,第一个参数是滤波器长度,第二个参数是截止频率。具体实现采用汉明窗作为窗口函数。
void cCoeffs(uint length,float fc)
{
 uint i = 0;
 double temp = 0,sum = 0,scaleCoeff = 0;
 uint uLength = length -1;
 for(i = 0;i < length;i++)
 {
  temp = i - uLength/2;
  if (temp != 0)
   dH[i] = sin(2 * PI * fc * temp) / temp;
  else
   dH[i] = 2 * PI * fc;
  dH[i] = dH[i] * (0.54 - 0.46 * cos(2 * PI * i / uLength));
  sum += dH[i];
 }
 for(i = 0;i < length;i++)
 {
  dH[i] = dH[i] / sum;
 }
}
有了数字滤波器参数,就可以对数字信号进行滤波。设系统的输入为x[n],数字滤波器单位冲击响应为h[n],则系统的输出为y[n]=x[n]*h[n]=h[n]*x[n]=E(h[i]x[n-i]),*为卷积运算,E为h[i]x[n-i]从0到L的累加,i=[0,L],L为滤波器长度。假设处理16位双声道音频数据。编写的函数如下所示:src为输入数据的首地址,dest为输出数据的首地址,samples为传入样本的个数,length为数字滤波器长度,
uint evaluateStereo(short *dest, short *src,uint samples,uint length)
{
 uint i = 0,j = 0;
 long suml = 0, sumr = 0;
 uint uLength = 2 * (samples - length);
 short *pDest = dest;
 
 for(j = 2 * (length - 1);j < uLength;j += 2)
 {
  suml = sumr = 0;
  for (i = 0; i < length; i ++)
  {
   suml += src[j - 2*i + 0] * h[i];
   sumr += src[j - 2*i + 1] * h[i];
  }
  pDest[0] = (short)suml;
  pDest[1] = (short)sumr;
  pDest++;
 }
 return samples - length;
}

完整实例如下:
//对16位双声道pcm编码的wav文件滤波。
//作者:核桃
#include
#include
#include

#define PI 3.1415926
#define LENGTH 33

#ifndef uint
typedef unsigned int uint;
#endif

typedef struct
{
    char riff_char[4];
    int  package_len;
    char wave[4];
} WavRiff;

typedef struct
{
    char  fmt[4];
    int   format_len;
    short fixed;
    short channel_number;
    int   sample_rate;
    int   byte_rate;
    short byte_per_sample;
    short bits_per_sample;
} WavFormat;

typedef struct
{
    char  data_field[4];
    uint  data_len;
} WavData;

typedef struct
{
    WavRiff   riff;
    WavFormat format;
    WavData   data;
} WavHeader;

static double h[LENGTH] = {0};
static float fCutoff = 0.0;

short inbuffer[4096];
short outbuffer[4096];

void cCoeffs(uint length,float fc);
void setRate(float newRate);
uint evaluateStereo(short *dest, short *src,uint samples,uint length);

void setRate(float newRate)
{
    if (newRate > 1.0f)
        fCutoff = 0.5f / newRate;
    else
        fCutoff = 0.5f * newRate;
}

void cCoeffs(uint length,float fc)
{
 uint i = 0;
 double temp = 0,sum = 0;
 uint uLength = length -1;
 for(i = 0;i < length;i++)
 {
  temp = i - uLength/2;
  if (temp != 0)
   h[i] = sin(2 * PI * fc * temp) / temp;
  else
   h[i] = 2 * PI * fc;
  h[i] = h[i] * (0.54 - 0.46 * cos(2 * PI * i / uLength));
  sum += h[i];
 }
 for(i = 0;i < length;i++)
 {
  h[i] = h[i] / sum;
 }
}

uint evaluateStereo(short *dest, short *src,uint samples,uint length)
{
 uint i = 0,j = 0;
 long suml = 0, sumr = 0;
 uint uLength = 2 * (samples - length);
 short *pDest = dest;
 
 for(j = 2 * (length - 1);j < uLength;j += 2)
 {
  suml = sumr = 0;
  for (i = 0; i < length; i ++)
  {
   suml += src[j - 2*i + 0] * h[i];
   sumr += src[j - 2*i + 1] * h[i];
  }
  pDest[0] = (short)suml;
  pDest[1] = (short)sumr;
  pDest++;
 }
 return samples - length;
}

int main(int arg, char **argc)
{
 FILE *pIn = NULL,*pOut = NULL;
 short *pInbuffer = NULL;
 WavHeader *wheader = NULL;
 uint isamples = 0,numBytes = 0,totalBytes = 0;
 if (arg < 3)
 {
  printf("usage: %s input.wav output.wav/n",argc[0]);
  return -1;
 }
 wheader = (WavHeader *)malloc(sizeof(WavHeader));
 setRate(0.5f);
 cCoeffs(LENGTH,fCutoff);
#if 0
 for (uint i = 0;i  {
  printf("h[%d] %f/r/n",i,h[i]);
 }
#endif 
 pIn = fopen(argc[1], "rb");
 pOut = fopen(argc[2], "wb");
 numBytes = fread(wheader,1,sizeof(WavHeader),pIn);
 fwrite(wheader,1,sizeof(WavHeader),pOut);
 
 while(!feof(pIn))
 {
  numBytes = fread(inbuffer,1,4096,pIn);
  numBytes = numBytes >> 2;
  if (numBytes < LENGTH)
   break;
  isamples = evaluateStereo(outbuffer,inbuffer,numBytes,LENGTH);
  isamples = isamples << 2;
  fwrite(outbuffer,1,isamples,pOut);
  totalBytes += isamples;
 }
 fseek(pOut, 0, SEEK_SET);
 wheader->data.data_len = totalBytes;
 wheader->riff.package_len = totalBytes + sizeof(WavHeader)-4*sizeof(char)-sizeof(int);
 fwrite(wheader,1,sizeof(WavHeader),pOut);
 free(wheader);
 fclose(pIn);
 fclose(pOut);
 return 0;
}
  ps:经过滤波以后,由于某些高频分量给过滤,声音的质量有所下降。

你可能感兴趣的:(音频处理)