CLAP 1.0公開を機にオーディオプラグイン規格の何たるかを知る (2)オーディオプラグインの拡張機能の種類

連載2回目の本エントリーでは、オーディオプラグインの「拡張機能」としてどのようなものが存在するのか、CLAPとLV2を例としていくつか見ていきましょう。各拡張機能の詳しい内容には今回は踏み込みません(以降の回に期待して下さい)。

まずはCLAPにどのような拡張機能が定義されているのか、一覧を画像で示します。

CLAP extensions

画像にしたのは、他のプラグインフォーマットの拡張機能リストと簡単に比較してみるためです。次の画像はLV2の拡張機能の一覧です。

LV2 features 似たような名前の機能がいくつか並んでいるのが見て取れます。

CLAP拡張機能へのアクセス

CLAPでは、拡張機能がクリーンに整備されています。CLAPの拡張機能にはプラグイン拡張とホスト拡張がありますが、全てのプラグイン拡張機能clap_plugin_*_tとして定義されており、同様に全てのホスト拡張機能clap_host_*_tとして定義されています。プラグイン拡張のAPIは「プラグインが実装し、ホストが呼び出す」ものであり、ホスト拡張の機能は「ホストが実装し、プラグインが呼び出すもの」と理解しておけばOKです。

拡張の種類 実装者 利用者
プラグイン拡張 プラグイン ホスト
ホスト拡張 ホスト プラグイン

プラグインが実装する拡張は、clap_plugin_factory_tcreate_plugin()で返されるclap_plugin_tget_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_tcreate()の定義について説明します。

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のメタデータが静的であることから生じる問題を解決する(すなわち、動的なメタデータ定義を可能にする)ために定義された拡張機能です。