DSP - 使用DFT进行频率过滤

4
我正在尝试实现一种基于DFT的8带均衡器,目的只是为了学习。为了证明我的DFT实现有效,我输入了一段音频信号,进行了分析,然后再次合成它,但没有对频谱做任何修改。到目前为止都还不错。
我正在使用所谓的“标准DFT计算方法”,即相关法。该方法计算长度为N/2 + 1的实部和虚部。要衰减某个频率,我只需执行以下操作:
float atnFactor = 0.6;
Re[k] *= atnFactor;
Im[k] *= atnFactor;

在重新合成后,我得到的信号略有失真,特别是在低频段。其中'k'是0到N/2范围内的索引。

输入信号采样率为44.1千赫兹,由于我只需要一个8频带均衡器,所以每次向DFT馈送16个样本,因此我有8个频率区间可供使用。

有人能告诉我我做错了什么吗?我试图在互联网上找到有关这个问题的信息,但没有找到任何信息。

提前感谢您。

1个回答

5
DFT和FFT在这个问题的目的上本质上是相同的。为了衰减FFT转换数组中的频率分量(或“带宽”),需要将实部和虚部乘以相同的因子,并且还要将相应的负频率分量的实部和虚部也乘以相同的因子。FFT生成一对变换后的数组,其中第一半值表示正频率分量,第二半表示负频率分量。以下是一个简化的代码示例,用于说明低通滤波器的含义:
// fftsize = size of fft window
int halfFFTsize = fftsize / 2;
float lowpassFreq1 = 1000.0;
float lowpassFreq2 = 2000.0;
for (int i = 0; i < halfFFTsize; i++)
{
    int ineg = fftsize - 1 - i; // index of neg. freq.
    float freq = (float)i * (44100.0F / (float)halfFFTsize);
    if (freq >= lowpassFreq2)
    {
        real[i] = 0;
        imag[i] = 0;
        real[ineg] = 0;
        imag[ineg] = 0;
    }
    else if (freq >= lowpassFreq1)
    {
        float mult = 1.0 - ((freq - lowpassFreq1) / 
            (lowpassFreq2 - lowpassFreq1));
        real[i] *= mult;
        imag[i] *= mult;
        real[ineg] *= mult;
        imag[ineg] *= mult;
    }

}
更新:在阅读您的编辑后,我必须说您的代码按预期工作。我原以为您会得到一个极度扭曲的重新合成信号,而不是“在低频时略微扭曲的信号”。
我认为您看到的扭曲是使用非常小的窗口大小造成的——如果您没有使用汉宁窗口方法重构原始信号,这种情况尤其明显。
尝试使用更典型的窗口大小(例如1024)运行您的代码。通常,8个滑块的设置将用于计算连接频域中8个点的曲线函数,然后使用该函数来设置更大、更细粒度的一组频率的bin幅度。
还有一个要点:频率bin均匀地划分了可用范围,因此无论窗口大小如何,超过一半的bin覆盖的频率对人耳来说都是听不见的。这就是为什么均衡器所涵盖的带宽通常是对数比例尺(例如,对于典型的3段均衡器,100Hz、1Khz和10Khz),因此不适用于相等数量的频率bin
在均匀间隔的8个bin窗口的情况下,5个bin的衰减肯定不会产生除扭曲可听频率外的任何听觉效果。

2
+1. 你最后一句话特别重要。在频率域中通过将值设置为零来进行过滤实际上是应用了一个方形滤波器,这将导致频率响应中出现明显的振铃(吉布斯现象)。请记住,在离散频率域中将一个值设置为零只会影响该精确频率处的响应。在频率样本之间的频率响应将会随着砖墙滤波器而剧烈振荡。我建议您查阅有关带通FIR设计的教程。 - Jason B
哦,我猜你删掉了那句话,但你也改变了代码以显示通带中更渐进的变化。 - Jason B
https://ccrma.stanford.edu/~jos/sasp/Generalized_Window_Method.html 提供了窗口设计方法的逐步说明。 - Jason B
如果您可以访问MATLAB,请查看remez函数(或firpm在较新版本中)。如果您没有MATLAB,我认为Octave具有相同的功能。很多时候,我只是使用MATLAB或Octave创建滤波器系数,因为它们具有良好的最优滤波器设计功能,然后在C或C ++程序中使用该滤波器。 - Jason B
如果你只得到了N/2个复杂样本,那么你并没有执行完整的DFT,因为它被定义为返回与输入相同数量的样本。有时只计算一半的样本,因为对于实际输入信号,负半部分是正半部分的镜像。然而,如果你想要进行滤波和重构,你需要使用所有的频率样本。 - Jason B
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接