如何使用 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 为音频创建低通滤波器的主要内容,如果未能解决你的问题,请参考以下文章

使用 fft 的 Matlab 低通滤波器

iOS Accelerate低通FFT滤波器镜像结果

低通android PCM音频数据

用MATLAB设计对信号进行频谱分析和滤波处理的程序

NAudio 低通滤波器

使用核心音频实现后处理低通滤波器