シュレーダーのリバーブを実装してみた

シュレーダーのリバーブ:スピーカーの音が残響生成回路を通って人工的な残響になる図

本記事では、これまで紹介したくし型フィルタによるリバーブの実装オールパスフィルタによるリバーブの実装を組み合わせた、有名なシュレーダーのリバーブを実装してみました。

シュレーダーのリバーブとは

シュレーダーのリバーブとは、くし型フィルタを並列に並べたものとオールパスフィルタを直列に並べたものを組み合わせた下図の回路のことです。

図:シュレーダーのリバーブの回路構成(加算器・遅延・ゲインのブロックまで詳細に示した回路図)
図:シュレーダーのリバーブの回路構成

くし型フィルタ単体の振幅特性は一定間隔でピークが立つ、いびつな特性でした。この欠点を解消するために提案されたのがオールパスフィルタです。

一方で文献 [1] では、別のアプローチとして「実際の部屋の周波数特性も、そもそも平坦ではない」という点にも着目しています。ピークの密度が十分に高ければ、人間の耳は平坦な特性との違いを聞き分けられないことが心理実験で確認されており、これを踏まえてくし型フィルタをあえて複数並列に使うことで実際の部屋に近いピーク密度を作り、そこにオールパスフィルタを直列に繋いでエコー密度を高める、という構成になっています。

くし型フィルタの並列配置

シュレーダーのリバーブでは、基本的なくし型フィルタを 4 本並列に並べることで、部屋の反射音に近い密度の周波数特性を作り出します。

図:くし型フィルタ4本を並列に接続した部分の拡大図
図:くし型フィルタ4本の並列接続

周波数特性

残響時間 1 秒程度の部屋では、100Hz あたりに 15 個程度の大きなピークが不規則に並ぶ特性になっています。遅延時間 \( \tau = 0.04 \) 秒のくし型フィルタ 1 個では、ピーク間隔が \( 1/\tau = 25\, \text{Hz} \) となり、下図のように100Hz あたり 4 個のピークしか作れません。

図:くし型フィルタ1個の振幅特性(遅延時間0.04秒、100Hzあたり4個のピーク)
図:くし型フィルタ1個の振幅特性

そこで遅延時間の異なるくし型フィルタを 3〜4 個並列に並べ、それぞれのピークをずらして重ねることで、実際の部屋に近いピーク密度を作り出します。

図:くし型フィルタ4本(遅延時間30/34/39/45ms)の振幅特性を重ねたもの
図:くし型フィルタ4本の振幅特性

残響時間の統一

くし型フィルタを並列にする際、各フィルタの遅延時間 \( \tau_n \) は変えつつ、残響時間 \( T \) はすべて揃える必要があります。そのための遅延・減衰率・残響時間の関係式を確認します。

残響時間は、W. C. Sabine の定義に従うと「音圧レベルが 60dB 減衰するまでの時間」です。くし型フィルタは 1 回のフィードバックループ(\( \tau \) 秒)ごとに振幅が \( g \) 倍になるので、1 回あたりの減衰量は \( -20 \log_{10}{|g|} \) dB です。60dB 減衰するまでの周回数は \( 60 / (-20 \log_{10}{|g|}) \) 回なので、残響時間 \( T \) は次の式になります。

\[ T = \frac{60}{-20 \log_{10}{|g|}} \cdot \tau = \frac{3\tau}{\log_{10}{|1/g|}} \]

この式を \( g \) について解くと、目標の残響時間 \( T \) と遅延時間 \( \tau \) から減衰率が決まります。

\[ g = 10^{-3\tau/T} \]

例えば \( \tau = 100\, \text{ms} \)、\( g = 0.708 \) のとき、この式で \( T \) を計算すると約 2 秒になります。この関係式を使えば、並列に並べる複数のくし型フィルタそれぞれの遅延時間を変えても、残響時間を全て揃えることができます。くし型フィルタの減衰率 \( g_1 \)〜\( g_4 \) は、この式を使って目標の残響時間 \( T \) から個別に求めます。

オールパスフィルタの直列配置

オールパスフィルタによるリバーブの実装で確認したとおり、オールパスフィルタは振幅特性を変えずにエコー密度をおよそ 3 倍に増やせます。この性質を使って、くし型フィルタ 4 本の出力にオールパスフィルタ 2 個を直列にかけ、エコー密度をさらに高めます。

図:オールパスフィルタA1とA2を直列に接続した部分の拡大図
図:オールパスフィルタA1とA2の直列接続

オールパスフィルタの遅延時間は \( \tau_5 \approx 5\, \text{ms} \)、\( \tau_6 \approx 1.7\, \text{ms} \)、減衰率は \( g_5 = g_6 = 0.7 \) が文献 [1] の推奨値です。

