经过前面几个小节的介绍,各位想必已经对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:经过滤波以后,由于某些高频分量给过滤,声音的质量有所下降。