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

logue SDKにはサイン波のウエーブテーブルが用意されており、そのテーブルのサイズは128サンプルです。これは半波長分なので、1波長あたりでは256サンプルとなります。

今回はこのウエーブテーブルを、オリジナルのウエーブテーブルに置き換えてみます。

ウエーブテーブル自体は単なるfloat型の配列です。配列さえ用意できれば、前回紹介したSDK内のコードがほぼそのまま利用できます。

配列を用意するのに一番手っ取り早いのは、オシレータの初期化時にウエーブテーブルも生成することです。
logue SDKのAPIには、オシレータの初期化時に呼び出されるフック関数OSC_INIT()がありますので、その中でウエーブテーブルを生成する処理を呼び出します。

ウエーブテーブルから値を読み出すコードは、前回紹介したosc_api.hの中のコードが、変数名だけ書き換えれば使えます。

ウエーブテーブルの初期化と読み出しのコードは以下のようになります。

このコードの処理は単純ですが、考察すべきことが2つあります。

1つは「ウエーブテーブルのサイズはどのくらいが適当なのか」、
もう1つは「線形補間は十分な音質が得られるのか」

です。

これらについては、既に十分な検討が行われています。

https://www.music.mcgill.ca/~gary/307/week4/wavetables.html
Further thoughts on wave table oscillators | EarLevel Engineering

まず、ウエーブテーブルの上限、つまり「十分大きなサイズ」については、2048サンプル程度と言われています。
これは、音の可聴域周波数が20Hz~20KHz程度だとすると、20Hzの信号の1波長を2048サンプルで表すことができれば、サンプリングレートは20×2048 = 40,960Hzになり、このレートなら20,480Hzまでの信号をカバーできるためです。

一方、テーブルサイズをどこまで小さくできるかについては、再生したい波形に含まれる倍音成分が必要とするサンプリングレートに依存します。例えば、波形が50倍音を必要とするなら、テーブルサイズは少なくとも波長あたり100サンプル必要、というわけです。

では倍音のない場合として、サイン波はどうでしょうか。
倍音が無くても、再生音の品質は補間方法の影響を受けます。一般論としては1波長分のサンプル数(サンプリング周波数/再生周波数)に比べてウエーブテーブルが含むサンプル数が少なければ少ないほど、大きな影響を受けます。

極端な場合として、ウエーブテーブルが8サンプルだったとします。
すると、線形補間により生成される波形は下図の赤線のような波形になります。
青線は実際のサイン波です。

赤と青の差分は、聴感上は雑音として現れてきます。

その雑音の特性ですが、青と赤は各サンプル点では差分がゼロになりますので、2サンプルを1周期とした周波数成分が含まれると思われます。
たとえば32サンプルなら再生波形の周波数の16倍の成分のノイズが現れます。
再生周波数が低ければノイズの周波数は可聴域で目立つことになります。

雑音の音量は、ウエーブテーブルのサイズが小さくなるほど増えることは想像できるでしょう。
その量は計算で評価できます。下記のリンク先では、その量を計算するプログラムが掲載されています。

Wavetable signal to noise ratio | EarLevel Engineering

実際に計算してみると、サンプル数を変えた場合のS/N比は以下のようになりました。

Table size: 32
Signal: -3.0103 dB RMS
Noise:  -52.1052 dB RMS
SNR:    49.0949 dB RMS

Table size: 64
Signal: -3.0103 dB RMS
Noise:  -64.1409 dB RMS
SNR:    61.1306 dB RMS

Table size: 128
Signal: -3.0103 dB RMS
Noise:  -76.1808 dB RMS
SNR:    73.1705 dB RMS

Table size: 256
Signal: -3.0103 dB RMS
Noise:  -88.2216 dB RMS
SNR:    85.2113 dB RMS

サンプル数32は問題がありそうですが、64なら60dB(=1000倍)ですので、感じ取れるけれど聴けないわけではないという感じです。
補間方法については、線形補間であってもウエーブテーブルが十分に大きければ、問題ないと考えられます。

実際には、小さなウエーブテーブルでは、線形補間による雑音よりも倍音の再現性が問題になるでしょう。
32サンプルでは16倍の倍音までしか表現できません。500Hzの基音に対して8KHzですから、倍音の再現性は問題になるでしょう。
64サンプルならその倍の周波数ですから、用途によっては許容できそうです。
128サンプルあれば、まあまあでしょう。この場合は64倍音まで表現できますから、48KHzサンプリングなら24000 / 64 = 375Hz以上なら鋸波や矩形波も再現できることになります。

というわけで、ウエーブテーブルのサイズは最低でも128、最大では2048程度が適切な範囲と考えられます。
補間は線形補間でもそれほど悪くないと思われます。線形補間は2サンプル間を直線で結びますが、3サンプルあれば二次関数、4サンプルあれば三次関数で結ぶことになります。もちろん計算量が増えますが、4サンプル使うCubic補間を使えば上記のような雑音はより抑制できます。ちなみに理想的な補間方法としてsinc補間がありますが、これはリアルタイムのシンセシスで使うのは無理がありそうなので割愛します。logue SDKでは線形補間の他にコサイン補間も用意されています。

最後にサイン波をテーブルサイズ32、64、128で鳴らしてみたデータを置いておきます。
32の場合は明らかな雑音があり、64でも低音部ではかすかに雑音(50Hzの音に対して1.5KHzくらいの音)が感じ取れます。

テーブルサイズ=32

テーブルサイズ=64

テーブルサイズ=128

コメント