如何使用 fft 为音频创建低通滤波器
Posted
技术标签:
【中文标题】如何使用 fft 为音频创建低通滤波器【英文标题】:How do I create a low pass filter for audio using fft 【发布时间】:2019-07-26 18:44:28 【问题描述】:我尝试为 aiff 文件创建低通滤波器,但发出的声音是白噪声。我只了解 FFT 如何工作的广泛概述,所以我猜我的问题与此有关。
基本上,我打开音频文件(比如钢琴循环),将其转换为单声道,然后对样本执行 FFT,然后我尝试通过将其设置为零来去除高频。最后我执行 IFTT 并将结果保存到一个新文件中。
import aifc
import struct
import numpy as np
def getMonoSamples(fileName):
enter code here`obj = aifc.open(fileName,'r')
obj.setpos(0)
numFrames = obj.getnframes()
myFrames = obj.readframes(numFrames)
samplingRate = obj.getframerate()
data = struct.unpack('nh'.format(n=numFrames*2), myFrames)
data = np.array(data)
dataLeft =[]
for i,x in enumerate(data):
if i%2==1:
dataLeft.append(x)
obj.close()
return dataLeft,numFrames,samplingRate
def writeMonoFile(fileName,samples,nframes):
mono_file=aifc.open(file, 'w')
comptype="NONE"
compname="not compressed"
nchannels=1
sampwidth=2
mono_file.setparams((nchannels, sampwidth, int(sampling_rate), nframes, comptype, compname))
print "writing sample aif..."
for s in samples:
mono_file.writeframes(struct.pack('h', s))
mono_file.close()
def lpFilter(dataFft):
new =[None]*len(dataFft)
for i,x in enumerate(dataFft):
#if the frequency is above 5000, remove it
if i>5000:
new[i]=0
else:
new[i]=x
return new
# get audio samples from a function that converts stereo to mono
sampleData,numFrames,samplingRate = getMonoSamples('beetP2.aif')
dataFft = np.fft.fft(sampleData)
filtered = lpFilter(dataFft)
invFft = np.fft.ifft(filtered)
invFft = [int(x) for x in invFft]
file = "test.aif"
writeMonoFile(file,invFft,numFrames)
我确实收到了一条警告:“ComplexWarning:将复数转换为实数会丢弃虚部”,但在简单地执行立体声到单声道的转换和保存时,我也会收到此警告。音频听起来不错,直到我尝试过滤它。我猜这是相关的,但不知道如何解决它。
我过滤的任何音频样本最终听起来像白噪声,而不是其自身的过滤版本。
【问题讨论】:
【参考方案1】:切换到实数到复数 numpy.fft.rfft
及其反向 numpy.fft.irfft
可能会解决问题。
由于复数到复数的 DFT 变换应用于实数数组sampleData
,因此输出数组是相同大小的复数数组dataFft
。该数组的第一项对应于 DC 分量,第二项对应于频率 1/N,第三项对应于 2/N……然而,数组的后半部分应该被描述为负频率分量。因此,数组最后一项的频率为-1/N,-2/N之前的项...如what FFTW really computes中所述
对于那些喜欢用正负频率来思考的人来说,这意味着正频率存储在输出的前半部分,负频率存储在输出的后半部分。 (频率-k/n与频率(n-k)/n相同。)
由于信号是实数,频率 -k/N 的分量必须是频率 k/N 的分量的复共轭。例如,频率为 k/N 的余弦波会产生频率为 k/N 和 -k/N 的两个相等的实分量。
通过将阵列的后半部分归零,具有低负频率的分量被丢弃,并且阵列不再对应于真实阵列的 DFT。它不是低通滤波器,可以解释产生的白噪声。由于应用了逆 DFT,invFft = np.fft.ifft(filtered)
,其结果invFft
是复杂的,具有与原始数组sampleData
相同的大小。
使用实数到复数 DFT 会将实数数组 sampleData
转换为大约一半大小的复数数组 dataFft
。将该阵列的一个分量归零意味着将正频率和负频率都归零,确保该阵列仍然可以被视为真实阵列的 DFT。这个真实数组最终可以通过应用逆变换irfft
来恢复。
【讨论】:
感谢您的回复!将我试图保留的频率的负半部分归零会大大改变声音,这当然是有道理的。我想你是说我应该基本上把dataFft = np.fft.fft(sampleData)
invFft = np.fft.ifft(filtered)
改成dataFft = np.fft.rfft(sampleData)
invFft = np.fft.irfft(filtered)
。这给了我一个结果(irfft 乘积),它大约是输入信号幅度的两倍,但听起来仍然像白噪声。也许我错过了什么?以上是关于如何使用 fft 为音频创建低通滤波器的主要内容,如果未能解决你的问题,请参考以下文章