以前の記事で,低域・高域を別々に処理するようなマルチバンド処理(オーディオクロスオーバー)の実装方法を紹介しました.
上記の記事では,補完フィルタのローパス・ハイパスフィルタの並列接続したことで生成できるオールパスフィルタの伝達関数の振幅特性がフラットになることを確認しました。
実用では,フィルタリングの際にこのオールパスフィルタのフィルタ係数を求める必要があります.
2つのIIRフィルタの伝達関数の積を取るのは手間ですが,補完フィルタの場合は多少計算が楽になることが検証できたため,メモとして残します
バターワース型のローパス/ハイパスフィルタのフィルタ係数の対称性
以下の記事の伝達関数を見ると、カットオフ周波数が等しいバターワース型のローパス/ハイパスフィルタの分母項は同じになっています。
実際に確認したところ,次数が同じであればローパス/ハイパスの伝達関数の分母項は同じでした.
また,伝達関数の分子項のフィルタ係数は,半分の次数で対称となります.
結局のところ,カットオフ周波数と次数の等しいバターワース型のローパス/ハイパスフィルタの並列回路が成すオールパスのフィルタ係数は,
- 分母項: ローパスもしくはハイパスフィルタの分母項
- 分子項: ローパスとハイパスフィルタの分子項の和
として簡単に計算することができます.
実際に周波数応答を目視で確認してみると,分子項と分母項で振幅特性は0dBで対称となり,伝達関数全体としては振幅特性がフラット,すなわち,オールパスフィルタとなっていることが確認できました.
例として,カットオフ周波数 5k Hz, 次数5の時のバターワース型のローパス/ハイパスフィルタの並列回路が成すオールパスのフィルタの周波数応答が以下になります.
Linkwitz-Riley の場合でも,ベースはバターワースフィルタなので同じ計算手順でオールパスフィルタのフィルタ係数を算出できるはずです.
実際のフィルタ係数
3次のフィルタ係数
fc: 500.0 Low b: [4.21357017e-05 1.26407105e-04 1.26407105e-04 4.21357017e-05] High b: [ 0.93122625 -2.79367876 2.79367876 -0.93122625] Low a: [ 1. -2.85755414 2.72507355 -0.86718233] High a: [ 1. -2.85755414 2.72507355 -0.86718233] All b: [ 0.93126839 -2.79355235 2.79380516 -0.93118412] fc: 1000.0 Low b: [0.00031507 0.00094522 0.00094522 0.00031507] High b: [ 0.86710351 -2.60131054 2.60131054 -0.86710351] Low a: [ 1. -2.71528536 2.46967434 -0.7518684 ] High a: [ 1. -2.71528536 2.46967434 -0.7518684 ] All b: [ 0.86741859 -2.60036532 2.60225576 -0.86678844] fc: 2000.0 Low b: [0.0022177 0.0066531 0.0066531 0.0022177] High b: [ 0.75131371 -2.25394114 2.25394114 -0.75131371] Low a: [ 1. -2.43191667 2.01412566 -0.56446738] High a: [ 1. -2.43191667 2.01412566 -0.56446738] All b: [ 0.75353142 -2.24728804 2.26059425 -0.74909601] fc: 5000.0 Low b: [0.02485108 0.07455325 0.07455325 0.02485108] High b: [ 0.4825145 -1.4475435 1.4475435 -0.4825145] Low a: [ 1. -1.598451 1.02946232 -0.23220267] High a: [ 1. -1.598451 1.02946232 -0.23220267] All b: [ 0.50736558 -1.37299025 1.52209674 -0.45766342] fc: 10000.0 Low b: [0.13246609 0.39739826 0.39739826 0.13246609] High b: [ 0.20561501 -0.61684504 0.61684504 -0.20561501] Low a: [ 1. -0.26786544 0.35232441 -0.02473027] High a: [ 1. -0.26786544 0.35232441 -0.02473027] All b: [ 0.3380811 -0.21944678 1.0142433 -0.07314893]
4次のフィルタ係数
fc: 500.0 Low b: [1.46901845e-06 5.87607379e-06 8.81411069e-06 5.87607379e-06 1.46901845e-06] High b: [ 0.91110247 -3.64440987 5.46661481 -3.64440987 0.91110247] Low a: [ 1. -3.81386538 5.45872379 -3.47494261 0.83010771] High a: [ 1. -3.81386538 5.45872379 -3.47494261 0.83010771] All b: [ 0.91110394 -3.644404 5.46662362 -3.644404 0.91110394] fc: 1000.0 Low b: [2.15209512e-05 8.60838049e-05 1.29125707e-04 8.60838049e-05 2.15209512e-05] High b: [ 0.82999258 -3.31997033 4.97995549 -3.31997033 0.82999258] Low a: [ 1. -3.6278442 4.95122513 -3.01192428 0.68888769] High a: [ 1. -3.6278442 4.95122513 -3.01192428 0.68888769] All b: [ 0.8300141 -3.31988424 4.98008461 -3.31988424 0.8300141 ] fc: 2000.0 Low b: [0.00029137 0.00116546 0.00174819 0.00116546 0.00029137] High b: [ 0.68811799 -2.75247196 4.12870794 -2.75247196 0.68811799] Low a: [ 1. -3.25656931 4.03376839 -2.24604368 0.47350645] High a: [ 1. -3.25656931 4.03376839 -2.24604368 0.47350645] All b: [ 0.68840936 -2.7513065 4.13045613 -2.7513065 0.68840936] fc: 5000.0 Low b: [0.00737405 0.02949621 0.04424432 0.02949621 0.00737405] High b: [ 0.38482178 -1.53928712 2.30893068 -1.53928712 0.38482178] Low a: [ 1. -2.15448443 1.98942448 -0.86509739 0.14814218] High a: [ 1. -2.15448443 1.98942448 -0.86509739 0.14814218] All b: [ 0.39219583 -1.50979091 2.35317499 -1.50979091 0.39219583] fc: 10000.0 Low b: [0.06917529 0.27670118 0.41505176 0.27670118 0.06917529] High b: [ 0.12432291 -0.49729164 0.74593746 -0.49729164 0.12432291] Low a: [ 1. -0.36316417 0.52774422 -0.07801676 0.02024141] High a: [ 1. -0.36316417 0.52774422 -0.07801676 0.02024141] All b: [ 0.1934982 -0.22059047 1.16098922 -0.22059047 0.1934982 ]
5次のフィルタ係数
fc: 500.0 Low b: [5.11979794e-08 2.55989897e-07 5.11979794e-07 5.11979794e-07 2.55989897e-07 5.11979794e-08] High b: [ 0.89110276 -4.45551379 8.91102758 -8.91102758 4.45551379 -0.89110276] Low a: [ 1. -4.76948342 9.10427938 -8.69409576 4.15336557 -0.79406413] High a: [ 1. -4.76948342 9.10427938 -8.69409576 4.15336557 -0.79406413] All b: [ 0.89110281 -4.45551353 8.91102809 -8.91102707 4.45551405 -0.89110271] fc: 1000.0 Low b: [1.46896340e-06 7.34481698e-06 1.46896340e-05 1.46896340e-05 7.34481698e-06 1.46896340e-06] High b: [ 0.7939203 -3.96960152 7.93920303 -7.93920303 3.96960152 -0.7939203 ] Low a: [ 1. -4.53905152 8.26066093 -7.53334038 3.44208742 -0.63030945] High a: [ 1. -4.53905152 8.26066093 -7.53334038 3.44208742 -0.63030945] All b: [ 0.79392177 -3.96959417 7.93921772 -7.93918834 3.96960886 -0.79391883] fc: 2000.0 Low b: [3.82287494e-05 1.91143747e-04 3.82287494e-04 3.82287494e-04 1.91143747e-04 3.82287494e-05] High b: [ 0.629391 -3.146955 6.29390999 -6.29390999 3.146955 -0.629391 ] Low a: [ 1. -4.07876493 6.72527084 -5.59474637 2.34559681 -0.39613303] High a: [ 1. -4.07876493 6.72527084 -5.59474637 2.34559681 -0.39613303] All b: [ 0.62942923 -3.14676385 6.29429228 -6.29352771 3.14714614 -0.62935277] fc: 5000.0 Low b: [0.00218185 0.01090923 0.02181846 0.02181846 0.01090923 0.00218185] High b: [ 0.3060313 -1.53015649 3.06031299 -3.06031299 1.53015649 -0.3060313 ] Low a: [ 1. -2.70699599 3.2483224 -2.06094486 0.68308792 -0.0936504 ] High a: [ 1. -2.70699599 3.2483224 -2.06094486 0.68308792 -0.0936504 ] All b: [ 0.30821314 -1.51924726 3.08213145 -3.03849453 1.54106572 -0.30384945] fc: 10000.0 Low b: [0.03598336 0.17991679 0.35983357 0.35983357 0.17991679 0.03598336] High b: [ 0.07487759 -0.37438796 0.74877593 -0.74877593 0.37438796 -0.07487759] Low a: [ 1. -0.45769421 0.70673415 -0.1603017 0.06704105 -0.00431185] High a: [ 1. -0.45769421 0.70673415 -0.1603017 0.06704105 -0.00431185] All b: [ 0.11086095 -0.19447118 1.1086095 -0.38894235 0.55430475 -0.03889424]
検証用Pythonコード
スクリプト本体
import sys import matplotlib.pyplot as plt import numpy as np import scipy.signal from bode_plot import bode_plot def compute_butterworth_filters(fc, sr, order=3): b, a = {}, {} # フィルタ係数・伝達関数算出(バターワースフィルタ) b['low'], a['low'] = scipy.signal.butter(order, fc/(sr/2), btype='low') w, h_lp = scipy.signal.freqz(b['low'], a['low']) b['high'], a['high'] = scipy.signal.butter(order, fc/(sr/2), btype='high') w, h_hp = scipy.signal.freqz(b['high'], a['high']) # 低域/高域用ハーフフィルタの並列接続時の伝達関数オールパスフィルタ _, h_ap = scipy.signal.freqz(b['low']+b['high'], a['high']) _, h_ap_n = scipy.signal.freqz(b['low']+b['high'], 1.0) _, h_ap_d = scipy.signal.freqz(1.0, a['high']) return w, h_lp, h_hp, h_ap, b, a, h_ap_n, h_ap_d sr = 44100 order = int(sys.argv[1]) #np.set_printoptions(precision=3) for fc in [5e2, 1e3, 2e3, 5e3, 1e4]: w, _, _, h_ap, b, a, h_ap_n, h_ap_d = compute_butterworth_filters(fc, sr, order=order) print(f"fc:\t{fc}") print(f"Low b:\t{b['low']}") print(f"High b:\t{b['high']}") print(f"Low a:\t{a['low']}") print(f"High a:\t{a['high']}") print(f"All b:\t{b['low'] + b['high']}") print("") bode_plot( w, [h_ap, h_ap_n, h_ap_d], labels=["allpass", "allpass_num", "allpass_denom"], sr=sr, fc=fc, ylim=[-40,40], )