$30 off During Our Annual Pro Sale. View Details »

audio plugin format study meetup 2022.7.6 (JP)

audio plugin format study meetup 2022.7.6 (JP)

オーディオプラグイン規格勉強会 (2022.7)
https://music-tech.connpass.com/event/252007/

Atsushi Eno

July 05, 2022
Tweet

More Decks by Atsushi Eno

Other Decks in Technology

Transcript

  1. オーディオプラグイン勉強会#1
    2022.7.6

    View Slide

  2. この勉強会の目的
    オーディオプラグインフォーマットの仕様・機能を勉強することで、
    - プラグイン開発やホスト開発のレベルを高められるようになる
    - オーディオプラグインの使い方を詳しく知ることができる

    View Slide

  3. 今回のお題
    CLAP 1.0仕様が登場したので、これを契機にプラグイン規格の機能を学ぶ
    ※「CLAP出羽〜」みたいな話はしますが各種規格の話をしましょう
    …!

    View Slide

  4. オーディオプラグイン規格の意義
    複数の{ホスト=DAW, プラグイン}で使える
    ※独自プラグインは特定DAW専用: FL native plugins, Reaper jsfx, Avid AAX...
    本質的には[音声とコントロール]の[入力から出力]を生成できれば何でもよい

    View Slide

  5. CLAPには何の意味がある?
    CLAPはVSTやAUやLV2の代わりに利用できるオーディオプラグイン規格
    Win/Mac/Linuxで使える
    商用利用の制限がごく小さいMITライセンス
    コミュニティで仕様が議論・決定できる(?)  ※現状Bitwig社員しかいなそう
    (Bitwig専用ならDAW専用のプラグインフォーマットとあまり変わらない…)

    View Slide

  6. CLAP 1.0
    6月にバージョン1.0としてu-heとBitwigが大々的に宣伝 (KVR / HN)
    この時点では商用製品はu-he製プラグインのみ、DAWはBitwig Studioのみ
    OSSプラグインはsurge synthesizerとclap-juce-extensionsを中心にいくつか
    「仕様書」が不在 - Cヘッダファイルのコメントがほぼ全て
     開発チームは「仕様は明確でわかりやすい」と主張
    利用できる環境が著しく限定されるという意味では実用性は未来形
    開発コミュニティは比較的活発

    View Slide

  7. clapdb

    View Slide

  8. CLAPの開発環境
    配布形態: 共有ライブラリ(拡張子 *.clap → dll/dylib/soに移行?)
    言語: C, C++, Rust? (clap-sys)
    plain SDK: clap, clap-helpers
    各種ライブラリで: clap-juce-extensions, iPlug2 (clap branch), Avendish, ...
    IDE等はvs/vscode/xcode/CLionなど何でも(一般的なプラグイン開発と同様)

    View Slide

  9. CLAPプラグイン開発のアプローチ
    1) JUCE, iPlug2など既存のプラグイン開発ライブラリを使う
    →モジュール追加等だけで既存コードを使えそう
    2) DSPは汎用的に作って、CLAP APIに自前で結びつける(多分割と簡単)
    clap-helpersを使うのもよし(ただそんなに多機能ではない)
    3) 最初からCLAP用に作る…?(メリットがない? / 完全にCLAPに乗っかるならこれ)
    clap-juce-extensionsではCLAPのAPIをjuce::AudioProcessorから直接呼び出せる
    (surgeがこれを利用している)

    View Slide

  10. 汎用的なDSPコード?
    (CLAPなら)init() → [ activate() → process() → deactivate() ] → destroy() の流れ
    process() でオーディオ・イベント入力を受け取って同出力を生成
     - オーディオバッファ(float*,double*,void* x channels, per ports,buses,buffers)
     - タイムスタンプ付きイベントストリーム(MIDIイベントなど)
    MATLAB, FAUST, SOULなどDSP開発に向いた言語もある(どれもCLAPには未対応)

    View Slide

  11. DAWでCLAPプラグインをロードする仕組み
    【プラグインの探索】
    CLAP_PATH以下でプラグイン
    (*.clapファイル)を検索
    (環境変数 or 既定パス)
    factoryからリスト取得可能→
    【プラグインのロード】
    auto plugin = dlopen("path/to/myplugin.clap", ...);
    auto entry = (clap_plugin_entry_t*)
    dlsym(plugin, "clap_entry");
    entry->init("/path/to/myplugin.clap");
    auto factory = entry->get_factory(
    CLAP_PLUGIN_FACTORY_ID);
    clap_plugin_descriptor_t *desc =
    factory->get_plugin_descriptor(factory, index);
    const clap_plugin_t *plugin =
    factory->create_plugin(factory, host, "pluginID");
    // hostは後で説明

    View Slide

  12. プラグインのリスト
    プラグインからメタデータを取得する
    (製品名、ベンダー、カテゴリ etc.)
    allowlist/denylistの管理にも使われる
    VST3, AU - プラグインの生成が必要
     → 重い
    LV2 - テキストで記述
     → 軽い / コードと不一致の可能性
    CLAP - プラグインDLLのロードが必要
     →プラグイン生成しないだけ軽い

    View Slide

  13. DAWの操作とプラグインAPI
    (説明していない機能もいくつか)
    ● プラグインをトラックに追加  create → activate → process
    ● プラグインをトラックから削除 (deactivate)? → destroy
    ● 有効・無効切り替え      activate/deactivate -or- process bypassed
    ● 再生             process (with sorted events)
    ● MIDIノートオン・オフ     send event -or- process
    ● プラグインUIの表示・非表示  show GUI, hide GUI
    ● パラメーターの変更      set parameter(s), (save state)?
    ● プログラム/プリセットの選択 load preset, (save preset/state)?
    → 追加機能は拡張 (extensions)として仕様で規定される

    View Slide

  14. オーディオプラグインの拡張機能(そもそも論)
    プラグイン規格の最重要課題は相互運用性の確保 = APIを破壊的に変更しない
    でも技術仕様に追加機能の要求は必ず発生する
     オーディオバス(サラウンド/3Dオーディオ対応)、MPE、MIDI 2.0、...
    一般的な機能でもプラグイン機能本体から切り離して追加定義することもある
     パラメーター定義/操作、GUI、状態(state)の読み書き、プリセット、...
    追加定義の破壊的変更はダメージが少ない
    VST3 MA (module architecture), LV2 extensions, CLAP extensions, ...

    View Slide

  15. CLAPの拡張機能

    View Slide

  16. CLAPプラグインが拡張機能を実装する例
    clap_plugin_state_t state_ext{
    my_plugin_state_save,
    my_plugin_state_load};
    void* my_plugin_get_extension(
    const clap_plugin_t* plugin,
    const char* id) {
    if (!strcmp(id, CLAP_EXT_STATE))
    return &state_ext;
    ...
    return nullptr;
    }
    bool my_plugin_state_save(
    const clap_plugin_t *plugin,
    const clap_ostream_t *stream) {
    ... // save to `stream`
    return true;
    }
    bool my_plugin_state_load(
    const clap_plugin_t *plugin,
    const clap_ostream_t *stream) {
    ... // load from `stream`
    return true;
    }

    View Slide

  17. process (1) オーディオバッファの処理
    VST3 ProcessData, AU AudioBufferList, LV2 void**, CLAP clap_process_t
     CLAPのオーディオ関連拡張: audio-ports-config, ambisonic, surround, cv
     LV2だけ事前にポインタをconnect_port()で用意しておく仕組み
     ※設計としては失敗… LV2: the good, bad, and ugly
     floatだけでなくdoubleを使えることもある
    一般にステレオ以外は事前にDAWとの間でオーディオ バス/ポートの調整が必要
    mainポート(mono/stereo/5.1/7.1/surround)とサイドチェイン / CVの違いも
    ⚠DRAFT ⚠DRAFT ⚠DRAFT

    View Slide

  18. process (2) MIDIイベントのサポート方式
    VST2: MIDIメッセージをプラグインが直接受け取って処理可能
    VST3: ホストがVSTイベントに変換したものを受け取る
    LV2: MIDIメッセージはAtom_Sequenceに格納されて渡される(直接処理可能)
    CLAP: MIDI 1/2, CLAPイベントの3種類に標準で対応 = 直接処理可能
     ※プラグインは「対応する種類」のイベントは全て実装しなければならない

    View Slide

  19. DAWはMIDI入力イベントをどうやってプラグインに渡す?
    プラグインによっては、MIDIから独自イベントに変換する(VST3)
    コントロールチェンジはmidi-mappingによって変換されうる(DAW, CLAP拡張)
    アクティブな状態と非アクティブな状態で流れが違う
    ● process()がリアルタイムで回っている時: オーディオ処理はタイトに回っているの
    で、非リアルタイム処理を差し込む余地はない
    → 入力イベントはタイムスタンプを付けてキューに溜め込んでおく
    → process()が呼び出されたときに楽曲中のイベントとマージする
    ● process()が回っていない時: そのままプラグインにイベントを送る

    View Slide

  20. パラメーターのサポート
    一般的にはfloat型(32bit)で0.0-1.0 / MIDI 2.0のCC等はuint32_t相当の整数
    パラメーターの取得・設定方法
     VST, CLAP, AU: イベント
     LV2: パラメーター毎のポート or 1つのイベントポートにLV2 Patchを流す
     ※前者はパラメーターが数百件になりうる現代では非現実的
     LV2 PatchはMIDI 2.0 Property Exchangeと似ている("GET", "SET" etc.)

    View Slide

  21. GUIのサポート
    設計がむずかしい
    - 一般的にはクロスプラットフォームGUIが前提 ※AudioUnit...
    - 完全なGUIフレームワークのAPIを規定するのは無謀
    - 開発ライブラリやSDKによってGUIの設計は変わる(言語/ランタイムも)
    →プラグインが用意したGUIをホストに統合できる仕組みだけ用意する
    どうやって?
    - VST3, LV2: プラグインがViewを返し、ホストのWindowにアタッチ
    - CLAP: ホストが親Windowをset_parent()でプラグインに渡す
    - Win32 (HWND) / Cocoa (NSView) / X11 (Window) の個別APIが定義される

    View Slide

  22. GUIのサポート
    GUIサポートAPIの具体的な内容
    - show/hide, get size / resize (is-fixed?), get/set scale factor, set window title ...
    - request show/hide, notify resize, notify closed, ...
    - APIによってはウィンドウの移動通知なども
    UIとロジックの分離
    - LV2はdll / dylib / soのレベルで分離(自分でDSPを参照することはまあ可能)
    - CLAPはそこまで分離しない

    View Slide

  23. 地味な拡張機能
    log - ログを記録する
     ※ ホストで全プラグインのログを統一的に出力できる
     ※ リアルタイムスレッドからは直接書き出せない
    state - ユーザーが調整したパラメーター状態をホストで保存・復元する
     ※ 内容(semantics)がわかるようにする? しない? (yes: LV2, no: CLAP)
    presets - パラメーター等のベンダー定義/ユーザー定義のプリセット
     ※ stateと機能的には重複する部分が多い
     ※ *.vstpresetファイル

    View Slide

  24. CLAPの一般的な拡張機能
    tail length
    ノートオフした後、完全に消音するまでの時間をホストが取得できる
    report latency
    プラグインにどれくらい遅延が生じるのかホストが取得できる

    View Slide

  25. CLAPの「拡張として有るのは珍しい」標準的な機能
    tunings
     微分音(microtonal)を実現するための拡張
     MTS (MIDI Tuning Standards) を受信できるプラグインなら拡張機能は不要
     プラグインも独自にscala/kbmサポートを実装でき、ホストとのやり取りは不要
     でもCLAPでCLAP eventsをサポートするならこれを使うしか無い
    check-for-update
     プラグインの更新版があれば教える(たぶん珍しい…VST/AUにある?)
    render
    オフライン(非リアルタイム)レンダリングモードを設定できる
    ⚠DRAFT
    ⚠DRAFT

    View Slide

  26. CLAPのユニークな機能
    パラメーターモジュレーションの仕様 ("non-destructive automation")
    ホストへのノート終了通知? (clap_event_note_end)
    host managed voices (voice-info draft)
    ホストによる thread_pool という名前の並列処理サポート

    View Slide

  27. CLAPのパラメーター関連イベント
    CLAP_EVENT_NOTE_EXPRESSION : per-note expression(対象はenum)
    CLAP_EVENT_PARAM_VALUE: パラメーターを設定(ノート別も可能)
    CLAP_EVENT_PARAM_MOD: モジュレーション
    オートメーションが終わったらパラメーターを元に戻せる ("non-destructive") ※ how?
    CLAP_EVENT_PARAM_GESTURE_BEGIN, ~_END: ジェスチャー操作 {開始/終了}
    ジェスチャー: begin〜endの間はユーザーがツマミを回しているので、それを前提に
    オートメーションの記録や再生を行える  ※プラグインが?

    View Slide

  28. ノートオン、ノートオフ、その他!?
    CLAPのノート命令はいろいろ追加機能が付いている
    Host >>> note on >>> Plugin
    Host >>> note off >>> Plugin
    Host <<< note end <<< Plugin (リリース処理完了)
    NOTE_CHOKE: ノートオフに伴うリリースを無視して遮音するためのもの:
     MIDIのAll notes offに相当する機能の実現
     closed hi-hatとopen hi-hatなど相互に排他的な楽器を演奏する命令
     ※DAWでの入力をどうやって実装するのかは不透明

    View Slide

  29. voice-info: 発音数管理
    プラグインから同時発音数・最大発音数を
    取得できる
    ※CLAPは「ホストからボイスを管理したい」
    という指向が強い。Bitwig-ism?
    ⚠DRAFT
    typedef struct
    clap_voice_info {
    uint32_t voice_count;
    uint32_t voice_capacity;
    uint64_t flags;
    } clap_voice_info_t;
    enum {
    CLAP_VOICE_INFO_SUPPORTS_OVER
    LAPPING_NOTES = 1 << 0,
    };
    // It is useful for the host when performing polyphonic modulations,
    // because the host needs its own voice management and should try to follow
    // what the plugin is doing:
    // - make the host's voice pool coherent with what the plugin has
    // - turn the host's voice management to mono when the plugin is mono

    View Slide

  30. thread_pool
    CLAPのthread_poolはスレッドプールではない(断言)
     マルチコアCPUを活用した処理の並列化が目的
      タスクもリアルタイムで完了することが前提 synth voiceの計算など
     単一プラグインの並列処理最適化をホストで実装する仕様はかしこい
     (トラックごとに1つのCPUコアでも十分という気もしなくもない …)
    LV2のWorkerはスレッドプール
     タスク(のハンドル)をプラグインとホストでやり取りするAPI
     ファイルI/Oなどで使う
     CLAPにはこの機能が無い(プラグインでスレッドプールを作れなくはない)

    View Slide

  31. プラグインフォーマットの設計に関する若干の私見
    プラグインAPIで何より重要なのは破壊的変更が無いこと
    その次にAPIに曖昧さ(解釈の幅)がないことが重要
    プラグインAPIがクリーンであることの価値は高いが必須ではない
     ※開発用ライブラリで吸収できる
    高度な機能は「必須でない」「使わなくても処理結果が変わらない」ことが重要
    特定の特定のDAWに特化した機能は汎用規格では規定しない
     ※代替性があることがユーザーにとっては重要

    View Slide