logue SDKでリバーブを作ってみる


これまでlogue SDKでオシレーターモジュレーターディレイなどを作ってきましたが、今回はリバーブを作ってみました。

リバーブはディレイとSDKの API はほとんど変わらないのですが、作るのはリバーブの方が格段に難しいです。ディレイは入力を一定時間を遅らせて出力に重ねれば作ることができますが、リバーブは残響音という名前の通り、元の音を時間的・空間的にぼかした音を作り出す必要があります。

今回作ったのは、シュレーダーリバーブという古典的なリバーブを素直に実装したものです。シュレーダーリバーブの解説はこちらにあります。

リバーブレーター 残響装置 : ソフトウェアと音響のデジタル信号処理

簡単に言えば、入力音声を遅延時間の異なる四つのディレイに入力し、その出力を合成した上で、 それらよりも更に遅延時間の短いディレイ二つにくぐらせて、反響音の数を大幅に増やしたものです。 前段の四つのディレイの遅延時間は30 msecから45 msec、後段の二つのディレイの遅延時間は5msecと1.7msecになっています。(正確には、後段はディレイではなくオールパスフィルタです。)

どのディレイもフィードバックが付いているので、例えば「カン!」という音を入力すると「カンカンカンカン・・・」と同じ音がだんだん小さくなりながら繰り返されることになります。この繰り返しの間隔が異なるディレイが4つ、さらにその繰り返し自体をより短い間隔で繰り返すディレイが2段重ねになっているようなイメージです。

音をだんだん小さくするための増幅率(1未満)は、4つのディレイの音が同時に消える(認識できないほど小さくなる)ように調節します。

シュレーダーリバーブが発表されたのは1962年で、 60年近く経っていますが、今でもこのタイプのリバーブは健在だそうです。

シュレーダーリバーブをデジタルで実装するにあたっては、ディレイの信号が他のディレイと重ならないように、 遅延時間を互いに素にしておく必要があります。実際には、遅延時間はサンプリング周期の整数倍ですから、この整数を素数にしておきます。(そのような整数の例は上述の解説記事に記載されています。)

ディレイそのものは単なるリングバッファで、しかも入力と出力が同期していますから、実装は簡単です。こんな感じになります。

typedef struct delay_buffer_t {
    int length;
    int pos;
    float gain;
    float *buf;
} delay_buffer_t;

inline void delay_add(delay_buffer_t *delay, float data) {
    delay->buf[delay->pos++] = data;
    if (delay->pos == delay->length) {
        delay->pos = 0;
    }
}

inline float delay_read_write(delay_buffer_t *delay, float data){
    float buf_in;
    float buf_out;

    buf_out = delay->buf[delay->pos];
    delay_add(delay, data + delay->gain * buf_out);
    return buf_out;
}

このようなディレイは、入力信号の中で、波長がバッファ長の整数分の1倍と一致するような周波数成分を強調し、バッファ長の1/1.5倍や1/2.5倍と一致するような周波数成分を減衰させます。強調する周波数と減衰する周波数が櫛(comb)の歯のように並ぶので、コムフィルタと呼ばれます。

一方、シュレーダーリバーブの後段の二つのディレイは、ディレイの出力に対して入力信号を反転した信号を加算するので、信号を強調したり減衰させたりする性質が弱まっています。後段の2つは、コムフィルターではなくオールパスフィルターと呼ばれます。実装は以下のようになります。

inline float allpass_read_write(delay_buffer_t *allpass, float data){
    float buf_in;
    float buf_out;

    buf_out = allpass->buf[allpass->pos];
    buf_in = data + allpass->gain * buf_out;
    delay_add(allpass, buf_in);
    return (buf_out - allpass->gain * buf_in);
}

実装したシュレーダーリバーブの音はこんな感じです。

今回のコードは以下のリポジトリにあります。

GitHub - boochow/schroeder: Basic implementation of Schroeder reverbrator for logue SDK
Basic implementation of Schroeder reverbrator for logue SDK - boochow/schroeder

この試作したシュレーダーリバーブですが、このままでは音質的には全く使い物になりません。 まず、モノラルで実装していますので、空間的な広がりは全く感じられません。また、特に遅延時間1.7msecのオールパスフィルターの特性があまり良くなく、入力信号の特定の周波数成分が強調されるのがはっきり感じられます。以下の音声は鋸波に対してリバーブ成分を増やしたり減らしたりしているところです。「キーン」という成分が聞き取れると思います。

Prologueやminilogue xdにデフォルトで搭載されている各種リバーブは、このような不自然さが感じられない高品質なものです。これに対抗しうる新しいリバーブを作るのは、なかなかハードルが高いように思います。

コメント