-
-
Save atsushieno/11cf7e8ba46bfbfdce3638ff9ab91eac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/1-intro.md b/1-intro.md | |
index d12bb09..f198d26 100644 | |
--- a/1-intro.md | |
+++ b/1-intro.md | |
@@ -7,15 +7,13 @@ | |
## 音楽の打ち込み作業とDAWの仕事 | |
-この本の読者である皆さんにとっては、DAWなどのソフトウェアを使用してコンピューターで作曲できることは自明でしょう。しかし、実際にDAWを使って作業しているかどうか、となると、誰もがYESとは答えられない質問であることでしょう。 | |
+この本の読者である皆さんにとっては、DAWなどのソフトウェアを使用すればコンピューターで作曲できることは自明でしょう。実際にDAWを使って全ての作業を行っているかどうかは人それぞれで、DAWを使用して音楽を制作している人も、最終的な楽曲ファイルを作成するまでの全ての工程をDAWで行っているとは限りませんが、ここでは楽曲ファイルを作成するまでの工程が全作業であるとして話を進めましょう。DAWを使用した楽曲制作には、次のような作業があります(順番通りというわけではありません)。 | |
-DAWを使用して音楽を制作している人も、最終的な楽曲ファイルを作成するまでの全ての工程をDAWで行っているとは限りませんが、ここでは楽曲ファイルを作成するまでの工程が全作業であるとして話を進めましょう。DAWを使用した楽曲制作には、次のような作業があります(順番通りというわけではありません)。 | |
- | |
-- 楽曲のパート構成を決めて、トラック群として編成する | |
-- 録音済みのオーディオサンプリングデータがあれば、それをオーディオのトラックに貼り付ける | |
-- インストゥルメンタル = 楽器を指定して譜面を打ち込む場合は、ピアノロールエディタなどを使用しながら各パートの音符を打ち込む | |
-- 各パートの楽器を定義する。主にオーディオプラグイン(VSTやAUなど)を指定して、そのパラメーターを調整する | |
-- 各パートの音量を調整し、パンを振り分けて左右のバランスをとり、EQ(イコライザー)などで各音域の厚みを調整する | |
+- 楽曲のパート構成を決めて、トラック群として編成する。 | |
+- 録音済みのオーディオサンプリングデータがあれば、それをオーディオのトラックに貼り付ける。あるいは他のパートを演奏しながら新しいトラックのセクションを録音する。 | |
+- インストゥルメンタル = 楽器を指定して譜面を打ち込む場合は、ピアノロールエディタなどを使用しながら各パートの音符を打ち込む。 | |
+- 各パートの楽器を定義する。主にオーディオプラグイン(VSTやAUなど)を指定して、そのパラメーターを調整する。 | |
+- 各パートあるいは楽曲全体の音量を調整し、パンを振り分けて左右のバランスをとり、EQ(イコライザー)などで各音域の厚みを調整する。 | |
これらの過程の中で、DAWがどのような仕事を行っているか、個別に見ていきましょう。 | |
@@ -56,16 +54,16 @@ DAWを使用して音楽を制作している人も、最終的な楽曲ファ | |
一般的なPCのユーザーは、オーディオデバイスを複数使用する必要は無いでしょう。しかし音楽制作においては、他のオーディオ出力を伴うアプリケーションに邪魔されないように、あるいは他のオーディオ入力とミキシングする目的で、オーディオインターフェースに音声を出力して、そこから受信したデータをデジタルに保存するようなことがあります。このため、DAWには特に使用するオーディオデバイスを個別に設定する機能が求められます。 | |
-また、MIDIデバイスを使用する場合は、どのデバイスを使用するかという環境設定も求められます。さらに、近年のMIDIデバイスには、MPE(MIDI polyphonic expression)など高度な入力情報を送信できるものが増えており、これらへの対応オプションを追加で指定できることが期待される場面もあります。今後MIDI-CI(capability inquiry)をはじめとする「MIDI 2.0」の機能(現在策定中)が実現してくると、この要求事項が広がってくることでしょう。 | |
+また、MIDIデバイスを使用する場合は、どのデバイスを使用するかという環境設定も求められます。さらに、近年のMIDIデバイスには、MPE(MIDI polyphonic expression)など高度な入力情報を送信できるものが増えており、これらへの対応オプションを追加で指定できることが期待される場面もあります。今後MIDI-CI(capability inquiry)をはじめとするMIDI 2.0の機能が実現してくると、その要求事項が広がってくることでしょう(MIDI 2.0については別途言及します)。 | |
## DAWではない音楽ソフトウェア・ツール | |
電子化された音楽は、多くの場合はDAWで制作されますが、たまにそれ以外のソフトウェアが使用されることもあります。本書のターゲットはシーケンサーエンジンなので、DAW以外の音楽ツールについても言及しておきます。@<fn>{other-tools} | |
-//footnote[other-tools][ここで言及する他にもトラッカーのような作曲ツールもあるのですが、機能的にはざっくりDAWに類するものと言えるので、ここでは言及しません。] | |
+//footnote[other-tools][ここで言及する他にもトラッカーのような作曲ツールもあるのですが、UIの構成を除く機能面は大まかにはDAWに類するものと言えるので、ここでは言及しません。] | |
-### VPL/APLの系譜 (Max/MSP, Pure Data etc.) | |
+### クリエイティブコーディングとVPL/APLの系譜 | |
//image[puredata][Pure Data(Wikimedia Commons: PureData-Harvie-Examples.png)][scale=0.7] | |
@@ -79,11 +77,13 @@ VPL(visual programming language)とは、愚直にプログラミングす | |
ヴィジュアルを処理しないものについては、APL (audio programming language)というカテゴリがあります。さまざまなオーディオ処理を記述するための言語という側面が強いです。音楽的な効果を記述するために使うこともまれにあるので、DAWの仕事と重複するところもあります。使用されている低レベルのソフトウェア基盤が同様の構成になっていることは多いです。 | |
+2020年の本版執筆時点では、ライブコーディング系の言語として広く知られているのはTidalCycles、Sonic Pi、SuperCollider、Extemporeなどが挙げられるでしょう。 | |
+ | |
### MMLの系譜 | |
//image[mml][MML (mugene samples)][scale=0.8] | |
-MML (music macro language@<fn>{not-music-markup})は、(APLとは異なり)音楽を記述する目的で作られた各種の言語の総称です。筆者もコンパイラーを開発して公開しているのですが@<fn>{mugene}、MIDI命令のような可読性の低いものを読みやすく簡潔に記述するものです(たとえば「オクターブ4のド」のノート・オンを`[90h, 40h, 50h]`などと記述する代わりに `o4c`ですませる)。20世紀に栄えていた技術であり、もともとは対象となる音源もMIDIではなくFM音源チップなどであり、DAWがメインストリームになった現在ではあまり使われていません。 | |
+MML (music macro language@<fn>{not-music-markup})は、(APLとは異なり)音楽を記述する(作編曲する)目的で作られた各種の言語の総称です。筆者もコンパイラーを開発して公開しているのですが@<fn>{mugene}、MIDI命令のような可読性の低いものを読みやすく簡潔に記述するものです(たとえば「オクターブ4のド」のノート・オンを`[90h, 40h, 50h]`などと記述する代わりに `o4c`ですませる)。20世紀に栄えていた技術であり、もともとは対象となる音源もMIDIではなくFM音源チップなどであり、DAWがメインストリームになった現在ではあまり使われていません。チップチューン系の作曲者がたまに使うことがあります。 | |
//footnote[mugene][https://github.com/atsushieno/mugene] | |
@@ -92,6 +92,27 @@ APLや生のMIDIメッセージを扱うライブラリでは、「音楽」を | |
//footnote[not-music-markup][Music Markup LanguageでMMLというものも存在しますが、全くの別物です] | |
//footnote[mml-literal][単独のコンパイラーツールではなく、ライブラリとして提供して、文字列リテラルをコンパイルする、というかたちでも使えるでしょうが、ユーザーが開発ツールを揃えないといけなくなるのはかえって面倒なので、DSLの一種と考えて記述するほうがまだ良いでしょう。] | |
+MML関連ツールには、特にデータフォーマットが独自である場合はプレイヤーとなるソフトウェアが必要であり、プレイヤーを開発するときは(本格的なDAWに比べるとだいぶ軽量な仕事ではあるものの)専用のシーケンサーエンジンを実装する必要があります。データフォーマットがSMFであったり、一部のFM音源用音楽データなど既にプレイヤーが存在するような既知のフォーマットなどであれば、自分でシーケンサーエンジンを実装する必要はないでしょう。 | |
+ | |
+ | |
+## オーディオ開発に適した言語とその特徴 | |
+ | |
+プログラミングにおけるオーディオ処理の世界はやや特殊な空間です。厳しいリアルタイム要件を満足するためには、ガベージコレクションのように「全てのスレッドを停止させてメモリを回収する」ような処理や、「実行時に必要になったら仮想マシンコードをネイティブコードに時間をかけてコンパイルする」JITのような処理は向いていないとされています。そのため、Javaや.NETのような仮想マシン技術が入り込むことがほぼありません@<fn>{vstnet}。 | |
+ | |
+//footnote[vstnet][VST2 SDKを.NET API化しただけのVST.NETというプロジェクトがありますが、これはGCによる遅延を受容するという前提で作られたものです。VST3にも対応していません。] | |
+ | |
+このため、オーディオプログラム開発においては、C++か、ネイティブコードとしてコンパイル済みの実行バイナリを生成する特殊な言語および開発ツールが使われます。Appleプラットフォーム専用のコードではSwiftやObjective-Cも用いられます(GUIだけSwift、というプラグインも少なくないでしょう)。C++以外で有望視されている言語はRustです。また、汎用言語でも、libsoundioの開発者が設計しているZigは、オーディオアプリケーション開発を念頭に置いて設計されています。@<fn>{v} | |
+ | |
+//footnote[v][他にはV言語などもno JIT, no GCでオーディオ開発との相性が良さそうですが、宣伝力が極端に強い言語で実態との乖離が大きいという側面があるので、ここでは脚注での言及にとどめます。] | |
+ | |
+//footnote[julia-aot][JuliaにはAOTで動作させるやり方もあるようなので、AOTで実用上問題なければこれは障害ではないかもしれません。] | |
+ | |
+ネイティブコード生成の文脈でよく登場するのがLLVMです。LLVMはAPL(audio programming language、オーディオプログラム開発言語)のバックエンドにもよく用いられます。この方面ではFAUSTがよく知られています。2020年までJUCEの開発元であったROLI@<fn>{roli}も2018年にSOULという類似の言語をアナウンスしています。一方でJITではなく静的コード生成が期待されることもあって、(APLではなく汎用言語ですが)同じLLVM言語の系譜でも独自の実行モジュール形式をもちJITを基本とするJuliaなどは似て非なる分類であるといえます@<fn>{julia-aot}。 | |
+ | |
+![FAUSTのコード例(公式ドキュメントのAdditive Synthesis)](faust-example.png) | |
+ | |
+//footnote[roli][ROLIは2020年にJUCE開発およびライセンシングのビジネスをPACE社に売却しています。ライセンス表記などにおける開発企業の名義は、JUCEの伝統的な開発企業の名義であったRaw Material Softwareとなっています。] | |
+ | |
## 音楽ツール開発の課題 | |
@@ -123,36 +144,33 @@ DAWのようなソフトウェアは、伝統的にデスクトップで行わ | |
| プラグイン機構 | Windows | Mac | Linux | iOS | Android | | |
| ------------------ | ------------ | --- | ----- | --- | -------- | | |
-| VST v2 (入手不可能) | ○ | ○ | ○ | ☓ | ☓ | | |
-| VST v3 | ○ | ○ | ○ | ☓ | ☓ | | |
-| AU (AudioUnit) | ☓ | ○ | ☓ | ○ | ☓ | | |
-| DSSI | ☓ | ☓ | ○ | ☓ | ☓ | | |
-| Lv2 (LADSPA v2) | (○) | (○) | ○ | ☓ | ☓ | | |
-| DirectX Plugins | ○ | ☓ | ☓ | ☓ | ☓ | | |
+| VST | ○ | ○ | ○ | - | - | | |
+| AU (AudioUnit) | - | ○ | - | ○ | - | | |
+| DSSI | - | - | ○ | - | - | | |
+| LV2 (LADSPA v2) | (○) | (○) | ○ | - | - | | |
+| DirectX Plugins | ○ | - | - | - | - | | |
(AAXなど、複数のプラグインホストに開かれているとは言えない技術については除外しています。) | |
VSTが特に有名ですが、VSTはv2とv3で大きく異なります。VST v3はGPLで公開されており、Linuxもサポートされているのですが、VST v3のAPIの設計に基づくプラグイン開発が煩雑になったこともあってか、現在でもVST v2に基づくプラグインのほうが多いというのが2019年初頭の現状です。VST v2は既にSteinbergがSDKを非公開にしており、入手不可能になっているので、今後は減少していく可能性が高いでしょう。 | |
-LADSPAやDSSI、LV2は、VSTがプロプラエタリなソフトウェアでMacとWindowsしかサポートしていなかった時代に、Linuxでオーディオプラグインを実現するための仕組みとして開発されました。オーディオプラグインには伝統的に商業用のものが多かったのですが、LADSPAはLGPL、LV2はISC Licenseで公開されています。(VST v3も、ライブラリであるにもかかわらずGPLv3で公開するというのは、実質的に商用ライセンス販売のためのライセンス選択です。) | |
+AUもv2とv3ではロード方式などアーキテクチャが異なる側面もある(特にiOSはv3のみサポートされている)ので、一概には同一視出来ないのですが、あくまで概要として見てください。 | |
+ | |
+VSTがプロプラエタリなソフトウェアでMacとWindowsしかサポートしていなかった時代に、Linuxでオーディオプラグインを実現するための仕組みとしてLADSPAやDSSI、LV2といった規格が開発されました。オーディオプラグインには伝統的に商業用のものが多かったのですが、LADSPAはLGPL、LV2はISC Licenseで公開されています。VST v3も、ライブラリであるにもかかわらずGPLv3で公開するというのは、実質的にSDKの商用ライセンス販売のためのライセンス選択です。 | |
-GUI部分でプラットフォーム固有の実装を混合する必要が多いという点で、Linuxとは相性の悪いソフトウェアの類型なのですが、オーディオプラグインが依存するWindowsのプラットフォーム機能やライブラリは最小限に抑えたものが多く、Wineで動作するものが少なからず存在しているようです@<fn>{vst-wine}。 | |
+GUI部分でプラットフォーム固有の実装を混合する必要が多いという点で、Linuxとは相性の悪いソフトウェアの類型なのですが、オーディオプラグインが依存するWindowsのプラットフォーム機能やライブラリは最小限に抑えたものが多く、Wineで動作するものが少なからず存在しているようです@<fn>{vst-wine}。興味がある読者はairwave, LinVst, LinVst3, vst-bridge, yabridgeといったプロジェクトを調べてみてください。 | |
//footnote[vst-wine][ライセンスチェッカーなどがOSを厳密にチェックする等の問題で期待通りにインストールされないことも多いです。] | |
このように、プラットフォームによってバラバラの世界になっているオーディオプラグインですが、複数のホストで動作することが求められることもあって、基本的な要求事項はどのプラグイン機構でもほぼ同様(オーディオ/MIDI の入力から出力までのパイプライン処理)であるといえます。そのため、これらを統一したクロスプラットフォームのAPIを提供するライブラリが開発されてきました。有名どころでは**JUCE**や**IPlug2**(2018年以前はWDL)などが挙げられます。本書で頻繁に言及するtracktion_engineもJUCEのモジュールです。 | |
-これらのオーディオプラグインは現状ほぼC++の独壇場であり、他のプログラミング言語ではほとんど開発されていない状態です。その理由は主にオーディオの厳格な低レイテンシー要求によるものが大きいです@<fn>{low-latency-requirements}。近年ではRustのような言語でもこの要求を満たせるはずですが、C++標準も言語機能を改善しつつあり、C++からの大規模な移行が起こるかどうかは未だに予見しがたいところです。 | |
- | |
-//footnote[low-latency-requirements][ガベージコレクションやJITコンパイルなどでオーディオ処理が止まってはならないわけです。] | |
- | |
### 音楽のデータモデルと演奏エンジン | |
オーディオやMIDIのI/Oが実現し、オーディオプラグインやMIDI楽器によって音源を用いた発音が可能になったら、次は音楽を演奏命令の集合体として表現するデータモデルと、それを忠実に演奏するプレイヤー、そしてその演奏データを編集できる機能が必要になります。 | |
いったんオーディオI/OやオーディオプラグインのAPIを抽象化してしまうと、この部分は純粋にプラットフォームやオーディオプラグインのAPIから独立した、純粋なデータモデルだけで実現できる部分です。これを最もわかりやすく表したのがMIDI規格であり、標準MIDIファイルです。 | |
-もっとも、MIDIで表現できる範囲で音楽が十分に表現できたのであれば、現代でもMIDI音源がDTMの中心になっていたことでしょう。現実はそうはならず、オーディオプラグイン固有の機能を利用したり、MIDIでは扱いきれない数の音源を操作したり、MIDI仕様には含まれないような高度なコントロール命令を駆使したり、といったモダンな手法に基づいて、作曲作業が行われます。 | |
+MIDIで表現できる範囲で音楽が十分に表現できたのであれば、現代でもMIDI音源がDTMの中心になっていたことでしょう。現実はそうはならず、オーディオプラグイン固有の機能を利用したり、MIDIでは扱いきれない数の音源を操作したり、MIDI仕様には含まれないような高度なコントロール命令を駆使したり、といったモダンな手法に基づいて、作曲作業が行われます。 | |
とはいえ、MIDIの時代に確立した技術の多くは現代でも通用するものです。たとえば音楽をトラックの集合体として表現し、その中に(グルーピングされたデータの展開などはまた別として)ノートオンやノートオフ、コントロールチェンジなどの命令とタイムスタンプを組み合わせて演奏命令のイベントとしてまとめ、時間の計算はテンポと4分音符の分解能に基づいて指定する、といった技術要素は、現代でも採用されています。DAWのシーケンサーエンジンは高度な命令をサポートしたMIDIシーケンサーエンジンである、と言ってもよいでしょう。 | |
@@ -168,18 +186,19 @@ GUIは、伝統的にはプラットフォーム固有の機能でした。こ | |
クロスプラットフォームのGUIフレームワークの中には、独自にGUI部品をレンダリングするものと、APIだけが共通であるライブラリをプラットフォーム別に実装してプラットフォーム固有のGUI部品を呼び出すものがあります。後者のアプローチは、ボタン、テキストボックス、リストビューなどについては、どのプラットフォームでも提供されているので有効ですが、音楽ソフトウェアには特有のGUIコントロール(たとえば「ツマミ」や2軸スライダーなど)があり、それらはプラットフォームネイティブでは提供されていません。vst4gui、JUCEやWDLはこれらを提供するGUIライブラリとしても選ばれてきました。 | |
-ただ、GUIフレームワークは、ともすればアプリケーションの全体的な構造、ひいては開発言語まで拘束するものであり、既存のものがあるとしてもそれを使うかどうかはアプリケーションの設計次第でしょう。逆に、JUCEやWDLが利用できるという理由でC++が選ばれてきたという側面はかなり大きいでしょう。一方でJUCEで開発されたGUIは…平たくいえば「未熟きわまりない」です(試しにLinux上でJUCEで実装されたファイルダイアログを開いてみるとわかります。メインメニューはどのプラットフォームでもアクセラレーションキーが効きません)。「GUIはプラットフォームごとに実装されるべき」と言われても仕方ないレベルです。 | |
+ただ、GUIフレームワークは、ともすればアプリケーションの全体的な構造、ひいては開発言語まで拘束するものであり、既存のものがあるとしてもそれを使うかどうかはアプリケーションの設計次第でしょう。逆に、JUCEやWDLが利用できるという理由でC++が選ばれてきたという側面はかなり大きいでしょう。一方でJUCEで開発されたGUIはプラットフォームGUIをリスペクトしたUIに比べると大きく劣ります@<fn>{juce-linux-gui}。モバイルプラットフォームではよく「GUIはプラットフォームごとに実装されるべき」という主張が見られますが、デスクトップでも当然に同様の議論があり、相応の妥当性があります。 | |
+ | |
+//footnote[juce-linux-gui][試しにLinux上でJUCEで実装されたファイルダイアログを開いてみるとわかります。メインメニューはどのプラットフォームでもアクセラレーションキーが効きません。] | |
もっとも、GUIでも、クロスプラットフォーム・クロス制作環境でサポートされるべき機能が無いわけではありません。たとえばコントロール・サーフィスをカスタム制御するためのUIを作成する機能があります。あるいはユーザーが指定するオーディオ・プラグインのパラメーターに対して、それを制御できるようなGUIコントロール(ツマミなど)を自由に割り当てられるビューを表示できるような仮想コントロール・サーフィスを作成する機能もあるでしょう。これらはある程度プラットフォームやDAWから独立した標準的な存在があるべきなのかもしれません。 | |
もうひとつ、おそらくホスト側とプラグイン側で事情が異なるのが、プラグイン側のGUIは、自身と無関係なキーイベント等をホスト側で処理するように仕向けてやらないといけない(自身で全てのキーイベントを処理したことにしてはならない)点です。これは一般的なモーダルダイアログのUIとは異なるところです。このように操作の所有権の所在が不明瞭なGUIの構成はデスクトップアプリケーションとして不適切であると筆者は考えますが、既存のオーディオプラグインやプラグインホストは、ほぼ例外なくそのようなUI設計を前提として構築されています。 | |
+本書でとても言及できそうにない話題として、オーディオアプリケーションにより高水準のアプリケーション開発アーキテクチャをどう適用するか、という問題があります。とても言及できそうにないのは、筆者の知識不足というより、オーディオアプリケーションの世界で使われているGUIツールキットがそこまで成熟していないという問題が大きいです。現状ではGUIが分離できていれば良いほうで、Flux, M-V-whatever(MVVM, MVU, MVIなど)といった話題はオーディオアプリケーション開発ではほとんど見られません。それどころか独自UIツールキットに立脚しているためにプラットフォームのUIガイドラインに沿ってもいない、アクセシビリティ対応なども弱い…といったものが多いです。このような現状であるべきアプリケーションアーキテクチャについて議論するのは、残念ながら筆者の手には余ります。 | |
+ | |
### GUIから切り離された音楽編集機能 | |
最後に、DAWでは音楽を再生するだけではなく、編集も出来なければなりません。これもシーケンサーエンジンに期待したい機能のひとつです。これはGUIからの操作に対応する機能を実現するもので、単なる再生機能に比べると格段に複雑な機能であるといえます。 | |
-GUIそのものはプラットフォームに強く結びついたかたちで開発されることが多いですが、GUIアプリケーション開発においてもUIとロジックの分離が求められるようになっており、エディターとしての機能をGUIから切り離してプラットフォーム依存を排除するというのも、技術トレンドのひとつであるといえます。 | |
- | |
- | |
- | |
+GUIそのものはプラットフォームに強く結びついたかたちで開発されることが多いですが、GUIアプリケーション開発においてもUIとロジックの分離が求められるようになっており、エディターのようにGUI上で動作することが大前提のアプリケーションであっても、さまざまな機能をGUIから切り離してプラットフォーム依存を排除するというのも、技術トレンドのひとつであるといえます。 | |
diff --git a/2-raw-audio-midi-access.md b/2-raw-audio-midi-access.md | |
index 6a42792..1f22a3f 100644 | |
--- a/2-raw-audio-midi-access.md | |
+++ b/2-raw-audio-midi-access.md | |
@@ -1,20 +1,23 @@ | |
-# オーディオ/MIDIのネイティブアクセス | |
+# オーディオ/MIDIへのアクセス | |
シーケンサー・エンジンを作成する場合、オーディオやMIDIの機能にアクセスするアプローチは2つありえます。 | |
-- 既存のオーディオプラグイン開発フレームワークに含まれる機能を使う | |
+- 既存のオーディオ開発フレームワークに含まれる機能を使う | |
- 自前でオーディオやMIDIに直接アクセスする | |
-前者の例としては、JUCE、WDL/iPlug2、Carlaといったライブラリが挙げられます。これらを使えば、この章で説明するようなオーディオI/OやMIDI I/Oについて、自分で実装すべきことは何もありません。これらのライブラリは商用で使う場合には注意すべきものがあるので@<fn>{audio-plugin-fx-licensing}、読者によっては自前で開発したいと考えるかもしれません。 | |
+前者の例としては、JUCE、WDL/iPlug2といったライブラリが挙げられます。これらを使えば、この章で説明するようなオーディオI/OやMIDI I/Oについて、自分で実装すべきことは何もありません。これらのライブラリは商用で使う場合には注意すべきものがあるので@<fn>{audio-plugin-fx-licensing}、読者によっては自前で開発したいと考えるかもしれません。 | |
//footnote[audio-plugin-fx-licensing][たとえばJUCEの場合、ライブラリ全体はGPLv3あるいは商用ライセンスであり、OSSライセンスでしか利用できない状況ではAppleのApp Storeにはリリースできません。オーディオI/Oをサポートする`juce_audio_devices`など一部のコンポーネントはISCライセンスで提供されています。] | |
いずれにしろ、本書はシーケンサー・エンジンで使用されている技術を解説するものなので、オーディオプラグインをブラックボックスとしてスルーするのではなく、その中で使用される技術を分解して眺めるのが筋でしょう。この章は、オーディオやMIDIに自前でアクセスする場合に必要になるAPIやライブラリを紹介します。 | |
+ | |
## MIDIアクセスAPI | |
-MIDIは本来、デバイス中立の規格であり、プラットフォーム中立の規格です。しかし、プラットフォーム中立の仕様であるはずのMIDIデバイスにアクセスするためには、プラットフォーム固有のAPIを使用してアクセスすることになります。一般的には、ハードウェアにアクセスするにはOS別に実装されたデバイスドライバーが必要であり(またMIDI規格はUSB規格が誕生する以前から存在する古い仕様であり)、デバイスドライバーに基づいてMIDIデバイスとして認識されたものをプラットフォームでどのようにアクセスするかは、プラットフォームの開発言語やフレームワークによるためです。同じOS、たとえばWindowsでも、古くからありCで利用できるWindows Multimedia API(WinMM)と、C#やJavaScript (WinJS)で利用できるUniversal Windows Platform (UWP)では、開発言語もフレームワークも異なります。 | |
+MIDIは本来、デバイス中立の規格であり、プラットフォーム中立の規格です。しかし、プラットフォーム中立の仕様であるはずのMIDIデバイスにアクセスするためには、プラットフォーム固有のAPIを使用してアクセスすることになります。一般的には、ハードウェアにアクセスするにはOS別に実装されたデバイスドライバーが必要です(MIDI規格はUSB規格が誕生する以前から存在する古い仕様であり、またMIDI I/Oはリアルタイム性の確保のためにOSの根幹に関わるという側面もあります@<fn>{web-midi-realtime})。デバイスドライバーに基づいてMIDIデバイスとして認識されたものをプラットフォームでどのようにアクセスするかは、プラットフォームの開発言語やフレームワークによるためです。同じOS、たとえばWindowsでも、古くからありCで利用できるWindows Multimedia API(WinMM)と、C#やJavaScript (WinJS)で利用できるUniversal Windows Platform (UWP)では、開発言語もフレームワークも異なります。 | |
+ | |
+//footnote[web-midi-realtime][Web MIDI APIで保証できない機能でもあります。] | |
少し話題の対象を変えて「MIDIをサポートするライブラリ」とぼかして言うと、これは必ずしもプラットフォーム依存の機能とは限りません。たとえば… | |
@@ -42,10 +45,22 @@ MIDIは本来、デバイス中立の規格であり、プラットフォーム | |
実のところ、この機能リストはWeb MIDI API仕様で定められている機能そのものです。いささか逆説的ですが、MIDI仕様に基づいてデバイスとやり取りできる機能はこの程度に限られています。 | |
-2019年現在、MMA (MIDI Manufacturers Association)では、MIDI 2.0と呼ばれる仕様の策定が進められています。既に具体化しつつある仕様のサブセットとしてはMIDI CI (Capability Inquiry)が挙げられます。これはMIDI機器の機能@<fn>{midi-ci-examples}のサポートの有無を「問い合わせることができる」というもので、メッセージングが単方向で「応答を受け取る」という概念が無いMIDI 1.0の仕様だけではまず実現できない機能です。このような新しい仕様が入ってくると、MIDIアクセスのライブラリに求められる機能が拡大するかもしれません。 | |
+2020年には、MMA (MIDI Manufacturers Association)によってMIDI 2.0と呼ばれる仕様が正式に公開されました。2020年の本版執筆時点ではまだ目に見えるトレンドにはなっていませんが、MIDI 2.0サポートが求められるようになってくると、次の機能も期待されるようになります。 | |
+ | |
+- MIDIデバイスでサポートしている機能(MIDI 2.0サポートの有無も含む)の問い合わせ (Capability Inquiry) | |
+- MIDIデバイスのプロパティの取得と設定 (Property Exchange) | |
+- MIDI 2.0 UMPに基づくデータパケットのメッセージ編成・解析・転送の仕組み | |
+ | |
+MIDI 2.0 UMPについては、MIDI 2.0プロトコルがMIDIアクセスAPIでサポートされていれば、UMPのサポートそのものはプラットフォーム非依存で実装できます@<fn>{midi-2.0-guidebook}。これはMIDI 1.0のメッセージが単純なバイト配列でやり取りできるのと同様です。 | |
+ | |
+//footnote[midi-2.0-guidebook][UMPについては筆者が同人誌「MIDI 2.0 UMPガイドブック」として詳しくまとめています。] | |
+ | |
+MIDI 2.0は複数の仕様の集合体であり、その中のひとつMIDI CI (Capability Inquiry)では、MIDI機器の機能@<fn>{midi-ci-examples}のサポートの有無を「問い合わせる」ことができます。これはメッセージングが単方向で「応答を受け取る」という概念が無いMIDI 1.0の仕様だけではまず実現できない機能です。今後、各OSプラットフォームでMIDI 2.0サポートが実現してくると、MIDIアクセスのライブラリに求められる機能が拡大するでしょう。 | |
//footnote[midi-ci-examples][たとえば「このデバイスはMPEを処理できるか」「このデバイスはバンク・セレクトやプログラム・チェンジで指定できる音色のリストや名前を返すことができるか」といった問い合わせが考えられます。] | |
+ | |
+ | |
### プラットフォーム別のMIDIアクセスAPI | |
MIDIアクセスのネイティブAPIは、プラットフォームごとにバラバラですが、次のリストで概ね列挙出来ているでしょう。 | |
@@ -86,22 +101,24 @@ portmidiには少し癖があり、通常は単なるバイト列として送受 | |
DAWの処理の大半は究極的にはオーディオ処理であり、「オーディオ処理」に求められることは数多くありますが、まずこの節ではプラットフォームのオーディオ入出力に関連する部分を議論しましょう。 | |
-### オーディオ再生のコールバック | |
+### オーディオネイティブアクセスの要求事項 | |
-既に何度か言及してきたことなので、繰り返す必要も無いでしょうが、オーディオI/OのAPIもプラットフォーム固有のかたちで発展してきたものです。音声データは通常は単なるサンプリングデータであり、そのフォーマットに注意を払えば、基本的にはプラットフォーム中立のかたちで処理できるものです…と、それだけであれば単純に聞こえるのですが、状況をひとつややこしくする要因として、オーディオ再生には、厳密な再生時間を再現できる「リアルタイム処理」が求められます。これはプログラミング言語・環境によっては実現が困難なので@<fn>{garbage-collection}、リアルタイム処理を実現できるプロセス/スレッド空間に隔離して処理が行われることがあります@<fn>{android-audio}。 | |
+何度も繰り返す必要も無い話ですが、オーディオI/OのAPIもプラットフォーム固有のかたちで発展してきたものです。音声データは通常は単なるサンプリングデータであり、そのフォーマットに注意を払えば、基本的にはプラットフォーム中立のかたちで処理できるものです…と、それだけであれば単純に聞こえるのですが、状況をひとつややこしくする要因として、オーディオ再生には、厳密な再生時間を再現できる「リアルタイム処理」が求められます。これはプログラミング言語・環境によっては実現が困難なので@<fn>{garbage-collection}、リアルタイム処理を実現できるプロセス/スレッド空間に隔離して処理が行われることがあります@<fn>{android-audio}。 | |
//footnote[garbage-collection][分かりやすい例としては、ガベージコレクションの停止時間やJITコンパイル時間が問題になります] | |
//footnote[android-audio][たとえばAndroidのオーディオAPIはAndroid NDKで提供されています(OpenSL ES / AAudio)] | |
-そして、リアルタイム処理を最適なかたちで実現するためのコードのロジックは、プラットフォームごとに異なります。リアルタイムのオーディオ処理というのは、オーディオ処理をゼロ処理時間に近づけるということではなく、人間がタイムラグを感じない程度にスライスされた時間の中で、オーディオ出力デバイスに音声データを渡す処理を「常に」「遅滞なく」行うことです。もし遅延が生じると、その時間は音声データが無いことになり、音声が不自然なブツ切りになってしまいます。また、遅延した場合に音声を引き伸ばしたりすると、予定されていた演奏時間よりも実際の演奏時間のほうが長かった、といった事態が生じてしまいます。 | |
+そして、リアルタイム処理を最適なかたちで実現するためのコードのロジックは、プラットフォームごとに異なります。リアルタイムのオーディオ処理というのは、オーディオ処理をゼロ処理時間に近づけるということではなく、人間がタイムラグを感じない程度にスライスされた時間の中で、オーディオ出力デバイスに音声データを渡す処理を「常に」「遅滞なく」行うことです。もし遅延が生じると、その時間は音声データが無いことになり、音声が不自然なブツ切りになってしまいます。また、遅延した場合に音声を引き伸ばしたりすると、予定されていた演奏時間よりも実際の演奏時間のほうが長かった、といった事態が生じてしまいます。@<fn>{adc19-realtime-101} | |
+ | |
+//footnote[adc19-realtime-101][リアルタイムオーディオ処理については、Audio Developers Conference 2019のセッション動画Real-time 101 (Part I / Part II)が参考になります。] | |
-これはMIDIの場合にも同じことが言えるのですが、MIDIデータの送受信が比較的低負荷であるのに対して、オーディオ処理の場合はリアルタイムで複雑な加工処理を要求しがちなので、現実的に問題になります。 | |
+これはMIDIの場合にも同じことが言えるのですが、MIDIデータの送受信が比較的低負荷でメッセージのバッファリングも基本的に不要であるのに対して、オーディオ処理の場合はリアルタイムで複雑な加工処理を要求しがちなので、現実的に問題になります。 | |
### 多様なクロスプラットフォーム・オーディオライブラリ | |
-いずれにしろ、リアルタイム処理の要求事項にこたえるために、オーディオアプリケーションやライブラリでは、多くの場合は「優先度」の高いオーディオスレッドを確保して、そこからのコールバックで再生するオーディオデータを渡すことになります。このとき、特にネイティブのオーディオAPIは「1回の呼び出しでバッファを全部埋めるか否か」みたいな厄介なところで違いが生じます。クロスプラットフォームMIDIのAPIに比べると、開発者の技術的なセンスを求められる余地が大きく@<fn>{xplat-callback-support}、実際数多くのクロスプラットフォーム・オーディオライブラリがCやC++で開発されてきました。しばしば名前が挙げられるものだけでも、PortAudio、RTAudio、SDL、JUCE、OpenAL Soft、cuceb(firefoxのオーディオ処理ライブラリ)などがあります。 | |
+いずれにしろ、リアルタイム処理の要求事項にこたえるために、オーディオアプリケーションやライブラリでは、多くの場合は「優先度」の高いオーディオスレッドを確保して、そこからのコールバックで再生するオーディオデータを渡すことになります。このとき、特にネイティブのオーディオAPIは「1回の呼び出しでバッファを全部埋めるか否か」みたいな厄介なところで違いが生じます。クロスプラットフォームMIDIのAPIに比べると、開発者の技術的なセンスを求められる余地が大きく@<fn>{xplat-callback-support}、実際数多くのクロスプラットフォーム・オーディオライブラリがCやC++で開発されてきました。しばしば名前が挙げられるものだけでも、PortAudio, RTAudio, libsoundio, SDL, JUCE, OpenAL Softなどがあります。 | |
-//footnote[xplat-callback-support][たとえばどのプラットフォームのオーディオAPIで要求されるコールバックの仕組みに適合するコールバック対応APIを適切に設計するセンスが求められます。] | |
+//footnote[xplat-callback-support][たとえばプラットフォームごとに異なるオーディオAPIで、要求されるコールバックの仕組みに適合するコールバック対応APIを適切に設計するセンスが求められます。] | |
オーディオI/OのAPIを標準化する動きは20世紀にはありませんでした。Webブラウザからオーディオデバイスへのストリーミング出力をサポートするものとしてMozillaがFirefoxでAudio Data APIを公開し、その後GoogleがChromeでWeb Audio APIを公開して、現在では後者がW3Cで仕様策定されています。オーディオノードの接続など野心的だが標準化するのに相応しいものなのか疑問符が出てくる仕様で、Audio Data APIのほうがシンプルだったのですが、仕様策定が始まったWeb Audio 2.0にはAudio Data API並にシンプルなAPIが追加される動きであるようです。 | |
@@ -113,6 +130,8 @@ DAWに求められるオーディオアクセスの機能は、実のところ | |
もっとも、この程度の簡単な機能は、ほとんどのクロスプラットフォームのオーディオI/Oライブラリで提供されているでしょう。ただ、MIDIと同様、オーディオデバイスの接続/解除の通知の有無は、プラットフォームによって異なる可能性があります(これもMIDIデバイスと同様、ポーリングすれば解決する問題ではあります)。 | |
+また、オーディオデバイスやドライバーには最適なサンプリング周波数や最適なバッファサイズなど、いくつかデバイス情報があります。アプリケーションがそれらを取得してI/Oを最適化できるように、それらを提供するAPIが求められます。 | |
+ | |
### オーディオデータの属性 | |
オーディオデータには、いくつかの属性があります。 | |
@@ -128,7 +147,9 @@ DAWに求められるオーディオアクセスの機能は、実のところ | |
自分で生成できるオーディオデータであれば、環境に合わせて最適なものを用意するのが良いでしょうが、事前にデータがわからない場合はオーディオストリームの設定を調整するくらいしかできないでしょう。 | |
-なお、属性の異なるオーディオデータを、変換すること自体はクロスプラットフォームなやり方で実現できるのですが、オーディオリアルタイム処理はシビアなものなので、もしネイティブのオーディオAPIで調整可能であれば可能な限りそのやり方で処理するというアプローチが望ましいところでしょう。これもクロスプラットフォームのオーディオライブラリの設計に影響するところです。 | |
+なお、属性の異なるオーディオデータを、変換すること自体はクロスプラットフォームなやり方で実現できるのですが、オーディオリアルタイム処理はシビアなものなので、もしネイティブのオーディオAPIで調整可能であれば可能な限りそのやり方で処理するというアプローチが望ましいところでしょう。これもクロスプラットフォームのオーディオライブラリの設計に影響するところです@<fn>{adc18-std-audio}。 | |
+ | |
+//footnote[adc18-std-audio][この話題については、Audio Developer Conference 2018のセッション動画"Towards std::audio"が参考になります。] | |
## オーディオ/MIDI環境設定 | |
diff --git a/3-audio-plugin.md b/3-audio-plugin.md | |
index 18ef72e..9b6c3b9 100644 | |
--- a/3-audio-plugin.md | |
+++ b/3-audio-plugin.md | |
@@ -1,79 +1,84 @@ | |
-# オーディオプラグイン開発ライブラリ | |
+# オーディオプラグインのサポート | |
## オーディオプラグインとプラグインホスト | |
オーディオプラグインは、一般的には、シーケンサーエンジンを使ってMIDIイベントなどを入力する方式で音楽制作するときに、トラックごとの音源を設定するためのもので、ごく簡潔に言えば**楽器**と**エフェクト**です。ギターの音色のプラグインに(あるオクターブの)「ド、ミ、ソ…」を送信すると、それがギターの所定の音階の音声データになり、その後にイコライザーで事前に設定した値のハイパスフィルター・ローパスフィルターを通して、ディストーションエフェクトで音声に歪みを与えて最終的な出力にする…といったことを実現します。 | |
-「オーディオプラグイン」には、W3C標準仕様のようなものはありません。しかし、複数のシーケンサーと複数の楽器の間を繋ぐことができるような、いくつかの業界標準のような規格があります。よく使われているのはSteinberg社のVST、AppleのAU (Audio Unit)@<fn>{microsoft-dxi}、あとはLinuxのLADSPAでしょう。MIDIがMIDI標準の範囲でのみ、楽器・シーケンサー間でのメッセージのやり取りが可能であるのに対して、オーディオプラグインはMIDIメッセージに加えて、独自のコントロール命令に基づいて、オーディオデータを生成したり(インストゥルメント = 楽器)、加工したり(エフェクト)できるものです。 | |
+「オーディオプラグイン」には、W3C標準仕様のようなものはありません。しかし、複数のシーケンサーと複数の楽器の間を繋ぐことができるような、いくつかの業界標準のような規格があります。よく使われているのはSteinberg社のクロスプラットフォーム規格であるVST (Virtual Studio Technology)、AppleのAU (Audio Unit)@<fn>{microsoft-dxi}、あとはLinuxでよく使われているLV2 (Linux Audio Developer's Simple Plugin API v2, LADSPA v2) でしょう。MIDIがMIDI標準の範囲でのみ、楽器・シーケンサー間でのメッセージのやり取りが可能であるのに対して、オーディオプラグインはMIDIメッセージに加えて、独自のコントロール命令に基づいて、オーディオデータを生成したり(インストゥルメント = 楽器)、加工したり(エフェクト)できるものです。 | |
-//footnote[microsoft-dxi][WindowsにもDirectX Instrument Pluginのような規格が存在しているのですが、普及度は無視できるレベルです] | |
+//footnote[microsoft-dxi][MicrosoftもWindows専用のDirectX Instrument Pluginという規格を作ったのですが、普及度は無視できるレベルです] | |
VSTやAUは、音源やエフェクトをソフトウェアとして販売している多数の企業と、DAWなどの音楽制作関連ソフトウェアを販売している多数の企業にとってのインターフェースとなっています。総称として、前者の製品群をオーディオプラグイン、後者の製品群をオーディオプラグインホストと呼びます。 | |
-一般的に、オーディオプラグインがインストゥルメント(楽器)として機能する場面では、DAWなどオーディオプラグインのホストからMIDIメッセージのようなかたちでオーディオプラグインにリクエストが送られます。MIDIメッセージでリクエストを受け取ったインストゥルメントは、その内容に沿って音声データを合成して、ホスト側に返します。そのデータの元はサンプリングであることが多いですが、FM音源のような波形合成のみによって生成されることもあります。オーディオプラグインがエフェクトとして機能する場合は、DAWから加工前のオーディオデータと加工を指示するMIDIメッセージなどがオーディオプラグインに送られてくるので、プラグインはこれに各種の加工処理を加えてホスト側に返します(MIDIデータを受け取ってMIDIデータを返すような「エフェクト」も考えられますが、あまり無いでしょう)。 | |
+![DAW上で楽器として動作するVSTプラグイン](audio-plugins-in-daw.png) | |
+一般的に、オーディオプラグインがインストゥルメント(楽器)として機能する場面では、DAWなどオーディオプラグインのホストからMIDIメッセージのようなかたちでオーディオプラグインにリクエストが送られます。MIDIメッセージでリクエストを受け取ったインストゥルメントは、その内容に沿って音声データを合成して、ホスト側に返します。そのデータの元はサンプリングであることが多いですが、FM音源のような波形合成のみによって生成されることもあります。オーディオプラグインがエフェクトとして機能する場合は、DAWから加工前のオーディオデータと加工を指示するMIDIメッセージなどがオーディオプラグインに送られてくるので、プラグインはこれに各種の加工処理を加えてホスト側に返します。MIDIデータを受け取ってMIDIデータを返すような「エフェクト」もあって、MIDIメッセージに一定の加工を施した上で別のインストゥルメントプラグインに渡します。 | |
-## オーディオプラグイン開発フレームワーク | |
-多くのオーディオプラグイン製品は、複数のフレームワークに対応しているのですが、VSTとAU、LADSPAはそれぞれ全く異なるAPIであり、公式に提供されるSDKがあってそれを使用するものについては、開発も別々に行うことになります。LADSPA(v2、Lv2)はクロスプラットフォームの方式で設計されていますが、LADSPAはLinux起源のテクノロジーであり、現状ほぼLinux専用となるでしょう。 | |
+## オーディオプラグインフレームワークとSDK | |
-オーディオプラグイン機構では、一般的に、オーディオI/Oに直接依存することはありません。オーディオ入力もオーディオ出力もストリーミングバッファとして表現され、多くは単なるメモリアドレスへのポインターです。そのため、複数プラットフォームをサポートするプラグイン機構でも、コア部分は特にプラットフォーム固有の実装を含むことなくクロスプラットフォームで設計されているのが一般的です。 | |
- | |
-昨今では後述するJUCEのようなクロスプラグインフレームワーク@<fn>{cross-plugin-fx}のライブラリが存在しており、自前で個別にプラグインフレームワークを開発する理由がなく、かつこれらの商用ライブラリ@<fn>{juce-gplv3}を導入できるのであれば、これが最も手っ取り早いかもしれません。 | |
- | |
-//footnote[cross-plugin-fx][クロスプラグインフレームワークという語句は一般には使われていないのですが、単に「クロスプラットフォーム」と書くだけでは本質を捉えているとは言い難いので、対象を限定した表現を試みています] | |
- | |
-//footnote[juce-gplv3][JUCEはGPL v3で公開されているので、GPLv3互換のフリーソフトウェアとしてプラグインを公開する場合は問題になりませんが、それ以外の状況では商用ライセンスを購入することになります] | |
+この節ではDAWのユーザーが目にするオーディオプラグイン規格をいくつか改めて紹介します。それぞれの技術的な詳細については、本書で詳しく解説することはありません。各種オーディオプラグイン機構について開発者視点で参考になる書籍として、英語圏ではよくWill Pirlke著 "Designing Audio Effect Plugins in C++: For AAX, AU, and VST3 with DSP Theory" が挙げられます。筆者もこれ以上に詳しい書籍には遭遇したことがないので、気になる読者には同書をお薦めします。 | |
-### 一般的なオーディオプラグインフレームワーク | |
- | |
-(1) VST | |
+### VST2, VST3 | |
いわゆるオーディオプラグインと呼ばれるもののなかで最もポピュラーであろうものです。Steinbergがプロプラエタリ・ソフトウェアとして公開していたものですが、現在ではGPL v3と商用のデュアルライセンスで配布されています。かつてはWindowsとMacのみサポートされていたのですが、2019年現在ではLinuxもサポートされています。 | |
VSTはv2が最も普及しているのですが、既にSDKすら公開停止されていて、新規開発はv3が想定されています。もっとも、VST v2を既にダウンロードしている開発者らが使い続けることは考えられますし、DAWの世界では古いソフトウェアがずっと使い続けられることも多く、そのようなユーザーをターゲットにしたオーディオプラグインも引きずられて長期のサポートを強いられる可能性もあり、Python v2とPython v3のように古いものがずっと残り続ける可能性はそれなりにあると筆者は未来を想像しています。 | |
-VSTにはVST-GUIというコンポーネントも存在しているのですが、これはあくまでオプションです。GUI部分は一般的なプログラミングの流儀に従い、音声処理から切り離して作成することが推奨されます。 | |
+VSTにはVST4GUIというコンポーネントも存在しているのですが、これはあくまでオプションです。GUI部分は一般的なプログラミングの流儀に従い、音声処理から切り離して作成することが推奨されます。@<fn>{vst4gui-independent} | |
+ | |
+//footnote[vst4gui-independent][実のところVST4GUIはVSTから完全に独立しており、たとえばLV2オーディオプラグインでも使われています。] | |
-(2) AU (Audio Unit) | |
+### AU (Audio Unit) v2 / v3 | |
AUはApple製品のOS(macOS / iOS)がサポートしているオーディオプラグインの仕様です。Appleの独自路線であり、他のOSでは利用できませんが、OSのSDKのみで提供できることもあってか、さまざまなプラグインによってサポートされています。 | |
AUにはバージョン2とバージョン3があり、両者は大きく異なる特徴をもっています。AUv3はAppExtensionとして独立プロセスでも動作するようになっています。インプロセスでも動作するモードがあるようですが、オーディオプラグイン開発者にとって大きな問題になるのは、App StoreでApp Extensionとして配布するものは独立プロセスで動作するものになるという点です。独立プロセスで動作するということは、メモリの読み書きも自在に行えないことになり、パフォーマンスの劣化が懸念されます。実際、Mac App Storeで配布されているDAWは(2019年の本稿執筆時点で)Logic Proくらいしか無いようです。 | |
-(3) LADSPA / LADSPA v2 (Lv2) | |
+### LV2 | |
-LADSPA v2(Lv2)は主にLinux上でVSTやAUのようなプラグイン規格を策定して実現したものです。ISCライセンスで提供されているので、AUと同様に追加コストを気にせずに使用できるのですが、Linuxをサポートする企業が少ないため、主にLinuxのOSSエコシステムの中でArdourやLMMS、QTractorなどで使われています。商用DAWでもBitwig StudioやTracktion Waveformなどでは部分的にサポートしているものもあります。LV2をサポートしている商用DAWは実のところほとんどありません。これはLinuxサポートが充実していないこともありますが、LV2の仕様がやや複雑でサポートが煩雑になるということもありそうです。 | |
+LV2は主にLinux上でVSTやAUのようなプラグイン規格を策定して実現したものです。ISCライセンスで提供されているので、AUと同様に追加コストを気にせずに使用できるのですが、Linuxをサポートする企業が少ないため、主にLinuxのOSSエコシステムの中でArdourやLMMS、QTractorなどで使われています。商用DAWでもBitwig StudioやTracktion Waveformなどでは部分的にサポートしているものもあります。LV2をサポートしている商用DAWは実のところほとんどありません。これはLinuxサポートが充実していないこともありますが、LV2の仕様がやや複雑でサポートが煩雑になるということもありそうです。@<fn>{lv2-developers-guide} | |
+//footnote[lv2-developers-guide][LV2については筆者が同人誌「LV2オーディオプラグイン開発者ガイド」で詳しくまとめてあります。] | |
-### オーディオプラグイン開発言語 | |
-プログラミングにおけるオーディオ処理の世界はやや特殊な空間です。厳しいリアルタイム要件を満足するためには、ガベージコレクションのように「全てのスレッドを停止させてメモリを回収する」ような処理や、「実行時に必要になったら仮想マシンコードをネイティブコードに時間をかけてコンパイルする」JITのような処理は向いていないとされています。そのため、Javaや.NETのような仮想マシン技術が入り込むことがほぼありません@<fn>{vstnet}。 | |
+## クロスオーディオプラグイン開発SDK | |
-//footnote[vstnet][VST2 SDKを.NET API化しただけのVST.NETというプロジェクトがありますが、これはGCによる遅延を受容するという前提で作られたものです] | |
+### クロスオーディオプラグイン開発SDKの必要性 | |
-このため、オーディオプラグイン開発においては、C++か、一般的にはネイティブコードとしてコンパイル済みの実行バイナリを生成する特殊な言語および開発ツールが使われます。この文脈でよく登場するのがLLVMです。APL(audio programming language)でもよく使われています。同じLLVM言語の系譜でも独自の実行モジュール形式をもちJITを基本とするJuliaなどは似て非なる分類であるといえます@<fn>{julia-aot}。 | |
+多くのオーディオプラグイン製品は、複数のフレームワークに対応しているのですが、VSTとAU、LV2はそれぞれ全く異なるAPIであり、公式に提供されるSDKがあってそれを使用するものについては、開発も別々に行うことになります。LV2はクロスプラットフォームの方式で設計されていますが、LV2はLinux起源のテクノロジーであり、現状ほぼLinux専用となるでしょう。 | |
-//footnote[julia-aot][JuliaはAOTでも動作するようなので、AOTで実用上問題なければこれは障害ではないかもしれません。] | |
+オーディオプラグイン機構では、一般的に、オーディオI/Oに直接依存することはありません。オーディオ入力もオーディオ出力もストリーミングバッファとして表現され、多くは単なるメモリアドレスへのポインターです。そのため、複数プラットフォームをサポートするプラグイン機構でも、コア部分は特にプラットフォーム固有の実装を含むことなくクロスプラットフォームで設計されているのが一般的です。 | |
-オーディオプログラム開発言語の例としては、FaustやExtemporeといったものが挙げられます。JUCEの開発元であるROLIも2018年にSOULという言語をアナウンスしており、いずれSDKが公開されることが期待されます。また、汎用言語でもzigなどはオーディオアプリケーション開発を念頭に置いて設計されています。@<fn>{v} | |
+各種オーディオプラグインのフレームワークは、それぞれ、オーディオI/OやMIDI I/Oと似たようなかたちでバラバラなのですが、基本的な機能要件は類似しており、多くのプラグインでは処理の共通化が可能なものです。たとえばプラグイン側は、VSTであればIAudioProcessorインターフェースの`process`関数、AUであれば `AUInternalRenderBlock`関数、LADSPAやLV2の`run()`関数は、概ね同様の処理を行います(ホスト側にはホスト側のAPIやライブラリがあります)。そのため、(必ずしもクロスプラットフォームというわけではありませんが)複数のプラグインフレームワークをビルドできるようなビルドツールや、プラグインにおけるオーディオ処理の共通化を可能にするライブラリが登場しました。 | |
-//footnote[v][他にはV言語などもno JIT, no GCでオーディオ開発との相性が良さそうですが、宣伝力だけが強い言語であるという側面があるので、ここでは脚注での言及にとどめます。] | |
+このことから、クロスプラグインフレームワーク@<fn>{cross-plugin-fx}のSDKがいくつか登場してきました。自前で個別にプラグインフレームワークを開発する理由がなく、かつこれらの商用ライブラリ@<fn>{juce-gplv3}を導入できるのであれば、これが最も手っ取り早いかもしれません。 | |
+ | |
+//footnote[cross-plugin-fx][クロスプラグインフレームワークという語句は一般には使われていないのですが、単に「クロスプラットフォーム」と書くだけでは本質を捉えているとは言い難いので、対象を限定した表現を試みています] | |
+//footnote[juce-gplv3][たとえばJUCEはGPL v3で公開されているので、GPLv3互換のフリーソフトウェアとしてプラグインを公開する場合は問題になりませんが、それ以外の状況では商用ライセンスを購入することになります] | |
-### クロスオーディオプラグイン開発フレームワーク | |
+### クロスオーディオプラグイン開発SDKの具体例 | |
-各種オーディオプラグインのフレームワークは、それぞれ、オーディオI/OやMIDI I/Oと似たようなかたちでバラバラなのですが、基本的な機能要件は類似しており、多くのプラグインでは処理の共通化が可能なものです。たとえばプラグイン側は、VSTであればIAudioProcessorインターフェースの`process`関数、AUであれば `AUInternalRenderBlock`関数、LADSPAやLV2の`run()`関数は、概ね同様の処理を行います(ホスト側にはホスト側のAPIやライブラリがあります)。そのため、(必ずしもクロスプラットフォームというわけではありませんが)複数のプラグインフレームワークをビルドできるようなビルドツールや、プラグインにおけるオーディオ処理の共通化を可能にするライブラリが、いくつか登場しました。 | |
+本書はシーケンサーエンジンの作り方について解説するのが主目的なので、プラグインの開発のみを対象とするライブラリやフレームワークは本来の話題の対象外なのですが、それらも含めていくつか紹介します。 | |
(1) JUCEはROLIが開発しているクロスオーディオプラグイン開発フレームワークで、この種のフレームワークで最も有名なものでしょう。VST (2, 3)とAUでコードを共通化でき@<fn>{ladspa-host}、それぞれのプラグインフォーマットに合わせたパッケージのビルドも自動的に行え、GUIのモジュールも公開しています。各種プラグインフォーマットに合わせたプロジェクトファイルを自動生成するIntroJucerというツールが便利だった(現在はProjucer)ということも、選ばれている一因であるようです。 | |
-//footnote[ladspa-host][JUCEではLADSPAホストもビルドできるとされていますが、ホストのみでプラグインのビルドはサポートされておらず、コミュニティの有志がDSSIやLV2サポートを実現しているというのが現状です。] | |
+//footnote[ladspa-host][JUCEではLADSPAホストもビルドできるとされていますが、ホストのみでプラグインのビルドはサポートされておらず、またLV2には対応していないので、コミュニティの有志がDSSIやLV2プラグイン開発のサポートを実現しているというのが現状です。] | |
-(2) WDLはReaperを開発しているCockosがzlibライセンスで公開しているクロスオーディオフレームワーク開発ライブラリです。実のところ現在では派生プロジェクトであるiPlug2がメインストリームあるいは最先端に近いと言えるでしょう(iPlug2はWDL-OLというWDLの派生プロジェクトからさらに派生したものです)。これもオーディオプラグイン機構に加えてGUIモジュールが組み込まれています。 | |
+(2) iPlug2はReaperを開発しているCockosがzlibライセンスで公開しているクロスオーディオフレームワーク開発ライブラリであるWDLを、作者が独自に発展させたもので、現在ではWDLより広く知られています。これもオーディオプラグイン機構に加えてGUIモジュールが組み込まれています。 | |
-本書では次章以降、シーケンサーエンジンの例としてtracktion_engineを題材として読み進めますが、これはJUCEのモジュールとして作成されているので、ときどきJUCEの設計について言及しながら解説していきます。しかし、VST3 SDKを使う、あるいはAUの含まれるAudioToolboxを使うのも、もちろん一般的です。 | |
+(3) DPF (DISTRHO Plugin Framework)は主にLV2(LV2, DSSI, LADSPA, VST)をサポートするプラグイン開発フレームワークで、LV2のAPIよりは直感的にC++で開発できるという立ち位置で用いられています。 | |
+ | |
+(4) 第1章で言及したオーディオプログラミング言語の一部、たとえばFAUSTなどは、オーディオプラグインをそのままビルドできるような仕組みも整備されています。商用製品ではMATLABなどもこの用途で利用可能です。 | |
+ | |
+![さまざまなIDEのプロジェクトをエクスポートできるProjucer](projucer-sshot.png) | |
+ | |
+本書では次章以降、シーケンサーエンジンの例としてTracktion Engineを題材として読み進めますが、これはJUCEのモジュールとして作成されているので、ときどきJUCEの設計について言及しながら解説していきます。しかし、VST3 SDKを使う、あるいはAUの含まれるAudioToolboxを使うのも、もちろん一般的です。 | |
+ | |
+オーディオプラグインホスト開発に関連して、もう1つここで紹介しておきたいのが、Carlaというプロジェクトです。これは主にLinuxデスクトップで動作する、単体でも高機能なUIを備えたオーディオプラグインホストで、VSTやLV2をサポートしているのですが、実際にはクロスプラットフォームのライブラリとして他のDAWが組み込んで使うこともできます。Linuxの比較的新しいDAWであるZrythmはこのCarlaを組み込んでおり、該当部分の開発コストが軽減されています。 | |
### オーディオプラグインフレームワークの依存関係 | |
@@ -81,80 +86,202 @@ LADSPA v2(Lv2)は主にLinux上でVSTやAUのようなプラグイン規格 | |
一般的に、オーディオプラグインフレームワークそのものは、オーディオI/Oに依存しないかたちで実装可能です。VST3SDKもLADSPA/Lv2も、オーディオデータの処理はバイト配列の集合を対象として行われます。加工結果をオーディオデバイスに流し込む必要は無いのです。 | |
-JUCEのような統合オーディオアプリケーション開発フレームワークでは、特に依存関係を切り離す必要はないのですが、オーディオI/Oを担う`juce_audio_devices`とオーディオプラグインの実装を担う`juce_audio_processors`は別のモジュールとして構成されています。逆にいえば、JUCEは単なるオーディオプラグイン開発フレームワークというよりは統合的な音楽アプリケーション開発フレームワークなので、JUCEにある全ての機能がオーディオプラグイン開発に必要というわけではありませんし、特にホスト開発で有用な機能とプラグイン開発に必須の機能には、大きな開きがあります。 | |
+後述するJUCEのようなクロスプラットフォームのオーディオ開発フレームワークでは、特に依存関係を切り離す必要はないのですが、オーディオI/Oを担う`juce_audio_devices`とオーディオプラグインの実装を担う`juce_audio_processors`は別のモジュールとして構成されています。JUCEは単なるオーディオプラグイン開発フレームワークというよりは統合的な音楽アプリケーション開発フレームワークなので、JUCEにある全ての機能がオーディオプラグイン開発に必要というわけではありませんし、特にホスト開発で有用な機能とプラグイン開発に必須の機能には、大きな開きがあります。 | |
-オーディオプラグインの処理に求められるのは、リアルタイムでも処理出来る程度に細分化された、単なるストリームバッファの加工であり、それもホストから呼び出されるものであるため、自らがリアルタイムのスレッドを管理したりすることもありません。 | |
+オーディオプラグインの処理に求められるのは、リアルタイムでも処理出来る程度に細分化された、単なるストリームバッファの加工であり、それもホストから呼び出されるものであるため、自らがリアルタイムのスレッドを管理したりすることもありません。もちろん、プラグイン開発では、ホスト側が用意してくれたリアルタイム性を台無しにしないようなプログラミングが求められますし、それは開発フレームワークにも当てはまります。 | |
ホスト側の機能としても、リアルタイムスレッドの用意などはアプリケーション側が行えばよいことです。オーディオバッファの適切な受け渡しや、プラットフォーム依存のやり方でプラグインを列挙したり動的にロードしたりする機能が、プラグインホストをサポートするAPIとしては期待されます。プラットフォーム固有の実装が求められますが、標準APIで実現できる範囲といえるでしょう。 | |
+ | |
+## オーディオプラグインとインスタンスのID | |
+ | |
+MIDIによって音楽を表現していた頃は、音色設定に相当するのはプログラム・チェンジと呼ばれる命令でした。この命令で実際に指定される音色は、接続されているMIDI出力デバイスによって異なるものでした。ただし、General MIDI(GM)などの標準では、プログラム・チェンジの指定値が音色をある程度規定しており、GM準拠の楽曲であればGM準拠のMIDIデバイスで概ね作成者の意図通りに楽曲を再生できました。 | |
+ | |
+MIDIの規定する音色は番号で識別する程度の表現力しかありません。どの環境でも同一の音声を再現するためには、サンプリングなどに基づくオーディオプラグインなどを楽器として使用する必要があります。現代的なDAWにおいては、音色設定はMIDIトラックごとに、(次に説明するルーティングによって接続済みの)オーディオプラグインを指定して、それを楽器として使用するのが一般的です。 | |
+ | |
+オーディオプラグインは、一般的には一意識別子(ユニークID)で、あるいはそれが存在しないプラグイン機構やIDの無いプラグインについては名前で識別されます。名前でも大抵の場合は識別が可能ですが、同じ名前をもつ人間がし得るように、同じ名前のプラグインが存在することもあり得ます。一意識別子には通常UUIDやハッシュ文字列の一部など「ほぼ衝突しない」ものが用いられます。もう少し原始的なDAWだと、オーディオプラグインのファイルパスで識別されることもあります。 | |
+ | |
+実際に楽曲制作する場面では、1つのプラグインについて複数のインスタンスがトラックごとあるいは同じトラックでもルーティング次第で2つ以上生成されることもあります。楽曲上ではどのプラグインのインスタンスに対するパラメーター設定やルーティングの接続かを識別する必要があるため、インスタンスIDはプラグインIDとは別に管理する必要があります。 | |
+ | |
+### 補論: 適切なオーディオプラグインの識別方法 | |
+ | |
+オーディオプラグインをファイル名で識別していると、PC環境を移動すると開けない、ということになってしまうので、あくまで補完的な情報として楽曲データに含める程度にするべきです。ちなみに、オーディオプラグインホストによってはファイルパスをあくまでパスのヒント情報として含めているということがあります(本書で度々登場するTracktion Engineがそのようになっています)。 | |
+ | |
+筆者の私見としては、ローカルのファイルパスには個人の環境を特定する情報が含まれている可能性があるので(たとえば匿名の作曲者として楽曲データをインターネット上で公開していても、そのデータに`c:\\Users\\atsushi\\VST3 Plugins`というパスが含まれていれば、名前が`atsushi`であることが公知情報になってしまいます)、ローカルパスは可能な限り楽曲データに含むべきではありません。 | |
+ | |
## オーディオプラグインの接続 | |
### ルーティング(パッチ) | |
-基本的なオーディオプラグインの使われ方は1つのホストと1つのプラグインの関係ですが、音楽制作者が実際にGUI上で目にするのは、複数のオーディオプラグインがチェーン状に繋がったかたちで次々に処理されて、最終的にオーディオ出力デバイスに送信される、という流れです。実際には、ホストがひとつのオーディオプラグインから処理結果を受け取ると、ホストはその結果をそのまま次のオーディオプラグインに逐次的に流します。この連鎖的な処理をオーディオのルーティングと言います。 | |
+基本的なオーディオプラグインの使われ方は1つのホストと1つのプラグインの関係ですが、音楽制作者が実際にGUI上で目にするのは、複数のオーディオプラグインがチェーン状に繋がったかたちで次々に処理されて、最終的にオーディオ出力デバイスに送信される、という流れです。実際には、ホストがひとつのオーディオプラグインから処理結果を受け取ると、ホストはその結果をそのまま次のオーディオプラグインに逐次的に流します。この連鎖的な処理はオーディオのルーティングと呼ばれます。また、オーディオプラグインが接続された全体的な構造はオーディオ(ノード)グラフと呼ばれます。 | |
ルーティングは、特にGUIで表示されると分かりやすいのですが、DAWから渡されるオーディオ入力およびMIDI入力、ひとつひとつのオーディオプラグイン、そしてオーディオ出力およびMIDI出力が「ノード」となるノードグラフとして表されます。次の画像はTerraプロジェクト@<fn>{terra}の公式サイトに含まれるオーディオグラフのスクリーンショットです。 | |
-//image[TerraScreenShot][Terraのオーディオグラフ(公式スクリーンショット)][scale=0.7] | |
- | |
//footnote[terra][https://github.com/hotwatermorning/Terra/] | |
ひとつのノードの出力が複数のノードに渡されることもあれば、複数のノードからの入力がひとつのオーディオプラグインで処理されることもあります。最終的な出力先も、設定次第で複数のオーディオデバイスに送信できることもあるでしょう。この部分はシーケンサーエンジンによって異なり、単純なリストになっていることが多いですが、シーケンサーエンジンが特徴を出す部分でもあります。たとえばaudiomultiという音楽制作ソフトウェアではオーディオプラグインのポートを柔軟にパッチできるUIを備えていると宣伝しています@<fn>{audiomulti-platforms}。 | |
//footnote[audiomulti-platforms][残念ながら存在するのはWindows版とMac版のみです。] | |
+//image[TerraScreenShot][Terraのオーディオグラフ(公式スクリーンショット)][scale=0.7] | |
+ | |
### チャンネルとオーディオバス | |
それぞれのオーディオトラックには、**オーディオバス**(audio bus)と呼ばれるオーディオチャンネル別のオーディオストリームの出入り口が用意されます。モノラルなら1つ、ステレオなら2つ、ambisonic対応のオーディオ出力であれば多数あります。一般的には、オーディオプラグイン側にバスを接続できるポートがあります。「ステレオの左側をオーディオ入力ポート1に、右側をオーディオ入力ポート2に接続」のようなかたちになります。ロータリーエンコーダーのようにパンを振り分けるエフェクトが、この仕組みを活用します。リバーブやディレイでも左右の広がり方をカスタマイズできるかもしれません。 | |
-論理的には、ステレオ出力のポートとambisonicなどのサラウンドチャンネル対応のポートは、別個のものとして存在するものです。しかし伝統的にオーディオプラグインはステレオチャンネルを前提として開発されてきたものであり、ステレオ出力にのみ対応するものがほとんどでした。しかし先のロータリーエンコーダーのようなエフェクトであればともかく、ディストーションなど多くのエフェクト、あるいはインストゥルメントプラグインでは、左右同じ内容の波形を出力しており、これはそのままサラウンドチャンネルで使っても支障がありません。実際、プラグイン側でサラウンド入力に対してはステレオ指定と同様の入出力を処理するのは一般的でしょう@<fn>{halion} | |
+論理的には、ステレオ出力のポートと3Dオーディオなどの多チャンネル対応のポートは、別個のものとして存在するものであり、特にVRサポートなどで3Dオーディオの対応要求が高まっているであろう現代では、ステレオに固定されない適切なオーディオバスのサポートは重要です。 | |
+ | |
+もっとも、伝統的にオーディオプラグインはステレオチャンネルを前提として開発されてきたものであり、ステレオ出力にのみ対応するものがほとんどでした。先のロータリーエンコーダーのようなエフェクトであればともかく、ディストーションなど多くのエフェクト、あるいはインストゥルメントプラグインでは、左右同じ内容の波形を出力しており、これはそのままサラウンドチャンネルで使っても支障がありません。実際、プラグイン側でサラウンド入力に対してはステレオ指定と同様の入出力を処理するのは一般的でしょう@<fn>{halion} | |
//footnote[halion][たとえばSteinberg HALionのマルチチャンネルエフェクトはそのようにドキュメントで説明されています。] | |
-これらのオーディオプラグインのポートの「接続」とは、具体的にはオーディオストリームの入出力に使われるバッファのポインターを設定する、ということです。古典的なデスクトップのDAWであれば、ホスト側でメモリバッファを用意して、そのポインターを直接プラグイン側に渡すだけで事足りるでしょう。 | |
+これらのオーディオプラグインのポートの「接続」は、基本的には単なる観念上のものですが、オーディオストリームの入出力に使われるバッファのポインターを設定する、というものもあります。LV2プラグインの仕組みではこれが必須になります。ただ、これではホスト側はリングバッファのポインタを動的に渡したり、空いているバッファを動的に使い回すといった最適化が行えない、筋の悪い仕組みです。 | |
-一方で、モバイルOSやモダンなデスクトップ環境では、ホストプログラムとそこからロードされたプラグインが、プロセスとしては別空間で動作している可能性があります。プラグインがクラッシュしてもホスト側が巻き添えを食って落ちることがない設計にするための仕組みなのですが、この場合、この両者の間では、共有メモリのみがデータの受け渡しに利用可能になるので、場合によってはオーディオバッファリングのパフォーマンスに大きな影響を与えることになります。 | |
+さらに、LV2にはオーディオポートの観念が無く全てがポートであるという設計上、オーディオバスを適切に判別・調整できないという問題があります。これは3Dオーディオの需要が高まる以前から、既にモノラルプラグインとステレオプラグインの違いで問題になっていました。LV2仕様設計者も開発メンバーであるLV2のDAWであるArdourでは、モノラルプラグインをステレオチャンネルに接続するときは、内部でインスタンスを2つ生成してパラメーター変更なども2つ同時に行うようなハックで対応していました。VST3ではホストとプラグインでオーディオバス調整のネゴシエーションが行われ、プラグインの適用はその調整に基づいて生成されたバッファ構造が渡されるので、このような問題は生じません。 | |
+また、ポインターを固定する方式は、古典的なデスクトップのDAWであれば、ホスト側でメモリバッファを用意して、そのポインターを直接プラグイン側に渡すだけで事足りますが、一方で、モバイルOSやモダンなデスクトップ環境では、ホストプログラムとそこからロードされたプラグインが、プロセスとしては別空間で動作している可能性があります。プラグインがクラッシュしてもホスト側が巻き添えを食って落ちることがない設計にするための仕組みなのですが、この場合、この両者の間では、共有メモリのみがデータの受け渡しに利用可能になるので、場合によってはオーディオバッファリングのパフォーマンスに大きな影響を与えることになります。 | |
-## オーディオプラグインの一般的な処理 | |
+## オーディオプラグインの機能 | |
-オーディオプラグイン機構はいくつか存在しますが、いずれの機構についても、次のような機能・処理をサポートしていることが期待されます。 | |
+オーディオプラグイン機構はいくつか存在しますが、いずれの機構についても、次の各節で示すような機能・処理をサポートしていることが期待されます。 | |
-(1)クエリー | |
+### インストールされているプラグインの列挙 | |
オーディオプラグインホストは、ユーザーの環境にインストールされているオーディオプラグインを列挙します。典型的な挙動としては、システム上の特定のパス(たとえばVSTであれば`c:\Program Files\VSTPlugins`など)にインストールされているプラグインが列挙されます。開発中であれば開発用のプラグインだけを列挙したい場合もあり、環境変数でオーバーライドできるプラグイン機構もあります。 | |
プラグインの列挙はそれなりに時間コストがかかる作業であり、またDAW上でプラグインを列挙するような操作は頻繁に呼び出されます。そのため、多くのDAWではプラグインを列挙した結果をキャッシュします。また、オーディオプラグインホスト側は複数のプラグイン機構をサポートしているのが一般的です。 | |
-(2)ロード・アンロード | |
+オーディオプラグインホスト上のUIにプラグインが列挙されると、ユーザーはプラグインを選択してトラックのプラグインリストに追加するでしょう。あるいはもし柔軟なルーティングを指定できるDAWやプラグインホストであれば、プラグインをルーティングできるようにノードの接続を調整するようになるでしょう。 | |
-オーディオプラグインホスト上のUIにプラグインが列挙されると、ユーザーはプラグインを選択して操作しようとします。あるいは、楽曲データでオーディオプラグインが指定されていたら、ロード時にそのプラグインもロードしようとするでしょう。 | |
+### インスタンスの生成・利用・破棄 | |
-オーディオプラグインは、一般的には一意識別子(ユニークID)で、あるいはそれが存在しないプラグイン機構やIDの無いプラグインについては名前で識別されます。名前でも大抵の場合は識別が可能ですが、同じ名前をもつ人間がし得るように、同じ名前のプラグインが存在することもあり得ます。一意識別子には通常UUIDやハッシュ文字列の一部など「ほぼ衝突しない」ものが用いられます。もう少し原始的なDAWだと、オーディオプラグインのファイルパスで識別されることもあります。 | |
+プラグインのインスタンスのライフサイクルは、どのフレームワークでもおよそ次のような準備や状態遷移を前提に作られています。 | |
-オーディオプラグインには、一般的にはネイティブコードの動的ライブラリが含まれており、ホスト側はプラグインを動的にロードして実行できる仕組みが必要になります。古典的なデスクトップ環境では比較的簡単で、Windowsであれば`LoadLibrary()`、Unix系OSであれば`dlopen()`を使用してロードすることになるでしょう。一方で、OSによってはプラグインがホストにとって「外部プログラム」になることがあり、その場合はプラットフォーム固有の方法でロードされることもあります。macOSやiOSのApp Extensionなどがこれに当てはまります。 | |
+(1)ロード・アンロード | |
-(3)バッファの準備 | |
+いったんプラグインがトラックに追加されたら、そのプラグインがDAWにロードされます。あるいは、楽曲データでオーディオプラグインが指定されていたら、ロード時にそのプラグインもロードしようとするでしょう。 | |
-前節で説明した通り、オーディオプラグインは、通常は1つのトラックに対して複数指定されます。トラックのオーディオプラグイン設定は、一般的には複数のプラグインを繋ぐ単純な一方向のリストになっています。それらのオーディオバスを、オーディオプラグインのポートに接続する処理が必要になります。何をどこに接続するかはホストが指定することになります。 | |
+オーディオプラグインには、一般的にはネイティブコードの動的ライブラリが含まれており、ホスト側はプラグインを動的にロードして実行できる仕組みが必要になります。古典的なデスクトップ環境では比較的簡単で、Windowsであれば`LoadLibrary()`、Unix系OSであれば`dlopen()`を使用してロードすることになるでしょう。一方で、OSによってはプラグインがホストにとって「外部プログラム」になることがあり、その場合はプラットフォーム固有の方法でロードされることもあります。macOSやiOSのApp Extensionの仕組みを使うAUv3などがこれに当たります。 | |
+ | |
+(2)バッファの準備 | |
+ | |
+前節で説明した通り、オーディオプラグインは、通常は1つのトラックに対して複数指定されます。トラックのオーディオプラグイン設定は、一般的には複数のプラグインを繋ぐ単純な一方向のリストになっています。それらのオーディオバスを、オーディオプラグインのポートに接続する処理が必要になります。何をどこに接続するかはホストが指定することになります。前述の通り、オーディオバスの概念が存在しないLV2と存在するVST3では、ここで期待される処理がだいぶ異なります。 | |
バッファの接続が必要なのはオーディオストリームだけではありません。コントロールパラメーターのやり取りにも、専用のバッファが用意されます。VSTi (Instrument) などは、オーディオストリームではなくコントロールパラメーターを受け取ってオーディオストリームを生成します。 | |
-(4)アクティベート・ディアクティベート | |
+(3)アクティベート・ディアクティベート | |
-オーディオプラグインによるストリームの加工は、パフォーマンス上の理由から、優先度の高いオーディオスレッドで行われるのが一般的です。この場合、オーディオプラグイン独自のスレッドで待機したり同期したりすることはないということになります。使用していない状態のオーディオプラグインの処理を走らせたままにしておくのはコンピューターリソースの無駄遣いですが、独自のスレッドで制御することはできないので、単純にプラグインのスイッチで有効・無効を切り替えて、無効のときは何も加工せずに処理を完了して呼び出し元に帰るようにします。これを実現するのがアクティベート処理です。処理といっても単にフラグを切り替えるだけかもしれません。 | |
+オーディオプラグインによる演奏中のストリームの加工は、リアルタイム制約により、優先度の高いオーディオスレッドで行われるのが一般的です。この場合、オーディオプラグイン独自のスレッドで待機したり同期したりすることはないということになります。使用していない状態のオーディオプラグインのリアルタイム処理を走らせたままにしておくのはコンピューターリソースの無駄遣いですが、独自のスレッドで制御することはできないので、単純にプラグインのスイッチで有効・無効を切り替えて、無効のときは何も加工せずに処理を完了して呼び出し元に帰るようにします。これを実現するのがアクティベート処理です。処理といっても、多くのプラグインでは単に内部的な状態フラグを切り替えるだけかもしれません。 | |
-(5)加工処理 | |
+(4)加工処理 | |
ここまで準備が整ったら、ようやくホストとのオーディオストリームのやり取りが可能になります。ここで何を行うかはプラグイン次第です。オーディオストリームの処理はリアルタイムで行われる必要があるので、あまり複雑な処理は行えません。一般的には、1回の呼び出しで全ポートの入力を処理することになります。またオーディオプラグインは連結しているので、複数のプラグインが全て処理を完了するまでには、それなりの時間がかかることになります。 | |
-ちなみに、加工処理とバッファ準備は別々の作業として扱われていますが、ホスト側でのバッファリングの実装によっては、加工処理の前に必ずバッファ設定が呼び出されるように実装されるかもしれません。そのようなプラグイン機構については、オーディオプラグイン側はこれを前提として設計する必要があります。LADSPA、LV2は加工処理`run()`の前にバッファを`connect_port()`で指定するので、たとえばリングバッファで毎回先頭ポインタが移動する場合には、毎回`connect_port()`の呼び出しが必要になります。VST3では加工処理の呼び出しごとにバッファが引数として渡されるので無関係です。 | |
+ちなみに、加工処理とバッファ準備は別々の作業として扱われていますが、ホスト側でのバッファリングの実装によっては、加工処理の前に必ずバッファ設定が呼び出されるように実装されるかもしれません。そのようなプラグイン機構については、オーディオプラグイン側はこれを前提として設計する必要があります。LV2は加工処理`run()`の前にバッファを`connect_port()`で指定するので、たとえばリングバッファで毎回先頭ポインタが移動する場合には、毎回`connect_port()`の呼び出しが必要になります。VST3では加工処理の呼び出しごとにバッファが引数として渡されるので無関係です。 | |
-(6)アンロード | |
+(5)アンロード | |
プラグインの処理が全て終わって、プラグインが不要になったら、メモリ上からアンロードします(次の使用に備えてロードしっぱなしにしておくかもしれません)。 | |
+### オーディオプラグインのパラメーターとstate | |
+ | |
+オーディオプラグインの調整は、理想的にはオーディオプラグインパラメーターのみで表現できることが望ましいですが、必ずしもこれが実現出来ない場合があります。たとえば… | |
+ | |
+- オーディオプラグイン作者が、DAWを経由してユーザーにリバースエンジニアリングされたくない・妨害したいので、パラメーターの名前や値を公開したくないという場合 | |
+- パラメーターのバージョンが上がったことで下位互換性などを維持できなくなり、単なる名前と値だけでは十分に意図を表現できなくなったりした場合@<fn>{param-backward-compat} | |
+- 変更をリアルタイムに反映できない程度に長大な処理を伴う場合 | |
+- プラグイン機構が数値以外のフォーマットをサポートしていないが、プラグイン側でユーザー設定が必要になる場合 | |
+- サンプリングデータなどパラメーターとして渡すには巨大すぎるデータの受け渡しが必要になる場合 | |
+ | |
+//footnote[param-backward-compat][実のところ、筆者がそういう話を聞いたことがあるという程度で、本当にパラメーターで表現できないことなのかは疑問の余地が大いにあるところです。] | |
+ | |
+特に、文字列データに関しては、DAW側のピアノロール等で入力させるUIを構築するのかという問題もありますし、リアルタイムでオーディオデータを処理しないといけないのにその中で文字列を処理できるのかという問題もあることから、基本的に文字列はサポートされるべきではないともいえます。列挙値などの使って代替するやり方が推奨されるところです。 | |
+ | |
+このような場合でも、オーディオプラグインとホストの間で情報をやり取りできるように、一般的なプラグイン機構ではstate(状態)と呼ばれるバイナリデータを取得・設定できるようになっています。 | |
+ | |
+オーディオプラグインの設計やDAWの設計において、stateを使う場合に一般的に注意すべきことは、stateはMIDIのコントロールチェンジや一般的なパラメーターとは異なり、通常はファイルI/Oなどを伴う可能性があり、一般的には演奏命令としてトラックの再生途中で変更できるようには作られていないという点です。プラグイン開発者は、stateを曲全体を通して固定となる音色設定などに付随するものとして使うべきですし、ホスト開発者は、stateの変更がリアルタイム処理中に発生することはない、と前提にできます。 | |
+ | |
+ | |
+## 高度な話題 | |
+ | |
+この節ではここまで取り上げなかったある程度高度な話題をいくつか取り上げていきます。 | |
+ | |
+### プラグイン本体とプラグインGUIのインタラクション | |
+ | |
+本書ではDAWの機能を実現するためのGUI構成要素については扱いませんが、シーケンサーエンジンからプラグインのGUIを呼び出したり管理したりする場面で生じる諸問題についてはある程度論じておきます。 | |
+ | |
+DAWでオーディオプラグインをロードしたら、その後トラックの編集画面でGUIを開いてパラメーターを調整する作業が可能になります。典型的なDAWの挙動としては、ユーザーがGUIを開く度にそのGUIが独立したウィンドウとして表示され、一般的なアプリケーションのウィンドウとはやや異なる規則に従って動作します。たとえばESCキーを押すだけでウィンドウが閉じたりします(DAWによります)。ウィンドウを閉じた場合でも、GUIのライブラリは再度ウィンドウを開く指示が出たときに直ちに表示できるように、ロードされたままの状態かもしれませんし、メモリを早く開放するために共有ライブラリをDAWプロセスからアンロードするかもしれません。 | |
+ | |
+オーディオプラグインではGUIも提供されるのが一般的で、もしオーディオプラグインのライブラリコードにプラグインのエディターGUIを表示するインターフェースが実装されていれば、DAWはそれをロードして表示します。一方で、プラグインにはGUIを提供しないという選択肢もあり、その場合でもプラグインがサポートするパラメーターのリストなどの情報があれば、そこから最低限のGUIを自動的に作成できます。 | |
+ | |
+およそ一般的なGUIアプリケーションがロジックとビューを分離するように、プラグインのGUIとプラグインのオーディオ処理コードは分離しているのがあるべき姿です。オーディオプラグインフレームワークは多かれ少なかれこれをAPIによって強制します。 | |
+ | |
+VST3, AU, LV2のうち、最も強制力がしっかりしているのはLV2で、オーディオプラグイン本体の共有ライブラリとプラグインGUIの共有ライブラリは別々に`dlopen()`でロードされます。ライブラリとして別々にロードされるので、よほど意図的に取り組んだりしない限り、望ましくないUIとロジックのコードの混合は生じません。GUIとプラグイン本体の間のやり取りは、ホストとプラグインの間のやり取りと同様に、プラグインのポートを通じて行われます(使用するAPIはLV2 UI拡張が提供する独自のものです)。 | |
+ | |
+VST3はそこまで厳しくできていません。まずプラグインのインスタンスに対するパラメーターの取得・設定などをサポートするVST-MAのコンポーネントのインターフェース`IEditController`を実装して、そのメンバーである`createView()`関数が返す`IPlugView`インターフェースを実装することで、GUI機能が実現します。プラグインの状態を操作する場合は、`IPlugView`からこの`IEditController`のAPIを経由して行うことが期待されます。 | |
+ | |
+AUも厳しくなく、プラグインGUIは`AUViewController`のサブクラスとして定義することになります。これはAUプラグインのインスタンスとなる`AUAudioUnit`を生成する`createAudioUnit`メソッドを実装することにもなるので、実質的に非UIロジックをどれだけ分離させるかは開発者の能力次第と評価できるでしょう。 | |
+ | |
+JUCEはVST3やAUのアプローチに近く、プラグイン開発者は`AudioProcessor`クラスから派生する自分のプラグインを実装する過程で、そのメンバー関数`createEditor()`から`AudioProcessorEditor`から派生した自分のプラグインUIのインスタンスを返すことになります。ここにはAPIによる適切な縛りが無く、`AudioProcessor`が`AudioProcessorEditor`のインスタンスを参照しないようにコーディングするのはプラグイン開発者の自己責任です。 | |
+ | |
+オーディオプラグインには、一般的なGUIアプリケーションよりもさらに大きな、プラグインのコードとGUIのコードがきちんと分離するべき理由があります。それは生存期間の違いです。GUIはDAWから表示する指示を受けない限りインスタンス生成すら必要がなく、多くの場合はそのトラックの編集作業を経ずに演奏できることになります。ウィンドウが開いている間だけビジネスロジックが生きていればよい、というデスクトップアプリケーションとは、この点でだいぶ勝手が異なるわけです。 | |
+ | |
+### ホストGUIとプラグインGUIのインタラクション | |
+ | |
+プラグイン本体とプラグインUIの関係も単純ではありませんが、DAWなどのプラグインホストがプラグインのUIを表示する場面も単純ではありません。 | |
+ | |
+まず典型的な問題として、典型的なDAWはオーディオプラグインをインプロセスでロードする@<fn>{inproc-explained-later}ため、同じアプリケーションのプロセスの中で複数のプラグインのライブラリが動的にロードされることになります。もしそれらのプラグインが、互いに同一アプリケーションの中で共存し得ないライブラリを参照していたらどうなるでしょうか? 結果はそのライブラリ次第ですが、きちんと動作しない可能性が高いでしょう。 | |
+ | |
+//footnote[inproc-explained-later][この詳細については節を改めて論じます。] | |
+ | |
+これはLinuxデスクトップ環境ではすでに具体的な問題となっています。それがGTK2, GTK3, Qt4, Qt5の排他的な関係です。GTK3は特にプラグインUI側でアプリケーションループを独自に確立する部分がDAW側のアプリケーションループの存在と相容れないようです。このため、2020年の本版執筆時点では、LinuxデスクトップではX11を直接操作するようにして、余計なUIフレームワークを使わないことが理想とされているのが現状です。 | |
+ | |
+### オーディオグラフを実現する技術 | |
+ | |
+本書でたびたび言及しているTracktion Engineでは、2020年に新機能の目玉として`tracktion_graph`という新しいオーディオグラフのモジュールが開発されており、その知見が2020年のAudio Developers Conferenceのセッションとして発表されました@<fn>{tracktion-graph-video}。これがオーディオグラフの設計と実装に関する知見の詰まった内容だったので、ここでもいくつかの問題を取り上げます。 | |
+ | |
+//footnote[tracktion-graph-video][youtubeで"Introducing Tracktion Graph: A Topological Processing Library for Audio"という動画を検索するとセッション動画が出てきます。] | |
+ | |
+#### DAG | |
+ | |
+オーディオグラフが単純な直線的なリストだけではない場合、どのプラグインのどのチャンネル/ポートがどこに繋がっているかを表現するデータモデルが必要になります。実際には、プラグインだけではなく、オーディオ入力やMIDI入力を起点として、オーディオプラグイン群を経て、オーディオ出力やMIDI出力@<fn>{audio-graph-midi-output}を終点とする構造です。これは情報工学では有向非巡回グラフ(directed acyclic graph, DAG)と呼ばれるものです。あるノードのポートが他のノードのポートに接続され、それが起点から終点まで一貫して向かっている構造になります。 | |
+ | |
+//footnote[audio-graph-midi-output][この場合、MIDI出力とは最終的な出力のことであり、MIDIシーケンスを加工して次のオーディオプラグインに処理させるものは含まれません。それは一般的なプラグインの出力ポート類です。] | |
+ | |
+オーディオプラグインのチェインが適切に処理されるためには、このグラフが正しく構成されている必要があります一部のポートの向きが逆になっていたり、ループ構造が作られてしまっていたりすると、正しく処理できなくなってしまうためです。間違ったグラフのままオーディを処理を開始してしまうと、永久ループに陥ってフリーズすることになります。 | |
+ | |
+DAGの正しさを検証しながら処理すべきノードの順序を構築する方法として、深さ優先検索 (depth-first search)と幅優先探索 (breadth-first search)というアルゴリズムが知られています。オーディオプラグインのルーティングの場合は、オーディオ出力(および存在する場合はMIDI出力)をルートノードとするグラフを構築する必要があります。 | |
+ | |
+#### オーディオグラフの動的な変更とタイミング補正 | |
+ | |
+オーディオグラフに沿って演奏処理が進むと、その過程で特定のオーディオグラフのノードで遅延が発生し、それがそのまま最終的な出力まで遅延となってトラック全体が遅延することになります。遅延の発生そのものは避けられない問題ですが、その影響を小さく抑えるために内部的にノードごとに遅延情報をもたせておいて、他のトラックと協調的に全体を遅延させるような処理がシーケンサーエンジンの内部で必要になります。 | |
+ | |
+一方で、オーディオグラフはユーザーが有効・無効を切り替えたり順列を組み替えたりすることがあり、これは現在進行形で進んでいる演奏処理に影響します。特に遅延情報については全てのノードで把握できる必要があるだけでなく、変更前と変更後のグラフで一貫性のある情報を持っていなければならない、といった実装上の課題が発生することでしょう。途切れないオーディオグラフのプレイヤーを実現するためには、こういった複雑な問題に取り組む必要があります。 | |
+ | |
+### プラグインサンドボックス機構 | |
+ | |
+プラグイン機構というものは、DAWに限らずソフトウェアのクラッシュを引き起こしやすくします。アドイン機構とは、別々の開発者によって作られたホストアプリケーションとプラグインが特定のインターフェースに沿って動作するものであり、そこには最初から何らかの齟齬が発生しうるだけでなく、開発が継続して実装が変わっていく過程で、これまでインターフェースではなく実装に依存して動作していた部分なども動作しなくなり、それらが実行時にホストアプリケーションとしてクラッシュするためです。 | |
+ | |
+Google Chromeが流行した最初のきっかけは、ブラウザの処理速度もさることながら、Webサイトごとにプロセスを分離して、あるページの処理でブラウザがクラッシュしても全体としては動作し続ける仕組みでユーザビリティを向上させたためでした。FirefoxもやがてChromeのようなプロセス分離を実装しました。 | |
+ | |
+DAWも往年のWebブラウザのようによくクラッシュしますが、その多くはプロセス分離が実装されていないからです。DAWでもプロセス分離すれば良いのではないでしょうか? 実際、以降で言及するモダンなDAWの一部はプロセス分離を実装しています。一方でこれは実装コストがかかる作業であり、かつ性能面で犠牲をともなう設計です。OSSのDAWであるArdourでは、プロセス分離は実装しないと明言しています@<fn>{ardour-crash-protection}。 | |
+ | |
+//footnote[ardour-crash-protection][Why doesn't Ardour offer "plugin crash protection" ? という題で記事が公開されているので、Webで検索してみてください。] | |
+ | |
+プロセスを分離して具体的に犠牲になるのは、リアルタイム性能です。DAWのプロセス内で全てのプラグインの処理が完結する場合と比べて、プロセス切り替えを伴うサンドボックス方式になると、プロセス間のCPUコンテキストスイッチが発生します。これが1件あたり数十ナノ秒くらいとされていて@<fn>{old-context-switch-paper}、Ardourの記事にある数字をそのまま引用すると、たとえば128トラックで384プラグインの間でコンテキストスイッチが発生すると、768件の切り替え処理だけで7.7〜23ミリ秒の遅延になります。オーディオ遅延の許容値がおよそ10ミリ秒だと考えると、これだけでも厳しいのは確かです。 | |
+ | |
+//footnote[old-context-switch-paper][前述のArdourの記事からリンクされているのですが、2010年頃の資料に依拠しています。] | |
+ | |
+もっとも、リアルタイム性が本当に問題になる場面はライブパフォーマンス時などに限られており、これだけを理由にサンドボックスの有用性を否定するのは妥当ではありません。100トラックもの構成になってくると、トラックメイキング時にはトラックのフリーズも併用しながら作曲するのが一般的なので、実際に発生するコンテキストスイッチの数も少なくなります。それでDAWが落ちて打ち込み作業の内容が消えてなくなる可能性が小さくなるなら、十分にやる理由になります。 | |
+ | |
+サンドボックス機構はいくつかのDAWで実装されていますが、その具体的な方法はいくつか考えられます。参考実装としてBitwig Studioを見てみましょう。 | |
+ | |
+![Bitwig Studio 3.2のプラグインサンドボックス設定](bitwig-studio-sandboxing.png) | |
+一番左から順に、DAWのインプロセスで動作するモード、プラグインだけを全てひとまとめにして処理するモード、製造者別にプラグインをまとめて処理するモード、プラグインの製品ごとにまとめて処理するモード、そして全てのプラグイン・インスタンスを個別のプロセスで動作させるモード、となっています。あるプラグインがクラッシュしたとき、どこまで巻き込んでクラッシュするかを選択していることになります。それぞれの塊の中で発生する切り替えはCPUのコンテキストスイッチにはならないので、これで合理的な範囲をユーザーが選択できることになります。 | |
+Tracktion Waveform11は別のやり方で対処しています。Waveform11にはBitwigのようなサンドボックス設定はありません。以前のバージョンでは、プラグインがクラッシュするとDAWがクラッシュしていました。Waveform11では、「オーディオエンジン」をリセットできるようになりました。プラグインがクラッシュすると"Audio Engine crashed"というエラーメッセージがダイアログ表示され、リセットするまでは音声処理が何も行われません。それでもDAWは生き残っているので、引き続き編集作業は可能な状態です。リセットもボタン1つで初期状態になるだけです。プラグインごとのコンテキストスイッチは発生しません。 | |
+Bitwigのアプローチは柔軟ですが、機能が豊富なので期待通りに動作しない可能性はあるでしょう。その代わり、演奏中にクラッシュしても一部のトラックが無音になるだけで引き続き演奏できる(かもしれない)のは魅力的かもしれません。筆者はTracktionのアプローチでも十分合理的な作業になると考える立場です。 | |
+最後に、これまでも何度か言及してきましたが、AndroidやiOS、Mac App Storeアプリケーションのような環境では、プラグインが必然的に別プロセス上で動作することになるため(Bitwig Studioでいえばプラグインごとかプラグインインスタンスごとのサンドボックスです)、アプリケーションのアーキテクチャとして、ここで論じてきたサンドボックスのモデルが強制されることになります。これらの環境ではリアルタイム性能にマイナス影響が出ることは避けられないでしょう。 | |
diff --git a/4-sequencer-engine.md b/4-sequencer-engine.md | |
index 6f0d53f..1c7b88e 100644 | |
--- a/4-sequencer-engine.md | |
+++ b/4-sequencer-engine.md | |
@@ -10,6 +10,8 @@ DAWの目的は音楽データを作成することです。音楽データに | |
こういった現状を踏まえて、まずはSMFがどのような構造になっているかを把握して、それからSMFの範囲を超えるデータ部分について論考を続けましょう。 | |
+ちなみに、本書の第1版は2019年に執筆されたものですが、本第2版の執筆時点である2020年12月は、この領域ではやや過渡期にあって、「MIDI 2.0仕様は正式版が公開されたが、それに基づく新しいSMFに相当する仕様はまだ公開されていない」という状況です。本版では引き続き、MIDI 2.0でカバーされた領域についてはSMFの範囲を超えるデータとして言及します。 | |
+ | |
### SMF構造とMIDIのデータ | |
一般に、音楽は複数のパートに分かれて構成されています。これはおそらく伝統的な理解であって、空間にオブジェクトを配置してお互いの反響などを考慮して録音するという考え方に基づいて「音」を考えると、必ずしも音楽をパートで分けるのは絶対的な考え方ではないのですが@<fn>{objects-based-audio}、DTMの範疇であればおよそ問題ないでしょう。 | |
@@ -56,50 +58,43 @@ SMFの範囲で音楽データを表現できるのであれば、データ保 | |
//footnote[vocaloid-v2][以前にも言及しましたが、Vocaloid v2の保存形式`.vsq`ファイルは実態としてはSMFでした] | |
-(1) オーディオプラグインの拡張パラメーター | |
- | |
-MIDIイベントはMIDI音源を制御するためのものでしたが、MIDI規格に縛られない各種DAWのシーケンサーエンジンでは、オーディオプラグインが一般的に受信できるMIDIメッセージに加えて、MIDIに限定されないパラメーターを送ることが出来るものもあります。これが可能かどうかはオーディオプラグインフォーマット次第です。AUにはパラメーター・ツリーというパラメーター定義構造があり、このツリーに基づいてプラグインの状態をイベントで操作できます。 | |
+(1)音源の情報 | |
-(2)オートメーションの表現方法 | |
- | |
-MIDIイベントやコントロール・パラメーターの変化は、MIDI標準仕様においてはスカラーデータ(値の直接指定)で表現するしかありませんでしたが、直線やサインカーブのようなベクターによる連続的な値の変化を指定したほうが、データ量としてはずっと小さかったり、変更を簡単に指定できたりします。このような表現はオーディオプラグインのコントロールパラメーターやDAWのオートメーションの一種として表現されます。演奏記録からMIDIパラメーターの変化をスカラーデータで吸い上げて記録し、それを再生するという形態のオートメーションも一般的で、この場合はスカラーデータ部分はその適用部分とは別にデータをもつことになるでしょう。 | |
+MIDIによって音楽を表現していた頃は、音色設定に相当するのはプログラム・チェンジと呼ばれる命令でした。この命令で実際に指定される音色は、接続されているMIDI出力デバイスによって異なるものでした。ただし、General MIDI(GM)などの標準では、プログラム・チェンジの指定値が音色をある程度規定しており、GM準拠の楽曲であればGM準拠のMIDIデバイスで概ね作成者の意図通りに楽曲を再生できました。 | |
-(3) 対象MIDI出力チャンネルの利用方法 | |
+MIDIの規定する音色は番号で識別する程度の表現力しかありません。どの環境でも同一の音声を再現するためには、サンプリングなどに基づくオーディオプラグインなどを楽器として使用する必要があります。現代的なDAWにおいては、音色設定はMIDIトラック(やインストゥルメンタルトラック)ごとに、(ルーティングによって接続済みの)オーディオプラグインを指定して、それを楽器として使用するのが一般的です。 | |
-MIDIメッセージのステータスバイトにはチャンネル指定が付随しますが、この部分は、本来なら1チャンネルあれば足りるはずである仮想インストゥルメントを中心とするオーディオプラグインの世界でも、しばしば活用されます。具体的には、楽器となるプラグイン、アプリケーションのインスタンスは1つしかないとしても、その中で8チャンネルほどの入力スロットが存在して、それぞれのチャンネルがDAWにおける1チャンネルのサポートとして接続できるように実装されることで、複数のプラグインやアプリケーションのインスタンスではなく1つのインスタンスで入力の全てを合成するようにして、処理負荷を低減することが出来ます。 | |
+個々のオーディオプラグインは、単にMIDIのプログラムチェンジのように数値を指定するだけでは識別できませんし、ファイルパスを用いてしまうと、作成したときのPC環境とは別のPC環境にファイルをコピーして開いたら再生できなくなってしまい、MIDI以上に楽曲の再現性がなくなってしまいます。オーディオプラグインの指定には識別子が用いられます。オーディオプラグインの識別子については、第3章で説明した通りです。 | |
-KontaktやOmnisphere、Roland SoundCanvas VAなど、いわゆる総合音源では、多くがこれを実装されています。 | |
+また、第3章で説明したstateも、音源のパラメーターと並んで重要な設定項目となります。パラメーターの設定についてはMIDIメッセージ(に類する演奏命令)の一部として保存できますが、それ以上の音源の調整項目はstateとしてDAWによって読み書きされます。読み書きといっても、これは音楽データファイルのセーブやロードでのみ使われる機能ではありません。プラグインUIを表示した場合にロードしたり、プラグインUIを閉じる時にセーブしたりすることもあります。 | |
-オーディオプラグインはMIDIの表現力を超えた音楽表現を実現するために開発されたものであり、シーケンサーエンジンもオーディオプラグインをホストとして操作する必要があります。 | |
+DAWによっては、オーディオプラグインの他に、SF2サウンドフォントやSFZファイルなどを指定するだけで、より手軽に楽器を指定できるものも存在します。たとえば、第3章で紹介したCarlaは、内部的にSFZeroなどをバンドルしていて、SFZをプラグインノードの一種として扱うことができます。Zrythmもこれを活用して、サウンドフォントをプラグインの代わりに音源として指定できます。このような場合は、音源の指定も少しだけシンプルなものになるでしょう。 | |
-### オーディオプラグインの識別子と名前 | |
+![さまざまなプラグインと楽器フォーマットをサポートするCarla](carla-sshot.png) | |
-MIDIによって音楽を表現していた頃は、音色設定に相当するのはプログラム・チェンジと呼ばれる命令でした。この命令で実際に指定される音色は、接続されているMIDI出力デバイスによって異なるものでした。ただし、General MIDI(GM)などの標準では、プログラム・チェンジの指定値が音色をある程度規定しており、GM準拠の楽曲であればGM準拠のMIDIデバイスで概ね作成者の意図通りに楽曲を再生できました。 | |
+(2)オーディオプラグインの拡張パラメーター | |
-MIDIの規定する音色は番号で識別する程度の表現力しかありません。どの環境でも同一の音声を再現するためには、サンプリングなどに基づくオーディオプラグインなどを楽器として使用する必要があります。現代的なDAWにおいては、音色設定はMIDIトラック(やインストゥルメンタルトラック)ごとに、(ルーティングによって接続済みの)オーディオプラグインを指定して、それを楽器として使用するのが一般的です。 | |
+MIDIイベントはMIDI音源を制御するためのものでしたが、MIDI規格に縛られない各種DAWのシーケンサーエンジンでは、オーディオプラグインが一般的に受信できるMIDIメッセージに加えて、MIDIに限定されないパラメーターを送ることが出来るものもあります。これが可能かどうかはオーディオプラグインフォーマット次第です。AUにはパラメーター・ツリーというパラメーター定義構造があり、このツリーに基づいてプラグインの状態をイベントで操作できます。 | |
-第3章でも説明したとおり、個々のオーディオプラグインは、単にMIDIのプログラムチェンジのように数値を指定するだけでは識別できませんし、ファイルパスを用いてしまうと、作成したときのPC環境とは別のPC環境にファイルをコピーして開いたら再生できなくなってしまい、MIDI以上に楽曲の再現性がなくなってしまいます。オーディオプラグインの指定には識別子が用いられます。 | |
+ちなみに、MIDI 2.0では、RPNに対するNRPNのように、CCに対するベンダー定義のCCのようなものを規定できます。MIDI 2.0では用語が変更され、RPNはRegistered Controllerとなり、NRPNで使われていたnon-registeredという語句はAssignableという単語になり、Assignable Controllerという名前になりました。また、ノート別コントローラーという概念が新しく定義され、ノートごとにCCの値を指定できるようになりました。 | |
-オーディオプラグインをファイル名で識別していると、PC環境を移動すると開けない、ということになってしまうので、あくまで補完的な情報として楽曲データに含める程度にするべきです。ちなみに、オーディオプラグインホストによってはファイルパスをあくまでパスのヒント情報として含めているということがあります(本書で度々登場するTracktion Engineがそのようになっています)。 | |
+MIDI 2.0ではさらにノート命令の属性という概念が新しく定義され、ノートごとに音の表情などを変更できる、いわゆるアーティキュレーションの機能がサポートされるようになりました。ヴォーカルシンセサイザーのブレシネスやオープンネス、ギターのミュートやピッキングといった概念が、この属性によって表現できるようになります。Kontakt KSPやSFZファイルではいわゆるキースイッチによって実現していましたが、そういうハックも不要になります。 | |
-筆者の私見としては、ローカルのファイルパスには個人の環境を特定する情報が含まれている可能性があるので(たとえば匿名の作曲者として楽曲データをインターネット上で公開していても、そのデータに`c:\\Users\\atsushi\\VST3 Plugins`というパスが含まれていれば、名前が`atsushi`であることが公知情報になってしまいます)、ローカルパスは可能な限り楽曲データに含むべきではありません。 | |
+(3)オートメーションの表現方法 | |
-### オーディオプラグインのパラメーターとstate | |
+MIDIイベントやコントロール・パラメーターの変化は、MIDI標準仕様においてはスカラーデータ(値の直接指定)で表現するしかありませんでしたが、直線やサインカーブのようなベクターによる連続的な値の変化を指定したほうが、データ量としてはずっと小さかったり、変更を簡単に指定できたりします。このような表現はオーディオプラグインのコントロールパラメーターやDAWのオートメーションの一種として表現されます。演奏記録からMIDIパラメーターの変化をスカラーデータで吸い上げて記録し、それを再生するという形態のオートメーションも一般的で、この場合はスカラーデータ部分はその適用部分とは別にデータをもつことになるでしょう。 | |
-(パラメーターツリーの構成については第3章で済ませる?) | |
+(4) 対象MIDI出力チャンネルの利用方法 | |
-オーディオプラグインの調整は、理想的にはオーディオプラグインパラメーターのみで表現できることが望ましいですが、必ずしもこれが実現出来ない場合があります。たとえば… | |
+MIDIメッセージのステータスバイトにはチャンネル指定が付随しますが、この部分は、本来なら1チャンネルあれば足りるはずである仮想インストゥルメントを中心とするオーディオプラグインの世界でも、しばしば活用されます。具体的には、楽器となるプラグイン、アプリケーションのインスタンスは1つしかないとしても、その中で8チャンネルほどの入力スロットが存在して、それぞれのチャンネルがDAWにおける1チャンネルのサポートとして接続できるように実装されることで、複数のプラグインやアプリケーションのインスタンスではなく1つのインスタンスで入力の全てを合成するようにして、処理負荷を低減することが出来ます。 | |
-- オーディオプラグイン作者が、DAWを経由してユーザーにリバースエンジニアリングされたくない・妨害したいので、パラメーターの名前や値を公開したくないという場合 | |
-- パラメーターのバージョンが上がったことで下位互換性などを維持できなくなり、単なる名前と値だけでは十分に意図を表現できなくなったりした場合@<fn>{param-backward-compat} | |
-- 変更をリアルタイムに反映できない程度に長大な処理を伴う場合 | |
-- サンプリングデータなどパラメーターとして渡すには巨大すぎるデータの受け渡しが必要になる場合 | |
+KontaktやOmnisphere、Roland SoundCanvas VAなど、いわゆる総合音源の多くで、これが実装されています。 | |
-//footnote[param-backward-compat][実のところ、筆者がそういう話を聞いたことがあるという程度で、本当にパラメーターで表現できないことなのかは疑問の余地が大いにあるところです。] | |
+オーディオプラグインはMIDIの表現力を超えた音楽表現を実現するために開発されたものであり、シーケンサーエンジンもオーディオプラグインをホストとして操作する必要があります。 | |
-このような場合でも、オーディオプラグインとホストの間で情報をやり取りできるように、多くのプラグイン機構ではstate(状態)と呼ばれるバイナリデータを取得・設定できるようになっています。 | |
+一つ注意すべきことですが、このような多チャンネルをサポートできているのはVST2のみで、VST3ではMIDIサポートが削除されたため、サポートできなくなっているといわれています。Steinbergに開発者のフィードバックとして寄せられており、実際に筆者がJUCEベースのプロジェクトでVST3としてエクスポートしたプラグインでも機能しませんでした@<fn>{pr-adlplug}。 | |
-オーディオプラグインの設計やDAWの設計において、stateを使う場合に一般的に注意すべきことは、stateはMIDIのコントロールチェンジや一般的なパラメーターとは異なり、通常はトラックの再生途中で変更することはできないという点です。stateは曲全体を通して固定となる音色設定などに付随するものとして使うべきです。 | |
+//footnote[pr-adlplug][ADLplugというFM音源エミュレーターのプラグインプロジェクトに筆者が送ったpull requestで詳細が見られます。] | |
### モダンな楽曲データモデルの構造例 | |
@@ -201,11 +196,11 @@ MIDI仕様では、同時に発音できるチャンネルが16しか無いた | |
DAWに限らず、データを編集するGUIを伴うアプリケーションでは、伝統的にはビューとロジックの分離が実現していることが理想とされてきました。これがもう少し発展すると、MVCやMVVMといった抽象的なコードの役割分担が開発モデルとして提唱されるようになってきました。 | |
-これは小さなアプリケーションでは比較的容易に実現してきたことですが、アプリケーションがクロスプラットフォームで動作することに価値が出てきた近年では、テキストエディターなど大規模なコードベースをもつアプリケーションにおいてもコードの分離が実現してきました。GUIにおいて(あるいはCUIでも)プラットフォーム依存のレイヤーにアクセスするのはおよそ必須なので、プラットフォームやフレームワークに依存する部分は切り離して環境ごとに実装する、というアプローチが採られることが多いです。 | |
+これは小さなアプリケーションでは比較的容易に実現してきたことですが、アプリケーションの改修コストに注目が集まったり、クロスプラットフォームで動作することに価値が出てきた近年では、テキストエディターなど大規模なコードベースをもつアプリケーションにおいてもコードの分離が実現してきました。多機能なアプリケーションのUIにおいてプラットフォーム依存のレイヤーにアクセスするのはおよそ必須なので、プラットフォームやフレームワークに依存する部分は切り離して環境ごとに実装する、というアプローチが採られることが多いです。 | |
GUI部分もある程度はプラットフォーム独立のかたちで実装できるのですが@<fn>{xplat-gui}、この節ではそこまで大掛かりな話に踏み込むのは避け、もう少し抽象化されたDAWのコード編成を念頭に置いて続けます。 | |
-//footnote[xplat-gui][たとえばGtk+やQt, Electron, Xamarin.Formsはクロスプラットフォームで動作しますし、モバイル限定ならReact Native, Flutter(安定版)などもあります。] | |
+//footnote[xplat-gui][たとえばGtk+やQt, Electron, Swing, Xamarin.Formsはクロスプラットフォームで動作しますし、モバイル限定ならReact Native, Flutter(安定版)などもあります。] | |
DAWの設計において、GUI上でトラック上に音符を追加したり、サンプリングを追加したりする、といった操作は、抽象的なモデルとして表現できます。GUIとして実際にトラックの内容をレンダリングするのはGUIレイヤーの仕事ですが、ユーザーから「ノートを追加する命令が届いた」時に実際にトラックにノートを追加したり、コピーや取消といった操作を(メインメニューからであれマウス操作からであれ)実行する、といった機能は、GUIから独立して設計・実装できます。 | |
@@ -234,7 +229,3 @@ GUIから独立したシーケンサーエンジンであれば、ここまで | |
- ピアノロールのグリッドへのスナップ設定(楽曲に最適化しているのかもしれないし、製作者の好みかもしれない) | |
本章でたびたび登場するTracktion Engineでは、これらの情報は概ね保存されます。これらの情報項目の設計は難しく、プロジェクトのファイルはあくまで作業者向けのファイルであって情報交換用ではない、と割り切っているとも考えられます。ただ、実際のところプロジェクトの内容に絶対ファイルパスが含まれていたりすると、同じ作業者ですら別のPC環境にコピーして作業することもできなくなるので、ファイルパスは相対位置で保存するなどの配慮は忘れないようにしたいところです。 | |
- | |
- | |
- | |
- | |
diff --git a/articles/postfix.re b/articles/postfix.re | |
index 4d6e28f..365f26c 100644 | |
--- a/articles/postfix.re | |
+++ b/articles/postfix.re | |
@@ -1,6 +1,8 @@ | |
= あとがき | |
+== 第1版あとがき | |
+ | |
シーケンサーエンジンで使用されている技術について、全4章で俯瞰的に説明してきました。GUI機構など巨大な構成要素についてなお空白を残したままではありますが、それを除いても包括的な内容であり、読み進めるのもなかなか大変な作業であったかと思います。とても詳細に丁寧に説明できたとは思えませんが、まずは全体図を描ききる、という本書の目的はある程度実現できたかと思います。 | |
@@ -8,3 +10,13 @@ | |
筆者はまだオーディオアプリケーション開発の世界に飛び込んだばかりで日も浅く、本書を書き終えるところまで時間を費やしても、なお十分な知見をもって詳述できなかったことが少なからずありました。また年月を経て、より深みのある解説が書けるようになるよう、知見を蓄えていきたいと思います。 | |
+ | |
+== 第2版あとがき | |
+ | |
+ | |
+第1版を発行してから1年強が経過しました。オーディオ開発には難しいトピックが多く、また筆者がどちらかといえばアプリケーションやプラグインを開発するプログラマーではないこともあって、いろいろ不足している知識だらけですが、少しずつ知識が埋まってきており、振り返って直すべき箇所を探して直そうと考え、今回本書の改訂に踏み切りました。 | |
+ | |
+ | |
+ | |
+第1版ではまだ十分に体系的に本書を編成できておらず、さまざまなトピックを雑多に拾い上げてきたのに対して、第2版は既に組み立てた編成に内容がより適切にフィットするように内容を並べ替えながら、新たに言及しておきたい技術上の課題・論点を追加しました。既に本版でもまだまだ改善の余地がいくつかあると考えていますが、それは次バージョンの課題としてひとまずここで筆を置こうと思います。 | |
+ | |
diff --git a/postfix.md b/postfix.md | |
index 33f4a77..3a2f3d2 100644 | |
--- a/postfix.md | |
+++ b/postfix.md | |
@@ -1,7 +1,14 @@ | |
# あとがき | |
+## 第1版あとがき | |
+ | |
シーケンサーエンジンで使用されている技術について、全4章で俯瞰的に説明してきました。GUI機構など巨大な構成要素についてなお空白を残したままではありますが、それを除いても包括的な内容であり、読み進めるのもなかなか大変な作業であったかと思います。とても詳細に丁寧に説明できたとは思えませんが、まずは全体図を描ききる、という本書の目的はある程度実現できたかと思います。 | |
筆者はまだオーディオアプリケーション開発の世界に飛び込んだばかりで日も浅く、本書を書き終えるところまで時間を費やしても、なお十分な知見をもって詳述できなかったことが少なからずありました。また年月を経て、より深みのある解説が書けるようになるよう、知見を蓄えていきたいと思います。 | |
+## 第2版あとがき | |
+ | |
+第1版を発行してから1年強が経過しました。オーディオ開発には難しいトピックが多く、また筆者がどちらかといえばアプリケーションやプラグインを開発するプログラマーではないこともあって、いろいろ不足している知識だらけですが、少しずつ知識が埋まってきており、振り返って直すべき箇所を探して直そうと考え、今回本書の改訂に踏み切りました。 | |
+ | |
+第1版ではまだ十分に体系的に本書を編成できておらず、さまざまなトピックを雑多に拾い上げてきたのに対して、第2版は既に組み立てた編成に内容がより適切にフィットするように内容を並べ替えながら、新たに言及しておきたい技術上の課題・論点を追加しました。既に本版でもまだまだ改善の余地がいくつかあると考えていますが、それは次バージョンの課題としてひとまずここで筆を置こうと思います。 | |
diff --git a/preface.md b/preface.md | |
index d668977..b909e74 100644 | |
--- a/preface.md | |
+++ b/preface.md | |
@@ -53,3 +53,13 @@ DAWにはさまざまな機能が含まれ、その中にはたとえばソー | |
本書は、このような条件で絞り込まれたDAW等におけるシーケンサーエンジンへの興味がある読者を主な対象としています。読者はこれらの知識を得ることで、よりロジカルにDAWのマクロを組んだり、プラグインを構築したり、さらにはオープンソースのDAWを自分で改良したりDAWを自作できるようになることでしょう…というのが本書の狙いです。 | |
+## 本書の構成 | |
+ | |
+本書は4つの章から成り立っています。 | |
+ | |
+- 第1章は本書が対象とするDAWのシーケンサーエンジンの位置付けと技術要素を総論的に概説し、以降の章の内容がシーケンサーエンジンのどの部分に関連するかを繋いでいきます。 | |
+- 第2章はオーディオおよびMIDIデバイスへのネイティブアクセスというトピックを切り出して取り上げます。 | |
+- 第3章は音源として重要な位置を占めるオーディオプラグインについて、主にプラグインをホストする側の観点で必要になる技術的課題を論じます。 | |
+- 第4章はオーディオサンプルとMIDIシーケンスによる楽曲トラックの編成を基本として、データモデルに含めるべき情報、楽曲の時間情報の構造、編集・加工処理や再生処理に用いられる技術などについて言及します。 | |
+ | |
+最後に付録として、本書の特に第4章で解説したような機能を実現しているTracktion Engineについて、その構成モジュールを概観できるように解説した表を加えました。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment