連載2回目の本エントリーでは、オーディオプラグインの「拡張機能」としてどのようなものが存在するのか、CLAPとLV2を例としていくつか見ていきましょう。各拡張機能の詳しい内容には今回は踏み込みません(以降の回に期待して下さい)。
まずはCLAPにどのような拡張機能が定義されているのか、一覧を画像で示します。
画像にしたのは、他のプラグインフォーマットの拡張機能リストと簡単に比較してみるためです。次の画像はLV2の拡張機能の一覧です。
似たような名前の機能がいくつか並んでいるのが見て取れます。
CLAP拡張機能へのアクセス
CLAPでは、拡張機能がクリーンに整備されています。CLAPの拡張機能にはプラグイン拡張とホスト拡張がありますが、全てのプラグイン拡張機能がclap_plugin_*_t
として定義されており、同様に全てのホスト拡張機能がclap_host_*_t
として定義されています。プラグイン拡張のAPIは「プラグインが実装し、ホストが呼び出す」ものであり、ホスト拡張の機能は「ホストが実装し、プラグインが呼び出すもの」と理解しておけばOKです。
拡張の種類 | 実装者 | 利用者 |
---|---|---|
プラグイン拡張 | プラグイン | ホスト |
ホスト拡張 | ホスト | プラグイン |
プラグインが実装する拡張は、clap_plugin_factory_t
のcreate_plugin()
で返されるclap_plugin_t
のget_extension()
メンバー(関数ポインター)の実装となる関数で、IDに対応するものを返すかたちで実装します。たとえばプラグインでstateを保存する機能(パラメーターが存在するプラグインではほぼ実装することになるでしょう)を実装する場合はこうです。
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; }
プラグインがホスト拡張を呼び出す方法については少し追加説明が必要でしょう。これに関連してはまず、拡張ではありませんが、CLAPのplugin factoryとなるclap_plugin_factory_t
のcreate()
の定義について説明します。
const clap_plugin_t *(*create_plugin)(const struct clap_plugin_factory *factory, const clap_host_t *host, const char *plugin_id);
この2番目の引数はclap_host_t*
という型になるのですが、これはホスト実装のポインターである必要はなく、プラグインが必要とするホスト機能を提供する実装でさえあれば十分です。clap_host_t
には次のようなメンバーがあります(コメントを削っています):
typedef struct clap_host { clap_version_t clap_version; void *host_data; const char *name; const char *vendor; const char *url; const char *version; const void *(*get_extension)(const struct clap_host *host, const char *extension_id); void (*request_restart)(const struct clap_host *host); void (*request_process)(const struct clap_host *host); void (*request_callback)(const struct clap_host *host); } clap_host_t;
ホストのメタ情報のほか、ホスト拡張機能を取得するためのget_extension()
、ホストのmain (UI)スレッドで処理を呼び出すためのrequest_callback()
、オーディオ処理を開始させるためのrequest_process()
やrequest_restart()
などが定義されています。ホスト拡張機能はこのget_extension()
で取得して使います。
一方で、筆者はCLAPが必須拡張と任意拡張を区別しないのは問題だと考えています。特定の拡張機能がプラグインでサポートされていない時に、それでも正常にオーディオ処理を続行できるのか、それとも無効化すべきなのかは、区別できるべき情報です。
参考: LV2の拡張機能アクセス
LV2の拡張機能は他のプラグイン機構と比べると特殊です。RDFメタデータによる定義だけでほぼ全てが完結していてCヘッダーでは定数のURL定義しか含まれていないような拡張機能が少なからずあります。それらの拡張機能は、RDFのクエリだけでプロパティを取得したり設定できたりするように作られているため、追加のAPI定義を必要としないのです。
一方でRDFの操作によって機能を実現する仕組みは弱い型付けに基づいているため、すぐ間違ったコードを書いてしまいがちという問題があります。メタデータはコンパイル時に検証できないため、メタデータを変更してコードを書き換えないことで問題がすぐ生じます。
前回のエントリでも書きましたが、LV2のRDFメタデータ中心の拡張機能設計は、動的に変わりうるメタデータ項目の扱いをややこしくするという副作用がありました。先にスクリーンショットで列挙したLV2拡張機能のうち、Dynamic Manifest、Morph、Resize Portの一部仕様は、LV2のメタデータが静的であることから生じる問題を解決する(すなわち、動的なメタデータ定義を可能にする)ために定義された拡張機能です。