「信号処理に欠かせないオールパスフィルタ―」で紹介したオールパスフィルタの利用例として、人工残響フィルタを実装してみました。
設計方法
直列オールパスフィルタによる人工残響生成はシュレーダリバーブ の一部として知られています。
具体的には、多段のオールパスフィルタで構成します.
実装は簡単ですが,オールパスフィルタの性質により振幅は指数関数的に減衰し、出力信号の音色に変な色付けがされない*1となっていることから、残響にとって都合よい特性となっています。
なお、よりよい残響生成のために、各オールパスフィルタの遅延は互いに素で、遅延の値の大きさが違っているほうがよいようです。
また、gは0.7前後に設定されるようです。
フィルタ応答の比較
パルスを入力して一定時間の応答をプロットしてみました。
遅延の量をうまく設定し、オールパスフィルタの数を増やすと残響の量・時間が長くなっていることがわかります。
なお、各フィルタの遅延量を互いに素にしない場合、応答の時間波形は疎になる傾向があります。
試聴
ドライなピアノの音に人工残響フィルタを適用してみました。
ピアノの音色自体には変化がなく残響が付いていることが確認できます。
ただし、少し金属的な響きが聞こえるため、直列オールパスフィルタ単体で人工残響を生成する場合はそういった響きを利用するような使い方(プレートリバーブとか)になるかと思います。
Python実装
今回はリアルタイム信号処理向けの実装ではなくシミュレーション用途だったためscipy.signal.lfilter
を使っています。
import numpy as np from scipy.signal import lfilter import soundfile as sf import matplotlib.pyplot as plt sr = 48000 duration = 0.5 gain = -0.7 #delay_list = [100, 250, 1000, 2000] #delay_list = [113, 347, 1021, 2017] delay_list = [113, 347, 1021, 2017, 3041, 4211] x = np.zeros(int(sr*duration)) x[0] = 1.0 #x, sr = sf.read("in.wav") #x = x[:,0] + x[:,1] y = np.zeros(x.shape) y[:] = x for delay in delay_list: b = np.zeros(delay+1) a = np.zeros(delay+1) b[0] = -gain a[0] = 1.0 b[delay] = 1.0 a[delay] = -gain length = max(a.shape[0], b.shape[0]) - 1 y[:] = lfilter(b, a, y) plt.subplot(2,1,1) plt.plot(x) plt.subplot(2,1,2) plt.title(f"g={gain:0.2f}, delay_list={delay_list}") plt.plot(y) plt.tight_layout() plt.show() sf.write("out.wav", y, sr)
参考文献
- "Late Reverberation - MUMT 307: Music & Audio Computing II",Gary P. Scavone, McGill University
- "Schroeder Allpass Sections - Physical Audio Signal Processing", Julius O. Smith III Center for Computer Research in Music and Acoustics (CCRMA), Stanford University
*1:無色/有色でない/Colorless