前回は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
型は、メンバ変数としてw0
、phase
、flags
があります。w0
は生成する音の周波数に対応した、位相の増分値です。
State
型のスタティック変数s_state
がオシレータの状態を保持します。
関数OSC_CYCLE()
は、バッファへのポインタyn
から、frames
個のサンプルを書き込みます。36行目からのループがその処理で、41行目ではphase
にw0
を加算しています。その次の行のphase -= (uint32_t)phase;
はphaseの整数部をゼロにしています。
ポインタy
、y_e
が指しているq31_t
型は符号付きで小数部が31ビット(実体は32ビット長整数で、1ビットが符合なので整数部は無し)の固定小数点数です。このように小数部のビット数をQで表すやり方をQ表記(Qフォーマット)といいます。固定小数点演算関係のライブラリの仕様はこちらにあります。
ポインタy
に付いている修飾子__restrict
はポインタが指すデータが、そのポインタ以外から書き込まれないことを表す修飾子で、あってもなくても処理は変わりませんが、あるとより最適化が行われます。
このOSCをビルドするには、このファイルを保存し(例えばosctest.cpp
)、project.mk
のUCXXSRC
にそのファイル名を指定します。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により生成されます。
コメント