音楽技術Advent Calendar 2019の11日目のエントリーです。(まあだいぶ穴が開いているのですが、マイペースに埋めていきます。)
What is LV2?
LV2は主にLinux環境で利用できる、クロスプラットフォームのオーディオプラグインの仕様です。
オーディオプラグインとは、主にDAW (digital audio workstation)環境でDTM(desktop music)の作業を行う場面で、楽器やエフェクターとして利用できるオーディオデータの生成・加工ソフトウェアとして使われるものです。オーディオプラグインは、さまざまな「ホスト」となるDAWなどのソフトウェア*1の上で、さまざまなものを繋ぎ合わせて使用することが多いです。よくある使い方としては、サンプラーのプラグインでサンプリング音源データをノート(キー)に合わせて波形を調整して、それをロータリーエンジンやリバーブのプラグインで加工して、ミキサープラグインでゲイン調整してオーディオデータとして出力する、といった流れになります。
オーディオプラグインは、複数のホスト、複数のプラグインベンダーの間で互換性が求められるもので、デファクトスタンダートとなる仕様がいくつか存在しています。有名どころではSteinberg VSTやApple AU (AudioUnit)が挙げられます。オーディオ処理がもともとプラットフォーム固有の実装になりがちだったこともあってか、ここには業界標準となるような仕様が存在しないのが現状です。また、DAW開発社でも、自分たちのDAWでしか使用できない独自のプラグイン形式を規定して公開しているものがあります。Avid Audio eXtensions (AAX)などがその典型です。
本稿で取り上げるLV2とはLADSPA v2の略であり、LADSPAとはLinux Audio Developer's Simple Plugin APIの略です。Linux環境ではVSTやAUのようなデファクトスタンダートのように使われている仕様のオーディオプラグイン機構が動作しなかったため、独自にオーディオプラグイン機構を開発して発展させる必要がありました。それでもっともポピュラーだったと言える仕様がLADSPA (v1)です。
オーディオI/Oそのものはプラットフォーム固有の実装とならざるを得ない部分があるのですが、一旦そこを抽象化すると、実際のオーディオ処理の部分は、実のところプラットフォーム固有の実装になることはあまり無かったため、LADSPA v2は最早L (Linux)固有のものではなく、MacでもWindowsでも動作可能なものになっています。LV2と同様、VSTもクロスプラットフォームでMacでもLinuxでも使用できる存在となっていきました。
LV2は2019年現在、ardourやqtractor、museなど主要なLinux用DAWでサポートされていますが、それ以外の製品、特にWindowsやMacでは未採用の製品が多いです。
LV2プラグインの概要
LV2は(VSTなどと同様)現在進行形で拡張されている仕様です。後方互換性を維持するために、LV2仕様はモジュラーアーキテクチャになっており、コア部分と拡張部分を切り分けてあります。そのため、使用可能な最低バージョンがモジュールによって異なります。これはVST3のMA(モジュールアーキテクチャ)と似ている側面があります。
どのプラグインフォーマットでも、プラグインにはメタ情報が含まれているものですが、LV2の場合はこれをRDFによって提供することになっています。実際には、XMLとしてのRDFではなく、RDFを「シンプルな」テキスト形式で記述するTurtle (Terse RDF Triple Language)と呼ばれる独自記法に変換した.ttl
というファイルに記述することになります。
RDFの記述内容は、プラグインのAPIについても当てはまり、VST3やAUの場合は、単にCやObjective-CのAPIを実装するだけで済むのですが、LV2の場合は、実際にコードとして実行するために必要になる最小限の部分のみをコードとして実装し、残りの部分はこのメタデータとして作成する作業が必要になってきます。すなわち、(1)プラグイン利用者の視点でいえば、そのプラグインにどんなポートやパラメータがあるか、どんな入力を受け付けるかは、RDFの情報だけでも把握できますし、(2)プラグイン作成者の視点でいえば、そのプラグインにどんなポートやパラメータがあるか、どんな入力を受け付けるかは、コードだけで完結せず、メタデータに記載しなければならないことになります。
プラグインでは独自の型を規定して公開することも可能です。MIDIメッセージやプリミティブ型(Atom型)などLV2標準に含まれる型の多くが、このルールに基づいて規定されています。もっとも、一般的なプラグインにおいて、複雑な独自型を公開型として定義する必要は滅多に無いでしょう。
モダンなプラグインフォーマットでは、プラグインが単一のライブラリファイルとして提供されていることはなく、関連ファイルとまとめて1つのフォルダなどにまとめて配置するものが多いです。LV2も同様で、「LV2パス」のいずれかに.lv2
という名前で終わる「プラグインバンドル」のディレクトリ*2に関連ファイルをまとめます。この中に入るのは典型的にはこのようなファイル群です:
- manifest.ttl : LV2プラグインファイル構成を記述したマニフェスト
- foobar.ttl : プラグイン定義の実体
- foobar{.so/.dylib/.dll} : プラグインライブラリ実体
ファイル名は任意に決定できますし、1つのプラグインバンドルに複数のプラグインを含めることもできます(manifest.ttl
に記載する必要があります)。マニフェストの内容は、このようなテキストにまで圧縮できます(Turtle syntaxとはこのようなものです):
<http://example.com/arbitrary-paths/foobar> a lv2:Plugin ; lv2:binary <foobar.so> ; rdfs:seeAlso <foobar.ttl> .
.so
はLinux環境における共有ライブラリなので、実際のプラットフォームに合わせて変わります。
LV2の拡張性
LV2の標準仕様は、バージョン1.0時点で既知であった最小限の機能のみを提供し、それ以降のバージョンで追加された機能については、ホストから渡されない限り、プラグインが利用することはできない仕組みになっています。
拡張機能の中には「URIと一位識別子URID(uint32)のマッピング」「プラグインのステートの保存と復元」「UIの呼び出し」など、かなり本質的なものが含まれているため、LV2をプログラムで扱うのであれば、LV2拡張に対する基本的な理解が不可欠です。
LV2拡張のインスタンスは、LV2_Featureという構造体で表されます。
struct LV2_Feature { const char *URI; void *data; }
LV2拡張はURI
で種別が識別され、それぞれの拡張に応じて必要になるデータがdata
に渡されます。多くの拡張機能はコードによる実装を必要とし、それらは(直接ないし間接的に)関数ポインタとしてホストからクライアントに渡されることになるので、data
が空であることは通常はありません。たとえばURIがLV2_URID_MAP_URI
(http://lv2plug.in/ns/ext/urid#map
)のとき、data
に渡されるべきものはLV2_URID_Map
構造体です。
// typedef void* LV2_URID_Map_Handle // typedef uint32_t LV2_URID struct LV2_URID_Map { LV2_URID_Map_Handle handle; LV2_URID(* map)(LV2_URID_Map_Handle handle, const char *uri); }
LV2プラグインは、lv2.h
をインクルードしてLV2_descriptor()
およびLV2_lib_descriptor()
という関数を定義することになります。いずれもLV2ホストがプラグインをdlopen()
でロードした後、dlsym()
で動的に呼び出すことになります。LV2_lib_descriptor()
ではLV2_Feature*
型のfeatures
が定義されており、ここにはLV2ホストから「ホストがサポートするLV2拡張」のデータを含むリストが渡されます。
先のLV2_URID_Mapの例でいえば、map
がこの拡張機能の実体であり、この関数ポインタをプラグイン側はLV2_URID_mapの実装として呼び出せる、という仕組みです。
LV2プラグインの後方互換性
LV2プラグイン機構は、LV2 Coreと数多くのプラグインによって成り立っていますが、それぞれのプラグインにはバージョンがあり、これらは必ずしも後方互換性があるとは限らないようです。筆者が試した範囲では、Atom型として定義されている型がXML Schema Datatypesに由来するxsd:*
型であるとしてRDF上で定義されているものがいくつかあり、これらのプラグインをlv2_validateツールで検証するとwarningが報告されました。LV2の仕様自体が後方互換性を意識していても、実際に後方互換性が達成できるかどうかは、拡張となるプラグインのバージョン間互換性・設計次第であるようです。
もしかしたらこれはLV2_Atomをサポートした時点で生じた非互換問題で、2012年に出たこの記事でQTractorの開発者に言及されている問題かもしれませんが、2012年の問題が2019年になっても問題になっているとしたら、7年も前の非互換変更が今でも尾を引いているということであり、後方互換性の問題は無視できないかもしれません。
LV2ホスティング
オーディオプラグインを使うためには、オーディオプラグインにMIDIなどの制御命令や音声データを渡して処理させるホストが必要です。一般的にはDAWと呼ばれるソフトウェアがこれを担いますが、プログラムとしてはLV2プラグインをロードして処理を実行できるものであれば何でもかまいません。たとえば、プラグインのテストには、テストのみを目的とするローダーが使われるでしょう。
LV2をサポートするDAWとしては、Ardour、Audacity、Carla、QTractorなどが有力です。
LV2ホスティングを実現するためのリファレンス的な実装として、lilvというライブラリがあります。ローカルにインストールされているLV2プラグインやプラグインクラスをリストアップして、プラグインのインスタンスを生成して、ポートをデータバッファに接続して、オーディオ処理を走らせることができます。
lilv
lilvはLV2ホスティングのためにあるCライブラリです。RDFの詳細の多くを隠蔽しつつ、プラグインクラスを検索し、プラグインのインスタンスを生成して、音声データ処理を行えます。
RDFの処理は、同じ作者が開発しているserd(RDFの解析)、sord(Turtle syntaxの解析)、sratom(LV2 atom型とRDFの相互変換)、そしてlv2の4つのソースコード・パッケージにのみ依存しています。実際にはlv2などがさらにlibsoundioやcairoなどをオプションとしてサンプルプラグインのビルド時に参照することになるので、依存関係はもう少し膨れ上がります。
LV2のRDFを書けるようになるためには、RDFについてそれなりに勉強しなければならないことになりますが、率直に言えば、オーディオプラグインを開発するためにRDFを勉強するというのは、全く本質的な作業ではない、と言わざるを得ないでしょう。これは、lilvのAPIを調べていくことである程度緩和されます。RDFの面倒な部分はとりあえず置いておいて、lilvのAPIでworld, plugin class, plugin, port, instanceといった概念を理解したほうが早いです。