改めて、ウエーブテーブルシンセサイザを作る(4)

一般的に、ウエーブテーブルシンセサイザでは1つの音色に対して複数のウエーブテーブルを用意し、切り替えながら用いることが多いです。音色には様々な倍音成分が含まれますが、DA変換のサンプリング周波数を超えた倍音成分はエイリアスノイズの原因になるからです。そのため、音高が高いところではローパスフィルタで高次の倍音をカットしたウエーブテーブルを用います。

下のスペクトラムは、αJunoで鋸波をC6(基本周波数約1KHz)を鳴らして96KHzでサンプリングした音声のものです。スペクトラム上は31倍音まで確認できます。

この高次の倍音はもちろん聴こえないのですが、この音を(ローパスフィルタを通さずに)48KHzのサンプリングレートで録音・再生すれば、24KHz以上の高域は折り返されてエイリアスノイズになります。ですから、この波形をウエーブテーブルにする場合には、例えばサンプリングレートが48KHzならば24KHz以上の倍音成分はカットして利用するわけです。

下のスペクトラムは、KORG prologueのデジタル側の矩形波(SQU2)でC5を鳴らしたものです。24KHzのところで倍音成分がカットされているのが分かります。

logue SDKでは予め矩形波や鋸波のウエーブテーブルが用意されていますが、これらの波形もそれぞれ複数(7個)のウエーブテーブルを持ち、音域に応じて使い分けるようになっています。osc_api.hでは、鋸波のウエーブテーブル関連は以下のように定義されています。

osc_bl_saw_idx()はMIDIノート番号を与えるとウエーブテーブルの番号を返します。番号の範囲は0..6で、7つのウエーブテーブルがあることが分かります。

マニュアルには記載はされていませんが、ノート番号とテーブル番号の対応は下図のようになっています。(テーブル番号に応じてパルス幅が変化するPWMシンセを作成して確かめました。)中央部分が概ね1オクターブごとに1つ、残りの音域は低域側と高域側に1つずつです。

テーブルの境界で急に音色が変化することを避けるために、実際には2つのウエーブテーブルの値をミックスして使います。このためにlogue SDK APIでは3つの関数が用意されています。

  __fast_inline float osc_sawf(float x) {
  }
  __fast_inline float osc_bl_sawf(float x, uint8_t idx) {
  }
  __fast_inline float osc_bl2_sawf(float x, float idx) {
  }

1つ目の関数は常に0番のテーブルを使います。パラメータは位相(波形上の位置を0.0~1.0で指定)だけです。この関数を使うと、高域でエイリアスノイズが発生します。

2つ目の関数はテーブル番号を指定できます。テーブル番号は前述のosc_bl_saw_idx()で得られるものです。osc_bl_saw_idxの結果はfloat型ですが、整数部だけが使われます。この関数を使うと、テーブルの境界で音色が変わります。

3つ目の関数はテーブル番号を指定できますが、整数部で1つ目のテーブルを指定し、小数部では1つ上のテーブルとのミックス比率を指定します。例えばテーブル番号が4.3なら、テーブル番号4のデータとテーブル番号5のデータを7:3の比率でミックスします。

スペクトルで考えると、下図の左上と左下のスペクトル(X・Y軸は対数のイメージです)を持つ波形同士を半々でミックスしたら、図の右のようなスペクトルになりますので、それでいいのか?と思いますが、割と広く使われている手法のようです。

フリーのソフトウェアシンセサイザで、軽量・高音質で有名なSynth1というVSTプラグインがありますが、その作者のDaichiさんは以下のように書かれています。

最初に白状すると、波形の生成には、WAVEテーブル参照方式+一次補間を使っている。ただしノイズ波形は除く。 「なあんだ」と思う人もいるかもしれない。(中略)WAVEテーブル方式のいいところは軽いこと。そして、少しだけ手間をかければ、エイリアスも気にならない程度に使いものになるのだ。 実際ハード/ソフトを問わず、アナログエミュレーションのオシレータのほとんどは、 WAVEテーブルを基本としているのではないだろうか?と勝手に思っている。

シンセプログラミング

コメント