今回は前回の記事の続きとして取得したインパルス応答の畳み込みについてお話しようかと思います。

前回インパルス応答の測定方法や意味について扱いましたので必要な方は適宜ご参照ください。

インパルス応答はシステムの特性を表すものです」ということは前回少しご説明したかと思います。

音響分野で言うと室の特性を例えば表していて,今回の記事の内容をマスターすれば好きな音源に好きな室の特性を付加することが出来ます。

これは例を出してみると好きなボーカルの声を自分の選んだ場所で歌っている声を聴けるということになります。
※細かいことを言えば実際はドライソースに畳み込む必要があります

今回の技術は過去記事でご紹介した内容の組み合わせですので少し短くなっております。
では早速本題に移りましょう。

離散時間領域における畳み込み

連続時間領域についての畳込みは以前の記事で紹介しました。

これを離散時間で考えるのはとても簡単です。以下のように書き換えれば良いです。

$$(f \ast g) (n) := \sum_{m=-\infty}^{\infty} f(n-m) g(m)$$

ここで$n$が離散時間インデックスです。

実際にコーディングする際は有限長の信号同士を畳み込むことになるので$\infty$回の足し合わせは必要ありません。

上の記事で紹介したように時間領域の畳み込みは周波数領域で掛け算になります。
これは計算機での計算量を考えた時(特に信号長が長い時)使わない手はありません。今回の記事でも周波数領域で計算する方法をご紹介します。

一応式で書いておくと以下のようになります。

$$(f \ast g) (n) = \mathscr{F}^{-1} [\mathscr{F}[f(n)] \mathscr{F}[g(n)]]$$

ここで$\mathscr{F}$は離散フーリエ変換です。

ただし,離散周波数領域で畳み込みを実現する場合には円状畳込みになりますので,$f(n), g(n)$のタップ数をそれぞれ$l_{1}, l_{2}$とすれば畳み込み後の信号長は$l_{1}+l_{2}-1$以上にする必要があります。

インパルス応答畳込みの実装

インパルス応答の取得方法については前回の記事でご紹介しましたので今回はこれを使って好きな信号に響きを付加してみましょう

先程考えた式で言うと使用したい信号を$f(n)$,インパルス応答を$g(n)$とすれば良いわけです。

では実際にpython言語でコーディングしてみましょう。

pythonによる実装

まずは畳み込みの関数をつくりましょう。
先に述べたように周波数領域で計算するコードを紹介します。

# convolution 1d-array on frequency domain (s1, s2: signal(1D-array))
def fftconv_1d(s1, s2):
    length = s1.shape[0] + s2.shape[0] - 1
    res = np.fft.irfft(np.fft.rfft(s1, n=length, axis=0) * np.fft.rfft(s2, n=length, axis=0))
    return res

これだけです。簡単ですね!

今回は音源には「もろみ先輩」という音声信号(moromi_senpy.wav)に対して以前個人的に収録したインパルス応答(ir.wav)を畳み込んでみます。
※音源の生成方法は以下の記事で紹介しています。

まず元の音源を聴いてみましょう。

これの短時間フーリエ変換は上の記事で実装しましたが次のような結果でした。

ではこの音源にインパルス応答を畳み込んでみましょう。
メインは先程作った関数を使って以下のようにすれば良いです。

s = wf.readWave("./moromi_senpy.wav")
ir = wf.readWave("./ir.wav")
res = fftconv_1d(s, ir)

結果は以下のようになりました。

これを短時間フーリエ変換して時間周波数領域の結果を見てみると以下のようになります。

畳み込む前に比べて音が伸びている(響いている)のが分かりますね。

まとめ

今回は好きな音声ファイルにインパルス応答を畳み込む方法をご紹介しました。
これを使うことで好きな響きを付加することが出来ます。

DAWなんかを使ってリバーブをかけている方は納得のいくリバーブがない場合は自分で作ってみてはいかがでしょうか。

本記事について何かご質問等ありましたらコメントでお願いします。

それではお疲れ様でした!