本文代码:https://gitee.com/LXP-Never/py-equalizer
音频均衡器,简称EQ(Equalizer),是一种用于调整音频信号中不同频率成分的设备。它可以帮助我们提升或削减特定频段的声音,以达到期望的音质效果。最早是用来提升电话信号在长距离的传输中损失的高频。
EQ的主要功能是通过多个滤波器对声音的某一个或多个频段进行增益或衰减处理。这些滤波器包括低通、高通、带通、带阻等类型,每种类型都有其特定的应用场景。
对于low Shelf和Low pass的区别可以参照网站。
图示均衡器通常具有多个固定频率的滑块,可以直观地调整各个频段的增益。但它的灵活性有限,无法精确调整特定频率。
下图是Audition设计图示均衡器,该均衡器有10个频段,每个频段增益为-20dB~20dB。利用Audition中的参数滤波器得到一组EQ参数,然后将其应用到尖峰滤波器(peaking filter)
但是图示均衡器有一个缺点,它只能改变固定频带的音量,假如我们想改变1.5kHz处的音量,就没有办法了,因为它只提供了调整1kHz和2kHz的推子。
参数均衡器使用峰值滤波器,允许用户自由定义中心频率、增益和品质因子Q,提供了更高的灵活性和精确度。
参数均衡器主要使用的是峰值滤波器,峰值滤波器在中心频率附近提供提升或削减。远离升压或削减的增益是统一的,因此可以方便地将多个这样的部分串联起来。峰值滤波器的主要参数
Audition有参数均衡器功能,这种 EQ 可以随意定义频点的频率,在写有Hz数的地方输入不同的数值,再输入更改的dB数(分贝),就能改变这个频率的音量。
数字滤波器的设计类型:
无限冲激响应(Infinite Impulse Response,IIR):计算量小,实时性好。
- IIR是一种适用于许多线性时不变系统的属性,这些系统的特征是具有一个冲激响应h(t),h(t)不会在特定点上完全变为零,而是无限期地持续。
有限冲激响应(Finite Impulse Response,FIR):稳定性好,相位可控。
- 在有限冲激响应(FIR)系统中,对于某个有限T,在时t>T时,冲激响应恰好变为零。
多滤波系统设计类型:
- 级联型:将多个滤波器一个接着一个连接在一起,上一个滤波器的输出作为下一个滤波器的输入,类似于串联。
- 并联型:各个滤波器并行处理,最后才将结果合并在一起。
我们选择二阶的biquad(IIR)设计滤波器,biquad响应函数如下:
H(z)=b0+b1z−1+b2z−2a0+a1z−1+a2z−2
上下同时除以a0,对a0进行归一化
H(z)=Y(z)X(z)=b0+b1⋅z−1+b2⋅z−21+a1⋅z−1+a2⋅z−2
转换到时域上差分方程计算方法:
y(n)=b0⋅x(n)+b1⋅x(n−1)+b2⋅x(n−2)−a1⋅y(n−1)−a2⋅y(n−2)
设计滤波器必要参数
然后计算几个中间变量:
A=√10dBgain/20=10dBgain/40 (for peaking and shelving EQ filters only)
w0=2∗pi∗f0/Fs
cos(w0)
sin(w0)
alpha = sin(w0)/(2*Q) (case: Q)
= sin(w0)*sinh( ln(2)/2 * BW * w0/sin(w0) ) (case: BW)
= sin(w0)/2 * sqrt( (A + 1/A)*(1/S - 1) + 2 ) (case: S)
供参考: 带宽与Q的关系为
shelf slope 与Q的关系为:1/Q=sqrt((A+1/A)∗(1/S−1)+2)
2∗sqrt(A)∗alpha=sin(w0)∗sqrt((A2+1)∗(1/S−1)+2∗A)是一个方便的中间变量,用于shelf EQ滤波器。
最后,计算每种滤波器的系数,以及对应的模拟滤波器原型 H(s):
LPF:H(s)=1/(s2+s/Q+1)
b0 = (1 - cos(w0))/2
b1 = 1 - cos(w0)
b2 = (1 - cos(w0))/2
a0 = 1 + alpha
a1 = -2*cos(w0)
a2 = 1 - alpha
fc=1000;Q=1√2
- def LowpassFilter(self, fc, Q=1 / np.sqrt(2.0)):
- """ 低通滤波器(Low Pass Filter)
- LPF: H(s) = 1 / (s^2 + s/Q + 1)
-
- b0 = (1 - cos(w0))/2;
- b1 = 1 - cos(w0);
- b2 = (1 - cos(w0))/2;
- a0 = 1 + alpha;
- a1 = -2*cos(w0);
- a2 = 1 - alpha;
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # ---------------------------------------------
- b0 = (1.0 - cos_w0) / 2.0
- b1 = 1.0 - cos_w0
- b2 = (1.0 - cos_w0) / 2.0
- a0 = 1.0 + alpha
- a1 = -2.0 * cos_w0
- a2 = 1.0 - alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
HPF:H(s)=s2/(s2+s/Q+1)
b0 = (1 + cos(w0))/2
b1 = -(1 + cos(w0))
b2 = (1 + cos(w0))/2
a0 = 1 + alpha
a1 = -2*cos(w0)
a2 = 1 - alpha
fc=1000;Q=1√2
- def HighpassFilter(self, fc, Q=1 / np.sqrt(2)):
- """ 高通滤波器(High Pass Filter)
- HPF: \(H(s)=\frac{s^2}{s^2 + s/Q + 1}\)
- b0 = (1 + cos(w0))/2
- b1 = -(1 + cos(w0))
- b2 = (1 + cos(w0))/2
- a0 = 1 + alpha
- a1 = -2*cos(w0)
- a2 = 1 - alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # ---------------------------------------------
- b0 = (1.0 + cos_w0) / 2.0
- b1 = -(1.0 + cos_w0)
- b2 = (1.0 + cos_w0) / 2.0
- a0 = 1.0 + alpha
- a1 = -2.0 * cos_w0
- a2 = 1.0 - alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
BPF:H(s)=s/(s2+s/Q+1) (constant skirt gain, peak gain = Q)
b0 = sin(w0)/2 = Q*alpha
b1 = 0
b2 = -sin(w0)/2 = -Q*alpha
a0 = 1 + alpha
a1 = -2*cos(w0)
a2 = 1 - alpha
fc=1000;Q=1√2
- def BandpassFilter_Q(self, fc, Q):
- """带通 Band Pass Filter (增益 = Q)
- BPF: H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
- b0 = sin(w0)/2 = Q*alpha
- b1 = 0
- b2 = -sin(w0)/2 = -Q*alpha
- a0 = 1 + alpha
- a1 = -2*cos(w0)
- a2 = 1 - alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # ---------------------------------------------
- b0 = sin_w0 / 2.0 # Q*alpha
- b1 = 0.0
- b2 = -sin_w0 / 2.0 # -Q*alpha
- a0 = 1.0 + alpha
- a1 = -2.0 * cos_w0
- a2 = 1.0 - alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
BPF: H(s)=(s/Q)/(s2+s/Q+1) (constant 0 dB peak gain)
b0 = alpha
b1 = 0
b2 = -alpha
a0 = 1 + alpha
a1 = -2*cos(w0)
a2 = 1 - alpha
fc=1000;Q=1√2
- def BandpassFilter_0dB(self, fc, Q=1 / np.sqrt(2)):
- """带通 Band Pass Filter( 0 db增益)
- BPF: H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
- b0 = alpha
- b1 = 0
- b2 = -alpha
- a0 = 1 + alpha
- a1 = -2*cos(w0)
- a2 = 1 - alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # ---------------------------------------------
- b0 = alpha
- b1 = 0.0
- b2 = -alpha
- a0 = 1.0 + alpha
- a1 = -2.0 * cos_w0
- a2 = 1.0 - alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
notch: H(s)=(s2+1)/(s2+s/Q+1)
b0 = 1
b1 = -2*cos(w0)
b2 = 1
a0 = 1 + alpha
a1 = -2*cos(w0)
a2 = 1 - alpha
fc=1000, dBgain = 10, Q=1√2
- def NotchFilter(self, fc, Q=1 / np.sqrt(2)):
- """Notch滤波器
- notch: H(s) = (s^2 + 1) / (s^2 + s/Q + 1)$
- b0 = 1
- b1 = -2*cos(w0)
- b2 = 1
- a0 = 1 + alpha
- a1 = -2*cos(w0)
- a2 = 1 - alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # ---------------------------------------------
- b0 = 1.0
- b1 = -2.0 * cos_w0
- b2 = 1.0
- a0 = 1.0 + alpha
- a1 = -2.0 * cos_w0
- a2 = 1.0 - alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
APF: H(s)=(s2−s/Q+1)/(s2+s/Q+1)
b0 = 1 - alpha
b1 = -2*cos(w0)
b2 = 1 + alpha
a0 = 1 + alpha
a1 = -2*cos(w0)
a2 = 1 - alpha
fc=1000, dBgain = 10, Q=1√2
- def AllpassFilter(self, fc, Q=1 / np.sqrt(2)):
- """全通 All Pass Filter
- APF: H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)$
- b0 = 1 - alpha
- b1 = -2*cos(w0)
- b2 = 1 + alpha
- a0 = 1 + alpha
- a1 = -2*cos(w0)
- a2 = 1 - alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # ---------------------------------------------
- b0 = 1.0 - alpha
- b1 = -2.0 * cos_w0
- b2 = 1.0 + alpha
- a0 = 1.0 + alpha
- a1 = -2.0 * cos_w0
- a2 = 1.0 - alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
peakingEQ: H(s)=(s2+s∗(A/Q)+1)/(s2+s/(A∗Q)+1)
b0 = 1 + alpha*A
b1 = -2*cos(w0)
b2 = 1 - alpha*A
a0 = 1 + alpha/A
a1 = -2*cos(w0)
a2 = 1 - alpha/A
fc=1000, dBgain = 10, Q=1√2
- def PeakingFilter(self, fc, dBgain, Q=1 / np.sqrt(2)):
- """峰值滤波器
- peakingEQ: H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
- b0 = 1 + alpha*A
- b1 = -2*cos(w0)
- b2 = 1 - alpha*A
- a0 = 1 + alpha/A
- a1 = -2*cos(w0)
- a2 = 1 - alpha/A
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- # cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # gain、A 仅用于峰值和shelf滤波器
- dBgain = round(float(dBgain), 3)
- A = 10 ** (dBgain / 40)
- # ---------------------------------------------
- b0 = 1 + alpha * A
- b1 = -2 * np.cos(w0)
- b2 = 1 - alpha * A
- a0 = 1 + alpha / A
- a1 = -2 * np.cos(w0)
- a2 = 1 - alpha / A
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
lowShelf: H(s)=A∗(s2+(sqrt(A)/Q)∗s+A)/(A∗s2+(sqrt(A)/Q)∗s+1)
b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha )
b1 = 2*A*( (A-1) - (A+1)*cos(w0) )
b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha )
a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha
a1 = -2*( (A-1) + (A+1)*cos(w0) )
a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha
fc=1000, dBgain = 10, Q=1√2
- def LowshelfFilter(self, fc, dBgain, Q=1 / np.sqrt(2)):
- """低切滤波器
- lowShelf: H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
- b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha )
- b1 = 2*A*( (A-1) - (A+1)*cos(w0) )
- b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha )
- a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha
- a1 = -2*( (A-1) + (A+1)*cos(w0) )
- a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # gain、A 仅用于峰值和shelf滤波器
- dBgain = round(float(dBgain), 3)
- A = 10.0 ** (dBgain / 40.0)
- # ---------------------------------------------
- b0 = A * ((A + 1) - (A - 1) * cos_w0 + 2 * np.sqrt(A) * alpha)
- b1 = 2 * A * ((A - 1) - (A + 1) * cos_w0)
- b2 = A * ((A + 1) - (A - 1) * cos_w0 - 2 * np.sqrt(A) * alpha)
- a0 = (A + 1) + (A - 1) * cos_w0 + 2 * np.sqrt(A) * alpha
- a1 = -2 * ((A - 1) + (A + 1) * cos_w0)
- a2 = (A + 1) + (A - 1) * cos_w0 - 2 * np.sqrt(A) * alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
highShelf: H(s)=A∗(A∗s2+(sqrt(A)/Q)∗s+1)/(s2+(sqrt(A)/Q)∗s+A)
b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha )
b1 = -2*A*( (A-1) + (A+1)*cos(w0) )
b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha )
a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha
a1 = 2*( (A-1) - (A+1)*cos(w0) )
a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha
fc=1000, dBgain = 10, Q=1√2
- def HighshelfFilter(self, fc, dBgain, Q=1 / np.sqrt(2)):
- """高切滤波器
- highShelf: H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
- b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha )
- b1 = -2*A*( (A-1) + (A+1)*cos(w0) )
- b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha )
- a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha
- a1 = 2*( (A-1) - (A+1)*cos(w0) )
- a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha
- """
- # 中间变量
- w0 = 2.0 * np.pi * fc / self.fs # 角频率
- cos_w0 = np.cos(w0) # cos(w0)
- sin_w0 = np.sin(w0) # sin(w0)
- alpha = sin_w0 / (2.0 * Q) # alpha
- # gain、A 仅用于峰值和shelf滤波器
- dBgain = round(float(dBgain), 3)
- A = 10.0 ** (dBgain / 40.0)
- # ---------------------------------------------
- b0 = A * ((A + 1) + (A - 1) * cos_w0 + 2 * np.sqrt(A) * alpha)
- b1 = -2 * A * ((A - 1) + (A + 1) * cos_w0)
- b2 = A * ((A + 1) + (A - 1) * cos_w0 - 2 * np.sqrt(A) * alpha)
- a0 = (A + 1) - (A - 1) * cos_w0 + 2 * np.sqrt(A) * alpha
- a1 = 2 * ((A - 1) - (A + 1) * cos_w0)
- a2 = (A + 1) - (A - 1) * cos_w0 - 2 * np.sqrt(A) * alpha
- numerator_B = np.array([b0, b1, b2], dtype=np.float32)
- denominator_A = np.array([a0, a1, a2], dtype=np.float32)
- return numerator_B / a0, denominator_A / a0
参见本文代码:https://gitee.com/LXP-Never/py-equalizer。用python画出各种类型的EQ频响曲线,并且进行串联滤波(顺便帮忙点个赞呗)
上述代码需要人工设置中心频率fc,Q值和dBgain,这些参数的调节需要一定的经验,并可借助一些软件如Audition快速方便地获取合适的值。
点击Audition菜单栏的【效果】——【滤波与均衡】——【参数均衡器】,调出参数滤波器。在界面中,可见“频率”(也即中心频率),“增益”和“Q/宽度”,在新版的Audition中还有一栏“频段”,是滤波器标识符。其中【HP】和【LP】,即高通和低通,高通指允许高频通过,滤掉低频信号;低通指允许低频通过,滤掉高频信号。可任意修改【HP】对应的频率,比如设置高通【HP】频率为100Hz,即允许100Hz以上的频率通过,去除100Hz以下噪声;同时可以修改【HP】和【LP】的增益斜率,比如修改增益斜率为24dB/Oct,即低频的曲线斜率为24dB/Oct(分贝/倍频程)。Au中的EQ处理方法——图形均衡器和参数均衡器
人耳可分辨的声音频率大约是在20Hz~20kHz,因此调音台中的四段均衡器把其分为的4个频段,根据德国柏林音乐研究所资料介绍,它们是低频、中低频、中高频、高频
音频频率对音色的影响是多方面的。以下是一些关键频率段及其对音色的影响:
低频(20Hz-200Hz):影响音色的混厚度和丰满度,像低音炮那种低沉的声音。
31Hz——这个频段需要播放器材有比较好的低频下潜能力,如果没有,当然就不容易听见,这个频段主要影响底鼓的延续音(sustain),就是踩下底鼓之后嗡嗡的声音,增强这个频段可以让音乐浑厚。
63Hz——这个频段是底鼓所在的主要频段,如果单纯把这个频点增强10dB,最明显的感受就是底鼓声变得很大,甚至破了,所以增强这个频段有助于音乐更厚实。
125Hz——这就主要是贝斯的频段了,贝斯常用的音高位置的音色主要在这一频段,当然不是说这一频段只有贝斯,增强这一频段音乐会更扎实。
中低频(200Hz-600Hz):影响音色的力茺和结实度。
250Hz——这个频段多了声音会很脏,少了声音会很干净,硬实,但它同时也是人声、弦乐、手鼓等等音色的主要共鸣点的所在频段。可以想象在水下的那种轰隆隆的感觉,是这一频段带给我的主要感受。
500Hz——和250Hz的感受相似,这一频段的增强会使一些铺底的合成器pad音色凸显出来,会使更多的男声凸显出来,这一频段多了还是会浑浊,稍微增加一些会使音乐有更多温暖、亲近的感觉。
中高频(600Hz-6kHz):影响音色的明亮度和清晰度。
1000Hz——这个频段可以算作一个分水岭,大部分乐器的基频都在200—1000Hz,所以调节1000以下的频段会更多的影响音色(不是影响音量),增强这一频段会使音色更明亮。
2000Hz——增强人声的可懂性,说白了听得更清楚,包括吉他贝斯的琴弦摩擦的声音,电吉他的尖刺感,两元店大喇叭里的广告,都可以让你更多的体会这一频段的特点,所以增强这一频段让音乐更清晰。
4000Hz——这一频段是很多音色的镶边,就像是相框的边框,衣服或者窗帘的下摆,很多时候这一频段可以让声音更完整,更具细节,更多现场感,但是过多的提升也会让人觉得刺耳,听觉疲劳。
5000Hz以上是几乎所有乐器的谐波成分,也是人耳最敏感的频段,比如把5000Hz提升6dB,有时会让人觉得整个音量被开大了一倍,如果过多的衰减则会让音乐听起来很远。
高频(6kHz-16kHz):影响音色的表现力和解析力,像音乐盒那种尖锐的声音。
8000Hz——这个频段比较明显的是各种镲声、弦乐摩擦琴弦的声音、还有就是齿音,比如提升该频段会放大歌手四、是、次、字一类的发音。一般很少会大幅提升这一频段。
16000Hz——事实上这一频段确实很难分辨,如果把一首歌的16000Hz提升10dB,我一般会去听各种镲,镲会显得更亮更大声了,反之,镲声会显得小了、暗了。如果不仔细听,会感觉音乐没什么变化。
这里说一下遮蔽效应,简单说就是比如你把125Hz调的很大,那么靠近125Hz的、dB数小的频率就会被遮蔽,听不到了。
【知乎】P8:滤波器(Filter)
【SeS】3-BAND TONE CONTROL / 7-BAND PARAMETRIC EQUALIZER
【stanford】Peaking Equalizers
【musicdsp】RBJ Audio-EQ-Cookbook; W3C Audio EQ Cookbook;
【github】Equalizer
【github】beqdesigner