Pure Dataパッチのlogue SDK for drumlogue向け自動変換(現状)

そろそろ今年も終わりです。振り返りをする前に、こちらで書いた現在開発中のdrumlogue用のhvcc external generatorの現状を書いておきます。

このexternal generatorは、これまで出してきた他のlogue SDK対応デバイス向けexternal generatorと同様、hvccを使ってPure Dataパッチからdrumlogueのlogue SDK用のCのコードを生成するものです。

現時点、まだ仕様を確定していない部分もあるのですが、検討項目が意外と多く、試行錯誤を経てようやくある程度、生成するコードの形がまとまってきたところです。前回のNTS-3版のリリースから1か月あまり作業してきましたが、記事にするのをサボっていたせいで長い記事になってしまいました。

概要

drumlogueは他のlogue SDK対応デバイスと異なる点が多々あるのですが、SDKで開発する立場からの一番の違いは、ユニットの最大サイズが「32MB」であることです。他のデバイスでは処理性能よりもフットプリントの方が制約になることが多かったのですが、drumlogueに限っては「唯一の制約はCPUの性能」といっても良いかと思います。ヒープメモリの確保も普通にmallocが使えます。使えるメモリが最大32MBあれば、ほぼ何でも作れるといっても過言ではないでしょう。

次に大きな違いは、作成できるユニットの種類です。drumlogueのlogue SDKでは、Synth、Delay、Reverb、Masterの4種類のユニットを作成できます。OSCとMod FXはもともとdrumlogueにはありません。

Synthユニットはシンセサイザなので、オシレータ以外のパーツ、つまりエンベロープやLFO、フィルタやVCAその他モジュレーションの仕組みなども実装していく必要があります。なので、OSCと比べるとだいぶハードルが高いのですが、Pure Dataで書いてよいとなれば、かなりそのハードルは下がります。

Master Fxは入力信号が2系統4チャネルあり、一方の信号が他方の信号を制御する、いわゆるサイドチェインという仕組みで、一方の信号レベルで他方の信号レベルを制御するダッキングといった効果を与えることができます。

現状、Synth、Delayのためのexternal generatorを開発中ですが、Reverbは実質的にDelayと同じです。MasterFXはまだ手をつけていません。

デモ

以下のビデオは、Synthユニットとして4音ポリのシンセ、Delayユニットとしてheavylibのreverb~を使ったリバーブを動かしています。heavylibは以前も紹介しましたが、hvccで使えるオブジェクトだけを使っているPure Dataライブラリです。

下図は、このデモで動かしている簡易シンセサイザです。
図の左側がメインで、右側は[voice~]abstractionの中身です。ポリフォニックを実現するのに、Pure Dataでは[clone]でabstractionを複数個生成するのが一般的ですが、hvccはcloneをサポートしていないので、voice~を4つ並べてrouteで振り分けています。

メイン部分で分かるように、drumlogue用のexternal generatorではnoteinオブジェクトが利用可能です。SDKではSynthユニットに対してNoteイベントが提供されているので、実装しました。ピッチベンド(bendin)とアフタータッチ(touchin、polytouchin)についても利用可能とする予定です。

下図はリバーブですが、heavylibの中にある[hv.reverb~]にノブからパラメータを送っているだけです。
これをビルドすると、ディレイラインを別にしてコードだけでも64KB程度になるので、他のデバイスでは動作させられなかったのですが、drumlogueなら余裕です。

Pure DataパッチでPCMを使う

これは今回、drumlogue用に追加したいと考えている新機能です。

drumlogueのlogue SDKでは、SynthユニットからPCMデータへのアクセスするためのAPIが用意されています。このAPIでは、bank番号とindex番号で、PCMデータへのポインタを得ることができます。PCMデータはあらかじめファイルをUSB経由で本体のストレージに保存しておくと、そのデータが起動時にメモリ上に読み込まれます。

hvccでは、Pure Dataのテーブル(arrayオブジェクト)のデータ本体にC/C++からアクセスするためのAPIがあります。Pure Dataのパッチで[table table_name table_size @hv_table] と最後に”@hv_table”を追加することによって、テーブルがこのAPIの対象になります。(この方法は以前、[noise~]の代替品を作るために利用しました。)

この機能を使って

・Pure Data側でテーブルを用意
・これをC/C++側へ渡しつつ、PCMデータを指定
・C/C++側では指定されたPCMデータをテーブルにコピー
・Pure Data側でテーブルを読み取って[dac~]へ送る

