NTS-1 mk IIではSDKがアップデートされています。SDKの概要は前回の記事に書きましたが、今回は試しにオシレータを作ってみました。
以前、旧NTS-1とdrumlogue用に作った、以下のシンプルなオシレータを、mk2に移植してみます。
NTS-1 mk2は中身はNTS-1に近いけれど、APIはDrumlogue寄り、ということで両者の中間のようなコードになります。
作ってみたosc.hは以下の通りです。ちょっと長いですが、実際に意味があるのは60行目から始まる関数Proces()
のところだけです。
旧NTS-1では、Process()
という関数の代わりに、OSC_CYCLE()
という関数が使われていました。どちらも、サンプルバッファを与えられてそこにサンプル値を書き込むのがメインの仕事です。
旧APIのOSC_CYCLE()
では、オシレータのピッチを含むパラメータとして、const user_osc_param_t * const params
が与えられていました。新しいSDKでは、このパラメータがありません。従ってOSCのピッチを別に取得する必要がありますが、上のコードではNoteOn()
でMIDIノート番号を取得し、それを保存して使用しました。
ただ、ここで以下のような疑問が出てきます。
・オシレータのピッチはピッチベンドやポルタメントがあるし、LFOやエンベロープなどでモジュレーションされるので、ノート番号だけでは情報が不十分。
・ピッチベンドについては関数PitchBend()
で取得できるが、ピッチベンドレンジが分からないとピッチには変換できない。
Drumlogueの場合は、オシレータではなくシンセサイザを実装するAPIでしたので、オシレータのピッチをどう決めるかはユーザ側の実装に任されていました。しかし、NTS-1 mk IIでは、オシレータのピッチは本体側のソフトウェアが決めています。
デフォルトで内蔵されているオシレータは、ピッチベンドやLFOのピッチ制御に反応します。どうやっているのか、サンプルとして提供されているオシレータWavesの実装を見てみました。すると・・・
一応ドキュメントに記載があるのですが、オシレータユニットの起動時に呼ばれる関数Init()
のパラメータ、unit_runtime_desc_t * desc
から、旧APIと同様のピッチ情報を取得していました。それもタイプキャストを使っていますから、ちょっと強引なやり方です。ちなみにキャストされているunit_runtime_osc_context_t
型はcommon/unit_osc.h
の中で定義されています。旧APIで与えられていたLFOの値もここで取得できます。
上のコードの65行目を以下のように変えると、プリインストールされているオシレータと同じ処理が行えます。これでLFOやピッチベンドにも正常に反応するようになります。
const unit_runtime_osc_context_t *ctxt = static_cast<const unit_runtime_osc_context_t *>(runtime_desc_.hooks.runtime_context); const float w0 = osc_w0f_for_note((ctxt->pitch)>>8, ctxt->pitch & 0xFF);
しかし、なんだかこれはAPIに欠陥があるような気がします。元のAPIがシンセ用なので、そのままオシレータのAPIとして使うのは無理があります。実装上は、おそらく旧APIの背景にあるコードがそのまま動いていて、そこから新APIをキックしているのではないかと思います。
また、NoteOn()
を使った場合と上記引用のコードを使った場合では、得られるピッチの情報が異なります。
キーを一つ押したまま、別のキーを押した場合、モノシンセなので、結果的には1音しか出ません。上記引用のコードを使った場合は、最も最近押されたキーのピッチを取得します。一方、APIのNoteOn()
関数は、キーが1つ以上押されている状態では、他のキーが押されても呼び出されないようです。(MIDI NoteOnイベント自体はMIDIインタフェースに複数出力されます。)NoteOn()
関数で複数のノートイベントを取得すれば、パラフォニックなオシレータが実装可能ではないかと妄想していたのですが、残念ながらNoteOn()
関数は旧APIの下位互換のようです。【2024/4/20追記】キーを複数押した場合の動作は、グローバルパラーメータ「EG Legato」の設定(デフォルトは1)により変わります。この値を0(レガートOFF)にした場合、キーを押すたびにエンベロープがトリガされますが、同時にオシレータにNoteOnイベントが送られます。ただ、現状では残念ながらNoteOffイベントは1回(最後のキーを離した時)しか送信されません。EGをキックするタイミングとしては正しいですが、パラフォニックシンセを実装するにはあまり向いていないと思われます。【追記終】【2024/6/14追記:このバグはファームウェアv1.2で修正済みです。】
もちろん、NoteOnイベントを取得できることにも利点はあります。おそらく最大の利点はベロシティをオシレータのモジュレーションソースとして使うことができるようになる点でしょう。NTS-1 mkIIのキーはベロシティは検出しませんが、外部シーケンサーやMIDIキーボードを接続したときに、より表現力のある音源を実装できる可能性があります。(【2024/4/27追記】残念ながら、現時点ではベロシティの値は常に0が入っているようです。【2024/6/14追記:このバグはファームウェアv1.2で修正済みです。】)
一方で、旧NTS-1のオシレータを移植する場合は、NoteOnではなく上記引用のコードを使うのが簡単で、動作の違いも起きにくいと思われます。
コメント
はじめまして。趣味で電子音楽をしている者です。
記事、興味深く読ませていただきました。
NTS-1が好きでカスタムオシレータを使っていましたが、mk2は発売から間もなく対応したカスタムオシレータが見つかりませんでした。
どなたか作っていないか?とweb検索してこの記事にたどり着きました。
これを機に自作してみようかと思ったのですが、音楽のプログラミングどころかそもそもプログラミング自体に触れたことがなく、何をどうしていいのやら…という状態です。
ど素人がNTS1 mk2に対応したオシレータを作ろうとしたとき、何から学び、何ができるようになる必要があるでしょうか?
もしよろしければ、入口だけでもアドバイスいただけないでしょうか?
コメントありがとうございます。
そうですね・・・いきなりNTS-1 mk2向けのプログラミングは、かなりハードルが高いと思います。ちょっと難しいご質問ですが、別途、記事にしてみたいと思います。しばらくお待ちください。
記事書きました。
https://blog.boochow.com/article/how-to-start-logue-sdk.html