KORG NTS-1 開発環境構築 on WSL

 KORG NuTekt NTS-1 digitalの開発環境をWSL(WSL1)を使ってWindowsに構築しました。
 環境構築手順は以下の公式のGithubに掲載されており、Windowsの場合はMSYS2を利用した手順は明確に記述されています。が、WSLを使った場合はGNU Arm Embedded Toolchainの導入は自力でソースからビルドして頑張れ的なことが書いてあります。
github.com
 個人的な備忘もかねて、WSL上に開発環境構築した手順を以下に記載します。
 

前提環境

OS
Windows 10 Pro (64bit版)
WSL
WSL1 / Ubuntu 18.04

 

開発環境構築手順

1. logue SDKダウンロード
 公式の手順通りGithubからcloneします。

 $ git clone https://github.com/korginc/logue-sdk.git
 $ cd logue-sdk
 $ git submodule update --init

2. GNU Arm Embedded Toolchainのインストール
 これはAMD64(x86)環境でARM用のクロスコンパイル環境を構築するために必要なものです。要するに、普通のPCでNTS-1で動作するバイナリを生成するために必要なものです。
 ところで、WSLが使用可能なWindowsは64bit版だけですが、64bit版Windowsで動作するソフトウェアは、Windows用アプリケーションなら64bit版バイナリだけではなく、WOW64の仕組みで32bit版バイナリも動作します。
 (試したことが無かったので知らなかったのですが、)WSLで実行できるのは64bit版ELFだけで、32bit版バイナリは実行できないという制約があるそうです。
 NTS-1の開発環境として要求されているGNU Arm Embedded Toolchainのバージョンは5-2016-q3-updateですが、Linux用は32bit版バイナリしか存在しません。
GNU Toolchain | GNU Arm Embedded Toolchain Downloads – Arm Developer
 試しに32bit版バイナリを導入してみましたが、以下のように"Exec format error"となり実行できませんでした。

# ファイルフォーマットを確認
$ file arm-none-eabi-gcc
arm-none-eabi-gcc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.8, stripped

# 32bit版ELFを実行してみる
$ ./arm-none-eabi-gcc
-bash: ./arm-none-eabi-gcc: cannot execute binary file: Exec format error

 なので、ソースコードからビルドしろとKORG公式手順には書いてあるのです。
 でもですね、5-2016-q3-updateという中途半端なバージョンではなく、例えば一つ次のバージョン6-2016-q4-majorや現時点で最新バージョンの10-2020-q4-majorではLinux用64bit版バイナリが公開されています。
 なので、最新版の10-2020-q4-majorのLinux用64bitバイナリを使って環境構築することにしました*1
 logue-sdk/tools/gccディレクトリで、以下のようにコマンドを叩けば10-2020-q4-majorのGNU Arm Embedded Toolchainのインストールが完了します。

# ダウンロード
$ wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2

# MD5が8312c4c91799885f222f663fc81f9a31と一致することを確認
$ md5sum gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
8312c4c91799885f222f663fc81f9a31  gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2

# 展開
$ tar -xjf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2

# SDKのMakefile中でgcc-arm-none-eabi-5_4-2016q3がディレクトリ指定に含まれるのでシンボリックリンクを貼っておく
$ ln -s gcc-arm-none-eabi-10-2020-q4-major gcc-arm-none-eabi-5_4-2016q3

3. GNU Makeのインストール
 元々何かの開発用にWSLを使っている方などで、makeがインストール済なら特にすることはありません。
 未インストールならbuild-essentialパッケージを導入しておけばmakeだけでなくコンパイラ類も諸々一緒に導入できますのでお勧めです。PC側で動かすちょっとしたツールが必要になってもサクッと作れますので。

$ sudo apt update
$ sudo apt install build-essential

4. demoのビルド
 正常に環境構築できたことを確認するために、SDKに含まれるオシレータのデモをビルドしてみます。
 logue-sdk/platform/nutekt-digital/demos/wavesディレクトリで、以下のコマンドを実行します。

$ make
Compiler Options
../../../../tools/gcc/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -DTHUMB_PRESENT -g -Os -mlittle-endian -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-precision-constant -fcheck-new -std=c11 -mstructure-size-boundary=8 -W -Wall -Wextra -Wa,-alms=./build/lst/ -DSTM32F446xE -DCORTEX_USE_FPU=TRUE -DARM_MATH_CM4 -D__FPU_PRESENT -I. -I./inc -I./inc/api -I../../inc -I../../inc/dsp -I../../inc/utils -I../../../ext/CMSIS/CMSIS/Include

