Nu:Tekt NTS-1 digital kitでサウンドプログラミング入門 #KORG #NTS1

Nu:Tekt NTS-1 digital kit

Korgが発売している1ボイスのハードウェアシンセサイザーです。

組み立て式のキットとして販売され、1ボイスのオシレータ、それにつながるフィルタとエンベロープ、さらにモジュレーション、ディレイ、リバーブといったエフェクトをかけることができます。

 外部からのオーディオ入力もサポートしており、外部音源に対するエフェクタとしても利用することができます。さらに、テンポシンクのための入出力、MIDI入力、USB(MIDI)とインターフェースも豊富です。

極めつけはカスタムのオシレータ、モジュレーションが作成・利用できるという機能まであります。

RakuChordなどで音を生成するプログラムをちょっと書いたことがある私にとっては、「プロ」の作ったシンセサイザーのハードウェアからソフトウェアまでを触ることができるこの製品は非常に魅力的でした。

組み立て

f:id:inajob:20191128231310p:plain

まずは組み立てです。といってもはんだ付けなどは必要ありません。

まずは基板兼ケースとなっているパネルを折り曲げて分離します。(辺に折り曲げてしまうと取り返しがつかないのでここが一番緊張しました。)

f:id:inajob:20191128231345p:plain

それからそれらをねじで止めて、鍵盤となるリボン抵抗をコネクタで接続し完成です。

USBコネクタに電源をつなげると起動し、鍵盤をタッチすることで音が鳴ることが確認できました。

f:id:inajob:20191128231413p:plain

音を出して遊ぶ

鍵盤を押すと、当然それに対応した音が鳴ります。しかしこのリボン抵抗は細かい操作ができず、音出しのテストくらいしか使用に耐えません。USB/MIDI接続を使いMIDIホストと接続することで、この問題は解消できそうです(まだ試していない)

しかしアルペジエータの機能を使うことで、それっぽいメロディをNTS-1だけでも演奏することができます。

プリセットのオシレータを使い、フィルタ、エンベロープをぐりぐり動かすだけでもかなり面白いです。

エフェクタとして遊ぶ

オーディオ入力端子に自作電子楽器のRakuChordをつなげて、エフェクタとして利用してみました。

モジュレーションのコーラスエフェクトやリバーブは音に深みが出て非常に面白いです。

ディレイはテンポと同期する機能がないため、うまく速度を調整するといい感じに動きますが、合わせるのが非常に難しいです。ぜひテンポと同期できるディレイを実装してほしいと思いました。

オシレータを自作して遊ぶ

プログラマとして見逃せないのがカスタムオシレータの自作です。調べるとNTS-1で利用できるSDKが公開されており、これを使うことでC/C++でオシレータやモジュレータが作れるようです。

github.com

しかし、このSDK、ドキュメントが非常に少なく、また海外・国内ともにあまりチュートリアルのようなものがありません。これは、結構苦労しそうです・・

↓に用意されているスクリプトを使うことでLinux,Windows,macOSそれぞれの環境でNTS-1で利用されているSTM32用のgccをセットアップすることができます。

logue-sdk/tools/gcc at master · korginc/logue-sdk · GitHub

しかし、これらをちゃんとセットアップするのは依存関係も含めて結構手間です。

ということで、まずはこのlogue-sdkの環境をまるっと閉じ込めたコンテナイメージを作成しました。

GitHub - inajob/logue-sdk-docker

このコンテナイメージを使うことで、Dockerをインストールするだけでlogue-sdkの開発を始めることができます。

さて、セットアップは簡単にできるようになったので、いよいよプログラミングです。

オシレータの作成例があるので、これを参考にします↓

logue-sdk/platform/nutekt-digital/demos/waves at master · korginc/logue-sdk · GitHub

このプログラムはよくできていてwaves.hppというファイルにC++のオブジェクトとして、オシレータが実装されており、waves.cppでlogue-sdkから呼び出されるフックの関数を実装しています。

よくできているのですが、SDKの利用方法を学ぶ際にはもう少し簡単な例が欲しいところです。

そこで、とりあえず簡単に「ラ」を鳴らすだけのオシレータを実装してみることにしました。

 

変更したファイルはこれだけ

  • waves.cpp, waves.hpp:これは削除
  • simple.cpp:新しく作成。後ほど説明
  • manifest.json:オシレータの名前やパラメータの設定
  • project.mk:書き換えてsimple.cppを指すように変更

実際にはsimple.cppを作るところ以外はほとんど機械的な変更だけです。

>> code
#include "userosc.h"

uint16_t param1; // オシレータのパラメータ記録用
uint32_t tot; // オシレータの位相の記録用

// このオシレータが起動したときに呼ばれる?
void OSC_INIT(uint32_t platform, uint32_t api)
{
  param1 = 0;
  tot = 0;
}
// オシレータが次の波形を計算するために呼ばれる?
// ynのアドレスからframes分までのメモリに波形を書き込む
// paramsに押された鍵盤の音階や周波数が入っている
void OSC_CYCLE(const user_osc_param_t * const params,
               int32_t *yn,
               const uint32_t frames)
{

  q31_t * __restrict y = (q31_t *)yn;
  // 終了メモリアドレス
  const q31_t * y_e = y + frames;

  // メモリへの書き込みループ
  for (; y != y_e; ) {
    // サンプリングレートは48KHzのようなので、以下の式で440Hzの矩形波が作れる
    // 別途param1を設定することで周波数を変更できるようにしてみる(本当は鍵盤で指定された周波数にすべき)
    float sig = (tot/(48000 / (440 + param1))%2 == 0)?0:1;
   // floatからq31に型変換しメモリに書き込む 
    *(y++) = f32_to_q31(sig);
    tot ++; // 位相を進める(オーバーフローするときに波形が乱れるのであまりよくないような気がする)
  }
}

