pyAudioKits是基于librosa和其他库的强大Python音频工作流支持。
API速查手册
通过pip安装:
pip install pyAudioKits
本项目的GitHub地址,如果这个项目帮助到了你,请为它点上一颗star,谢谢你的支持!如果你在使用过程中有任何问题,请在评论区留言或在GitHub上提issue,我将持续对该项目进行维护。
本节将介绍线性时不变系统理论,并引出LTI滤波器的概念。本节还将分别讨论FIR和IIR滤波器,并介绍使用pyAudioKits实现这两类滤波器的方法。该部分专业性较强,pyAudioKits对这一方面仅实现基本的支持。可以将pyAudioKits与SciPy的信号处理工具箱结合使用。
线性时不变系统
在信号与系统理论中,信号是一个或多个独立变量的函数,而系统则对于输入的信号产生响应,包括将信号代入函数、更改信号自变量,输出一个或多个信号。最基本的系统可以描述为。
信号分为:
- 连续时间信号和离散时间信号,区别是时间上是否连续。音频信号是连续时间信号,而在被我们采样为数字音频信号后就变成了离散时间信号。
- 确定信号和随机信号,区别是是否随机。
- 周期信号和非周期信号,区别是在全区间上是否存在周期。频率成分随时间改变的确定音频信号在经过分帧加窗后,在每一帧内可以视为周期信号。具有明显音高的平稳随机音频信号也具有准周期性。
- 偶信号和奇信号。由于我们对音频信号进行截断,数字音频信号的时间戳从开始,因此不存在偶信号和奇信号之分。
- 因果信号(<0的部分全为0)和非因果信号(<0的部分有非0值)。同样地,由于我们对音频信号进行截断,因此数字音频信号都是因果的。
- 根据信号能量分类:有限能量信号、有限功率信号和无限信号(功率和能量都无限),这在第三节讨论功率和能量时已经介绍过。
系统分为:
- 记忆性系统和无记忆系统:系统的输出是否过去和未来有关。
- 可逆和非可逆系统:输出信号是否能通过输入信号被还原。
- 线性和非线性系统:线性系统满足。
- 时不变和时变系统:时不变系统满足。
- 因果和非因果系统:因果系统的输出取决于过去或现在(而非未来)的输入。
- BIBO稳定性:如果输入的信号有界,则输出的信号有界。
线性时不变系统的特性使得我们可以通过求取其冲激响应函数来对其响应特点进行分析。所有离散信号都可以表示为一系列冲激函数的叠加:。我们假设,而,是线性时不变系统,则有,且。因此只需知道简单冲激函数通过线性时不变系统的响应h,通过卷积运算就能求得复杂函数x通过线性时不变系统的响应。h可以用于描述系统线性时不变系统,任意信号x通过该系统就是作x与h的卷积。
线性时不变(LTI)滤波器
离散时间傅里叶变换有一个非常重要的特性:时域的卷积等效为频域的相乘,即若,则有。这一特性就是线性时不变滤波器设计的基础:
- ,因此我们可以通过设计,使得能对的各个频率成分幅度进行加权,从而使得的各个频率成分具有理想的幅值,就被称为系统的幅度响应。
- ,因此通过设计同样可以使得在经过系统后得到的的各个频率成分具有理想的相位,就被称为系统的相位响应。
通常,我们希望滤波器具有线性相位响应特性,或者零相位响应特性。获取零相位响应特性可以使用双向滤波的方式,即进行一次正向卷积运算后再进行一次反向卷积运算,但这会使得幅度响应变为原来的二次方。
对于数字音频信号,由于计算机只能表示信号离散傅里叶变换的结果和,因此我们需要得到对应的。对于离散傅里叶变换,有特性则,其中表示n对傅里叶变换点数N取余数,被称为循环卷积。当取全部样本时,就可以对信号一次性进行滤波。否则,可以对信号进行分帧加窗,在每帧按帧长进行傅里叶变换,分别进行滤波,然后对信号进行还原。
用z变换设计滤波器
对进行离散时间傅里叶变换,得到,其收敛条件是绝对可积。为了使得任意都可以进行类似的变换,只需要将其乘以实指数后再进行离散傅里叶变换即可。可以通过调整的大小来确保绝对可积。这样,就有,令,我们就得到了,这就是z变换。同样,信号和也可以通过z变换变换为和。
离散时间傅里叶变换将信号变换到频域,频域是单变量的,单纯由这一参数决定函数数值。而z变换则可以将系统和信号变换到z域,由于,z域有两个变量和,因此是一个平面,称为z平面,可以使用极坐标来描述:为幅值,而为幅角。傅里叶变换则对应锁定时的z变换,对应围绕着极坐标原点的一个单位圆。取不同值时,对应的就在单位圆上旋转,以为周期,这也和之前第四节我们对特性的讨论一致。
z变换同样有和离散时间傅里叶变换类似的特性:时域的卷积等效为z域的相乘,即若,则有。
一个有理的z变换可以因式分解成,其中是所有零点,使得分子为0;是所有极点,使得分母为0。则对于z平面上的每一个点,其z变换的计算可以换成以所有零点、极点为起点,以z平面上某点为终点向量之间的计算:
- 幅度:
- 相位:
当取时,就可以得到系统的幅度响应和相位响应。也就是说当我们得到系统的所有零点和极点后,其特性就被确定了。因此,设计LTI滤波器可以通过设计零点和极点在z平面上的位置来完成。注意需要确保时绝对可积。
FIR滤波器和IIR滤波器
假设常数,对的分子和分母进行展开,有,可记为
。
z变换的另一个重要的特性是,若的z变换为,则的z变换为,因此在z域乘以一个就相当于在时域延时1个样本点。若,则有,即,则有,即。则在时,系统输入必定包含的延时项,即系统存在反馈,此时系统被称为无限冲激响应(IIR)系统。只有当时,系统输入仅仅是及其延时项的加权和,此时系统被称为*有限冲激响应(FIR)系统。满足IIR系统或FIR系统特性的滤波器就称为IIR滤波器或FIR滤波器。
简单的FIR滤波器设计
pyAudioKits提供了通过指定和来设计FIR滤波器和IIR滤波器的函数。由于通过指定和来设计IIR滤波器较为困难,仅对固定并指定来设计FIR滤波器进行演示。
import pyAudioKits.audio as ak
import pyAudioKits.filters as flt
import pyAudioKits.analyse as aly
首先生成两个频率的信号的叠加。
signal = ak.create_Single_Freq_Audio(0.1,8,256,0.5) + ak.create_Single_Freq_Audio(0.1,32,256,0.5)
signal.plot()
此时信号幅度谱具有两个峰值,且幅度是相同的。
aly.FFT(signal).plot()
差分是典型的高通滤波过程。一阶差分的系统函数为,对应差分方程为
signal_diff = flt.ltiFilter(signal, [1,-1], [1]) #构造线性时不变滤波器,设置分子系数为b0=1、b1=-1,设置分母系数为a0=1
signal_diff.plot()
对比原始信号和一阶差分后的信号的前5个样本。注意一阶差分后的信号的首位并不是有效的,使用0进行填充,有效信号整体上向右移动了1个样本,因此该系统并非是零相位响应的。
signal[:5].samples, signal_diff[:5].samples
'''
outputs:
(array([0. , 0.09021971, 0.13826834, 0.1262677 , 0.07071068]),
array([ 0.09021971, 0.09021971, 0.04804863, -0.01200064, -0.05555702]))
'''
绘制差分后信号的幅度谱。
aly.FFT(signal_diff).plot()
可以看见低频成分被抑制,高频成分的幅度此时大于低频成分。
接下来尝试使用双向滤波来实现零相位响应。
signal_diff2 = flt.ltiFilter(signal, [1,-1], [1], zero_phase=True)
signal_diff2.plot()
对比三个信号的前五和后五个样本。双向差分后,信号的首位和末位都不是有效的,因此有效信号整体居中。
signal[:5].samples, signal[-5:].samples, signal_diff[:5].samples, signal_diff[-5:].samples, signal_diff2[:5].samples, signal_diff2[-5:].samples
'''
outputs:
(array([0. , 0.09021971, 0.13826834, 0.1262677 , 0.07071068]),
array([-0.01243628, -0.07071068, -0.1262677 , -0.13826834, -0.09021971]),
array([ 0.09021971, 0.09021971, 0.04804863, -0.01200064, -0.05555702]),
array([-0.02004833, -0.0582744 , -0.05555702, -0.01200064, 0.04804863]),
array([0. , 0.04217108, 0.06004927, 0.04355638, 0.00271737]),
array([ 0.03822607, -0.00271737, -0.04355638, -0.06004927, 0. ]))
'''
由于幅度响应是原来的二次方,对低频成分的抑制程度更加强烈,现在低频成分幅度已远小于高频成分。
aly.FFT(signal_diff).plot()
平滑是典型的低通滤波过程。以9点均值平滑为例,其系统函数为,对应差分方程为。
signal_ma = flt.ltiFilter(signal, [1/9 for i in range(9)], [1]) #构造线性时不变滤波器,设置分子系数为b0~b8=1/9,设置分母系数为a0=1
signal_ma.plot()
滤波后的信号和单频正弦波类似,查看其幅度谱可以发现,此时高频成分被过滤了很多。
aly.FFT(signal_ma).plot()
使用双向滤波同样可以使得该低通滤波器保持零相位响应。此外,如果平滑是中心化的,即,则对应的滤波器也具有零相位响应,但这个系统就会是非因果的。
基于巴特沃斯滤波器设计IIR滤波器
IIR滤波器的设计需要考虑反馈,设计起来较为复杂,一般通过已有的模拟滤波器的变换来进行设计。pyAudioKits提供了以巴特沃斯滤波器为原型的IIR滤波器设计函数。
巴特沃斯滤波器需要指定阶数,我们先来看看不同阶数的巴特沃斯滤波器的效果区别。
首先生成白噪声,并绘制其幅度谱。
wn = ak.create_Single_Freq_Audio(0.1,440,44100,1) .addWgn(0.1) - ak.create_Single_Freq_Audio(0.1,440,44100,1)
aly.FFT(wn).plot()
可以看见白噪声幅度谱均匀分布在[0,采样率/2]上。
首先构造一个1阶高通巴特沃斯滤波器来进行滤波,指定截止频率为11050Hz。对于高通滤波器而言,低频为阻带,高频为通带。滤波器将会抑制阻带的频率成分,阻带的功率相比通带将会衰减。而截止频率就是阻带功率相比通带衰减的位置。
aly.FFT(flt.highPassButterN(wn, 1, 11050)).plot()
虽然截止频率被指定为11050Hz,但直到5000Hz以下依然还具有较高的幅度。
接着尝试5阶高通巴特沃斯滤波器。
aly.FFT(flt.highPassButterN(wn, 5, 11050)).plot()
可以看到此时从通带到阻带的衰减变得迅速。继续尝试10阶的滤波器。
aly.FFT(flt.highPassButterN(wn, 10, 11050)).plot()
通带到阻带的衰减变得更加迅速了。
同样的规律也适用于低通、带通和带阻滤波器。
aly.FFT(flt.lowPassButterN(wn, 1, 11050)).plot(), aly.FFT(flt.lowPassButterN(wn, 5, 11050)).plot(), aly.FFT(flt.lowPassButterN(wn, 10, 11050)).plot()
带通滤波器和带阻滤波器有两个截止频率。对于带通滤波器而言,两个截止频率之间为通带;对于带阻滤波器而言,两个频率之间为阻带。
aly.FFT(flt.bandPassButterN(wn, 1, 7500, 13500)).plot(), aly.FFT(flt.bandPassButterN(wn, 5, 7500, 13500)).plot(), aly.FFT(flt.bandPassButterN(wn, 10, 7500, 13500)).plot()
aly.FFT(flt.bandStopButterN(wn, 1, 7500, 13500)).plot(), aly.FFT(flt.bandStopButterN(wn, 5, 7500, 13500)).plot(), aly.FFT(flt.bandStopButterN(wn, 10, 7500, 13500)).plot()
通过尝试不同阶数来调节衰减速度比较麻烦,可以直接指定通带截止频率和阻带截止频率来设计滤波器。此外,通带功率的最大损失和阻带功率的最小衰减也是可以指定的。
aly.FFT(flt.highPassButter(wn, 11050, 5000, 1, 10)).plot() #巴特沃斯高通滤波器,指定通带截止频率为11050Hz、阻带截止频率为5000Hz,通带功率最多损失1dB,阻带功率至少衰减10dB
aly.FFT(flt.highPassButter(wn, 11050, 10000, 1, 10)).plot() #巴特沃斯高通滤波器,指定通带截止频率为11050Hz、阻带截止频率为10000Hz,通带功率最多损失1dB,
阻带功率至少衰减10dB
同样以11050Hz为通带截止频率,阻带截止频率为5000Hz时和阻带截止频率为10000Hz时相比,通带到阻带的衰减速度就慢得多。