JUCEのバス関連クラスの俯瞰

JUCEのバス関連クラスや関数がいろいろと存在して、わかりにくいので、調べてまとめてみました。

対象のソースは、現在のリリース版である、5.2.0です。

まとめ方が雑なのはご容赦ください。また、網羅的に解説しているわけではなく、ある程度自明な関数や、あまりに内部的な関数や、ややこしくて目を背けた関数やなんかは含めていません。

それと、勘違いしている部分や正しくない書き方になっている箇所があるかもしれないです。気づいた方はぜひお知らせください。

## AudioProcessor::BusesProperties

<https://juce.com/doc/structAudioProcessor_1_1BusesProperties>

* `AudioProcessor`に設定するバス情報を構築するためのヘルパークラス。
* `withInput()`／`withOutput()`メンバ関数によって、メソッドチェーン方式で入力用／出力用それぞれのバス情報を構築できる。
* このオブジェクトでバス情報を構築して、それを`AudioProcessor`クラスのコンストラクタに渡すと、それに従って`AudioProcessor`のバスが設定される。

```cpp
class MyAudioPlugin : 
{
  MyAudioPlugin()
  : AudioProcessor(BusesProperties()
                  .withInput  ("Input",  AudioChannelSet::stereo(), true)
                  .withInput  ("Side Chain", AudioChannelSet::mono(), true)
                  .withOutput ("Output", AudioChannelSet::stereo(), true)
                  )
                  // ステレオ入力／ステレオ出力と
                  // サイドチェイン入力（モノラル）をもつプラグインになる
    {}
    /* ... */
};
```

## AudioProcessor::BusProperties

<https://juce.com/doc/structAudioProcessor_1_1BusProperties>

* `BusesProperties`が保持する、入力用あるいは出力用のバスひとつの情報を表すクラス。
* 以下の3つのプロパティを持つ。（`BusesProperties`の`withInput()`／`withOutput()`メンバ関数に渡されたものがここに設定される。）
  * バス名
  * デフォルトのチャンネル情報（c.f. `AudioChannelSet`）
  * デフォルトで有効かどうかのフラグ
     * 具体的には、下記の`Bus`クラスの初期時に、デフォルトのチャンネル情報が、`getCurrentLayout()`メンバ関数の値として最初からセットされるかどうかを表す

## AudioChannelSet

<https://juce.com/doc/classAudioChannelSet>

* チャンネル情報のコンテナ。
* 複数のチャンネルを保持し、どのチャンネルがどのスピーカーに対応するかの情報も持つ。

### isDisabled() constメンバ関数

* 自身のチャンネル情報が有効か／無効かを判定する。
* デフォルトコンストラクタによる構築後など、チャンネル情報が一つも設定されてない状態のときは`true`を返し、そうでないときは`false`を返す。

## AudioProcessor::Bus

<https://juce.com/doc/classAudioProcessor_1_1Bus>

* `AudioProcessor`が持つバス本体のクラス。このクラスのオブジェクトひとつでバスひとつ分を表す。
* このクラスは、`AudioProcessor`クラスのコンストラクタの中で、引数として渡された`BusesProperties`の情報をもとにして初期化される。
* `Bus`が表すチャンネル情報は、プラグインがサポートしていれば`BusesProperties`で指定したもの以外に変更できる。
  * `Bus`は、その`Bus`デフォルト値としてのチャンネル情報と、現在のチャンネル情報、最後に有効な値がセットされたときのチャンネル情報の3つのチャンネル情報を持つ。

### const AudioChannelSet& getDefaultLayout() constメンバ関数

* `BusesProperties`で指定した、デフォルト値としての`AudioChannelSet`を返す。

### const AudioChannelSet& getCurrentLayout() constメンバ関数

* 現在`Bus`に設定されている`AudioChannelSet`を返す。
* `Bus`には無効な`AudioChannelSet`が設定されている可能性がある、その時は、この戻り値も無効な`AudioChannelSet`となる。

### const AudioChannelSet& getLastEnabledLayout() constメンバ関数
  
* 有効なチャンネル表現を持っていて最後にBusに設定された`AudioChannelSet`を返す。
* `AudioProcessor`のコンストラクタ呼び出し直後は、`getDefaultLayout()`メンバ関数と同じものが返る。

### bool isEnabled() constメンバ関数

* `Bus`の有効／無効状態を返す。
* 実際には、`Bus`の現在のチャンネル情報（つまり`getCurrentLayout()`メンバ関数で返る`AudioChannelSet`）の有効／無効状態を返す。