void OSC_NOTEON(const user_osc_param_t * const params)
{
  // note onの時に呼ばれる
}

void OSC_NOTEOFF(const user_osc_param_t * const params)
{
  // note offの時に呼ばれる
}

// オシレータのパラメータが変更されたときに呼ばれる
void OSC_PARAM(uint16_t index, uint16_t value)
{
  switch (index) { // どのパラメータが変更されたかが入っている
  case k_user_osc_param_id1: //今回はこの変更のみで処理する
    {
      param1 = value; // 0..15 // OSC_CYCLEで使うので変数に入れておく
    }
    break;
  default:
    break;
  }
}
 
<<

 

これが今回の”「ラ」が鳴るだけオシレータ”です。

よく見るととても簡単です。要はOSC_CYCLEでどんどんメモリに次の波形を書き込んでいけばよいわけです。

ビルドは先ほど紹介したlogue-sdk-dockerを使えば簡単にできます。ビルドができると拡張子ntkdigunitのファイルができるので、Librarianという専用ツールでNTS-1に書き込みます。

これでOSCに新しいオシレータが追加されます。

ついでに、このプログラムを改造して押された音階に対応する音が鳴るようにしてみます。

>> code

void OSC_CYCLE(const user_osc_param_t * const params,
               int32_t *yn,
               const uint32_t frames)
{
  q31_t * __restrict y = (q31_t *)yn;
  const q31_t * y_e = y + frames;

  // メモリへの書き込みループ
  for (; y != y_e; ) {
    // osc_w0f_for_note(音階番号, 音階からのずれ周波数?) でオシレータの変分を返す
    // これらは引数のparams->pitchに入っている
    // 参考: https://github.com/korginc/logue-sdk/blob/master/platform/nutekt-digital/inc/userosc.h#L56
    // > high byte: note number, low byte: mod (0-255)

    float w = osc_w0f_for_note((params->pitch)>>8, params->pitch & 0xFF);
    // nowに今の位相が記録されている、これをフレームごとにずらしていく
    now += w;
    // プリセットのウェーブテーブルのnowの位相の値を取り出す
    float sig = osc_wave_scanf(wavesB[param1], now);
    // floatをq32に変更しメモリに書き込む
    *(y++) = f32_to_q31(sig);
  }
}

<<

こんな感じです。osc_w0f_for_noteとかosc_wave_scanfとか、wavesBとかはほかのソースコードを見ながらで書いています。この辺もう少しドキュメントが欲しいところです。(そもそもプリセットのwaves[A-D][0-15]にはどんな波形が入っているか書いてあるドキュメントが見当たらない・・)

 

と、こんな具合でC言語と勘があれば自分でオシレータを作ることができることがわかりました。

 

(ここまでのソースコードhttps://github.com/inajob/simple-oscで公開しています。)

追記: 日本語のドキュメントが公開されました! https://korginc.github.io/logue-sdk/ja/

感想

このガジェットはDTMをたしなむ人はもちろんですが、サウンドプログラミングを学びたい人」にとっても非常に便利なものであると感じました。

音をソースコードで記述する「サウンドプログラミング」ですが、リアルタイムに鳴らそうとすると、たくさんのタスクが並列に処理されるOSの上では案外書きにくいものです。

そこで役に立つのがマイコン上でのサウンドプログラミングです。これまではArduinoやmbedでも似たようなことができましたが、これらはオーディオに特化していないため、パソコンからのMIDI信号受信や、外部音源の取り込み、ディレイを扱うための大きなメモリなど、かなりの周辺機器を自作しないとまともなオーディオプログラミング環境になりません。これにはかなり高度な電子工作、マイコンプログラミングの知識が必要です。

しかしこのNTS-1はこれらの面倒な部分をすべて受け持ってくれて「サウンドプログラミング」の「おいしいところ」だけを扱うことができます。

 

といった感じで、本当にこれはサウンドプログラミング界のArduinoというべき開発環境であると感じました。

また、単純にハードウェアMIDI音源、オーディオエフェクタとしても非常に使いやすいし、品質も良いためプログラミングとは無縁のDTMユーザにもおすすめできます。(まぁ私はDTMユーザではないのでこの部分の評価はほかの人の記事を見ていただくほうが良いです)

 

私は自作電子楽器RakuChordの後段のエフェクタとして、また自作電子楽器の音源の研究という2つの目的でこれからもこのNTS-1を活用していきたいと考えています。

 

記事の中にも書いた通りプログラミング周りはドキュメントなどが少なく、これからの盛り上がりが非常に大事であると考えています。

ぜひぜひ、NTS-1を買った人は得られた知見を公開してもらえると助かります!

 

またTwitterで情報を記載する際にはハッシュタグ#NTS1を使うことをお勧めします(製品名であるNTS-1はハイフンが含まれておりハッシュタグにできないため。)

 

私も自分が発見したことをブログや下記Scrapboxのページにまとめていこうと思うので、NTS-1ユーザの方は見てみてください。

scrapbox.io

 

Amazonで買えます↓