我一直在使用FFT的Exocortex实现,但我遇到了一些问题。
每当我在调用iFFT之前修改频率分档的幅度时,结果信号包含一些咔嗒声和砰砰声,特别是当信号中存在低频时(如鼓或低音)。但是,如果我用相同的因子衰减所有箱子,就不会发生这种情况。
让我举一个4样本FFT输出缓冲器的例子:
// Bin 0 (DC)
FFTOut[0] = 0.0000610351563
FFTOut[1] = 0.0
// Bin 1
FFTOut[2] = 0.000331878662
FFTOut[3] = 0.000629425049
// Bin 2
FFTOut[4] = -0.0000381469727
FFTOut[5] = 0.0
// Bin 3, this is the first and only negative frequency bin.
FFTOut[6] = 0.000331878662
FFTOut[7] = -0.000629425049
输出由成对的浮点数组成,每个浮点数代表单个bin的实部和虚部。因此,bin 0(数组索引0,1)将代表DC频率的实部和虚部。正如你所看到的,第1和第3个箱子都有相同的值(除了Im部分的符号),所以我猜bin 3是第一个负频率,最后索引(4,5)将是最后的正值频率箱。
然后为了衰减频率仓1,这就是我所做的:
// Attenuate the 'positive' bin
FFTOut[2] *= 0.5;
FFTOut[3] *= 0.5;
// Attenuate its corresponding negative bin.
FFTOut[6] *= 0.5;
FFTOut[7] *= 0.5;
对于实际测试,我使用的是1024长度的FFT,并且我总是提供所有样本,因此不需要0填充。
// Attenuate
var halfSize = fftWindowLength / 2;
float leftFreq = 0f;
float rightFreq = 22050f;
for( var c = 1; c < halfSize; c++ )
{
var freq = c * (44100d / halfSize);
// Calc. positive and negative frequency indexes.
var k = c * 2;
var nk = (fftWindowLength - c) * 2;
// This kind of attenuation corresponds to a high-pass filter.
// The attenuation at the transition band is linearly applied, could
// this be the cause of the distortion of low frequencies?
var attn = (freq < leftFreq) ?
0 :
(freq < rightFreq) ?
((freq - leftFreq) / (rightFreq - leftFreq)) :
1;
// Attenuate positive and negative bins.
mFFTOut[ k ] *= (float)attn;
mFFTOut[ k + 1 ] *= (float)attn;
mFFTOut[ nk ] *= (float)attn;
mFFTOut[ nk + 1 ] *= (float)attn;
}
显然我做错了什么但是弄不清楚是什么。
我不想使用FFT输出作为生成一组FIR系数的方法,因为我正在尝试实现一个非常基本的动态均衡器。
在频域中过滤的正确方法是什么?我错过了什么?
此外,是否真的需要衰减负频率?我已经看到了一个带有neg的FFT实现。频率值在合成之前归零。
提前致谢。
答案 0 :(得分:36)
有两个问题:使用FFT的方式和特定的过滤器。
传统上,过滤在时域中实现为卷积。你输入和滤波器信号的频谱相乘是正确的。但是,当您使用离散傅立叶变换(DFT)(使用快速傅立叶变换算法实现速度)时,实际上会计算真实频谱的采样版本。这有很多含义,但与滤波最相关的一个含义是时域信号是周期性的。
这是一个例子。考虑在周期中具有1.5个周期的正弦输入信号x
和简单的低通滤波器h
。在Matlab / Octave语法中:
N = 1024;
n = (1:N)'-1; %'# define the time index
x = sin(2*pi*1.5*n/N); %# input with 1.5 cycles per 1024 points
h = hanning(129) .* sinc(0.25*(-64:1:64)'); %'# windowed sinc LPF, Fc = pi/4
h = [h./sum(h)]; %# normalize DC gain
y = ifft(fft(x) .* fft(h,N)); %# inverse FT of product of sampled spectra
y = real(y); %# due to numerical error, y has a tiny imaginary part
%# Depending on your FT/IFT implementation, might have to scale by N or 1/N here
plot(y);
这是图表:
区块开头的故障并不是我们所期望的。但如果你考虑fft(x)
,这是有道理的。离散傅立叶变换假设信号在变换块内是周期性的。就DFT而言,我们要求改变一个时期:
当使用DFT进行过滤时,这会导致第一个重要的考虑因素:您实际上是在实现circular convolution,而不是线性卷积。因此,当您考虑数学时,第一个图中的“故障”并不是一个真正的故障。那么问题就变成了:有没有办法解决周期问题?答案是肯定的:使用overlap-save processing。基本上,您如上所述计算N长产品,但仅保留N / 2点。
Nproc = 512;
xproc = zeros(2*Nproc,1); %# initialize temp buffer
idx = 1:Nproc; %# initialize half-buffer index
ycorrect = zeros(2*Nproc,1); %# initialize destination
for ctr = 1:(length(x)/Nproc) %# iterate over x 512 points at a time
xproc(1:Nproc) = xproc((Nproc+1):end); %# shift 2nd half of last iteration to 1st half of this iteration
xproc((Nproc+1):end) = x(idx); %# fill 2nd half of this iteration with new data
yproc = ifft(fft(xproc) .* fft(h,2*Nproc)); %# calculate new buffer
ycorrect(idx) = real(yproc((Nproc+1):end)); %# keep 2nd half of new buffer
idx = idx + Nproc; %# step half-buffer index
end
这是ycorrect
的图表:
这张照片很有意义 - 我们期望从滤波器启动瞬态,然后结果稳定到稳态正弦响应。请注意,现在x
可以任意长。限制为Nproc > 2*min(length(x),length(h))
。
现在进入第二个问题:特定的过滤器。在循环中,您创建一个过滤器,其频谱基本上为H = [0 (1:511)/512 1 (511:-1:1)/512]';
如果您执行hraw = real(ifft(H)); plot(hraw)
,则会得到:
很难看到,但是在图的最左边有一堆非零点,然后在最右边有一堆非零点。使用Octave的内置freqz
函数来查看我们看到的频率响应(通过执行freqz(hraw)
):
幅度响应从高通包络到零有很多波纹。同样,DFT中固有的周期性也起作用。就DFT而言,hraw
一遍又一遍地重复。但如果您使用hraw
的一个句点,如freqz
那样,则其频谱与周期版本的频谱完全不同。
所以让我们定义一个新信号:hrot = [hraw(513:end) ; hraw(1:512)];
我们只需旋转原始DFT输出,使其在块内连续。现在让我们看看使用freqz(hrot)
的频率响应:
好多了。所需的信封就在那里,没有所有的涟漪。当然,实现并不是那么简单,你必须完全复杂乘以fft(hrot)
,而不是仅仅缩放每个复杂的bin,但至少你会得到正确的答案。
请注意,对于速度,您通常会预先计算填充h
的DFT,我将其单独留在循环中以便更容易与原始数据进行比较。
答案 1 :(得分:11)
您的主要问题是频率在短时间间隔内定义不明确。对于低频率尤其如此,这就是为什么你注意到那里最常见的问题。
因此,当您从声道中取出非常短的片段,然后对其进行滤波时,滤波的片段不会以产生连续波形的方式进行滤波,并且您会听到片段之间的跳跃,这就是生成点击此处。
例如,取一些合理的数字:我开始时的波形为27.5 Hz(钢琴上的A0),数字化为44100 Hz,它看起来像这样(红色部分为1024个样本长):
alt text http://i48.tinypic.com/zim802.png
首先,我们将从40Hz的低通开始。因此,由于原始频率低于40Hz,具有40Hz截止频率的低通滤波器实际上不会产生任何影响,我们将获得几乎与输入完全匹配的输出。对? 错误,错误,错误 - 这基本上是您问题的核心。问题是,对于短截面,27.5 Hz的想法没有明确定义,并且在DFT中无法很好地表示。
通过查看下图中的DFT,可以看出27.5 Hz在短段中没有特别意义。请注意,虽然较长段的DFT(黑点)在27.5 Hz处显示峰值,但较短的点(红点)不会。
alt text http://i50.tinypic.com/14w6luw.png
显然,然后过滤低于40Hz,将捕获直流偏移,40Hz低通滤波器的结果显示为绿色。
alt text http://i48.tinypic.com/2vao21w.png
蓝色曲线(以200 Hz截止频率拍摄)开始更好地匹配。但请注意,不是低频使其匹配良好,而是包含高频。直到我们在短段中包含可能的每个频率,高达22KHz,我们才能最终得到原始正弦波的良好表示。
所有这一切的原因是27.5 Hz正弦波的一小部分不一个27.5 Hz的正弦波,它的DFT与27.5 Hz没有太大关系。< / p>
答案 2 :(得分:2)
您是否将DC频率样本的值衰减为零?在您的示例中,您似乎根本没有衰减它。由于您要实现高通滤波器,因此您还需要将DC值设置为零。
这可以解释低频失真。如果DC值因非常大的过渡而非零,那么在低频时频率响应会产生很大的纹波。
以下是MATLAB / Octave中的一个示例,用于演示可能发生的情况:
N = 32;
os = 4;
Fs = 1000;
X = [ones(1,4) linspace(1,0,8) zeros(1,3) 1 zeros(1,4) linspace(0,1,8) ones(1,4)];
x = ifftshift(ifft(X));
Xos = fft(x, N*os);
f1 = linspace(-Fs/2, Fs/2-Fs/N, N);
f2 = linspace(-Fs/2, Fs/2-Fs/(N*os), N*os);
hold off;
plot(f2, abs(Xos), '-o');
hold on;
grid on;
plot(f1, abs(X), '-ro');
hold off;
xlabel('Frequency (Hz)');
ylabel('Magnitude');
frequency response http://www.freeimagehosting.net/uploads/e10109e535.png
请注意,在我的代码中,我创建了一个DC值非零的示例,然后是一个突然变为零,然后是一个斜坡上升。然后,我将IFFT转换为时域。然后我在该时域信号上执行零填充fft(当你传入比输入信号更大的fft时由MATLAB自动完成)。时域中的零填充导致频域中的内插。使用它,我们可以看到过滤器如何在过滤器样本之间做出响应。
要记住的最重要的事情之一是,即使您通过衰减DFT的输出设置给定频率的滤波器响应值,这也不保证采样点之间发生的频率。这意味着您的更改越突然,样本之间的过冲和振荡就越多。
现在回答您关于如何进行此过滤的问题。有许多方法,但最容易实现和理解的方法之一是窗口设计方法。您当前设计的问题是过渡宽度很大。大多数情况下,您需要尽可能快的转换,尽可能少的纹波。
在下一个代码中,我将创建一个理想的过滤器并显示响应:
N = 32;
os = 4;
Fs = 1000;
X = [ones(1,8) zeros(1,16) ones(1,8)];
x = ifftshift(ifft(X));
Xos = fft(x, N*os);
f1 = linspace(-Fs/2, Fs/2-Fs/N, N);
f2 = linspace(-Fs/2, Fs/2-Fs/(N*os), N*os);
hold off;
plot(f2, abs(Xos), '-o');
hold on;
grid on;
plot(f1, abs(X), '-ro');
hold off;
xlabel('Frequency (Hz)');
ylabel('Magnitude');
frequency response http://www.freeimagehosting.net/uploads/c86f5f1700.png
请注意,突然变化会引起很多振荡。
FFT或离散傅立叶变换是傅立叶变换的采样版本。傅立叶变换应用于连续范围-infinity到无穷大的信号,而DFT应用于有限数量的样本。当使用DFT时,这实际上导致时域中的方形窗口(截断),因为我们仅处理有限数量的样本。不幸的是,方波的DFT是一个sinc型函数(sin(x)/ x)。
过滤器中出现急剧转换的问题(在一个样本中从0快速跳转到1)是因为它在时域中有很长的响应,它被方形窗口截断。因此,为了帮助最小化此问题,我们可以将时域信号乘以更渐进的窗口。如果我们通过添加以下行来增加汉宁窗口:
x = x .* hanning(1,N).';
参加IFFT后,我们得到了这样的答复:
frequency response http://www.freeimagehosting.net/uploads/944da9dd93.png
所以我建议尝试实现窗口设计方法,因为它非常简单(有更好的方法,但它们变得更复杂)。由于您正在实现均衡器,我假设您希望能够动态更改衰减,因此我建议在参数发生变化时在频域中计算和存储滤波器,然后您就可以应用它通过获取输入缓冲区的fft,乘以频域滤波器样本,然后执行ifft以返回到时域,来输入每个输入音频缓冲区。这比您为每个样本所做的所有分支都要高效得多。
答案 3 :(得分:1)
首先,关于规范化:这是一个已知(非)问题。 DFT / IDFT在每一个中需要一个因子 1 / sqrt(N)(除了标准余弦/正弦因子)(直接反向),使它们成为同步且真正可逆的。另一种可能性是将它们中的一个(直接或反向)除以 N ,这是方便和品味的问题。通常,FFT例程不执行此标准化,用户应该意识到它并按照他的喜好进行标准化。 See
第二:在(比方说)16点DFT中,你所谓的 bin 0 将对应零频率(DC), bin 1 低频率。 。 bin 4 中频, bin 8 到最高频率, bins 9 ... 15 到“负频率”。在您的示例中, bin 1 实际上是低频和中频。除了这种考虑之外,你的“均衡”中没有任何概念上的错误。我不明白你的意思“信号在低频时失真”。你怎么看?