という処理を実装すれば、Pure Dataパッチからdrumlogueが持っているPCMデータを鳴らすことが可能です。

なおデータのコピーですが、drumlogueではステレオのPCMデータが利用可能で、ステレオの場合はデータはインターリーブ(LRLR・・・と並ぶ)されています。一方、Pure DataではステレオのPCMデータを扱う標準的な方法はありません。ですので、

方法①モノラルにミックスしなおす
方法②ステレオかモノラルかをPure Dataから指定、もしくはSDKから通知したうえで、
 ②-1 1つのテーブルにインターリーブで書き込む
 ②-2 1つのテーブルに前半と後半に分けてLとRのデータを書き込む(planar stereo)

といった処理が必要になります。

PCMに関するメタデータのやり取り

PCMデータを受け取るテーブルをPure DataからC/C++側にexportする以外に、Pure Dataとlogue SDK間でメタデータのやり取りが必要になります。logue SDKでのPCMデータを表す構造体とAPI関数はこちらで定義されています。PCMデータをテーブルに書き込んで再生する上で必須なのは、以下の2つのメタデータです。

【logue SDK⇒Pd】テーブルへ読み込んだPCMデータのデータサイズ(PCMデータ再生に必要)
【Pd⇒logue SDK】テーブルへ読み込むPCMデータを指定する、バンク番号とインデックス番号

【logue SDK⇒Pd】の部分は、これまでにノブやタッチパネルの操作をPure Data側に送っていたのと同じ仕組みになります。

【Pd⇒logue SDK】の部分は、hvccではsendHook関数が利用できるようになっています。この関数は、Pure Data側からパラメータがsendされたときに、そのパラメータの名前、パラメータのハッシュ値、sendされたメッセージへのポインタを受け取ります。

以上を通常のPure DataでのPCMデータの扱いと較べてみます。

通常のPure Dataでは、ファイルからPCMデータをテーブルへロードする目的で、[soundfiler]オブジェクトがよく使われます。このオブジェクトはコマンドをメッセージの形で受け取り、それを実行します。

下図はその典型例です。

