Pure DataのPatchからDrumlogueのSynthユニットを作ってみた(1)音を出す


今回は、Pure DataのパッチをCのコードに変換して、そのコードとlogue SDKを組み合わせて、Drumlogue用のシンセユニットを作成してみます。

Pure Dataのパッチをlogue SDK上で動作させる方法について、2つ前の記事で、以下のように書きました。

組み込み機器でPure Dataのパッチを動かすには、

(1)Pure Dataのエンジン部分であるlibpdを組み込み向けにビルドしてファームウェアに組み込む
(2)Pure DataのパッチをCのコードにコンパイルし、そのCのコードをファームウェアに組み込む

という2つの手法が主なようです。

drumlogueは(1)の方法が良さそうです。メモリの制約はあまり無いですし、処理能力の面でもなんとかなるのではないかと思います。
NTS-1 mkIIやNTS-3では、(2)の方法でPureDataパッチを動かせる可能性がありそうです。

その後、これら2つの方法についてもう少し調べてみたのですが、以下のことが分かりました。

(1)の方法は、さらに

(1)-1:Pure Dataパッチとlibpdを含んだ形でDrumlogue用プラグインを作る。パッチは文字列データに変換するなどしてコードに含める。
(1)-2:Pure Dataパッチをファイルシステム上に置き、それをDrumlogue用プラグインから読み込めるようにする。

という2つの方法が考えられます。だだし、(1)-2は

・ファイルシステムを何らかの形で実装する必要がある。現状、Drumlogueのユニットからアクセスできる外部データはPCMデータだけなので、そこにパッチを埋め込むしか手段が無さそう
・パラメータに関して、ユニット起動後に名称その他の設定を動的に変更できないので、パッチを動的に読み込もうとすると使い勝手が良く無さそう

といった課題があります。logue SDKを拡張したくなりそうです。

また、共通の課題として、logue SDKとの連携方法(メモリ管理や入出力のAPI等)をもう少し調べる必要があります。libpdにはいろいろフックが用意されており、連携はできそうです。

(2)の方法は、生成されるコード自体は、使い勝手が良さそうに見えました。ただ、変換できる(利用できる)オブジェクトの種類に制約があります。例えば、前回の記事で使ったvline~はサポートされません。

hvcc/docs/09.supported_vanilla_objects.md at develop · Wasted-Audio/hvcc
The heavy hvcc compiler for Pure Data patches. Updated to python3 and additional generators - Wasted-Audio/hvcc
hvcc/docs/10.unsupported_vanilla_objects.md at develop · Wasted-Audio/hvcc
The heavy hvcc compiler for Pure Data patches. Updated to python3 and additional generators - Wasted-Audio/hvcc

今回、まずは最も実現性の高そうな「(2)の方法 × Drumlogue」という組み合わせを動かしてみました。

例題として試すPure Dataパッチは、前々回の記事で使った非常にシンプルな下記のパッチです。440Hzの正弦波を出すだけのパッチですね。これをhvccを使ってCのコードに変換し、それを使ってDrumlogueのSynthユニットを作成してみます。

以下の環境はLinux(Ubuntu24.04)で動かしています。
まずhvccをダウンロードします。なおhvccはPythonで書かれており、Python3.8~3.12が必要です。

$ mkdir hvcc-test
$ cd hvcc-test/
$ python -m venv VENV
$ source VENV/bin/activate
$ pip3 install hvcc

これでhvccが使えるようになったので、Pure Dataパッチ(osc440.pdというファイル名にしています)を変換してみます。

$ hvcc ./osc440.pd -o osc440 -n osc440

-oオプションは出力先のディレクトリを指定(なければ作成)します。-nオプションはパッチの名前で、出力されるソースコードのファイル名、クラス名、関数名などに使われます。省略時は”heavy”という名前が使われます。

結果を確認してみましょう。出力先のディレクトリに「c」「hv」「ir」という3つのサブディレクトリが作られますが、Cのコードはサブディレクトリ「c」に出力されています。

$ cd osc440/c
$ ls

HeavyContext.cpp           HvHeavy.h          HvMessagePool.c   HvSignalVar.h
HeavyContext.hpp           HvHeavyInternal.h  HvMessagePool.h   HvTable.c
HeavyContextInterface.hpp  HvLightPipe.c      HvMessageQueue.c  HvTable.h
Heavy_osc440.cpp           HvLightPipe.h      HvMessageQueue.h  HvUtils.c
Heavy_osc440.h             HvMath.h           HvSignalPhasor.c  HvUtils.h
Heavy_osc440.hpp           HvMessage.c        HvSignalPhasor.h
HvHeavy.cpp                HvMessage.h        HvSignalVar.c

