JUCE Advent Calendar 2022、2日目のエントリーです。
2021年の半ば頃からJUCE開発者がサポート計画を公言して以来、コミュニティで話題になっていたLV2プラグインフォーマットのサポートが、ついにJUCE 7.0のリリースで実現しました。
LV2プラグインなんて知らなかったという人向けに軽く説明すると、主にLinuxデスクトップ環境でデファクトスタンダードとして使われているオーディオプラグインフォーマットです。近年ではVST3もクロスプラットフォームで使えるようになって、Linux上でのシェアはLV2と同程度かそれ以上に大きくなりつつありますが、以前はVSTがクロスプラットフォームではなかったため、「WindowsではVST、MacではAU、LinuxではLV2」のような状況がありました。JUCEがLinuxでVST3をサポートするようになったのも2020年にリリースされたJUCE 6.0からです。
LV2もクロスプラットフォームの仕組みであり、WindowsやMacでも利用可能ですが、現実的にLV2をサポートするホストはLinuxを中心に成長してきたDAWがほとんどであり、Linux環境以外でのバイナリパッケージの配布例も多くはないでしょう。
LV2プラグインについては過去に「LV2オーディオプラグイン開発ガイド」という、世界でも他に類を見ない同人誌を書いたので、興味のある方はそちらをどうぞ(宣伝 (まあ英語では開発者直筆のlv2 bookが充実しているので不要ともいえます。)
従来のLV2サポート: コミュニティプロジェクト
JUCEはマルチプラグインフォーマットのフレームワークであり、そのエクスポーターとしてLV2を追加することは決して不自然でも不可能ではなく、実際DISTRHO/JUCEというforkではLV2プラグインのビルドが追加されていました。JUCEからLV2版プラグインを作るときはほぼこれが使われていたと思います(他にもいくつかLV2サポートのforkがありましたが、とりあえずこのforkに収束していたと考えてよいでしょう)。
一方で、JUCEはプラグインを作るだけのフレームワークではなく、プラグインホスト(DAWなど)をビルドできるフレームワークでもあります。DISTRHO/JUCEはこの実装を持っておらず、lvtk/jlv2というプロジェクトがLV2ホスティングをサポートしていました(こっちはそんなに需要が無かったのか、contributrorも自分しかいませんでした)。
JUCE7.0でのLV2サポートの追加によって、これらのプロジェクトは今後は不要になるはず…というわけにはおそらくいかず、少なくともDISTRHO/JUCEは残り続けるでしょう。オーディオプラグインのパラメーターなどは、打ち込まれた楽曲の内容に影響を与えないために、後方互換性を維持しなければなりません。DISTRHO/JUCEとJUCE7のLV2サポートで生成されるプラグインメタデータに相違があれば、後方互換性が損なわれる可能性があります。実際に内容の異なるメタデータが生成されるものなのか(= DISTRHOが後述するPatchの方式もサポートしていたりしないか)は筆者は把握していませんが、JUCE7にDISTRHOとの後方互換性を維持する動機は全く無いので、期待値はさほど高くありません。
一方でホスティングのほうは、JUCEでは(フレームワークとしての制約が無い限り)原則としてあらゆるLV2プラグインをロードできる必要があり(VST3やAUと同じことがいえます)、またホスト側には後方互換性を維持しなければならない理由は特に無いので、lvtk/jlv2はその役割を終えたといえますし、実際プロジェクトもarchivedとなっています。
LV2プラグインをビルドする
既存のオーディオプラグインプロジェクトにLV2ビルドを追加するには、CMakeのjuce_add_plugin()
のFORMATS
オプションにLV2
を加えるだけです。one liner changeです追記: もうひとつLV2URI "..."
というプロパティを追加する必要があるのでtwo liner changeでした。とはいっても、JUCE 7.0以降を使う必要があるので、APIの破壊的変更には追従しないといけないでしょう。
もし今でもProjucerを使っているプロジェクトであっても、LV2サポートは追加されています。他のプラグインフォーマットと同様、プロジェクトのconfigオプションで指定できます。
ホスト側のプロジェクトも、他のプラグインフォーマットと同様、juce_audio_processors
のオプションで指定できます。
試しに筆者がjpcima/HeraをLV2対応にしたときのパッチを置いておきます(CLAP対応も数行混ざっているのでクリーンなパッチではないです): https://gist.github.com/atsushieno/36b8963db9ba93b6e00a27008ae476ee
エクスポートされたLV2プラグインの見どころ
JUCE 7.0のLV2プラグインは、筆者の私見でいえば、2022年時点でのJUCEコードベースから構築できるLV2プラグインのベストプラクティスが実現しているといえます。その特徴をいくつか挙げます。LV2の基礎的な理解が必要な話なので、興味がない人は読み飛ばしましょう。
(1) LV2実装を実現するために、LV2のSDKとしてserd, sord, lilvがそのまま使われています。仕様上、LV2プラグインのAPIを実装するために必要なのはlv2/lv2に含まれるLV2ヘッダーファイルのみですが、LV2仕様で特にserd/sordなしでTurtle Syntaxのパーサーを自前実装するのはやや無謀ですし、lilvを使わずにプラグインローダーを実装するメリットもほぼ無いでしょう。
(2) プラグインパラメーターそれぞれにPortを割り当てる伝統的な方法ではなく、パラメーターごとにLV2 Parameterを定義して、パラメーターの更新はAtom Sequence入力ポートにLV2 Patchを送ることで実現し、パラメーターの変更通知もAtom Sequence出力ポートにLV2 Patchを送るように作られています。Atom Sequenceを使うと、sample accurateなパラメーター変更/通知を実現でき、またMIDI入力と順序を維持した指示をプラグインに送信できます。CLAPのイベント入力ポートも同様の仕組みに則っているといえます(参考)。
先のHeraのLV2ビルドから生成されたdsp.ttl
には、以下のようなパラメーター定義が含まれます(抜粋):
plug:VCADepth a lv2:Parameter ; rdfs:label "VCA depth" ; rdfs:range atom:Float ; lv2:default 0.5 ; lv2:minimum 0 ; lv2:maximum 1 . plug:VCAType a lv2:Parameter ; rdfs:label "VCA type" ; rdfs:range atom:Float ; ...
これが後にpatch:writable
およびpatch:readable
として定義されます(数百行にもなります):
patch:writable plug:VCADepth , plug:VCAType , ...
そしてpatch:message
を処理できるAtom Input portが後から定義されます:
a lv2:InputPort , atom:AtomPort ; rsz:minimumSize 10064 ; atom:bufferType atom:Sequence ; atom:supports midi:MidiEvent , patch:Message , time:Position ; lv2:designation lv2:control ; lv2:index 2 ; lv2:symbol "in" ; lv2:name "In" ;
(3) レイテンシーを通知するControl Output Port、有効・無効を制御するControl Input Port、Free Wheelingを入力できるControl Input Portが別途作成され、一般的な共通コントローラーとして利用できます。
ベストプラクティスを実現できているのか疑問が生じる技術的選択として、LV2UIの実体はDSPと同一の共有ライブラリのバイナリになっています。LV2では「DSPとGUIは分離しているべき」とされるので、ベストプラクティスに反する実装といえますが、そもそもGUIが分離していないjuce::AudioProcessor
を使ってプラグインを作っている時点でコードの分離は実現しようがないので、LV2エクスポートの実装には期待できません。LV2ポートのみを経由したGUIとDSPのインタラクションは実現しているので、LV2仕様が想定するバッドプラクティスのパターンには陥っていないとはいえるでしょう。(この辺りの問題は以前に少しCLAPのGUI拡張に関連して書いたことがあります。)
実例
Pianoteqは7まで従来型のLV2サポートを提供していましたが、Pianoteq 8でJUCE 7.0の標準的なLV2サポートに切り替えたと考えられます。Pianoteq 7までのパラメーターはLV2 ControlPortによるものでした。Pianoteq 8はLV2 Patchの方式に変更されています。両者はプラグインとして別々のインスタンスなので(~/.lv2/Pianoteq 7
vs. ~/.lv2/Pianoteq 8
)、互換性を維持する必要がない事例ですが、Pianoteq 7のなまのLV2打ち込みデータをPianoteq 8に流用することはできなくなっているはずなので注意したほうがよいでしょう。とはいっても、大抵のDAW = ホスト側ではどちらも「パラメーター」として扱うでしょうから、ここに違いが生じることはない気もします。
ホスト側は対応できているのか
JUCEでLV2プラグインがビルドできるようになったとして、そのプラグインが正しくホストでロードできるかどうかは別の問題です。本当は別の問題であるべきではないところですが、LV2は仕様上の制約が小さい上にプラグインの数がVSTなどと比べると多くないため、ホストにとって想定外のメタデータをもつプラグインが出現する可能性が高い仕様です。
というわけでホスト側をいくつか実験したいところですが、その前にまずJUCE7のLV2プラグインを用意する必要があります。そんなわけで、上記Pianoteq 8のほか、いくつかLV2ビルドを作ってみました。
- Monique -
CMakeLists.txt
のjuce_add_plugin()
呼び出しの部分でFORMATS
にLV2
を足して、その下の行にでもLV2URI "(適当なURL)"
を追加するとビルドできます - Dexed - Moniqueと同様の修正を加えるとビルド自体はできます(ただし
CMakeLists.txt
はSource
以下のもの)。ただ、少なくともJUCE 7.0.2以前では生成されるdsp.ttl
に「パラメーター名に含まれる.
を正しくエスケープしない」問題があって、パラメーター名から.
を消して回る必要があります。それが出来たら、ロードできるようになるでしょう。
というわけでプラグインが揃ったので、いよいよLV2をサポートしているホストをいくつか試してみました。Reaperはv6.71、QTractorとZrythmは11月末頃のmasterブランチ(commit hashを特定するほどでもないでしょう)、AudioPluginHostはJUCE 7.0.2に含まれているものです。
DAW | Pianoteq 8 | Monique | Dexed |
---|---|---|---|
Zrythm | *1 | |
*2 |
QTractor | *3 | |
*4 |
Reaper | OK | OK | OK |
AudioPluginHost | OK | OK | OK |
- *1 音は出るけど打ち込みを演奏できず https://todo.sr.ht/~alextee/zrythm-bug/1013
- *2 ほぼ問題なく動作する。プラグインUIのウィンドウサイズが意思疎通できていないっぽい
- *3 プラグインUIのダイアログ表示状態管理が甘いのか、頻繁にクラッシュする。クラッシュしなければ音は鳴るし演奏もできる
- *4 音は出るけど(打ち込みプレイバックも可能)、再生速度が2倍くらいになる?
どうも実際に正常に利用できる環境とプラグインの組み合わせはまだ限られるようです。VST3も安定しない時期はこうだったんじゃないかという印象があります。
JUCE7によるLV2サポートの展望
これまでもDISTRHO/JUCEなどでLV2プラグインのビルドは可能だったといえますが、JUCE7でLV2が公式にサポートされたことによって、さらにLV2サポートの可能性が広がることが期待されます。特に期待できるのはTracktion WaveformなどJUCEを利用して構築されたDAWでLV2プラグインがついに使えるようになると見込まれるところです。Helio Workstationや、何ならZenBeatsでも使えるようになるかもしれません(Rolandのやる気次第でしょうか)。
プラグインのバイナリパッケージ配布も、前述したとおり従来はLinuxのみのものが大半でしたが、JUCEからカジュアルにビルドして配布できるのであれば、今後はWindows/Mac用LV2プラグインが公開され、プラットフォームを跨いで利用できるポータビリティの高いプラグインが増えてくるかもしれません。今後のJUCE7/LV2採用事例がたのしみですね。