### bool setCurrentLayout(const AudioChannelSet&)メンバ関数

* この`Bus`の現在のチャンネル情報を、指定した`AudioChannelSet`に変更する。
* 内部的には、この`Bus`を保持している`AudioProcessor`の`setChannelLayoutOfBus()`メンバ関数を呼び出すだけ。

### bool setCurrentLayoutWithoutEnabling(const AudioChannelSet& layout)メンバ関数
* `setCurrentLayout()`と同じように、`Bus`のチャンネル情報を、指定した`AudioChannelSet`に変更するが、`Bus`の有効／無効状態は変化させない。
* もし`Bus`が有効なら、`Bus`の`setCurrentLayout()`を呼び出して、指定した`AudioChannelSet`を`Bus`の現在のチャンネル情報として設定する。
* もし`Bus`が無効なら、指定した`AudioChannelSet`がこの`Bus`によってサポートされているものかどうかを判定した上で、最後に有効だった`AudioChannelSet`（つまり`getLastEnabledLayout()`で返るもの）として、チャンネル情報を保持する。

### bool setNumberOfChannels(int channels)メンバ関数
* 指定したチャンネル数を持つように、`Bus`のチャンネル情報を設定する。
* 実質的には、指定したチャンネル数に適合する`AudioChannelSet`を作成し、`setCurrentLayout()`の処理と同じように、`AudioProcessor`の`setChannelLayoutOfBus()`を呼び出す

### bool isLayoutSupported(const AudioChannelSet& set, BusesLayout* currentLayout = nullptr) constメンバ関数
* 指定した`AudioChannelSet`を`Bus`に設定可能かどうかを返す
* `currentLayout`が`nullptr`でない場合は、`AudioProcessor`のバス全体が、そのスナップショットの状態であるとみなして、処理を行う。そして、関数から抜ける際に、指定された`AudioChannelSet`を適用した場合に、`AudioProcessor`のバス全体の状態がどうなるかを、`currentLayout`に設定する。
  * 一つの`Bus`に対するチャンネル情報の変更によって、他の`Bus`の状態も変更されてしまう可能性がある。そのため、このようにして、変更後の状態を確認できるようになっている。

### bool isNumberOfChannelsSupported (int channels) constメンバ関数

* 指定したチャンネル数を持つようにこの`Bus`のチャンネル設定を変更できるかどうかを返す。

### AudioChannelSet supportedLayoutWithChannels(int channels) constメンバ関数

* この`Bus`が、指定したチャンネル数のチャンネル設定をサポートしている場合に、そのチャンネル設定を返す。

### BusesLayout getBusesLayoutForLayoutChangeOfBus(const AudioChannelSet&) constメンバ関数

* 指定した`AudioChannelSet`をこの`Bus`に適用しようとした場合に、バス全体の状態がどのようになるかを、`BusesLayout`で返す。

## AudioProcessor::BusesLayout

<https://juce.com/doc/structAudioProcessor_1_1BusesLayout>

* `AudioProcessor`が保持しているバス全体のスナップショットを表すクラス。
* 入力用／出力用それぞれのバスのチャンネル情報を、2つの`AudioChannelSet`の配列に保持する。
* `AudioProcessor`の現在のバス状態を表すだけではなく、「`AudioProcessor`のバスひとつのチャンネル情報を変更した場合に、バス全体のチャンネル状態がどうなるか」というような、仮のバス状態を表すためにも使用される。

## AudioProcessor

<https://juce.com/doc/classAudioProcessor>

* プラグイン本体。
* `Bus`クラスを、入力用／出力用の2つの配列に保持して、複数のバスを扱う。

### virtual bool canAddBus(bool isInput) const／virtual bool canRemoveBus(bool isInput) constメンバ関数