ファイルがたくさん生成されましたが、メインとなるのはHeavy_osc440*の各ファイルです。インタフェースとしてC用とC++用の両方が用意されています。

次に、Drumlogue用の空のシンセユニットを用意します。SDKに用意されているダミーのユニットのディレクトリをコピーし、その中に”Hv”というサブディレクトリを作って、その中に上で生成したファイルをコピーします。

$ cd ~/logue-sdk/platform/drumlogue
$ cp -r dummy-synth hvcc-test
$ cd hvcc-test
$ mkdir Hv

logue SDKのファイルを修正して、hvccが生成した関数を呼び出すようにします。今回は、呼び出さなければならないのは初期化と音声出力の2つだけです。他に、ヘッダファイルと状態保持用のオブジェクトを追加します。

hvccが生成するコードのAPIは、下記ドキュメントに記載されています。初期化のAPIのパラメータはサンプリングレート、音声出力のAPIのパラメータは入力バッファ・出力バッファへのポインタと出力する音声フレームの数です。なお、音声出力のAPIはデータフォーマットにより複数ありますが、logue SDKではバッファにステレオのデータが交互に並ぶ形式(LRLR・・・)ですので、hv_processInlineInterleaved()を使用します。

hvcc/docs/05.c.md at develop · Wasted-Audio/hvcc
The heavy hvcc compiler for Pure Data patches. Updated to python3 and additional generators - Wasted-Audio/hvcc

追加する必要のあるコードはほんの少しだけですので、以下に差分のあるところだけ示します。

synth.h:

#include "Heavy_osc440.h"
  inline int8_t Init(const unit_runtime_desc_t * desc) {
(略)
    hvContext_ = hv_osc440_new(desc->samplerate);
    if (hv_getNumOutputChannels(hvContext_) != 2)
        return k_unit_err_geometry;

    return k_unit_err_none;
  }
  fast_inline void Render(float * out, size_t frames) {
    hv_processInlineInterleaved(hvContext_, NULL, out, frames);
  }
  /*===========================================================================\
*/
  /* Private Member Variables. */
  /*===========================================================================\
*/
  HeavyContextInterface* hvContext_;

また、C++のソースファイルの拡張子が.ccではなく.cppになっているので、Makefileの修正が必要です。この修正は以前も行ったことがあり、以下のリポジトリのMakefileがそのまま利用できます。

GitHub - boochow/maxisynth: KORG logue SDK 2 for drumlogue synth unit sample code using Maximilian library
KORG logue SDK 2 for drumlogue synth unit sample code using Maximilian library - boochow/maxisynth

config.mkには以下の変更を行います:
プロジェクト名の追加(任意)

PROJECT := pd_osc440

ソースコードの追加

# C sources                                                                     
CSRC = header.c Hv/HvLightPipe.c Hv/HvMessageQueue.c Hv/HvTable.c Hv/HvMessage.c Hv/HvSignalPhasor.c Hv/HvUtils.c Hv/HvMessagePool.c Hv/HvSignalVar.c

# C++ sources                                                                   
CXXSRC = unit.cc Hv/Heavy_osc440.cpp Hv/HvHeavy.cpp Hv/HeavyContext.cpp

インクルードパスの追加

UINCDIR  = Hv

コンパイルオプションの追加

UDEFS = -DHV_SIMD_NEON

最後のコンパイルオプションはhvccが生成したコード用のオプションで、CPUアーキテクチャに合わせたベクトル演算を利用することで演算処理を高速化します。

このほか、Drumlogue上でのプラグインの表示名を変更したいときはheader.cの中の.nameの値を変更します。

これをビルドしてみるとこんな感じになります。

$ make install
Linking build/pd_osc440.drmlgunit
Stripping build/pd_osc440.drmlgunit
Creating build/pd_osc440.hex
Creating build/pd_osc440.bin
Creating build/pd_osc440.dmp

   text	   data	    bss	    dec	    hex	filename
  38001	    912	     12	  38925	   980d	build/pd_osc440.drmlgunit
Creating build/pd_osc440.list

Done

生成されたdrmlgunitファイルをDrumlogueにインストールすると、440Hzの音が無事再生され、動作が確認できました。
次回は、logue SDKのパラメータ関連のAPIを使って、Drumlogueのノブで周波数を変更してみます。

コメント