这是产生接近所需效果的最新版本
void DeleteFrequencies(short *audioDataBuffer, const int bufferSize, int lowestFrequency, int highestFrequency, int sampleRate )
{
int frequencyInHzPerSample = sampleRate / bufferSize;
/* __________________________
/* ___________ __________________________ filter kernel */
int nOfPointsInFilterKernel = (lowestFrequency / frequencyInHzPerSample) + ( bufferSize - highestFrequency / frequencyInHzPerSample);
U u;
double *RealX = new double[bufferSize];
double *ImmX = new double[bufferSize];
ShortArrayToDoubleArray(audioDataBuffer, RealX, bufferSize);
// padd with zeroes, so that inputSignalSamplesNumber + kernelLength - 1 = bufferSize
// convert to frequency domain
ForwardRealFFT(RealX, ImmX, bufferSize);
// cut frequences < 300 && > 3400
int Multiplyer = 1;
for (int i = 0; i < 512; ++i)
{
if (i * 8000 / 1024 > 3400 || i * 8000 / bufferSize < 300 )
{
RealX[i] = 0;
ImmX[i] = 0;
}
if (i < lowestFrequency / frequencyInHzPerSample || i > highestFrequency / frequencyInHzPerSample )
Multiplyer = 0;
else
Multiplyer = 1;
RealX[i] = RealX[i] * Multiplyer /*ReH[f]*/ - ImmX[i] * Multiplyer;
ImmX[i] = ImmX[i] * Multiplyer + RealX[i] * Multiplyer;
}
ReverseRealFFT(RealX, ImmX, bufferSize);
DoubleArrayToShortArray(RealX, audioDataBuffer, bufferSize);
delete [] RealX;
delete [] ImmX;
}
但为什么会这样工作?
重要的是我刚开始学习DSP ,所以我可能不知道一些重要的想法
(我为此道歉,但我有需要解决的任务:我需要减少记录器语音中的背景噪音,我试图通过切断记录语音频率的范围<300&amp;&gt;&gt;来解决这个问题。 3700(作为[300; 3700]范围内的人声)我从那个方法开始,因为它很简单,但我找到了
out - 它不能应用(请参阅 - https://dsp.stackexchange.com/questions/6220/why-is-it-a-bad-idea-to-filter-by-zeroing-out-fft-bins/6224#6224 - 感谢@SleuthEye供参考)。
那么请你建议我基于FFT使用的简单解决方案,这将允许我至少删除给定的频率范围?
我正在尝试实现理想的带通滤波器。但它并没有像我预期的那样工作 - 只有高频被削减。
以下是我的实施说明:
union U
{
char ch[2];
short sh;
};
std::fstream in;
std::fstream out;
short audioDataBuffer[1024];
in.open ("mySound.pcm", std::ios::in | std::ios::binary);
out.open("mySoundFilteres.pcm", std::ios::out | std::ios::binary);
int i = 0;
bool isDataInBuffer = true;
U u;
while (in.good())
{
int j = 0;
for (int i = 0; i < 1024 * 2; i+=2)
{
if (false == in.good() && j < 1024) // padd with zeroes
{
audioDataBuffer[j] = 0;
}
in.read((char*)&audioDataBuffer[j], 2);
cout << audioDataBuffer[j];
++j;
}
// Algorithm
double RealX [1024] = {0};
double ImmX [1024] = {0};
ShortArrayToDoubleArray(audioDataBuffer, RealX, 1024);
// convert to frequency domain
ForwardRealFFT(RealX, ImmX, 1024);
// cut frequences < 300 && > 3400
for (int i = 0; i < 512; ++i)
{
if (i * 8000 / 1024 > 3400 || i * 8000 / 1024 < 300 )
{
RealX[i] = 0;
ImmX[i] = 0;
}
}
ReverseRealFFT(RealX, ImmX, 1024);
DoubleArrayToShortArray(RealX, audioDataBuffer, 1024);
for (int i = 0; i < 1024; ++i) // 7 6 5 4 3 2 1 0 - byte order hence we write ch[1] then ch[0]
{
u.sh = audioDataBuffer[i];
out.write(&u.ch[1], 1);
out.write(&u.ch[0], 1);
}
}
in.close();
out.close();
当我将结果写入文件时,打开它大胆并检查光谱分析,看到高频率被削减,但仍然保持低频率(它们从0开始)
我做错了什么?
这是
之前的声频频谱这是我将所需值
归零后的声音频率请帮忙!
更新
这是我提出的代码,我应该用Zeroes填充的内容???
void DeleteFrequencies(short *audioDataBuffer, const int bufferSize, int lowestFrequency, int highestFrequency, int sampleRate )
{
// FFT must be the same length as output segment - to prevent circular convultion
//
int frequencyInHzPerSample = sampleRate / bufferSize;
/* __________________________
/* ___________ __________________________ filter kernel */
int nOfPointsInFilterKernel = (lowestFrequency / frequencyInHzPerSample) + ( bufferSize - highestFrequency / frequencyInHzPerSample);
U u;
double *RealX = new double[bufferSize];
double *ImmX = new double[bufferSize];
ShortArrayToDoubleArray(audioDataBuffer, RealX, bufferSize);
// padd with zeroes, so that inputSignalSamplesNumber + kernelLength - 1 = bufferSize
// convert to frequency domain
ForwardRealFFT(RealX, ImmX, bufferSize);
// cut frequences < 300 && > 3400
int Multiplyer = 1;
for (int i = 0; i < 512; ++i)
{
/*if (i * 8000 / 1024 > 3400 || i * 8000 / bufferSize < 300 )
{
RealX[i] = 0;
ImmX[i] = 0;
}*/
if (i < lowestFrequency / frequencyInHzPerSample || i > highestFrequency / frequencyInHzPerSample )
Multiplyer = 0;
else
Multiplyer = 1;
RealX[i] = RealX[i] * Multiplyer /*ReH[f]*/ - ImmX[i] * Multiplyer;
ImmX[i] = ImmX[i] * Multiplyer + RealX[i] * Multiplyer;
}
ReverseRealFFT(RealX, ImmX, bufferSize);
DoubleArrayToShortArray(RealX, audioDataBuffer, bufferSize);
delete [] RealX;
delete [] ImmX;
}
它会产生以下光谱(低频被削减,但不高)
void ForwardRealFFT(double* RealX, double* ImmX, int nOfSamples)
{
short nh, i, j, nMinus1, nDiv2, nDiv4Minus1, im, ip, ip2, ipm, nOfCompositionSteps, LE, LE2, jm1;
double ur, ui, sr, si, tr, ti;
// Step 1 : separate even from odd points
nh = nOfSamples / 2 - 1;
for (i = 0; i <= nh; ++i)
{
RealX[i] = RealX[2*i];
ImmX[i] = RealX[2*i + 1];
}
// Step 2: calculate nOfSamples/2 points using complex FFT
// advantage in efficiency, as nOfSamples/2 requires 1/2 of the time as nOfSamples point FFT
nOfSamples /= 2;
ForwardDiscreteFT(RealX, ImmX, nOfSamples );
nOfSamples *= 2;
// Step 3: even/odd frequency domain decomposition
nMinus1 = nOfSamples - 1;
nDiv2 = nOfSamples / 2;
nDiv4Minus1 = nOfSamples / 4 - 1;
for (i = 1; i <= nDiv4Minus1; ++i)
{
im = nDiv2 - i;
ip2 = i + nDiv2;
ipm = im + nDiv2;
RealX[ip2] = (ImmX[i] + ImmX[im]) / 2;
RealX[ipm] = RealX[ip2];
ImmX[ip2] = -(RealX[i] - RealX[im]) / 2;
ImmX[ipm] = - ImmX[ip2];
RealX[i] = (RealX[i] + RealX[im]) / 2;
RealX[im] = RealX[i];
ImmX[i] = (ImmX[i] - ImmX[im]) / 2;
ImmX[im] = - ImmX[i];
}
RealX[nOfSamples * 3 / 4] = ImmX[nOfSamples / 4];
RealX[nDiv2] = ImmX[0];
ImmX[nOfSamples * 3 / 4] = 0;
ImmX[nDiv2] = 0;
ImmX[nOfSamples / 4] = 0;
ImmX[0] = 0;
// 3-rd step: combine the nOfSamples frequency spectra in the exact reverse order
// that the time domain decomposition took place
nOfCompositionSteps = log((double)nOfSamples) / log(2.0);
LE = pow(2.0,nOfCompositionSteps);
LE2 = LE / 2;
ur = 1;
ui = 0;
sr = cos(M_PI/LE2);
si = -sin(M_PI/LE2);
for (j = 1; j <= LE2; ++j)
{
jm1 = j - 1;
for (i = jm1; i <= nMinus1; i += LE)
{
ip = i + LE2;
tr = RealX[ip] * ur - ImmX[ip] * ui;
ti = RealX[ip] * ui + ImmX[ip] * ur;
RealX[ip] = RealX[i] - tr;
ImmX[ip] = ImmX[i] - ti;
RealX[i] = RealX[i] + tr;
ImmX[i] = ImmX[i] + ti;
}
tr = ur;
ur = tr * sr - ui * si;
ui = tr * si + ui * sr;
}
}
答案 0 :(得分:4)
使用FFT / IFFT的快速卷积滤波要求零填充至少为滤波器长度的两倍(出于性能原因通常为2的下一次幂),然后使用重叠添加或重叠保存方法来删除循环卷积伪像
答案 1 :(得分:2)
您可能需要查看this answer,了解您正在观察的效果的解释。
否则,理想的&#39;因为频域中的矩形函数(具有零转换和无限阻带衰减)对应于时域中的无限长度脉冲响应,所以你试图实现的滤波器是一种比实际实现更多的数学工具。
要获得更实用的滤波器,必须首先根据您的特定应用需求定义所需的滤波器特性,例如转换宽度和阻带衰减。 基于这些规范,可以使用各种滤波器设计方法之一来导出滤波器系数,例如:
也许最接近你正在做的是Window方法。使用该方法,像triangular window这样简单的东西可以帮助增加阻带衰减,但您可能想要尝试其他窗口选择(许多可从同一链接获得)。增加窗口长度有助于减小过渡宽度。
完成滤镜设计后,您可以使用overlap-add method或overlap-save method在频域中应用滤镜。使用这些方法中的任何一种,您可以将输入信号分成长度为L的块,并填充到某个方便的大小N> = L + M-1,其中M是滤波器系数的数量(例如,如果您有一个滤波器)如果有42个系数,你可以选择N = 128,其中L = N-M + 1 = 87)。
答案 2 :(得分:0)
在进行实际FFT后,您可以获得两次光谱数据:一次在0到512的区间,以及区域513到1024中的镜像光谱。但是,您的代码只能清除较低的光谱。
试试这个:
for (int i = 0; i < 512; ++i)
{
if (i * 8000 / 1024 > 3400 || i * 8000 / 1024 < 300 )
{
RealX[i] = 0;
ImmX[i] = 0;
// clear mirror spectrum as well:
RealX[1023-i] = 0;
ImmX[1023-i] = 0;
}
}
除非您的FFT实施自动执行此步骤,否则这可能会有所帮助。
顺便说一下,像你一样将频率分档归零并不是做这种过滤器的好方法。期待一个非常讨厌的阶段响应和一个很多在您的信号中振铃。