Compiling _unit.c
cc1: warning: option '-mstructure-size-boundary' is deprecated
Compiling waves.cpp
Linking build/waves.elf
Creating build/waves.hex
Creating build/waves.bin
Creating build/waves.dmp

   text    data     bss     dec     hex filename
   2168       4     144    2316     90c build/waves.elf

Creating build/waves.list
Packaging to ./waves.ntkdigunit

Done

 上記では"cc1: warning: option '-mstructure-size-boundary' is deprecated"と警告が1つ出ています。Makefile中のCOPTでコンパイラに引き渡すオプションに-mstructure-size-boundary=8が含まれるために発生しています。5-2016-q3-update時点ではdeprecatedではなかったのが、10-2020-q4-majorまでのどこかのバージョンでdeprecatedになったようです。deprecatedなだけで実害は無い警告なので無視して問題ないでしょう。
 正常にビルドが完了し、実行体が生成され、必要なリソース諸々含めてwaves.ntkdigunitファイル*2にパッケージングされています。
 このファイルをNTS-1 digital LibrarianでNTS-1実機に転送すると、正常に動作することが確認できます。
 

開発

 まだ私はオシレータだけしか作っていませんが、気付いたことを雑多に記載してみます。

  • 自分で独自のディレクトリ構造を切るとMakefileの修正が面倒です*3。公式のドキュメントにも書かれている通り、既存サンプルのディレクトリをコピーして開発に着手するのが手っ取り早いです。
  • オシレータを書くなら最低限OSC_CYCLE()の中だけ実装すればよく、Cが書ける人ならかなり簡単だと思います。
  • ちなみにOSC_NOTEON()、OSC_NOTEOFF()はCalled whenever a note on/off event occursとか書いてありますが、大嘘です。
    • 複数のNOTE ONを(NOTE OFFされる前に)受信しても最初のNOTE ONしかフックされず、同様に最後のNOTE OFFしかフックされないようです。
    • OSC_NOTEON()の引数で得られるuser_osc_param_t型の値から特定したノートナンバー(ピッチ)を使うと、複数のNOTE ONを(NOTE OFFされる前に)受信した場合、最初に受信したNOTE ONしか判らないので、後から受信したNOTE ONが無視されるのと同等の状態になってしまいます。
    • 一般的なモノシンセのように後着優先で発音させる場合、OSC_CYCLE()の引数で取得可能なuser_osc_param_t型の値からノートナンバー(ピッチ)を特定すれば事実上問題ありません。NTS-1はモノフォニックですから、この挙動でもいいですが…*4
  • OSC_NOTEON()、OSC_NOTEOFF()が前述の挙動をすることを踏まえるとNTS-1ではあまり有効に活用できる場面は無いと思われます。つまり初期設定をするOSC_INIT()と、パラメータ変更時にフックされるOSC_PARAM()、そして実際に波形生成を行うOSC_CYCLE()の3つの関数を実装することでオシレータを開発します。
  • OSC_CYCLE()では最終的にQ31フォーマットで波形を生成しますが、Q31で表せる範囲は-1<=value<1です。f32_to_q31マクロで32bit floatからQ31への変換が行えますが、変換元となるfloat型のvalueの範囲も-1<=value<1とする必要があります。
  • ドキュメントに書いてないことで躓きそうなことは他には思いつかず*5、Cが書ける人なら本当に簡単に開発できます(が、一般的なデバッグ手法が使えないので、想定外の挙動時に調査するのは困難です)。

 



以上。

*1:KORGの言う"Required version"ではありませんので、予期せぬ不具合を踏む可能性は否定できないことにご注意ください。

*2:Makefileの記述からも明らかですが、中身はZIPフォーマットですので、気になる方は展開してみると良いでしょう。

*3:というか、このディレクトリ・ファイルはドキュメントに説明されてないけど何なの?何で必要なの?みたいな沼に落ちます。

*4:Called whenever a note on/off event occursの通りに動作してくれればポリフォニック化も簡単に出来るのに

*5:そういえば、オシレータ一つ当たりのプログラムサイズは32KB以下という制約があるとFAQに書いてあります。つまり、実用性はともかく8bit 44.1kHzの0.5秒程度のサンプルを使ったPCMシンセだって書けます。というか書きました。