logue SDKを使ったNTS-1用カスタムOSC作成テスト


前回はlogue SDKを使ったOSCのビルドと転送を試してみましたが、今回はこのSDKの基本的な使い方を見てみます。

SDKには、OSCのサンプルとしてbipolar, maxsize, sin, squareという4つのOSCが含まれています。(それぞれのディレクトリでmakeすればビルドすることができます。私の環境ではmaxsizeはメモリ領域不足のリンクエラーになりましたが…)
今回は、この中でも最も単純な矩形波のOSCの実装から、PWMやLFOパラメータを省いてさらに簡略化してみました。

まず、オシレータの波形は時間軸を0.0から1.0、振幅を-1.0から1.0の範囲で考えます。
矩形波の場合は、下図のように時間軸0.5と1.0(0.0)を境に振幅が1.0と-1.0に変化します。

この、1波長内での時間軸を位相(phase)と呼びます。

オシレータの実装は、DA変換用のバッファに対して、音高(ピッチ)を考慮しながら振幅の値を書き込んでいきます。これは、位相の値をどんどん増やしながら、その時の振幅の値をバッファに書き込むことに相当します。(位相は1.0になったら0.0にリセットします。)

ここで、位相の値を増やすステップは、生成したい音高と、バッファを読みだすサンプリングレートによって変わります。

NTS-1ではサンプリングレートは48KHzとなっていますので、例えば1KHzの音を出すには、1波形を48サンプル使って出すことになります。ということは、位相は1サンプルごとに1/48 = 0.020833ずつ増やせばよいわけです。

SDKでは、この「位相の増分値」をノートナンバー(とモジュレーションの値)から求める関数

float osc_w0f_for_note(uint8_tnote, uint8_t mod)

用意されています。この関数を使うことで、サンプリングレートを考慮する必要は無くなります。

矩形波を出力するオシレータのソースコードを以下に示します。
5つの関数
OSC_INIT()OSC_CYCLE()OSC_NOTEON()OSC_NOTEOFF()OSC_PARAM()
があり(API仕様はこちら)、波形を生成するのはOSC_CYCLE()です。

オシレータの状態を表す構造体State型は、メンバ変数としてw0phaseflagsがあります。w0は生成する音の周波数に対応した、位相の増分値です。
State型のスタティック変数s_stateがオシレータの状態を保持します。

関数OSC_CYCLE()は、バッファへのポインタynから、frames個のサンプルを書き込みます。36行目からのループがその処理で、41行目ではphasew0を加算しています。その次の行のphase -= (uint32_t)phase;はphaseの整数部をゼロにしています。

ポインタyy_eが指しているq31_t型は符号付きで小数部が31ビット(実体は32ビット長整数で、1ビットが符合なので整数部は無し)の固定小数点数です。このように小数部のビット数をQで表すやり方をQ表記(Qフォーマット)といいます。固定小数点演算関係のライブラリの仕様はこちらにあります。

ポインタyに付いている修飾子__restrictはポインタが指すデータが、そのポインタ以外から書き込まれないことを表す修飾子で、あってもなくても処理は変わりませんが、あるとより最適化が行われます。

このOSCをビルドするには、このファイルを保存し(例えばosctest.cpp)、project.mkUCXXSRCにそのファイル名を指定します。Makefile、manifest.jsonはとりあえず修正しなくても大丈夫です。

$ cd logue-sdk/platform/nutekt-digital/
$ cp -r osc osctest
$ cd osctest
$ cat > ./osctest.cpp  #上記のコードを書き込む

project.mk:

# #############################################################################
# Project Customization
# #############################################################################
PROJECT = osc_test
UCSRC =
UCXXSRC = osctest.cpp
UINCDIR =
UDEFS =
ULIB = 
ULIBDIR =
$ 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
Compiling osctest.cpp
Linking build/osc_test.elf
Creating build/osc_test.hex
Creating build/osc_test.bin
Creating build/osc_test.dmp

   text	   data	    bss	    dec	    hex	filename
    644	      0	     28	    672	    2a0	build/osc_test.elf

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

Done

$ ../../../tools/logue-cli/logue-cli-linux64-0.07-2b/logue-cli load -u ./osc_test.ntkdigunit -s 1 -i 2 -o 2
> Parsing nutekt digital unit archive
> Parsing manifest
> Parsing unit binary payload
> Handshaking...
> Target platform: "nutekt digital"
> Target module: "Oscillator"
size: 708 crc32: 199bd0d4

【2024/4/29追記】上の例ではmakeで.ntkdigunitファイルが生成されていますが、現在のSDKではmakeはコンパイルのみで、.ntkdigunitはmake installにより生成されます。

コメント