bangボタンを押すと[openpanel]がファイル選択ダイアログを開き、ファイルが選択されるとメッセージ[read -resize fileName buf ( が作られます(openpanelの結果が$1に代入される)。このメッセージが[soundfiler]に送られると、ファイル「fileName」をテーブル「buf」へロードします。bufには既定のテーブル長がありますが、-resizeを指定すると、bufはロードされるデータサイズに合わせてリサイズされます。

そして、soundfilerの左アウトレットからは、ロードされたデータのサイズ(-resize指定なしの場合は、最大でbufの既定サイズ)が出力されます。右アウトレットはサンプリングレートやチャンネル数などのメタ情報が出力されます。

ではdrumlogueではどうするかですが、データのテーブルへのロードについて[soundfiler]相当の機能を実現するのであれば、hvccでの実装は例えば

【Pd⇒logue SDK】バンク番号とインデックス番号を特定のパラメータにsend
【logue SDK⇒Pd】データサイズを別の特定のパラメータでreceive

というようになりそうです。

データサイズについては、Pure Dataではテーブルの大きさを[expr size(“buf”)]のようにexprオブジェクトで調べることもできます。しかし、必ずしもテーブルの大きさ=PCMデータサイズであるとは限りません。また、hvccは現状exprをまだサポートしていません。(ただ現在、hvccでのexprの実装はかなり進められていて、次のリリースには正式に取り込まれるかもしれません。⇒【2026/1/3追記:v0.15から、[expr]と[expr~]が正式にサポートされました。】)

PCM選択のユーザインタフェース

ユーザにデータを選択させる[openpanel]相当の機能も必要になります。これ自体はdrumlogueのノブで選択できるようにするしかないですが、パラメータの値を文字列で表示するlogue SDKの機能を使えば、番号ではなくデータの名前で選択が可能になります。

ただし、PCMデータ選択専用のノブが必要になりますので、

・1テーブルごとにPCMデータ選択用のノブを1つずつ、常に用意するか
・バンク番号とインデックス番号を独立に入力するかセットにするか
・バンク番号およびインデックス番号の値域やデフォルト値を許容するか

などなど、実装にあたって検討が必要な点があります。

(1)1テーブルごとにPCMデータ選択用のノブを1つ、常に用意するか

必ずしもユーザにPCMデータを選択させるのが良いとは限らなくて、特定のPCMデータを決め打ちで使いたいという場合もありそうです。従って、logue SDK側のパラメータを用意する(ノブで選択可能とする)かどうかは、Pure Dataパッチの内容から決定できるのが望ましいです。

1つの解としては、特定の名称のパラメータ(例えば、テーブル名_indexMenu)がC/C++へexportされている時だけ、PCMデータ選択用のノブを用意するという方法が考えられます。

(2)バンク番号とインデックス番号を独立に入力するかセットにするか

drumlogueではバンクがその音の種別(クローズドハイハット、オープンハイハット、リムショット、クラップ、その他、ユーザ用、拡張用)と結びついています。現状、「その他」バンクが64音色、ユーザ用と拡張用は128音色(いずれもデフォルトでは空)、残りのバンクは16音色となっていて、合計384のサンプルが利用可能です。

drumlogue本体のサンプル再生機能では、バンク番号とインデックス番号を個別に指定することができます。ユーザーインタフェースとしても、バンク番号だけ指定して、そのバンクの中のどのPCMを使うかはユーザに選択させる、というケースは普通にありそうです。もちろん、任意の音を選択させたい/特定の音を決め打ちで使いたい、といったケースもありえます。

一方で、バンクとインデックスという2つのパラメータを消費するのはもったいない、という考え方もあります。384個程度であれば、全サンプルを通番で管理することも出来なくはなさそうです。

(3)バンク番号およびインデックス番号の値域やデフォルト値を許容するか

これは、あって困ることは無さそうです。特に、「その他(MISC)」のバンクの中にはジャンルの異なる音が入っていますので、用途によっては範囲を絞りたいということはありそうです。

若干残念なのは、drumlogueではノブの最大値・最小値はビルド時に決まってしまうことです。PCMデータの場合、利用できるデータの個数はバンクごとに異なるのですが、それに合わせてノブの最大値を変化させることはできないわけです。

バンクとインデックス番号を無視して、全データを通番で考えるという方法もありますが、これだと作成したプログラムを保存する場合もパラメータとしては通番で保存されてしまいます。すると、自分が使っているよりも前のバンクでPCMデータが追加/削除されると、通番とデータの対応が崩れてしまいます。

様々な使い方に対応できるベストな解というのは難しく、ある程度は妥協した仕様にせざるを得無さそうです。

現時点の仕様と実装

最終的な仕様はまだ流動的ですが、現時点では以下のような仕様にしています。

・PCMデータを受け取るテーブル

末尾が”_s”で終了し、サイズの後にパラメータとして”@hv_table”が付いたテーブル宣言は、PCMデータ用として扱われます。また、テーブル変数名の”_s”より前の部分を、以降で「テーブル名」と呼びます。テーブルの中身は、後述のバンク番号およびインデックス番号の少なくとも一方が変更された場合、更新されます。

・PCMデータを受け取った後のテーブルのサイズ

テーブル名の後に”_size”を付けたパラメータに、テーブルが更新されたときにそのサイズ(サンプル数)がsendされます。min, max, default等のオプションは無効です。

・バンク番号とインデックス番号の指定

テーブル名に”_set”を付けたパラメータを、C/C++側にテーブルに関する情報を送るために利用します。送る情報はメッセージで、”属性 値”の形で作成します。バンク番号およびインデックス番号を指定するために、属性”bank”と”index”が利用できます。1つのメッセージで複数の属性を送る場合は、[bank 4, index 3( というように属性と値のペアを”,”で区切ってつなげます。

このメッセージを@hv_param付きでsendすることで、テーブルに特定のPCMデータを指定することができます。bankとindexのデフォルト値はいずれもゼロです。なお、bankやindexについて、現在の値と同じ値を指定するメッセージが送られた場合は、変更があったとはみなされず、テーブルのデータは更新されません。

・ノブでPCMデータを選択させる

テーブル名に”_indexMenu”を付けたパラメータ名は、ユーザがノブでPCMデータを選択した結果を受信します。結果は選択されたデータのindex番号です。

Pure Dataパッチ内にこの受信パラメータが存在する([r tablename_indexMenu @hv_param]が存在する)場合、PCMデータ選択用にdrumlogueのノブが1つ使われます。

indexMenuはPCMデータ選択に特化したノブを作成しますが、PCMデータそのものは変更しません。実際に選択されたPCMデータでテーブルを更新するためには、indexMenuで受信した値を[index 値”( というメッセージにして、上述のsetパラメータにsendする必要があります。

・Pure Dataパッチの例

下図は、bankとindexを指定するパッチの例です。黄色くしてある部分がPCM関連の設定を行っています。

中央上部で”sample_s”というテーブルを宣言しています。@hv_tableがパラメータにあるので、このテーブルはhvccコンテキストへexportされ、C/C++側から参照可能になります。drumlogue用のexternal generatorでは、”sample”がテーブル名として認識され、以下のパラメータが利用可能となります。

sample_size : テーブル”sample”が保持するPCMデータのサンプル数(テーブル自身のサイズはこの値+2になります)を受信します。
sample_set : テーブル”sample”にロードしたいPCMデータのバンク番号やインデックス番号をメッセージ形式で設定(送信)します。
sample_indexMenu : 特定のバンク番号のデータの中からテーブル”sample”にコピーするPCMデータをノブで選択します。

パッチの左側はPCMを再生する処理です。noteinからNOTE ONが送られたとき、またはsample_sizeが更新されたとき、テーブルの先頭(0)からsample_size番目までのデータを再生します。(noteinのアウトレットは、左がノート番号、中央がベロシティ、右がMIDIチャネル番号です。NOTE OFFはベロシティがゼロになります。)

次のパッチは、ノブでPCMデータを選択できるようにした例です。

このパッチでは、通常のパラメータであるSPBankで、バンク番号を(ノブで)選択することができます。また、sample_indexMenuが使用されていますので、PCM選択用のノブが作成されます。このパッチでは、デフォルトはbankが4でindexが51ということになります。

ユーザがノブを回すと、都度その番号がsample_indexMenuへ送られます。これを”index 番号”というメッセージにして、sample_setへ送信すると、それはC/C++側のsendHook()関数で受信されます。

数の表示方法

Pure Dataでは数値は基本的には浮動小数点数で、値の範囲も「0から10,000」ような大きな範囲になることもあります。drumlogueでは負数や小数点付きの数も表示できますが、浮動小数点数や大きな整数のについてはやや不便な点もあります。

例えば、小数点以下の部分の桁数の指定ができません。また、1より小さい単位で値を変化させることは可能ですが、値の変化のステップの指定もやや限定されていて、例えば「0.00から1.00まで0.01刻み」というような表示ができません。

別の問題点として、drumlogueのエンコーダ式のノブ固有の問題があります。エンコーダの1クリックについてパラメータは1ずつ変化するので、例えばパラメータのminとmaxが0と1000の場合、minからmaxに変化させるにはエンコーダを1000クリック分も回さなくてはなりません。

また、ノブの角度がパラメータの具体的な値と結びつかないので、「最大値にする」「最小値にする」「中央値にする」といった操作が直観的には行えないのも不便です。

こういった不満があったので、数の表示についてはdrumlogue内蔵の表示方法ではなく、専用の表示用関数を作成しました。これはパラメータ名の末尾が”_f”の場合(浮動小数点数)のみ有効で、整数のパラメータについてはdrumlogueのデフォルトの表示方法を使います。

この専用の表示用関数では、まずパラメータの最小値と最大値の区間を100等分して、ノブは100クリックで最小値と最大値の間を行き来できるようにします。つまり、最小値と最大値が0.0と1.0であれば、パラメータは0.01ずつ変化しますし、最小値が0で最大値が1000であれば、パラメータは10ずつ変化するというわけです。これによって、パラメータの値域の広い・狭いに関わらず、最小値から最大値まで同じクリック数で行き来できます。

なぜ最小値から最大値まで100ステップにしたかというと、これを128ステップ以下にしておくほうが、値域が7bitであるMIDIコントロールチェンジとの相性が良さそうだからです。128ステップのような半端な数にすると、画面上に表示される値が無駄に細かくなるので、きりの良い100ステップにしています。

パラメータの表示形式の指定

drumlogueでは、パラメータの表示において、値の後に「Hz」「msec」などの単位をつけることができます。この機能を活用するため、Pure Data側から単位を指定できるようにしました。

hvccでは、@hv_paramの後にmin/max/defaultに続く4番目のオプションとして、以下のように任意の文字列を追加することができます。
[r Gain @hv_param -80 0 -12 db]
この文字列の扱いは個々のgeneratorに依存しますが、一例としては”int”、”float”、”bool”のようにパラメータの型を指定するために使われています。

これまでlogue SDK用のexternal generatorでは、このオプションは利用していませんでしたが、drumlogueではパラメータの表示形式の指定子として取り入れて、logue SDKの表示機能を活用してみました。

現時点、利用できるtypeと画面上に表示される単位は以下の通りです。

percent : “%”
db : “db”
cents : “C”
hertz : “Hz”
khertz : “KHz”
msec : “msec”
sec : “s”

receiveオブジェクトの@hv_paramの後、min/max/defaultの次の4番目に上記の単位を指定することで、ディスプレイにパラメータの値が単位付きで表示されます。

今後の検討予定

ここまででもそこそこ動作するようなものは作れるようになっているのですが、サイズの制約が無くなったこともあり、いろいろ追加したい機能があります。実際にどこまでやるかは未定ですが、現在考えているものとして、以下のようなものがあります。

・4点補間機能を持つPure Dataオブジェクト([tabosc4~]、[tabread4~]など)への対応

Pure Dataでは、サンプルを補完する機能を持つオブジェクトがあります。これらはいずれも4サンプルを使って補間を行います。そのため、N個のサンプルを生成するためには、補間のために先頭のサンプルの前に1サンプル、末尾のサンプルの後に2個のサンプルが必要になります。

処理としては末尾のサンプルを先頭に、先頭の2サンプルを末尾に付け足すだけですが、これをやるかやらないかは”_set”変数を通じてPure Data側から明示的に指定できるようにするのが良いのかなと思っています。

・ステレオ対応

同様にステレオ対応についても、”_set”変数を通じてPure Data側から指定できるようにしようと思っています。選択肢としては
①常にモノラル
②常にステレオ、インターリーブ
③常にステレオ、ノンインタリーブ
④データをそのままコピーしてくる
くらいかと思いますが、④については別途、C/C++側からPure Data側にデータ形式を通知する機能が必要になります。まあここまでやる必要はないかもしれません。

・bank選択用変数

ノブによるindex選択については、サンプル名がスクリーンに表示されて、番号ではなく名前でサンプルを選べるように特別な実装を追加しています。
同様に、bankの選択についても、メニューを表示するための変数と実装を用意すれば、ジャンル名でバンクを選べるようにできます。

・メタファイルのサポート

Pure Dataでは記述できないがlogue SDKユニットでは必要になる情報は、別の方法で提供する必要があります。たとえばユニットの名前は、今はパッチのファイル名から作っています。他にも、デベロッパID、ユニットID、ユニットバージョン番号などの情報が必要ですが、現状、これらはデフォルト値をconfig.mkに書いてあります。config.mkを編集するか、make時にオプションを与えることで変更可能ですが、メタファイルを使って明示的に記述できるようにしたいですね。

・NTS-1系のWave tableへ拡張

PCMデータがlogue SDKから使えるのはdrumlogueだけですが、旧logue SDKおよびNTS-1 mkIIでは、PCMデータではなくウエーブテーブルが提供されています。これもPCMデータと同様にバンクとインデックスで参照するようになっており、テーブルサイズが小さい(128サンプル程度)という以外には基本的には同じものと言えます。ですので、このウエーブテーブルをPure Dataから[tabosc4~]で鳴らせないかと思っています。

・Webアプリのマルチファイル対応

これはhvccではなく、Webからhvccを利用できるようにするオンラインコンパイラのWebアプリに関する課題です。現在開発済みのWebアプリは単一のファイルしかアップロードできません。しかし、複雑なPure Dataパッチを開発するには、abstractionが使えるほうが望ましいです。abstractionを使うパッチは、複数ファイルから構成されます。このようなパッチに対応したオンラインコンパイラを作るには、Webアプリを複数ファイルがアップロードできるように修正する必要があります。hvcc自体はabstractionに対応していますが、メインのpdファイルを指定する必要がありますので、Webアプリで複数ファイル対応した場合には、どのファイルがメインファイルなのかを指定する方法は必要になりそうです。

・コードの整理

これは全く別の話ですが、これまで各デバイスの各種ユニット用にコードをつぎ足し付け足しで開発してきた関係で、コードがかなりぐちゃぐちゃになっています。開発の初期に内容が整理できない状態でコードが複数ファイルに分散してしまうと、修正も複数ファイルにまたがることになってかえって面倒なので、コード全体の整理は先延ばしにしていたのですが、そろそろ手をつけてもいいかなと思っています。

というわけで1万字以上の長い記事になってしまいましたが、いったん中間報告(?)でした。願わくば年末年始に、前に進める時間が取れると良いのですが・・・。

コメント