updateGraphもだいぶ理解できてきました。
どこがどんな動作をしているかがわかれば、こちらのものだね
引き続き、updateGraphのプログラムを読み進めていきます。チュートリアルページはこちらのページになります。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・VST、AUプラグイン開発の最初の一歩を踏み出したい人
・JUCEのチュートリアルをやっている人
updateGraph関数、コネクション部分
前回のスロットの選択に変化があったかどうかを判定する処理で、「hasChanged」フラグがTrueになった場合、次の処理を行います。
※メモ:slotは「ReferenceCountedObjectPtr」でslotsは「ReferenceCountedArray」というクラスです。mainProcessorは「AudioProcessorGraph」クラスです。
if (hasChanged)
{
//[5]最初にAudioProcessorGraphのすべてのコネクションを初期化します。
for (auto connection : mainProcessor->getConnections())
mainProcessor->removeConnection(connection);
//↓一時的な保存用slot、次の処理で利用するために定義しておきます。
juce::ReferenceCountedArray<Node> activeSlots;
[5]では、最初にmainProcessorのコネクション(音声処理ノード間の接続)を初期化します。ここで利用されているfor文は、C++の「範囲ベース for ループ」というやつでしょうか。
getConnectionsは、「std::vector」を返してきますので、範囲forの範囲のほうに設定して、それをconnectionという変数にひとつづつ取り出してきて、すべてのconnection回数分繰り返すようになっています。次のページがわかりやすかったです。
activeSlots変数は、変更したslotのコネクションを作成する過程で一時的に保存する変数となります。
範囲for文なるものがあるのですね。
//slotsもReferenceCountedArrayなので、範囲for文の範囲に指定できるようです。
//slotにひとつづつ要素を代入して要素数分繰り返しを行います。
for (auto slot : slots)
{
//slotはReferenceCountedObjectPtrで、何かしら音声処理が選択されているかどうかを判定しています。
if (slot != nullptr)
{
//[6]activeSlotsにslotのAudioProcessorを加えます。
activeSlots.add(slot);
//[6-1]
slot->getProcessor()->setPlayConfigDetails(getMainBusNumInputChannels(),
getMainBusNumOutputChannels(),
getSampleRate(), getBlockSize());
}
}
[6]では、activeSlotというReferenceCountedArrayクラスの変数にslotを追加しています。slotに入れてしまうと、現在利用しているものを直接かきかえてしまうので、変更を加える際には、このように一時変数を利用してノードを変更していきます。
[6-1]が少しややこしかったです^^;
slotは「AudioProcessorGraph::Node」クラスのオブジェクトである音声処理ノードへのポインタです。そのため、アロー演算子で「AudioProcessorGraph::Node」のgetProcessor()を呼び出すことができます。この段階でAudioProcessorクラスの音声処理ノードを得ることができます。続けて、AudioProcessorクラスの関数である「setPlayConfigDetails」関数を呼び出し、音声入出力バス、サンプルレートなどの設定を行っています。
//[7]activeSlotsが「empty」のときはシンプルにconnectAudioNodesを呼び出します。
if (activeSlots.isEmpty())
{
connectAudioNodes();
}
else//[8]1つ以上の音声処理ノードが設定された場合の処理です。
{
//スロットの数分繰り返します。
for (int i = 0; i < activeSlots.size() - 1; ++i)
{
//音声チャンネル分(ステレオなので2)繰り返します。
for (int channel = 0; channel < 2; ++channel)
mainProcessor->addConnection({ { activeSlots.getUnchecked(i)->nodeID, channel },
{ activeSlots.getUnchecked(i + 1)->nodeID, channel }
});
}
//[9]音声入力ノードを1つ目の音声処理ノードへ、最後の音声処理ノードを音声出力ノードへ接続します。
for (int channel = 0; channel < 2; ++channel)
{
mainProcessor->addConnection({ { audioInputNode->nodeID, channel },
{ activeSlots.getFirst()->nodeID, channel } });
mainProcessor->addConnection({ { activeSlots.getLast()->nodeID, channel },
{ audioOutputNode->nodeID, channel } });
}
}
[7]では、activeSlotsのそれぞれのslotがemptyのときは、音声入力をそのまま音声出力に接続するだけなので、コンストラクタに以前定義した「connectAudioNodes」関数を呼び出します。
[8]では、スロット間のコネクションを確立します。activeSlotsの一つ以上のslotに音声処理ノードが設定されれば、こちらの条件となります。そして、mainProcessorのaddConnectionで、スロットの音声ノードの入力と出力を順番に接続していきます。forでスロットの数くりかえされますので、スロット1をスロット2のノードへと順番に接続されて行きます。
そして、[9]で、音声入力ノードを一番最初のスロットへ接続、音声出力ノードを一番最後のスロットへとaddConnection関数で接続しています。
//[10]MIDI I/Oを接続し、ノードのバスを有効にします。
connectMidiNodes();
//すべての音声処理ノードのバスを有効にします。
for (auto node : mainProcessor->getNodes())
node->getProcessor()->enableAllBuses();
//「node->getProcessor」はAudioProcessorクラスです。
}
最後に、MIDIの入出力を接続し、ノードからAudioProcessorクラスのenableAllBuses関数を呼び出してバスを有効にして完了です。
今回もだらだらとプログラムを読み解いてしましました。
所感
今回は、クラスの関数の呼び出しがいろいろと複雑になってきましたので、次のクラスを意識すると少しだけ理解できると思いました。
・slotsは「ReferenceCountedArray」クラスです。
・slotは、「AudioProcessorGraph::Node」クラスのオブジェクトを示します。
・「slot->getProcessor」はAudioProcessorクラスのオブジェクトを示します。
・実際の音声処理ノードの接続はmainProcessorの関数を呼び出して行います。
この部分がしっかり定着してくると、ずいぶんシンプルに理解することができました。