我实现了我的过滤器,其中重叠添加方法以防止使用循环骚动 输入 - 带噪声的文件,输出应该是过滤文件 我的结果:输出略有修改,频率不会被削减
我的猜测是我错误地将滤波器内核上的频域输入信号相乘 (我的目的是切断不在[300,3700]范围内的频率)。如何进行乘法运算?
我使用blackmanwindow构建内核 - 我的理解是否正确? (我计算每个滤波器样本的频率,然后通过样本并查看它是否在我想要切断的范围内我使用Blackman窗口的公式计算频率。)
我刚开始学习DSP。
这是我的实现(它有什么问题?):
void DeleteFrequencies(char* fileWithNoise, char* resultFile, const int bufferSize, int lowestFrequency, int highestFrequency, int sampleRate )
{
// |1|. files
std::fstream in;
std::fstream out;
in.open (fileWithNoise, std::ios::in | std::ios::binary);
out.open(resultFile, std::ios::out | std::ios::binary);
// |2|. Filter kernel design. I shall use blackman window
// fundamental params
const int filterKernelLength = 200; // 512
const int filterMaxFrequency = sampleRate / 2; // 8000 / 2
const int frequencyPerSamle = filterMaxFrequency / filterKernelLength;
double *RealFilterResp = new double [bufferSize / 2];
double *ImmFilterResp = new double [bufferSize / 2];
// coefficients for Blackman window
const double a0 = 0.42659;
const double a1 = 0.49656;
const double a2 = 0.076849;
// construct filter kernel
for (int i = 0 ; i < bufferSize / 2; ++i)
{
if ( i >= filterKernelLength ) // padd filter kernel with zeroes
{
RealFilterResp[i] = 0;
ImmFilterResp[i] = 0;
}
else if (i * frequencyPerSamle < lowestFrequency || i * frequencyPerSamle > highestFrequency)
{
// apply blackman window (to eleminate frequencies < 300 hz and > 3700 hz)
RealFilterResp[i] = a0 - a1 * cos (2 * M_PI * i / (bufferSize / 2 - 1)) + a2 * cos (4 * M_PI / (bufferSize / 2 - 1));
ImmFilterResp[i] = a0 - a1 * cos (2 * M_PI * i / (bufferSize / 2 - 1)) + a2 * cos (4 * M_PI / (bufferSize / 2 - 1));
}
else
{
RealFilterResp[i] = 1;
ImmFilterResp[i] = 1;
}
}
// |3|. overlap add method
// calculate parameters for overlap add method (we use it to prevent circular convultion)
const int FFT_length = pow (2.0 ,(int)(log(bufferSize + filterKernelLength - 1.0)/log(2.0)) + 1.0);
double *OLAP = new double[bufferSize / 2 ]; // holds the overlapping samples from segment to segment
memset(OLAP,0, bufferSize / 2 * sizeof (double));
double *RealX = new double[bufferSize];
memset(RealX, 0, bufferSize * sizeof(double));
double *ImmX = new double[bufferSize];
memset(ImmX, 0, bufferSize * sizeof(double));
short* audioDataBuffer = new short[bufferSize];
memset(audioDataBuffer, 0 , sizeof(short) * bufferSize);
// start reading from file by chunks of bufferSize
while (in.good())
{
// get proper chunk of data
FillBufferFromFile(audioDataBuffer, bufferSize, in); // read chunk from file
ShortArrayToDoubleArray(audioDataBuffer, RealX, bufferSize); // fill RealPart
ForwardRealFFT(RealX, ImmX, bufferSize); // go to frequency domain
// perform convultion as multiplication in frequency domain
for (int j = 0; j < bufferSize / 2; ++j)
{
double tmp = RealX[j] * RealFilterResp[j] - ImmX[j] * ImmFilterResp[j];
ImmX[j] = RealX[j] * ImmFilterResp[j] + ImmX[j] * RealFilterResp[j];
RealX[j] = tmp;
}
// Inverse FFT
ReverseRealFFT(RealX, ImmX, bufferSize); // go to time domain
// add last segment overlap to this segment
for (int j = 0; j < filterKernelLength - 2; ++j )
{
RealX[j] += OLAP[j];
}
// save samples that will overlap the next segment
for (int j = bufferSize/2 + 1; j < bufferSize; ++j )
{
OLAP[j - bufferSize/2 - 1] = audioDataBuffer[j];
}
// write results
DoubleArrayToShortArray(RealX, audioDataBuffer, bufferSize);
FillFileFromBuffer(audioDataBuffer, bufferSize, out);
}
/*ReverseRealFFT(RealX, ImmX, bufferSize
);
DoubleArrayToShortArray(RealX, audioDataBuffer, bufferSize);*/
delete [] audioDataBuffer;
delete [] RealFilterResp;
delete [] ImmFilterResp;
delete [] OLAP;
delete [] RealX;
delete [] ImmX;
in.close();
out.close();
}
答案 0 :(得分:1)
您的窗口系数错误 - 窗口函数纯粹是真实的,您将使用这些实际系数乘以(复数)频域数据。所以你的过滤器系数初始化:
double *RealFilterResp = new double [bufferSize / 2];
double *ImmFilterResp = new double [bufferSize / 2];
if ( i >= filterKernelLength ) // padd filter kernel with zeroes
{
RealFilterResp[i] = 0;
ImmFilterResp[i] = 0;
}
else if (i * frequencyPerSamle < lowestFrequency || i * frequencyPerSamle > highestFrequency)
{
// apply blackman window (to eleminate frequencies < 300 hz and > 3700 hz)
RealFilterResp[i] = a0 - a1 * cos (2 * M_PI * i / (bufferSize / 2 - 1)) + a2 * cos (4 * M_PI / (bufferSize / 2 - 1));
ImmFilterResp[i] = a0 - a1 * cos (2 * M_PI * i / (bufferSize / 2 - 1)) + a2 * cos (4 * M_PI / (bufferSize / 2 - 1));
}
else
{
RealFilterResp[i] = 1;
ImmFilterResp[i] = 1;
}
应该是:
double *FilterResp = new double [bufferSize / 2];
if ( i >= filterKernelLength ) // padd filter kernel with zeroes
{
FilterResp[i] = 0;
}
else if (i * frequencyPerSamle < lowestFrequency || i * frequencyPerSamle > highestFrequency)
{
FilterResp[i] = a0 - a1 * cos (2 * M_PI * i / (bufferSize / 2 - 1)) + a2 * cos (4 * M_PI / (bufferSize / 2 - 1));
}
else
{
FilterResp[i] = 1;
}
和频域乘法:
for (int j = 0; j < bufferSize / 2; ++j)
{
double tmp = RealX[j] * RealFilterResp[j] - ImmX[j] * ImmFilterResp[j];
ImmX[j] = RealX[j] * ImmFilterResp[j] + ImmX[j] * RealFilterResp[j];
RealX[j] = tmp;
}
应该是:
for (int j = 0; j < bufferSize / 2; ++j)
{
RealX[j] *= FilterResp[j];
ImmX[j] *= FilterResp[j];
}
答案 1 :(得分:1)
如果您打算使用window method来实现滤波器,窗口应该乘以与理想带通滤波器的无限脉冲响应相对应的时域序列。
具体来说,对于带宽w 0 = 2 * pi *(3700-300)/ 8000的带通滤波器,以w c = 2 * pi *为中心(300 +3700)/ 8000,理想的脉冲响应是(对于-infinity&lt; n&lt; infinity):
w0*sinc(0.5*w0*n/pi) * cos(wc*n) / pi
您将转移到区间[0,N-1],然后应用您计算的窗口:
double sinc(double x) {
if (fabs(x)<1e-6) return 1.0;
return sin(M_PI * x)/(M_PI * x);
}
void bandpassDesign(int N, double* filterImpulseResponse) {
double w0 = 2*(3700-300)*M_PI/8000;
double wc = 2*(300+3700)*M_PI/8000;
double shift = 0.5*N;
for (int i = 0; i < bufferSize; ++i) {
double truncatedIdealResponse = w0*sinc(0.5*w0*(i-shift)/M_PI) * cos(wc*i) / M_PI;
double window = a0 - a1 * cos (2 * M_PI * i / (N- 1)) + a2 * cos (4 * M_PI * i / (N- 1));
filterImpulseResponse[i] = truncatedIdealResponse * window;
}
}
然后,您可以使用FFT来获得频域系数。请记住,如果您打算使用此过滤器过滤数据,则时间序列必须为零填充。
例如,如果您希望使用1024点FFT和重叠相加方法,并假设128点滤波器内核符合您的滤波器设计规范,您可以使用bandpassDesign
调用N=128
,填充使用1024-128 = 896个零,然后进行FFT。