C++
AudioUnit
audio
VST
JUCE
JUCEDay 22

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

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

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

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

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

AudioProcessor::BusesProperties

https://juce.com/doc/structAudioProcessor_1_1BusesProperties

  • AudioProcessorに設定するバス情報を構築するためのヘルパークラス。
  • withInput()withOutput()メンバ関数によって、メソッドチェーン方式で入力用/出力用それぞれのバス情報を構築できる。
  • このオブジェクトでバス情報を構築して、それをAudioProcessorクラスのコンストラクタに渡すと、それに従ってAudioProcessorのバスが設定される。
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つのプロパティを持つ。(BusesPropertieswithInput()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を保持しているAudioProcessorsetChannelLayoutOfBus()メンバ関数を呼び出すだけ。

bool setCurrentLayoutWithoutEnabling(const AudioChannelSet& layout)メンバ関数

  • setCurrentLayout()と同じように、Busのチャンネル情報を、指定したAudioChannelSetに変更するが、Busの有効/無効状態は変化させない。
  • もしBusが有効なら、BussetCurrentLayout()を呼び出して、指定したAudioChannelSetBusの現在のチャンネル情報として設定する。
  • もしBusが無効なら、指定したAudioChannelSetがこのBusによってサポートされているものかどうかを判定した上で、最後に有効だったAudioChannelSet(つまりgetLastEnabledLayout()で返るもの)として、チャンネル情報を保持する。

bool setNumberOfChannels(int channels)メンバ関数

  • 指定したチャンネル数を持つように、Busのチャンネル情報を設定する。
  • 実質的には、指定したチャンネル数に適合するAudioChannelSetを作成し、setCurrentLayout()の処理と同じように、AudioProcessorsetChannelLayoutOfBus()を呼び出す

bool isLayoutSupported(const AudioChannelSet& set, BusesLayout* currentLayout = nullptr) constメンバ関数

  • 指定したAudioChannelSetBusに設定可能かどうかを返す
  • currentLayoutnullptrでない場合は、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)メンバ関数

  • 指定したバスに対して、指定したチャンネル情報をセットする。
  • BussetCurrentLayout()メンバ関数の呼び出しも、この関数の呼び出しとなる。
  • 内部でBusgetBusesLayoutForLayoutChangeOfBus()を呼び出して、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 SettingsPlugin 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ファイル内に、

#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クラスを使用する必要がある。