エコー密度を確認すると、遅延 30〜45ms のくし型フィルタ 4 個を合計したエコー密度は \( 1/0.030 + 1/0.034 + 1/0.039 + 1/0.045 \approx 111 \) 回/秒です。オールパスフィルタ 1 個はエコー密度をおよそ 3 倍にするので、2 個直列にすると \( 111 \times 3 \times 3 \approx 1000 \) 回/秒となり、文献 [1] が目標とするフラッター(音がはためく現象)が聞こえなくなる密度に到達します。

最後に、オールパスフィルタの出力を減衰率 \( g_7 \) で直接音とミックスして出力とします。

プログラム

シュレーダーのリバーブを適用するプログラムをPythonで実装しました。

入力データにシュレーダーのリバーブを適用するプログラムは以下です。

import soundfile as sf
import numpy as np
import scipy.signal as sg

# パラメータ
T = 2.0                                   # 目標残響時間 [s]
tau_comb = [0.030, 0.034, 0.039, 0.045]   # くし型フィルタの遅延時間 [s](互いに素な値にする)
tau_allpass = [0.005, 0.0017]             # オールパスフィルタの遅延時間 [s]
g_allpass = [0.7, 0.7]                    # オールパスフィルタの減衰率
g_mix = 0.5                               # 直接音とリバーブ音の混合比
wav_in_name  = "input.wav"   # 入力音データ名
wav_out_name = "output.wav"  # 出力音データ名

# WAVファイルを読み込む
x, fs = sf.read(wav_in_name)

# くし型フィルタ4本を並列にかけて加算する
y_comb = np.zeros_like(x)
for tau in tau_comb:
    D = int(tau * fs)
    g = 10 ** (-3 * tau / T)  # 残響時間の式から減衰率を求める

    b = np.zeros(D + 1)
    a = np.zeros(D + 1)
    b[0] = 1.0
    a[0] = 1.0
    a[D] = -g

    y_comb += sg.lfilter(b, a, x)

# オールパスフィルタ2段を直列にかけてエコー密度を高める
y_reverb = y_comb
for tau, g in zip(tau_allpass, g_allpass):
    D = int(tau * fs)

    b = np.zeros(D + 1)
    a = np.zeros(D + 1)
    b[0] = -g
    a[0] = 1.0
    a[D] = -g
    b[D] = 1.0

    y_reverb = sg.lfilter(b, a, y_reverb)

# 直接音とリバーブ音を混合する
y = x + g_mix * y_reverb

# wavファイルを書き込む
y = y / np.max(np.abs(y))   # ノーマライズ
sf.write(wav_out_name, y, fs, subtype='PCM_16')

処理結果

以下のようにパラメータを設定して、打楽器音にリバーブをかけました。

表:リバーブのパラメータ
パラメータ名 記号 変数名 設定値
目標残響時間 \(T\) T 2.0 [s]
くし型フィルタの遅延時間 \(\tau_{1}\)〜\(\tau_{4}\) tau_comb 30, 34, 39, 45 [ms]
オールパスフィルタの遅延時間 \(\tau_{5}\), \(\tau_{6}\) tau_allpass 5, 1.7 [ms]
オールパスフィルタの減衰率 \(g_{5}\), \(g_{6}\) g_allpass 0.7, 0.7
直接音との混合比 \(g_{7}\) g_mix 0.5

入力音には、Pythonで打楽器音を合成してみたで紹介した Karplus-Strong 法で合成した打楽器音を使いました。

残響の減衰がはっきり聴こえるように、最後の一打のあとに 5 秒ほど無音を入れてあります。以下の音を聴き比べていきます。

入力した打楽器音

シュレーダーのリバーブを使用した打楽器音

実測のホールのリバーブを使用した打楽器音

シュレーダーのリバーブをかけた音は、一打ごとの余韻が自然に伸びています。一方で実測のホールと聴き比べると、残響の尾に金属的な色付きが残っていて、やはり自然さがない感じはあります。

オールパスフィルターだけの構成よりはシュレーダーのリバーブのほうが自然さはありますが、実測のリバーブに自然さは届かなそうです。最近の研究では、この金属的な響きがなくなるように調整した人工的なリバーブを作ることができているのですかね?今度調べてみます。

おわりに

本記事では、シュレーダーのリバーブを実装しました。文献 [1] にはさらに直接音との混合比の調整や周波数依存の残響時間、複数スピーカーへの分配(アンビオフォニック)といった発展的な内容もあるので、気になる方はぜひ読んでみてください。

■参考文献
[1] M.R. Schroeder, “Natural Sounding Artificial Reverberation,” J. Audio Eng. Soc., vol.10, p.219, 1962.

■使用したインパルス応答について
この記事で使用したインパルス応答は Openair で提供されている以下のインパルス応答を使用させていただきました。

・ “Central Hall, University of York” © 2015 Alexander Vilkaitis, Ilias Antonopoulos, Joska De Langen, Xuan Liu (Licensed under CC BY 4.0) https://www.openairlib.net