* プラグインのバスを動的に追加／削除できるかどうかを判定する関数。
* このメンバ関数をオーバーライドすると、自身のプラグインでバスを動的に追加／削除できるかどうかを指定できる。
* ただし、VST/VST3ではこの仕組みはサポートされていないので、このメンバ関数をオーバーライドしてもバスを追加／削除可能にはできない。コンストラクタで最初にバスを設定しておく必要がある。
* AudioUnitではこの仕組みがサポートされているので、オーバーライドによって、バスの追加／削除を可能にできる。(<https://github.com/WeAreROLI/JUCE/blob/9f6126779c63cbe2e07719d9453c0d506b562a25/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm#L295>)

### bool addBus(bool isInput)メンバ関数

* この`AudioProcessor`にバスを一つ追加する。`canAddBus()`や`canApplyBusCountChange()`で`false`を返した場合には、バスは追加されずに、`false`が返る。

### bool removeBus(bool isInput)メンバ関数

* この`AudioProcessor`の末尾のバスを一つ削除する。`canRemoveBus()`や`canApplyBusCountChange()`で`false`を返した場合には、バスは追加されずに、`false`が返る。

### bool setBusesLayout(const BusesLayout&)メンバ関数

* 指定した`BusesLayout`が保持する状態を、すべての`Bus`の現在のチャンネル情報として適用する
* 内部的に`canApplyBusesLayout()`を呼び出して、このレイアウトが適用可能かどうかを判定する。その呼び出しで`false`が返された場合には、レイアウトを適用しない。

### bool setBusesLayoutWithoutEnabling(const BusesLayout&)メンバ関数

* `setBusesLayout()`と同じように、指定した`BusesLayout`が保持する状態を、すべての`Bus`に適用する
* ただし、`setBusesLayout()`と異なり、各`Bus`の有効／無効状態は変化させない。
  * 有効な`Bus`に対して有効な`AudioChannelSet`が適用されようとした場合は、`setBusesLayout()`と同様に、`Bus`の現在のチャンネル情報が更新される。
  * 無効な`Bus`に対して有効な`AudioChannelSet`が適用されようとした場合は、その`Bus`の最後に有効だったチャンネル情報が更新される。

### virtual bool isBusesLayoutSupported(const BusesLayout&) const

* 渡されたレイアウトが、このプラグインでサポートされているものかどうかをbool値で返す。
* 開発しているプラグインで、特定のレイアウトをサポートするかどうかをカスタマイズしたいときは、このメンバ関数をオーバーライドして、挙動を変更する。
* 例えば、ある`Bus`では5.1chサラウンドのチャンネル設定を許可したいとき、渡されたレイアウトで、この`Bus`に対する`AudioChannelSet`がステレオのようなチャンネル数が異なるものになっていたり、チャンネルの構成が"6.0ch"や"hexagonal"のようなチャンネル数は合っていてもチャンネルの構成が期待通りでない場合に`false`を返すと、そのレイアウトを拒否できる。

### virtual bool canApplyBusesLayout(const BusesLayout&) const

* 渡されたレイアウトを、この`AudioProcessor`に実際に適用できるかどうかを返す
* `isBusesLayoutSupported()`が`true`を返す場合でも、レイアウトの適用を拒否したい状況のために、このメンバ関数が用意されている。
  * 例えば、`isBusesLayoutSupported()`が`true`を返したとしても、実際にそのレイアウトを適用できるかどうかがプラグインの現在のパラメータ状態に依存する、というようなケースなど。ちょっと特殊。
* 通常は、プラグイン開発者がこのメンバ関数をオーバーライドすることは、ほとんどないはず。既定の実装は、単に`isBusesLayoutSupported()`を呼び出してその結果を返すだけ。
  * ホストとしてVST3プラグインをロードするときにJUCEの内部的に使用される`VST3PluginFormat`というクラスでは、この関係が逆転して、`isBusesLayoutSupported()`のオーバーライドから`canApplyBusesLayout()`のオーバーライドを呼び出している。

### virtual bool canApplyBusCountChange(bool isInput, bool isAddingBuses, BusProperties& outNewBusProperties)メンバ関数

* `AudioProcessor`のバスを追加／削除できるかどうかを判定する関数。
* `isBusesLayoutSupported()`に対する`canApplyBusesLayout()`と同じような意味合いで、`canAddBus()`／`canRemoveBus()`が`true`を返す場合でも、バスの追加／削除を拒否したいような状況を考慮して、このメンバ関数が提供されている。ちょっと特殊。
* 通常は、プラグイン開発者がこのメンバ関数をオーバーライドするケースはほとんどないはず。
* 既定の実装では、入力用か出力用に指定されたバスの追加／削除を、`canAddBus()`／`canRemoveBus()`で判定し、`isAddingBuses == true`のときは、追加するバスの仮の情報を`outNewBusProperties`に設定するようになっている。

### bool checkBusesLayoutSupported(const BusesLayout& layouts) constメンバ関数

* 指定した`BusesLayout`が、この`AudioProcessor`のバスでサポートされているかどうかを返す。
* 実質的には、`layouts`に含まれる入力用／出力用それぞれのバスの数が、`AudioProcessor`の現在のバスの数と一致するかどうかをチェックしてから、`isBusesLayoutSupported(layouts)`の呼び出し結果を返すだけ。

### bool setChannelLayoutOfBus(bool isInputBus, int busIdx, const AudioChannelSet& layout)メンバ関数

* 指定したバスに対して、指定したチャンネル情報をセットする。
* `Bus`の`setCurrentLayout()`メンバ関数の呼び出しも、この関数の呼び出しとなる。
* 内部で`Bus`の`getBusesLayoutForLayoutChangeOfBus()`を呼び出して、`layout`適用後のバス状態を取得し、`layout`が正しく適用されるどうかを判定する。適用されること確認できた場合は、`applyBusLayouts()`を呼び出して、`layout`適用後のバス全体の状態をすべてのバスに適用する。

### bool applyBusLayouts(const BusesLayout &layouts)メンバ関数

* 指定したレイアウトをバス全体に適用する関数。
* 処理が成功した場合は、`audioIOChanged()`メンバ関数を呼び出す。

### void audioIOChanged(bool busNumberChanged, bool channelNumChanged)メンバ関数

* `addBus()`／`removeBus()`／`applyBusLayouts()`などによって、バスの状態が変更されたときに呼び出される関数。
* 各`Bus`が内部的に持っているチャンネル数の情報を更新し、`numBusesChanged()`や`numChannelsChanged()`などのコールバック関数を呼び出す。

### containsLayout()／getNextBestLayoutInLayoutList() staticメンバ関数

* Projucerのチャンネル設定機能を使用したときに使用されるメンバ関数。下のセクションを参照。

# Projucerにある、プラグインのチャンネル設定機能について

今のバス関連API（`MultiBus API`）が用意される以前のJUCEでは、プラグインのバスがサポートするチャンネル情報は、Introjucer(Projucerの前身）によって設定していた。

具体的には、Introjucerで`.jucer`ファイルを開いたときに、`Project Settings`の`Plugin Channel Configuration`というプロパティに`{1, 1}, {2, 2}`のようにして値を設定すると、1in/1outか2in/2outのバス接続をサポートするようなプラグインが作成できる仕組みになっていた。

この方式は、プラグインがサポートできるチャンネル数がコンパイル時に決まっている必要があり、また、どのチャンネルがどのスピーカーに対応するかというチャンネル情報も指定できないため、柔軟性に欠けていた。

それを解決するための新しいバスの仕組みが一度JUCE 4.1.0で導入されたが、いろいろ問題があったのかその仕組みもdeprecatedとなり、その後、今の形に近い`MultiBus API`がJUCE 4.3.0のあたりで導入された。
https://forum.juce.com/t/multibus-api/18491

柔軟性に欠けるとはいえ、この方式は簡潔であり、またおそらく後方互換性も考慮してか、この仕組みはいまも取り除かれずに残されている。
（というか、JUCE 4.1.0の時点でこの仕組みは、deprecated扱いになっていたのだが、現在もProjucerで使用でき、deprecatedという表示も外されているようなので、このまま残していく方針なのだろうか？）

また、`juce_RTAS_Wrapper.cpp`というソースには、このマクロに適切な値が設定されていることを期待している箇所があるので、もしRTASという規格でプラグインを作成する場合は、この仕組みを使う必要があるようだ。

現在、Projucerで`.jucer`ファイルを開いて、このプロパティに値(例えば`{1, 2}, {3, 4}`)を設定すると、`AppConfig.h`ファイル内に、

```cpp
#ifndef  JucePlugin_MaxNumInputChannels
 #define JucePlugin_MaxNumInputChannels    3
#endif
#ifndef  JucePlugin_MaxNumOutputChannels
 #define JucePlugin_MaxNumOutputChannels   4
#endif
#ifndef  JucePlugin_PreferredChannelConfigurations
 #define JucePlugin_PreferredChannelConfigurations  {1, 2}, {3, 4}
#endif
```
のようなマクロが定義される。

このマクロが定義されているときは、`MultiBus API`の機能が制限されて、いくつかの箇所で、このマクロの値を優先して使用するようになる。また、前のセクションの最後にある、`containsLayout()`／`getNextBestLayoutInLayoutList()` staticメンバ関数は、このマクロが定義されているときにだけ使用される。

Projucerで表示されるこのプロパティの説明文にあるように、この方式を使用したときには、サイドチェインやAUX出力用のバスをプラグインに指定できない。そのため、そのような機能を持つプラグインを作成したい場合には、このプロパティの値を空にして上記のマクロが定義されないようにし、冒頭の`BusesProperties`クラスを使用する必要がある。