VST3のバスについて
前回は最小構成のVST3プラグインを作成しましたが、その中にあったバスの概念について簡単にメモいたします。
(VST SDKのバスに関する情報がないため一部推測による記載になります。)
VST3のプラグインには「バス」というものがあります。
「バス」とはコンピュータでは”データの流れる道”になります。
VST3のプラグインには「AudioBus」(オーディオバス)と「EventBus」(イベントバス)の2種類のバス(データの流れる道)があります。
- AudioBus
- EventBus
オーディオデータ(音声波形データ)の流れる道。
オーディオバスには「モノラルのオーディオバス」や「ステレオのオーディオバス」等の構成がある。
イベントデータ(ノートオン、ノートオフメッセージ等)の流れる道。
VST3のプラグインでは入力、出力はすべてバス単位で扱われます。
例として、VST3プラグインでは下記のような入力・出力のバスの組み合わせがあります。(他にも色々なバスの組み合わせはあります。)
- 例1:モノラルのエフェクター
- 例2:ステレオのエフェクター
ステレオのオーディオバス入力…1つ
ステレオのオーディオバス出力…1つ - 例3:サイドチェイン付きステレオエフェクター
- 例4:シンセサイザー
- 例5:ボコーダー機能付きシンセサイザー
モノラルのオーディオバス入力…1つ
モノラルのオーディオバス出力…1つ
ステレオのオーディオバス入力…2つ(ひとつがサイドチェイン用)
ステレオオーディオバス出力…1つ
イベントバス入力…1つ(ノートオン、ノートオフメッセージの受け取り用)
ステレオオーディオバス出力…1つ
イベントバス入力が1つ(ノートオン、ノートオフメッセージの受け取り用)
ステレオのオーディオバス入力…1つ(音声入力用)
ステレオオーディオバス出力…1つ
VST3プラグイン開発時のバス設定
VST3を開発する際は、開発するVST3プラグインに入力と出力のバスがいくつあるかを設定する必要があります。
この設定は、initialize()関数内でaddAudioInput()関数・addAudioOutput()関数・addEventInput()関数・addEventOutput()関数を追加したいバスの分だけ呼び出せば設定が可能です。
※initialize()関数は自作Processorクラス(AudioEffectを継承して作るクラス)のメンバー関数としてオーバーライドして定義します。
→参考…自作Processorクラス
それぞれの関数については下記の通りです。
- addAudioInput()関数、addAudioOutput()関数
概要 | VST3プラグインにオーディオ入力バス、オーディオ出力バスを設定する関数。 | ||
---|---|---|---|
戻り値 | 型 | 概要 | |
AudioBus* | 追加したオーディオバスのクラスのポインタ | ||
引数 | 型 | 変数名 | 概要 |
const TChar* | name | 追加するオーディオバスの名前。 \0で終端する。何文字でも可。(おそらく) |
|
SpeakerArrangement | arr | 追加するオーディオバスの構成。 ステレオの場合…SpeakerArr::kStereo モノラルの場合…SpeakerArr::kMono 他の構成(5.1ch…k51 等)もある。 |
|
BusType | busType | 追加するオーディオバスの種類。引数を省略すると標準でkMainとなる。 エフェクター入力/出力のような場合…kMain サイドチェイン入力のような場合…kAux |
|
int32 | flags | 追加するオーディオバスのフラグ。BusInfo::kDefaultActiveのみ指定可能。省略可。 |
- addEventInput()関数、addEventOutput()関数
概要 | VST3プラグインにイベント入力バス、イベント出力バスを設定する関数 | ||
---|---|---|---|
戻り値 | 型 | 概要 | |
EventBus* | 追加したイベントバスのクラスのポインタ | ||
引数 | 型 | 変数名 | 概要 |
const TChar* | name | 追加するイベントバスの名前。 \0で終端する。何文字でも可。(おそらく) |
|
int32 | channels | 追加するイベントバスで受け取るノートオン、ノートオフイベントのチャンネル数。 標準で16。省略可。 |
|
BusType | busType | 追加するイベントバスの種類。引数を省略すると標準でkMainとなる。 | |
int32 | flags | 追加するイベントバスのフラグ。BusInfo::kDefaultActiveのみ指定可能。省略可。 |
オーディオバス
オーディオバスの接続
VST3プラグインがホストアプリケーション(Cubase,Sonar等のDAW)に読み込まれたとき、オーディオ入力バスやオーディオ出力バスはホストアプリケーションの用意したバスに接続されます。
addAudioInput()関数・addAudioOutput()関数で開発するVST3プラグインにオーディオの入出力バスの設定を行いますが、必ずしも設定した入出力バス構成にならない場合があります。
例えば、「ホストアプリケーション(Cubase,Sonar等のDAW)のモノラルトラックにステレオ入出力のVST3プラグインが読み込まれた」などの場合です。
このとき、ホストアプリケーションの用意した入出力バスの数や構成(モノラル・ステレオ等)にVST3プラグインのバス構成が対応しているかどうかを判断しなければなりません。
この判断を行うためにsetBusArrangements()関数がVST3にはあります。
- setBusArrangements()関数
概要 | ホストアプリケーションが用意したオーディオバス構成に対応しているかどうかを返す関数。 VST3プラグインが呼び出されたときや、バスの接続が変わった場合に呼び出される。 |
||
---|---|---|---|
戻り値 | 型 | 概要 | |
tresult | ホストアプリケーションが用意したオーディオバス構成に対応しているかどうかの結果 設定に成功した場合…kResultTrue、設定に失敗した場合…kResultFalse |
||
引数 | 型 | 変数名 | 概要 |
SpeakerArrangement* | inputs | 入力バス構成(SpeakerArr::kStereo、SpeakerArr::kMono等)の配列。配列の数はnumInsとなる。 | |
int32 | numIns | 設定しようとしている入力バスの数 | |
SpeakerArrangement* | outputs | 出力バス構成の配列。配列の数はnumOutsとなる。 | |
int32 | numOuts | 設定しようとしている出力バスの数 |
setBusArrangements()関数が呼び出された際、開発するVST3プラグインが対応したオーディオバス構成だった場合は、継承元setBusArrangements()関数を呼び出し、kResultTrueを返すようにします。
基本的には「numInsとnumOutsがaddAudioInput()関数・addAudioOutput()関数で設定したバス数と同じか?」と「inputs[]とoutputs[]に入っているそれぞれのバスの構成がaddAudioInput()関数・addAudioOutput()関数で設定したものと同じか?」を確認すれば問題ありません。
→参考…自作Processorクラス
前回作成したVST3プラグインでは、オーディオ入力/出力バスは1つで、共にステレオのみの対応でしたので下記のようにしております。
34 35 36 37 38 39 40 41 42 43 44 |
tresult PLUGIN_API MyVSTProcessor::setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) { // inputとoutputのバスが1つずつで、かつinputとoutputの構成がステレオの場合 if (numIns == 1 && numOuts == 1 && inputs[0] == SpeakerArr::kStereo && outputs[0] == SpeakerArr::kStereo) { return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts); } // 対応していないバス構成の場合、kResultFalseを返す。 return kResultFalse; } |
複数のオーディオバス構成に対応させるのであれば、複数のパターンでkResultTrueを返すようにすれば対応できます。
例えば、入力バスをモノラルにも対応させたい場合は下記のようにします。
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
tresult PLUGIN_API MyVSTProcessor::setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) { // inputとoutputのバスが1つずつで、かつinputとoutputの構成がステレオの場合 if (numIns == 1 && numOuts == 1 && inputs[0] == SpeakerArr::kStereo && outputs[0] == SpeakerArr::kStereo) { return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts); } // inputとoutputのバスが1つずつで、inputの構成がモノラル・outputの構成がステレオの場合 else if (numIns == 1 && numOuts == 1 && inputs[0] == SpeakerArr::kMono && outputs[0] == SpeakerArr::kStereo) { return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts); } // 対応していないバス構成の場合、kResultFalseを返す。 return kResultFalse; } |
ホストアプリケーションが用意したオーディオバス構成にVST3プラグインが対応していない場合、基本的に、ホストアプリケーションはVST3プラグインで設定したオーディオバス構成に合わせようとします。
(ホストアプリケーションの実装によります。VST3プラグインの読み込みをあきらめるホストアプリケーションもあるようです。)
なお、ホストアプリケーションはオーディオバス構成に合わせるため、getBusArrangements()関数を呼び出します。
getBusArrangements()関数については継承元のAudioEffectクラスで定義されていますが、開発時に変更を加える必要はほとんどありませんので、詳細は割愛します。
process()関数でのオーディオバスの取り扱い
ホストアプリケーションとのオーディオバスの接続に問題がなければ、ホストアプリケーションから音声入出力データが渡されるようになります。
ホストアプリケーションから渡される音声入出力データはprocess()関数で処理することになりますが、各オーディオバスの音声入出力データは引数のdata構造体にすべて入っております。
前回の「process()関数について」で説明した通り、入力オーディオバス・出力オーディオバスの数はそれぞれdata.numInputs・data.numOutputsで取得できます。
この入出力オーディオバスの数はdata.inputs[]、data.outputs[]の配列サイズになります。
注意しなければならないのは、入出力オーディオバスの数はaddAudioInput()関数・addAudioOutput()関数を呼び出した回数ではなく、ホストアプリケーションが用意した入出力オーディオバスの数になります。
(これが前回「厳密には違う」と書いた部分になります。)
また、各入出力オーディオバスごとのチャンネル数はdata.inputs[XX].numChannels・data.outputs[XX].numChannelsで取得できます。
このチャンネル数は、data.inputs[XX].channelBuffers32[][]・data.outputs[XX].channelBuffers32[][]の1次元目の配列サイズになります。
data.inputs[XX].channelBuffers32[バスのチャンネル数][音声データのサイズ]
data.outputs[XX].channelBuffers32[バスのチャンネル数][音声データのサイズ]
なお、音声データのサイズはdata.numSamplesで指定されます。
複数のオーディオバス構成に対応させるのであれば、data.numInputs・data.numOutputsやdata.inputs[XX].numChannels・data.outputs[XX].numChannelsをチェックしながら対応させる必要があります。
例えば、「オーディオバスの接続」の例のように「入出力オーディオバスともにステレオ」と「入力オーディオバスはモノラル、出力オーディオバスはステレオ」に対応させたい場合は下記のようにします。
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
tresult PLUGIN_API MyVSTProcessor::process(ProcessData& data) { // 入力と出力のバス数をチェック。 if (data.numInputs == 1 && data.numOutputs == 1) { // 入出力ともにステレオの場合の処理 if (data.inputs[0].numChannels != 2 || data.outputs[0].numChannels != 2) { // 入力・出力バッファのポインタをわかりやすい変数に格納 Sample32* inL = data.inputs[0].channelBuffers32[0]; Sample32* inR = data.inputs[0].channelBuffers32[1]; Sample32* outL = data.outputs[0].channelBuffers32[0]; Sample32* outR = data.outputs[0].channelBuffers32[1]; // numSamplesで示されるサンプル分、音声を処理する for (int32 i = 0; i < data.numSamples; i++) { outL[i] = inL[i]; outR[i] = inR[i]; } return kResultTrue; } // 入力がモノラルで出力がステレオの場合の処理 else if (data.inputs[0].numChannels != 1 || data.outputs[0].numChannels != 2) { // 入力・出力バッファのポインタをわかりやすい変数に格納 Sample32* in = data.inputs[0].channelBuffers32[0]; Sample32* outL = data.outputs[0].channelBuffers32[0]; Sample32* outR = data.outputs[0].channelBuffers32[1]; // numSamplesで示されるサンプル分、音声を処理する for (int32 i = 0; i < data.numSamples; i++) { outL[i] = in[i]; outR[i] = in[i]; } return kResultTrue; } } // 対応していない入出力バス構成の場合、何もせずkResultTrueを返す return kResultTrue; } |
前回のprocess()関数など、このWebサイトでのサンプルの多くはprocess()関数内でオーディオバス構成のチェックをしていませんが、VST3プラグインを開発する際はオーディオバス構成のチェックすることが望ましいと思われます。
イベントバス
イベントバスはイベントデータ(ノートオン、ノートオフメッセージ等)を入力・出力するためのバスです。
ここでは、VST3プラグインの作り方について順を追って説明していますので、概要だけを説明させていただき、イベントバスの詳細については省略させていただきます
イベントバスの使い方については、「VST3プラグインでMIDIメッセージを受信する方法1」や「MIDIノートオン・オフの出力」をご参照ください。
イベントデータの種類
VST3プラグインはイベントバスを通じてホストアプリケーションから様々なイベントデータを受け取ることができます。
イベントデータには下記の種類があります。
イベントの種類 | 概要 |
---|---|
ノートオンイベント | MIDIノートオンメッセージのイベントです。 |
ノートオフイベント | MIDIノートオフメッセージのイベントです。 |
データイベント | 何らかのデータ(MIDIシステムエクスクルーシブなど)に関するイベントです。 |
ポリプレッシャーイベント | MIDIポリプレッシャーのイベントです。 |
ノートエクスプレッションイベント | Cubaseのノートエクスプレッションのイベントです。 |
コードイベント | コード(和音)情報に関するイベントです。(Cubaseのコードトラックなどで設定したコードの情報。) |
スケールイベント | スケール(音階)情報に関するイベントです。(Cubaseのコードトラックなどで設定したスケールの情報。) |
旧MIDI CCイベント | MIDIコントロールチェンジメッセージのイベントです。出力イベントバスでのみ使用できます。 |
イベントバスの接続
イベントバスには接続を調整する処理(setBusArrangements()関数のようなもの)はありません。
addEventInput()関数・addEventOutput()関数で設定した入力・出力のイベントバスが接続されます。
process()関数でのイベントバスの取り扱い
ホストアプリケーションから渡されるイベントデータはprocess()関数で処理することになります。
このイベントデータもprocess()関数の引数であるdata構造体にすべて入っています。
入力イベントデータはdata.inputEvents(IEventListクラスへのポインタ)に入っており、イベントデータを出力する場合はdata.outputEvents(IEventListクラスへのポインタ)に出力イベントデータを書き込みます。
イベントデータを扱う方法については、ここではいったん省略いたします。
先に知りたい場合は、「VST3プラグインでMIDIメッセージを受信する方法1」や「MIDIノートオン・オフの出力」をご参照ください。
最後に
バスについては以上です。上記の内容で間違いないと考えていますが、正式な情報がないため推測の部分もございますのでご了承ください。
イベントデータの詳細については、後ほどご説明を予定しております。
なお、ご指摘があればいただけるとうれしいです。
次回は、パラメーターの追加について記載できればと思います。
次回→パラメーター実装方法1
VST3プラグイン作りの情報はこちらにもございます → はじめてのVST3プラグイン作り
ご指摘やご質問などがございましたら、コメント欄か掲示板、Twitterでご連絡いただければと思います。
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp
私の頭の中は、、、煮えたぎってるw