前回紹介した適応ノッチフィルタ は、正弦波信号を除去するアルゴリズムでした。
しかし、音楽信号処理でよくある楽音を抽出したいという要求には適応ノッチフィルタは利用しにくいです。
そこで今回は、正弦波信号を抽出する適応逆ノッチフィルタを紹介します。
適応逆ノッチフィルタの構成
逆ノッチフィルタの周波数応答(振幅特性)は下図のようになります。
ちょうどノッチフィルタの振幅特性を反転したような特性です。
そのため、ノッチフィルタの回路を使って実装することができます。
逆ノッチフィルタの回路は以下のようになります。
赤い破線で囲まれているのがノッチフィルタの回路です。
[px]
この理由ですが、ノッチフィルタ出力信号の周波数特性は以下のようになります、
- ωcから離れるほど、振幅・位相特性は入力信号とほぼ同じになる
- ωcに近づくと、振幅は急激に小さくなる
従って、入力信号からノッチフィルタ出力信号を引くと、
- ωcの遠方では、逆位相の関係で打ち消しあう
- ωcの近傍では、ノッチフィルタ出力信号は入力信号の振幅に影響を与えない
というように動くため、結果的に入力信号のωc近傍の成分のみを抽出することができます。
[px]
[px]
なお、式を整理すると、
となるため、以下のような回路として扱うこともできます。
直列適応逆ノッチフィルタ
適応ノッチフィルタと同様に、適応逆ノッチフィルタも直列接続することで複数の正弦波を抽出することが可能です。
注意として、直列適応逆ノッチフィルタの構成は以下のように並列回路となります。
適応ノッチフィルタを直列接続することで複数の正弦波を除去した信号を得て、その信号の逆相信号を入力信号に加えることで複数の正弦波のみが残った信号を得ることができます。
実装
結局のところ、前回の適応ノッチフィルタ や直列適応ノッチフィルタを利用すればOKです。
AdaptiveNotchFilter
クラスは前回記事を参考にしてください。
if __name__ == "__main__": filepath = "./orgel.wav" savefilepath = "./save.wav" sig, sr = sf.read(filepath, always_2d=True) M = 8 bandwidth = 0.99 anf_list = [[AdaptiveNotchFilter(sr, bandwidth=bandwidth), AdaptiveNotchFilter(sr, bandwidth=bandwidth)] for m in range(M)] current_frame = 0 blocksize = 512 tmpdata = np.zeros((blocksize, sig.shape[1])) tmpdata2 = np.zeros((blocksize, sig.shape[1])) savedata = np.zeros(sig.shape) def callback(outdata, frames, time, status): global current_frame, tmpdata, tmpdata2, anf, anf_list chunksize = min(sig.shape[0] - current_frame, frames) tmpdata[:] *= 0.0 tmpdata[0:chunksize] = sig[current_frame:current_frame + chunksize] for m in range(M): for k in range(outdata.shape[1]): anf_list[m][k].online(tmpdata[:,k], tmpdata2[:,k]) tmpdata[:] = tmpdata2 tmpdata2[:] *= 0.0 tmpdata2[0:chunksize] = sig[current_frame:current_frame + chunksize] outdata[:] = tmpdata2 - tmpdata if chunksize < frames: raise sd.CallbackStop() else: savedata[current_frame:current_frame + chunksize] = outdata[:] current_frame += chunksize event = threading.Event() with sd.OutputStream( samplerate=sr, blocksize=blocksize, channels=sig.shape[1], callback=callback, finished_callback=event.set ): event.wait() sf.write(savefilepath, savedata, sr)
実行結果
前回と同じ「正弦波1~2本+白色雑音」の音源に適用してみました。
適応ノッチフィルタの数は 1 (中図) と 8 (下図)を試しています。
適応ノッチフィルタの数が1つだと、前回は2つの正弦波が同時になっている区間ではうまく抽出できていません。
一方で、適応ノッチフィルタの数が8つだと2つの正弦波を抽出できています。
ただし、抽出に利用されていないフィルタが関係のない周波数成分を抽出しているため、ミュージカルノイズのような雑音が聞こえています。
[px]
まとめ
Pythonで適応逆ノッチフィルタによる調波成分の抽出を実装しました。
C言語でのプログラミングやチューニングのコツについては、プログラム101付き 音声信号処理 が参考になると思います。
参考文献
- Sugiura, Yosuke & Kawamura, Arata & Iiguni, Youji. (2013). Performance Analysis of an Inverse Notch Filter and Its Application to F0 Estimation. Circuits and Systems. 04. 117-122. 10.4236/cs.2013.41017.
- 内田 剛, 川村 新, 飯國 洋二, 零交差数に基づく適応逆ノッチフィルタによるMIDI情報抽出, 自動制御連合講演会講演論文集, 2009, 52 巻, 第52回自動制御連合講演会