NTS-1にMIDIケーブルがつながるようになったので、先日テスト用に作ったOSCを鳴らしてみると、高音部でエイリアシングが気になります。
DA/AD変換でサンプリング周波数f/2よりも高い周波数f0の信号を変換すると、その信号はf/2より低い周波数{f/2 – (f0 – f/2)}へ折り返されて出現します。これがエイリアシングです。
上記のOSCの実装では、信号の生成を
float sig = (phase - 0.5f <= 0.f) ? 1.f : -1.f;
としていますので、振幅は1.0か-1.0のどちらかでその中間は全くない、という(理想的な)矩形波が生成されます。しかし数学的にはこの信号には、無限に高い周波数の成分が含まれており、DA変換するときにはこれがノイズの元になります。
以下の録音を聴くと、音高が高くなって基本周波数がサンプリング周波数/2に近づくと、高域成分が全部低域へ折り返されてきてノイジーになっていることが分かります。
エイリアスノイズを取り除くには、信号をDA変換にかける前に高域成分を除去します。その結果、高い音高では矩形波でも鋸波でもサイン波に近づいていきます。
実装方法としては、デジタルシンセサイザーの波形合成では「BLIT(Band Limited Impulse Train)」という方式があります。また、国産のソフトウェアシンセサイザーの草分けであるSynth1は、音高ごとに高域成分を含まないウエーブテーブルを用意しているそうです。
NTS-1では、エイリアスノイズが出ない矩形波を作るためのウエーブテーブルが用意されています。NTS-1に最初から入っている矩形波のOSCでは、高い音を鳴らしてもエイリアスノイズが出ませんが、おそらくこのウェーブテーブルを使っていると思われます。
ウエーブテーブルを使って矩形波を合成するためのAPIは以下です。
osc_bl2_sqrf()
__fast_inline float osc_bl2_sqrf(float x,
float idx
)
Band-limited square wave lookup.(interpolated version).
Parameters
x Phase in [0, 1.0].
idx Fractional wave index in [0,6].
Returns
Wave sample.
パラメータのidxは、ウエーブテーブルのインデックス番号を指定します。こちらはまだドキュメントが用意されていないようですが、ヘッダファイルを見るとノートナンバーからインデックス番号を求める関数が用意されています。ノートナンバーは整数ではなくfloatで指定するようになっています。
/**
* Get band-limited square wave index for note.
*
* @param note Fractional note in [0-151] range.
* @return Corresponding band-limited wave fractional index in [0-6].
*/
float _osc_bl_sqr_idx(float note);
__fast_inline float osc_bl_sqr_idx(float note) {
return _osc_bl_sqr_idx(note);
}
このAPIはOSC_CYCLE関数の中で、以下のように使います。
const float note = (params->pitch >> 8) + (params->pitch & 0xFF)/256.0f;
/* 波形生成ループ開始 */
float sig = osc_bl2_sqrf(phase, osc_bl_sqr_idx(note));
/* 波形生成ループ終了 */
関数名がosc_bl2_
で始まるAPIはウエーブテーブルを補完して使用していますが、補完せずに高速に処理するosc_bl_
で始まる関数も用意されています。(補完なしの場合、ウエーブテーブルのインデックス番号はuint8_t
型になります。)
鋸波およびパラボリック波についても同様のウエーブテーブルおよび関数群が用意されています。
冒頭のOSCをこのAPIを使って書き直したものの出力音が以下です。
なお、矩形波のウエーブテーブルはデューティ比50%のものしかありません。
PWMができるパルス波を実現するには、矩形波ではなく鋸波のウエーブテーブルを使います。
下図のように、二つの同じ周波数の鋸波を逆向きにして足し合わせると、両者の減少と増加が打ち消しあって矩形波を作ることができます。
このとき、2つの鋸波の位相をずらすことにより、デューティ比が制御できます。
鋸波のウエーブテーブルはlogue SDKに含まれていますので、これを使うとPWMつきのパルス波を作ることができます。鋸波のウエーブテーブルの値域は-1.0 .. 1.0ですから、生成されるパルス波の値域は、デューティ比50%のときは-1.0 .. 1.0ですが、デューティ比が0%や100%に近いとき(二つの鋸波の頂点が近接しているとき)は下図のように-2.0 .. 0や0 .. 2.0になっていきます。そのため、(1.0 - 2*デューティ比)を加算して補正する必要があります。
コメント