| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the Water library.
 -    Copyright (c) 2015 ROLI Ltd.
 -    Copyright (C) 2017-2020 Filipe Coelho <falktx@falktx.com>
 - 
 -    Permission is granted to use this software under the terms of the GNU
 -    General Public License as published by the Free Software Foundation;
 -    either version 2 of the License, or any later version.
 - 
 -    This program is distributed in the hope that it will be useful, but WITHOUT
 -    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 -    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 - 
 -    For a full copy of the GNU General Public License see the doc/GPL.txt file.
 - 
 -   ==============================================================================
 - */
 - 
 - #include "AudioProcessorGraph.h"
 - #include "../containers/SortedSet.h"
 - 
 - namespace water {
 - 
 - //==============================================================================
 - namespace GraphRenderingOps
 - {
 - 
 - struct AudioGraphRenderingOpBase
 - {
 -     AudioGraphRenderingOpBase() noexcept {}
 -     virtual ~AudioGraphRenderingOpBase() {}
 - 
 -     virtual void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                           AudioSampleBuffer& sharedCVBufferChans,
 -                           const OwnedArray<MidiBuffer>& sharedMidiBuffers,
 -                           const int numSamples) = 0;
 - };
 - 
 - // use CRTP
 - template <class Child>
 - struct AudioGraphRenderingOp  : public AudioGraphRenderingOpBase
 - {
 -     void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                   AudioSampleBuffer& sharedCVBufferChans,
 -                   const OwnedArray<MidiBuffer>& sharedMidiBuffers,
 -                   const int numSamples) override
 -     {
 -         static_cast<Child*> (this)->perform (sharedAudioBufferChans,
 -                                              sharedCVBufferChans,
 -                                              sharedMidiBuffers,
 -                                              numSamples);
 -     }
 - };
 - 
 - //==============================================================================
 - struct ClearChannelOp  : public AudioGraphRenderingOp<ClearChannelOp>
 - {
 -     ClearChannelOp (const int channel, const bool cv) noexcept
 -         : channelNum (channel), isCV (cv) {}
 - 
 -     void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                   AudioSampleBuffer& sharedCVBufferChans,
 -                   const OwnedArray<MidiBuffer>&,
 -                   const int numSamples)
 -     {
 -         if (isCV)
 -             sharedCVBufferChans.clear (channelNum, 0, numSamples);
 -         else
 -             sharedAudioBufferChans.clear (channelNum, 0, numSamples);
 -     }
 - 
 -     const int channelNum;
 -     const bool isCV;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (ClearChannelOp)
 - };
 - 
 - //==============================================================================
 - struct CopyChannelOp  : public AudioGraphRenderingOp<CopyChannelOp>
 - {
 -     CopyChannelOp (const int srcChan, const int dstChan, const bool cv) noexcept
 -         : srcChannelNum (srcChan), dstChannelNum (dstChan), isCV (cv) {}
 - 
 -     void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                   AudioSampleBuffer& sharedCVBufferChans,
 -                   const OwnedArray<MidiBuffer>&,
 -                   const int numSamples)
 -     {
 -         if (isCV)
 -             sharedCVBufferChans.copyFrom (dstChannelNum, 0, sharedCVBufferChans, srcChannelNum, 0, numSamples);
 -         else
 -             sharedAudioBufferChans.copyFrom (dstChannelNum, 0, sharedAudioBufferChans, srcChannelNum, 0, numSamples);
 -     }
 - 
 -     const int srcChannelNum, dstChannelNum;
 -     const bool isCV;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (CopyChannelOp)
 - };
 - 
 - //==============================================================================
 - struct AddChannelOp  : public AudioGraphRenderingOp<AddChannelOp>
 - {
 -     AddChannelOp (const int srcChan, const int dstChan, const bool cv) noexcept
 -         : srcChannelNum (srcChan), dstChannelNum (dstChan), isCV (cv) {}
 - 
 -     void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                   AudioSampleBuffer& sharedCVBufferChans,
 -                   const OwnedArray<MidiBuffer>&,
 -                   const int numSamples)
 -     {
 -         if (isCV)
 -             sharedCVBufferChans.addFrom (dstChannelNum, 0, sharedCVBufferChans, srcChannelNum, 0, numSamples);
 -         else
 -             sharedAudioBufferChans.addFrom (dstChannelNum, 0, sharedAudioBufferChans, srcChannelNum, 0, numSamples);
 -     }
 - 
 -     const int srcChannelNum, dstChannelNum;
 -     const bool isCV;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (AddChannelOp)
 - };
 - 
 - //==============================================================================
 - struct ClearMidiBufferOp  : public AudioGraphRenderingOp<ClearMidiBufferOp>
 - {
 -     ClearMidiBufferOp (const int buffer) noexcept  : bufferNum (buffer)  {}
 - 
 -     void perform (AudioSampleBuffer&, AudioSampleBuffer&,
 -                   const OwnedArray<MidiBuffer>& sharedMidiBuffers,
 -                   const int)
 -     {
 -         sharedMidiBuffers.getUnchecked (bufferNum)->clear();
 -     }
 - 
 -     const int bufferNum;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (ClearMidiBufferOp)
 - };
 - 
 - //==============================================================================
 - struct CopyMidiBufferOp  : public AudioGraphRenderingOp<CopyMidiBufferOp>
 - {
 -     CopyMidiBufferOp (const int srcBuffer, const int dstBuffer) noexcept
 -         : srcBufferNum (srcBuffer), dstBufferNum (dstBuffer)
 -     {}
 - 
 -     void perform (AudioSampleBuffer&, AudioSampleBuffer&,
 -                   const OwnedArray<MidiBuffer>& sharedMidiBuffers,
 -                   const int)
 -     {
 -         *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum);
 -     }
 - 
 -     const int srcBufferNum, dstBufferNum;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (CopyMidiBufferOp)
 - };
 - 
 - //==============================================================================
 - struct AddMidiBufferOp  : public AudioGraphRenderingOp<AddMidiBufferOp>
 - {
 -     AddMidiBufferOp (const int srcBuffer, const int dstBuffer)
 -         : srcBufferNum (srcBuffer), dstBufferNum (dstBuffer)
 -     {}
 - 
 -     void perform (AudioSampleBuffer&, AudioSampleBuffer&,
 -                   const OwnedArray<MidiBuffer>& sharedMidiBuffers,
 -                   const int numSamples)
 -     {
 -         sharedMidiBuffers.getUnchecked (dstBufferNum)
 -             ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0);
 -     }
 - 
 -     const int srcBufferNum, dstBufferNum;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (AddMidiBufferOp)
 - };
 - 
 - //==============================================================================
 - struct DelayChannelOp  : public AudioGraphRenderingOp<DelayChannelOp>
 - {
 -     DelayChannelOp (const int chan, const int delaySize, const bool cv)
 -         : channel (chan),
 -           bufferSize (delaySize + 1),
 -           readIndex (0), writeIndex (delaySize),
 -           isCV (cv)
 -     {
 -         buffer.calloc ((size_t) bufferSize);
 -     }
 - 
 -     void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                   AudioSampleBuffer& sharedCVBufferChans,
 -                   const OwnedArray<MidiBuffer>&,
 -                   const int numSamples)
 -     {
 -         float* data = isCV
 -                     ? sharedCVBufferChans.getWritePointer (channel, 0)
 -                     : sharedAudioBufferChans.getWritePointer (channel, 0);
 -         HeapBlock<float>& block = buffer;
 - 
 -         for (int i = numSamples; --i >= 0;)
 -         {
 -             block [writeIndex] = *data;
 -             *data++ = block [readIndex];
 - 
 -             if (++readIndex  >= bufferSize) readIndex = 0;
 -             if (++writeIndex >= bufferSize) writeIndex = 0;
 -         }
 -     }
 - 
 - private:
 -     HeapBlock<float> buffer;
 -     const int channel, bufferSize;
 -     int readIndex, writeIndex;
 -     const bool isCV;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (DelayChannelOp)
 - };
 - 
 - //==============================================================================
 - struct ProcessBufferOp   : public AudioGraphRenderingOp<ProcessBufferOp>
 - {
 -     ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& n,
 -                      const Array<uint>& audioChannelsUsed,
 -                      const uint totalNumChans,
 -                      const Array<uint>& cvInChannelsUsed,
 -                      const Array<uint>& cvOutChannelsUsed,
 -                      const int midiBuffer)
 -         : node (n),
 -           processor (n->getProcessor()),
 -           audioChannelsToUse (audioChannelsUsed),
 -           cvInChannelsToUse (cvInChannelsUsed),
 -           cvOutChannelsToUse (cvOutChannelsUsed),
 -           totalAudioChans (jmax (1U, totalNumChans)),
 -           totalCVIns (cvInChannelsUsed.size()),
 -           totalCVOuts (cvOutChannelsUsed.size()),
 -           midiBufferToUse (midiBuffer)
 -     {
 -         audioChannels.calloc (totalAudioChans);
 -         cvInChannels.calloc (totalCVIns);
 -         cvOutChannels.calloc (totalCVOuts);
 - 
 -         while (audioChannelsToUse.size() < static_cast<int>(totalAudioChans))
 -             audioChannelsToUse.add (0);
 -     }
 - 
 -     void perform (AudioSampleBuffer& sharedAudioBufferChans,
 -                   AudioSampleBuffer& sharedCVBufferChans,
 -                   const OwnedArray<MidiBuffer>& sharedMidiBuffers,
 -                   const int numSamples)
 -     {
 -         HeapBlock<float*>& audioChannelsCopy = audioChannels;
 -         HeapBlock<float*>& cvInChannelsCopy  = cvInChannels;
 -         HeapBlock<float*>& cvOutChannelsCopy = cvOutChannels;
 - 
 -         for (uint i = 0; i < totalAudioChans; ++i)
 -             audioChannelsCopy[i] = sharedAudioBufferChans.getWritePointer (audioChannelsToUse.getUnchecked (i), 0);
 - 
 -         for (uint i = 0; i < totalCVIns; ++i)
 -             cvInChannels[i] = sharedCVBufferChans.getWritePointer (cvInChannelsToUse.getUnchecked (i), 0);
 - 
 -         for (uint i = 0; i < totalCVOuts; ++i)
 -             cvOutChannels[i] = sharedCVBufferChans.getWritePointer (cvOutChannelsToUse.getUnchecked (i), 0);
 - 
 -         AudioSampleBuffer audioBuffer (audioChannelsCopy, totalAudioChans, numSamples);
 -         AudioSampleBuffer cvInBuffer  (cvInChannelsCopy, totalCVIns, numSamples);
 -         AudioSampleBuffer cvOutBuffer (cvOutChannelsCopy, totalCVOuts, numSamples);
 - 
 -         if (processor->isSuspended())
 -         {
 -             audioBuffer.clear();
 -             cvOutBuffer.clear();
 -         }
 -         else
 -         {
 -             const CarlaRecursiveMutexLocker cml (processor->getCallbackLock());
 - 
 -             callProcess (audioBuffer, cvInBuffer, cvOutBuffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse));
 -         }
 -     }
 - 
 -     void callProcess (AudioSampleBuffer& audioBuffer,
 -                       AudioSampleBuffer& cvInBuffer,
 -                       AudioSampleBuffer& cvOutBuffer,
 -                       MidiBuffer& midiMessages)
 -     {
 -         processor->processBlockWithCV (audioBuffer, cvInBuffer, cvOutBuffer, midiMessages);
 -     }
 - 
 -     const AudioProcessorGraph::Node::Ptr node;
 -     AudioProcessor* const processor;
 - 
 - private:
 -     Array<uint> audioChannelsToUse;
 -     Array<uint> cvInChannelsToUse;
 -     Array<uint> cvOutChannelsToUse;
 -     HeapBlock<float*> audioChannels;
 -     HeapBlock<float*> cvInChannels;
 -     HeapBlock<float*> cvOutChannels;
 -     AudioSampleBuffer tempBuffer;
 -     const uint totalAudioChans;
 -     const uint totalCVIns;
 -     const uint totalCVOuts;
 -     const int midiBufferToUse;
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (ProcessBufferOp)
 - };
 - 
 - //==============================================================================
 - /** Used to calculate the correct sequence of rendering ops needed, based on
 -     the best re-use of shared buffers at each stage.
 - */
 - struct RenderingOpSequenceCalculator
 - {
 -     RenderingOpSequenceCalculator (AudioProcessorGraph& g,
 -                                    const Array<AudioProcessorGraph::Node*>& nodes,
 -                                    Array<void*>& renderingOps)
 -         : graph (g),
 -           orderedNodes (nodes),
 -           totalLatency (0)
 -     {
 -         audioNodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros
 -         audioChannels.add (0);
 - 
 -         cvNodeIds.add ((uint32) zeroNodeID);
 -         cvChannels.add (0);
 - 
 -         midiNodeIds.add ((uint32) zeroNodeID);
 - 
 -         for (int i = 0; i < orderedNodes.size(); ++i)
 -         {
 -             createRenderingOpsForNode (*orderedNodes.getUnchecked(i), renderingOps, i);
 -             markAnyUnusedBuffersAsFree (i);
 -         }
 - 
 -         graph.setLatencySamples (totalLatency);
 -     }
 - 
 -     int getNumAudioBuffersNeeded() const noexcept    { return audioNodeIds.size(); }
 -     int getNumCVBuffersNeeded() const noexcept       { return cvNodeIds.size(); }
 -     int getNumMidiBuffersNeeded() const noexcept     { return midiNodeIds.size(); }
 - 
 - private:
 -     //==============================================================================
 -     AudioProcessorGraph& graph;
 -     const Array<AudioProcessorGraph::Node*>& orderedNodes;
 -     Array<uint> audioChannels, cvChannels;
 -     Array<uint32> audioNodeIds, cvNodeIds, midiNodeIds;
 - 
 -     enum { freeNodeID = 0xffffffff, zeroNodeID = 0xfffffffe, anonymousNodeID = 0xfffffffd };
 - 
 -     static bool isNodeBusy (uint32 nodeID) noexcept     { return nodeID != freeNodeID; }
 - 
 -     Array<uint32> nodeDelayIDs;
 -     Array<int> nodeDelays;
 -     int totalLatency;
 - 
 -     int getNodeDelay (const uint32 nodeID) const        { return nodeDelays [nodeDelayIDs.indexOf (nodeID)]; }
 - 
 -     void setNodeDelay (const uint32 nodeID, const int latency)
 -     {
 -         const int index = nodeDelayIDs.indexOf (nodeID);
 - 
 -         if (index >= 0)
 -         {
 -             nodeDelays.set (index, latency);
 -         }
 -         else
 -         {
 -             nodeDelayIDs.add (nodeID);
 -             nodeDelays.add (latency);
 -         }
 -     }
 - 
 -     int getInputLatencyForNode (const uint32 nodeID) const
 -     {
 -         int maxLatency = 0;
 - 
 -         for (int i = graph.getNumConnections(); --i >= 0;)
 -         {
 -             const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
 - 
 -             if (c->destNodeId == nodeID)
 -                 maxLatency = jmax (maxLatency, getNodeDelay (c->sourceNodeId));
 -         }
 - 
 -         return maxLatency;
 -     }
 - 
 -     //==============================================================================
 -     void createRenderingOpsForNode (AudioProcessorGraph::Node& node,
 -                                     Array<void*>& renderingOps,
 -                                     const int ourRenderingIndex)
 -     {
 -         AudioProcessor& processor = *node.getProcessor();
 -         const uint numAudioIns = processor.getTotalNumInputChannels(AudioProcessor::ChannelTypeAudio);
 -         const uint numAudioOuts = processor.getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio);
 -         const uint numCVIns = processor.getTotalNumInputChannels(AudioProcessor::ChannelTypeCV);
 -         const uint numCVOuts = processor.getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV);
 -         const uint totalAudioChans = jmax (numAudioIns, numAudioOuts);
 - 
 -         Array<uint> audioChannelsToUse, cvInChannelsToUse, cvOutChannelsToUse;
 -         int midiBufferToUse = -1;
 - 
 -         int maxLatency = getInputLatencyForNode (node.nodeId);
 - 
 -         for (uint inputChan = 0; inputChan < numAudioIns; ++inputChan)
 -         {
 -             // get a list of all the inputs to this node
 -             Array<uint32> sourceNodes;
 -             Array<uint> sourceOutputChans;
 - 
 -             for (int i = graph.getNumConnections(); --i >= 0;)
 -             {
 -                 const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
 - 
 -                 if (c->destNodeId == node.nodeId
 -                     && c->destChannelIndex == inputChan
 -                     && c->channelType == AudioProcessor::ChannelTypeAudio)
 -                 {
 -                     sourceNodes.add (c->sourceNodeId);
 -                     sourceOutputChans.add (c->sourceChannelIndex);
 -                 }
 -             }
 - 
 -             int bufIndex = -1;
 - 
 -             if (sourceNodes.size() == 0)
 -             {
 -                 // unconnected input channel
 -                 bufIndex = getFreeBuffer (AudioProcessor::ChannelTypeAudio);
 -                 renderingOps.add (new ClearChannelOp (bufIndex, false));
 -             }
 -             else if (sourceNodes.size() == 1)
 -             {
 -                 // channel with a straightforward single input..
 -                 const uint32 srcNode = sourceNodes.getUnchecked(0);
 -                 const uint srcChan = sourceOutputChans.getUnchecked(0);
 - 
 -                 bufIndex = getBufferContaining (AudioProcessor::ChannelTypeAudio, srcNode, srcChan);
 - 
 -                 if (bufIndex < 0)
 -                 {
 -                     // if not found, this is probably a feedback loop
 -                     bufIndex = getReadOnlyEmptyBuffer();
 -                     wassert (bufIndex >= 0);
 -                 }
 - 
 -                 if (inputChan < numAudioOuts
 -                      && isBufferNeededLater (AudioProcessor::ChannelTypeAudio,
 -                                              ourRenderingIndex,
 -                                              inputChan,
 -                                              srcNode, srcChan))
 -                 {
 -                     // can't mess up this channel because it's needed later by another node, so we
 -                     // need to use a copy of it..
 -                     const int newFreeBuffer = getFreeBuffer (AudioProcessor::ChannelTypeAudio);
 - 
 -                     renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer, false));
 - 
 -                     bufIndex = newFreeBuffer;
 -                 }
 - 
 -                 const int nodeDelay = getNodeDelay (srcNode);
 - 
 -                 if (nodeDelay < maxLatency)
 -                     renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay, false));
 -             }
 -             else
 -             {
 -                 // channel with a mix of several inputs..
 - 
 -                 // try to find a re-usable channel from our inputs..
 -                 int reusableInputIndex = -1;
 - 
 -                 for (int i = 0; i < sourceNodes.size(); ++i)
 -                 {
 -                     const int sourceBufIndex = getBufferContaining (AudioProcessor::ChannelTypeAudio,
 -                                                                     sourceNodes.getUnchecked(i),
 -                                                                     sourceOutputChans.getUnchecked(i));
 - 
 -                     if (sourceBufIndex >= 0
 -                         && ! isBufferNeededLater (AudioProcessor::ChannelTypeAudio,
 -                                                   ourRenderingIndex,
 -                                                   inputChan,
 -                                                   sourceNodes.getUnchecked(i),
 -                                                   sourceOutputChans.getUnchecked(i)))
 -                     {
 -                         // we've found one of our input chans that can be re-used..
 -                         reusableInputIndex = i;
 -                         bufIndex = sourceBufIndex;
 - 
 -                         const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (i));
 -                         if (nodeDelay < maxLatency)
 -                             renderingOps.add (new DelayChannelOp (sourceBufIndex, maxLatency - nodeDelay, false));
 - 
 -                         break;
 -                     }
 -                 }
 - 
 -                 if (reusableInputIndex < 0)
 -                 {
 -                     // can't re-use any of our input chans, so get a new one and copy everything into it..
 -                     bufIndex = getFreeBuffer (AudioProcessor::ChannelTypeAudio);
 -                     wassert (bufIndex != 0);
 - 
 -                     markBufferAsContaining (AudioProcessor::ChannelTypeAudio,
 -                                             bufIndex, static_cast<uint32> (anonymousNodeID), 0);
 - 
 -                     const int srcIndex = getBufferContaining (AudioProcessor::ChannelTypeAudio,
 -                                                               sourceNodes.getUnchecked (0),
 -                                                               sourceOutputChans.getUnchecked (0));
 -                     if (srcIndex < 0)
 -                     {
 -                         // if not found, this is probably a feedback loop
 -                         renderingOps.add (new ClearChannelOp (bufIndex, false));
 -                     }
 -                     else
 -                     {
 -                         renderingOps.add (new CopyChannelOp (srcIndex, bufIndex, false));
 -                     }
 - 
 -                     reusableInputIndex = 0;
 -                     const int nodeDelay = getNodeDelay (sourceNodes.getFirst());
 - 
 -                     if (nodeDelay < maxLatency)
 -                         renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay, false));
 -                 }
 - 
 -                 for (int j = 0; j < sourceNodes.size(); ++j)
 -                 {
 -                     if (j != reusableInputIndex)
 -                     {
 -                         int srcIndex = getBufferContaining (AudioProcessor::ChannelTypeAudio,
 -                                                             sourceNodes.getUnchecked(j),
 -                                                             sourceOutputChans.getUnchecked(j));
 -                         if (srcIndex >= 0)
 -                         {
 -                             const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (j));
 - 
 -                             if (nodeDelay < maxLatency)
 -                             {
 -                                 if (! isBufferNeededLater (AudioProcessor::ChannelTypeAudio,
 -                                                            ourRenderingIndex, inputChan,
 -                                                            sourceNodes.getUnchecked(j),
 -                                                            sourceOutputChans.getUnchecked(j)))
 -                                 {
 -                                     renderingOps.add (new DelayChannelOp (srcIndex, maxLatency - nodeDelay, false));
 -                                 }
 -                                 else // buffer is reused elsewhere, can't be delayed
 -                                 {
 -                                     const int bufferToDelay = getFreeBuffer (AudioProcessor::ChannelTypeAudio);
 -                                     renderingOps.add (new CopyChannelOp (srcIndex, bufferToDelay, false));
 -                                     renderingOps.add (new DelayChannelOp (bufferToDelay, maxLatency - nodeDelay, false));
 -                                     srcIndex = bufferToDelay;
 -                                 }
 -                             }
 - 
 -                             renderingOps.add (new AddChannelOp (srcIndex, bufIndex, false));
 -                         }
 -                     }
 -                 }
 -             }
 - 
 -             CARLA_SAFE_ASSERT_CONTINUE (bufIndex >= 0);
 -             audioChannelsToUse.add (bufIndex);
 - 
 -             if (inputChan < numAudioOuts)
 -                 markBufferAsContaining (AudioProcessor::ChannelTypeAudio, bufIndex, node.nodeId, inputChan);
 -         }
 - 
 -         for (uint outputChan = numAudioIns; outputChan < numAudioOuts; ++outputChan)
 -         {
 -             const int bufIndex = getFreeBuffer (AudioProcessor::ChannelTypeAudio);
 -             CARLA_SAFE_ASSERT_CONTINUE (bufIndex > 0);
 -             audioChannelsToUse.add (bufIndex);
 -             markBufferAsContaining (AudioProcessor::ChannelTypeAudio, bufIndex, node.nodeId, outputChan);
 -         }
 - 
 -         for (uint inputChan = 0; inputChan < numCVIns; ++inputChan)
 -         {
 -             // get a list of all the inputs to this node
 -             Array<uint32> sourceNodes;
 -             Array<uint> sourceOutputChans;
 - 
 -             for (int i = graph.getNumConnections(); --i >= 0;)
 -             {
 -                 const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
 - 
 -                 if (c->destNodeId == node.nodeId
 -                     && c->destChannelIndex == inputChan
 -                     && c->channelType == AudioProcessor::ChannelTypeCV)
 -                 {
 -                     sourceNodes.add (c->sourceNodeId);
 -                     sourceOutputChans.add (c->sourceChannelIndex);
 -                 }
 -             }
 - 
 -             int bufIndex = -1;
 - 
 -             if (sourceNodes.size() == 0)
 -             {
 -                 // unconnected input channel
 -                 bufIndex = getFreeBuffer (AudioProcessor::ChannelTypeCV);
 -                 renderingOps.add (new ClearChannelOp (bufIndex, true));
 -             }
 -             else if (sourceNodes.size() == 1)
 -             {
 -                 // channel with a straightforward single input..
 -                 const uint32 srcNode = sourceNodes.getUnchecked(0);
 -                 const uint srcChan = sourceOutputChans.getUnchecked(0);
 - 
 -                 bufIndex = getBufferContaining (AudioProcessor::ChannelTypeCV, srcNode, srcChan);
 - 
 -                 if (bufIndex < 0)
 -                 {
 -                     // if not found, this is probably a feedback loop
 -                     bufIndex = getReadOnlyEmptyBuffer();
 -                     wassert (bufIndex >= 0);
 -                 }
 - 
 -                 const int newFreeBuffer = getFreeBuffer (AudioProcessor::ChannelTypeCV);
 - 
 -                 renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer, true));
 - 
 -                 bufIndex = newFreeBuffer;
 - 
 -                 const int nodeDelay = getNodeDelay (srcNode);
 - 
 -                 if (nodeDelay < maxLatency)
 -                     renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay, true));
 -             }
 -             else
 -             {
 -                 // channel with a mix of several inputs..
 - 
 -                 {
 -                     bufIndex = getFreeBuffer (AudioProcessor::ChannelTypeCV);
 -                     wassert (bufIndex != 0);
 - 
 -                     const int srcIndex = getBufferContaining (AudioProcessor::ChannelTypeCV,
 -                                                               sourceNodes.getUnchecked (0),
 -                                                               sourceOutputChans.getUnchecked (0));
 -                     if (srcIndex < 0)
 -                     {
 -                         // if not found, this is probably a feedback loop
 -                         renderingOps.add (new ClearChannelOp (bufIndex, true));
 -                     }
 -                     else
 -                     {
 -                         renderingOps.add (new CopyChannelOp (srcIndex, bufIndex, true));
 -                     }
 - 
 -                     const int nodeDelay = getNodeDelay (sourceNodes.getFirst());
 - 
 -                     if (nodeDelay < maxLatency)
 -                         renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay, true));
 -                 }
 - 
 -                 for (int j = 1; j < sourceNodes.size(); ++j)
 -                 {
 -                     int srcIndex = getBufferContaining (AudioProcessor::ChannelTypeCV,
 -                                                         sourceNodes.getUnchecked(j),
 -                                                         sourceOutputChans.getUnchecked(j));
 -                     if (srcIndex >= 0)
 -                     {
 -                         const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (j));
 - 
 -                         if (nodeDelay < maxLatency)
 -                         {
 -                             const int bufferToDelay = getFreeBuffer (AudioProcessor::ChannelTypeCV);
 -                             renderingOps.add (new CopyChannelOp (srcIndex, bufferToDelay, true));
 -                             renderingOps.add (new DelayChannelOp (bufferToDelay, maxLatency - nodeDelay, true));
 -                             srcIndex = bufferToDelay;
 -                         }
 - 
 -                         renderingOps.add (new AddChannelOp (srcIndex, bufIndex, true));
 -                     }
 -                 }
 -             }
 - 
 -             CARLA_SAFE_ASSERT_CONTINUE (bufIndex >= 0);
 -             cvInChannelsToUse.add (bufIndex);
 -             markBufferAsContaining (AudioProcessor::ChannelTypeCV, bufIndex, node.nodeId, inputChan);
 -         }
 - 
 -         for (uint outputChan = 0; outputChan < numCVOuts; ++outputChan)
 -         {
 -             const int bufIndex = getFreeBuffer (AudioProcessor::ChannelTypeCV);
 -             CARLA_SAFE_ASSERT_CONTINUE (bufIndex > 0);
 -             cvOutChannelsToUse.add (bufIndex);
 -             markBufferAsContaining (AudioProcessor::ChannelTypeCV, bufIndex, node.nodeId, outputChan);
 -         }
 - 
 -         // Now the same thing for midi..
 -         Array<uint32> midiSourceNodes;
 - 
 -         for (int i = graph.getNumConnections(); --i >= 0;)
 -         {
 -             const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
 - 
 -             if (c->destNodeId == node.nodeId && c->channelType == AudioProcessor::ChannelTypeMIDI)
 -                 midiSourceNodes.add (c->sourceNodeId);
 -         }
 - 
 -         if (midiSourceNodes.size() == 0)
 -         {
 -             // No midi inputs..
 -             midiBufferToUse = getFreeBuffer (AudioProcessor::ChannelTypeMIDI); // need to pick a buffer even if the processor doesn't use midi
 - 
 -             if (processor.acceptsMidi() || processor.producesMidi())
 -                 renderingOps.add (new ClearMidiBufferOp (midiBufferToUse));
 -         }
 -         else if (midiSourceNodes.size() == 1)
 -         {
 -             // One midi input..
 -             midiBufferToUse = getBufferContaining (AudioProcessor::ChannelTypeMIDI,
 -                                                    midiSourceNodes.getUnchecked(0),
 -                                                    0);
 -             if (midiBufferToUse >= 0)
 -             {
 -                 if (isBufferNeededLater (AudioProcessor::ChannelTypeMIDI,
 -                                          ourRenderingIndex, 0,
 -                                          midiSourceNodes.getUnchecked(0), 0))
 -                 {
 -                     // can't mess up this channel because it's needed later by another node, so we
 -                     // need to use a copy of it..
 -                     const int newFreeBuffer = getFreeBuffer (AudioProcessor::ChannelTypeMIDI);
 -                     renderingOps.add (new CopyMidiBufferOp (midiBufferToUse, newFreeBuffer));
 -                     midiBufferToUse = newFreeBuffer;
 -                 }
 -             }
 -             else
 -             {
 -                 // probably a feedback loop, so just use an empty one..
 -                 midiBufferToUse = getFreeBuffer (AudioProcessor::ChannelTypeMIDI); // need to pick a buffer even if the processor doesn't use midi
 -             }
 -         }
 -         else
 -         {
 -             // More than one midi input being mixed..
 -             int reusableInputIndex = -1;
 - 
 -             for (int i = 0; i < midiSourceNodes.size(); ++i)
 -             {
 -                 const int sourceBufIndex = getBufferContaining (AudioProcessor::ChannelTypeMIDI,
 -                                                                 midiSourceNodes.getUnchecked(i),
 -                                                                 0);
 - 
 -                 if (sourceBufIndex >= 0
 -                      && ! isBufferNeededLater (AudioProcessor::ChannelTypeMIDI,
 -                                                ourRenderingIndex, 0,
 -                                                midiSourceNodes.getUnchecked(i), 0))
 -                 {
 -                     // we've found one of our input buffers that can be re-used..
 -                     reusableInputIndex = i;
 -                     midiBufferToUse = sourceBufIndex;
 -                     break;
 -                 }
 -             }
 - 
 -             if (reusableInputIndex < 0)
 -             {
 -                 // can't re-use any of our input buffers, so get a new one and copy everything into it..
 -                 midiBufferToUse = getFreeBuffer (AudioProcessor::ChannelTypeMIDI);
 -                 wassert (midiBufferToUse >= 0);
 - 
 -                 const int srcIndex = getBufferContaining (AudioProcessor::ChannelTypeMIDI,
 -                                                           midiSourceNodes.getUnchecked(0),
 -                                                           0);
 -                 if (srcIndex >= 0)
 -                     renderingOps.add (new CopyMidiBufferOp (srcIndex, midiBufferToUse));
 -                 else
 -                     renderingOps.add (new ClearMidiBufferOp (midiBufferToUse));
 - 
 -                 reusableInputIndex = 0;
 -             }
 - 
 -             for (int j = 0; j < midiSourceNodes.size(); ++j)
 -             {
 -                 if (j != reusableInputIndex)
 -                 {
 -                     const int srcIndex = getBufferContaining (AudioProcessor::ChannelTypeMIDI,
 -                                                               midiSourceNodes.getUnchecked(j),
 -                                                               0);
 -                     if (srcIndex >= 0)
 -                         renderingOps.add (new AddMidiBufferOp (srcIndex, midiBufferToUse));
 -                 }
 -             }
 -         }
 - 
 -         if (processor.producesMidi())
 -             markBufferAsContaining (AudioProcessor::ChannelTypeMIDI,
 -                                     midiBufferToUse, node.nodeId,
 -                                     0);
 - 
 -         setNodeDelay (node.nodeId, maxLatency + processor.getLatencySamples());
 - 
 -         if (numAudioOuts == 0)
 -             totalLatency = maxLatency;
 - 
 -         renderingOps.add (new ProcessBufferOp (&node,
 -                                                audioChannelsToUse,
 -                                                totalAudioChans,
 -                                                cvInChannelsToUse,
 -                                                cvOutChannelsToUse,
 -                                                midiBufferToUse));
 -     }
 - 
 -     //==============================================================================
 -     int getFreeBuffer (const AudioProcessor::ChannelType channelType)
 -     {
 -         switch (channelType)
 -         {
 -         case AudioProcessor::ChannelTypeAudio:
 -             for (int i = 1; i < audioNodeIds.size(); ++i)
 -                 if (audioNodeIds.getUnchecked(i) == freeNodeID)
 -                     return i;
 - 
 -             audioNodeIds.add ((uint32) freeNodeID);
 -             audioChannels.add (0);
 -             return audioNodeIds.size() - 1;
 - 
 -         case AudioProcessor::ChannelTypeCV:
 -             for (int i = 1; i < cvNodeIds.size(); ++i)
 -                 if (cvNodeIds.getUnchecked(i) == freeNodeID)
 -                     return i;
 - 
 -             cvNodeIds.add ((uint32) freeNodeID);
 -             cvChannels.add (0);
 -             return cvNodeIds.size() - 1;
 - 
 -         case AudioProcessor::ChannelTypeMIDI:
 -             for (int i = 1; i < midiNodeIds.size(); ++i)
 -                 if (midiNodeIds.getUnchecked(i) == freeNodeID)
 -                     return i;
 - 
 -             midiNodeIds.add ((uint32) freeNodeID);
 -             return midiNodeIds.size() - 1;
 -         }
 - 
 -         return -1;
 -     }
 - 
 -     int getReadOnlyEmptyBuffer() const noexcept
 -     {
 -         return 0;
 -     }
 - 
 -     int getBufferContaining (const AudioProcessor::ChannelType channelType,
 -                              const uint32 nodeId,
 -                              const uint outputChannel) const noexcept
 -     {
 -         switch (channelType)
 -         {
 -         case AudioProcessor::ChannelTypeAudio:
 -             for (int i = audioNodeIds.size(); --i >= 0;)
 -                 if (audioNodeIds.getUnchecked(i) == nodeId && audioChannels.getUnchecked(i) == outputChannel)
 -                     return i;
 -             break;
 - 
 -         case AudioProcessor::ChannelTypeCV:
 -             for (int i = cvNodeIds.size(); --i >= 0;)
 -                 if (cvNodeIds.getUnchecked(i) == nodeId && cvChannels.getUnchecked(i) == outputChannel)
 -                     return i;
 -             break;
 - 
 -         case AudioProcessor::ChannelTypeMIDI:
 -             for (int i = midiNodeIds.size(); --i >= 0;)
 -             {
 -                 if (midiNodeIds.getUnchecked(i) == nodeId)
 -                     return i;
 -             }
 -             break;
 -         }
 - 
 -         return -1;
 -     }
 - 
 -     void markAnyUnusedBuffersAsFree (const int stepIndex)
 -     {
 -         for (int i = 0; i < audioNodeIds.size(); ++i)
 -         {
 -             if (isNodeBusy (audioNodeIds.getUnchecked(i))
 -                  && ! isBufferNeededLater (AudioProcessor::ChannelTypeAudio,
 -                                            stepIndex, -1,
 -                                            audioNodeIds.getUnchecked(i),
 -                                            audioChannels.getUnchecked(i)))
 -             {
 -                 audioNodeIds.set (i, (uint32) freeNodeID);
 -             }
 -         }
 - 
 -         // NOTE: CV skipped on purpose
 - 
 -         for (int i = 0; i < midiNodeIds.size(); ++i)
 -         {
 -             if (isNodeBusy (midiNodeIds.getUnchecked(i))
 -                  && ! isBufferNeededLater (AudioProcessor::ChannelTypeMIDI,
 -                                            stepIndex, -1,
 -                                            midiNodeIds.getUnchecked(i), 0))
 -             {
 -                 midiNodeIds.set (i, (uint32) freeNodeID);
 -             }
 -         }
 -     }
 - 
 -     bool isBufferNeededLater (const AudioProcessor::ChannelType channelType,
 -                               int stepIndexToSearchFrom,
 -                               uint inputChannelOfIndexToIgnore,
 -                               const uint32 nodeId,
 -                               const uint outputChanIndex) const
 -     {
 -         while (stepIndexToSearchFrom < orderedNodes.size())
 -         {
 -             const AudioProcessorGraph::Node* const node = (const AudioProcessorGraph::Node*) orderedNodes.getUnchecked (stepIndexToSearchFrom);
 - 
 -             for (uint i = 0; i < node->getProcessor()->getTotalNumInputChannels(channelType); ++i)
 -                 if (i != inputChannelOfIndexToIgnore
 -                         && graph.getConnectionBetween (channelType,
 -                                                        nodeId, outputChanIndex,
 -                                                        node->nodeId, i) != nullptr)
 -                     return true;
 - 
 -             inputChannelOfIndexToIgnore = (uint)-1;
 -             ++stepIndexToSearchFrom;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     void markBufferAsContaining (const AudioProcessor::ChannelType channelType,
 -                                  int bufferNum, uint32 nodeId, int outputIndex)
 -     {
 -         switch (channelType)
 -         {
 -         case AudioProcessor::ChannelTypeAudio:
 -             CARLA_SAFE_ASSERT_BREAK (bufferNum >= 0 && bufferNum < audioNodeIds.size());
 -             audioNodeIds.set (bufferNum, nodeId);
 -             audioChannels.set (bufferNum, outputIndex);
 -             break;
 - 
 -         case AudioProcessor::ChannelTypeCV:
 -             CARLA_SAFE_ASSERT_BREAK (bufferNum >= 0 && bufferNum < cvNodeIds.size());
 -             cvNodeIds.set (bufferNum, nodeId);
 -             cvChannels.set (bufferNum, outputIndex);
 -             break;
 - 
 -         case AudioProcessor::ChannelTypeMIDI:
 -             CARLA_SAFE_ASSERT_BREAK (bufferNum > 0 && bufferNum < midiNodeIds.size());
 -             midiNodeIds.set (bufferNum, nodeId);
 -             break;
 -         }
 -     }
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (RenderingOpSequenceCalculator)
 - };
 - 
 - //==============================================================================
 - // Holds a fast lookup table for checking which nodes are inputs to others.
 - class ConnectionLookupTable
 - {
 - public:
 -     explicit ConnectionLookupTable (const OwnedArray<AudioProcessorGraph::Connection>& connections)
 -     {
 -         for (int i = 0; i < static_cast<int>(connections.size()); ++i)
 -         {
 -             const AudioProcessorGraph::Connection* const c = connections.getUnchecked(i);
 - 
 -             int index;
 -             Entry* entry = findEntry (c->destNodeId, index);
 - 
 -             if (entry == nullptr)
 -             {
 -                 entry = new Entry (c->destNodeId);
 -                 entries.insert (index, entry);
 -             }
 - 
 -             entry->srcNodes.add (c->sourceNodeId);
 -         }
 -     }
 - 
 -     bool isAnInputTo (const uint32 possibleInputId,
 -                       const uint32 possibleDestinationId) const noexcept
 -     {
 -         return isAnInputToRecursive (possibleInputId, possibleDestinationId, entries.size());
 -     }
 - 
 - private:
 -     //==============================================================================
 -     struct Entry
 -     {
 -         explicit Entry (const uint32 destNodeId_) noexcept : destNodeId (destNodeId_) {}
 - 
 -         const uint32 destNodeId;
 -         SortedSet<uint32> srcNodes;
 - 
 -         CARLA_DECLARE_NON_COPY_CLASS (Entry)
 -     };
 - 
 -     OwnedArray<Entry> entries;
 - 
 -     bool isAnInputToRecursive (const uint32 possibleInputId,
 -                                const uint32 possibleDestinationId,
 -                                int recursionCheck) const noexcept
 -     {
 -         int index;
 - 
 -         if (const Entry* const entry = findEntry (possibleDestinationId, index))
 -         {
 -             const SortedSet<uint32>& srcNodes = entry->srcNodes;
 - 
 -             if (srcNodes.contains (possibleInputId))
 -                 return true;
 - 
 -             if (--recursionCheck >= 0)
 -             {
 -                 for (int i = 0; i < srcNodes.size(); ++i)
 -                     if (isAnInputToRecursive (possibleInputId, srcNodes.getUnchecked(i), recursionCheck))
 -                         return true;
 -             }
 -         }
 - 
 -         return false;
 -     }
 - 
 -     Entry* findEntry (const uint32 destNodeId, int& insertIndex) const noexcept
 -     {
 -         Entry* result = nullptr;
 - 
 -         int start = 0;
 -         int end = entries.size();
 - 
 -         for (;;)
 -         {
 -             if (start >= end)
 -             {
 -                 break;
 -             }
 -             else if (destNodeId == entries.getUnchecked (start)->destNodeId)
 -             {
 -                 result = entries.getUnchecked (start);
 -                 break;
 -             }
 -             else
 -             {
 -                 const int halfway = (start + end) / 2;
 - 
 -                 if (halfway == start)
 -                 {
 -                     if (destNodeId >= entries.getUnchecked (halfway)->destNodeId)
 -                         ++start;
 - 
 -                     break;
 -                 }
 -                 else if (destNodeId >= entries.getUnchecked (halfway)->destNodeId)
 -                     start = halfway;
 -                 else
 -                     end = halfway;
 -             }
 -         }
 - 
 -         insertIndex = start;
 -         return result;
 -     }
 - 
 -     CARLA_DECLARE_NON_COPY_CLASS (ConnectionLookupTable)
 - };
 - 
 - //==============================================================================
 - struct ConnectionSorter
 - {
 -     static int compareElements (const AudioProcessorGraph::Connection* const first,
 -                                 const AudioProcessorGraph::Connection* const second) noexcept
 -     {
 -         if (first->sourceNodeId < second->sourceNodeId)                return -1;
 -         if (first->sourceNodeId > second->sourceNodeId)                return 1;
 -         if (first->destNodeId < second->destNodeId)                    return -1;
 -         if (first->destNodeId > second->destNodeId)                    return 1;
 -         if (first->sourceChannelIndex < second->sourceChannelIndex)    return -1;
 -         if (first->sourceChannelIndex > second->sourceChannelIndex)    return 1;
 -         if (first->destChannelIndex < second->destChannelIndex)        return -1;
 -         if (first->destChannelIndex > second->destChannelIndex)        return 1;
 - 
 -         return 0;
 -     }
 - };
 - 
 - }
 - 
 - //==============================================================================
 - AudioProcessorGraph::Connection::Connection (ChannelType ct,
 -                                              const uint32 sourceID, const uint sourceChannel,
 -                                              const uint32 destID, const uint destChannel) noexcept
 -     : channelType (ct),
 -       sourceNodeId (sourceID), sourceChannelIndex (sourceChannel),
 -       destNodeId (destID), destChannelIndex (destChannel)
 - {
 - }
 - 
 - //==============================================================================
 - AudioProcessorGraph::Node::Node (const uint32 nodeID, AudioProcessor* const p) noexcept
 -     : nodeId (nodeID), processor (p), isPrepared (false)
 - {
 -     wassert (processor != nullptr);
 - }
 - 
 - void AudioProcessorGraph::Node::prepare (const double newSampleRate, const int newBlockSize,
 -                                          AudioProcessorGraph* const graph)
 - {
 -     if (! isPrepared)
 -     {
 -         setParentGraph (graph);
 - 
 -         processor->setRateAndBufferSizeDetails (newSampleRate, newBlockSize);
 -         processor->prepareToPlay (newSampleRate, newBlockSize);
 -         isPrepared = true;
 -     }
 - }
 - 
 - void AudioProcessorGraph::Node::unprepare()
 - {
 -     if (isPrepared)
 -     {
 -         isPrepared = false;
 -         processor->releaseResources();
 -     }
 - }
 - 
 - void AudioProcessorGraph::Node::setParentGraph (AudioProcessorGraph* const graph) const
 - {
 -     if (AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
 -             = dynamic_cast<AudioProcessorGraph::AudioGraphIOProcessor*> (processor.get()))
 -         ioProc->setParentGraph (graph);
 - }
 - 
 - //==============================================================================
 - struct AudioProcessorGraph::AudioProcessorGraphBufferHelpers
 - {
 -     AudioProcessorGraphBufferHelpers() noexcept
 -         : currentAudioInputBuffer (nullptr),
 -           currentCVInputBuffer (nullptr) {}
 - 
 -     void setRenderingBufferSize (int newNumAudioChannels, int newNumCVChannels, int newNumSamples) noexcept
 -     {
 -         renderingAudioBuffers.setSize (newNumAudioChannels, newNumSamples);
 -         renderingAudioBuffers.clear();
 - 
 -         renderingCVBuffers.setSize (newNumCVChannels, newNumSamples);
 -         renderingCVBuffers.clear();
 -     }
 - 
 -     void release() noexcept
 -     {
 -         renderingAudioBuffers.setSize (1, 1);
 -         currentAudioInputBuffer = nullptr;
 -         currentCVInputBuffer = nullptr;
 -         currentAudioOutputBuffer.setSize (1, 1);
 -         currentCVOutputBuffer.setSize (1, 1);
 - 
 -         renderingCVBuffers.setSize (1, 1);
 -     }
 - 
 -     void prepareInOutBuffers (int newNumAudioChannels, int newNumCVChannels, int newNumSamples) noexcept
 -     {
 -         currentAudioInputBuffer = nullptr;
 -         currentCVInputBuffer = nullptr;
 -         currentAudioOutputBuffer.setSize (newNumAudioChannels, newNumSamples);
 -         currentCVOutputBuffer.setSize (newNumCVChannels, newNumSamples);
 -     }
 - 
 -     AudioSampleBuffer        renderingAudioBuffers;
 -     AudioSampleBuffer        renderingCVBuffers;
 -     AudioSampleBuffer*       currentAudioInputBuffer;
 -     const AudioSampleBuffer* currentCVInputBuffer;
 -     AudioSampleBuffer        currentAudioOutputBuffer;
 -     AudioSampleBuffer        currentCVOutputBuffer;
 - };
 - 
 - //==============================================================================
 - AudioProcessorGraph::AudioProcessorGraph()
 -     : lastNodeId (0), audioAndCVBuffers (new AudioProcessorGraphBufferHelpers),
 -       currentMidiInputBuffer (nullptr), isPrepared (false), needsReorder (false)
 - {
 - }
 - 
 - AudioProcessorGraph::~AudioProcessorGraph()
 - {
 -     clearRenderingSequence();
 -     clear();
 - }
 - 
 - const String AudioProcessorGraph::getName() const
 - {
 -     return "Audio Graph";
 - }
 - 
 - //==============================================================================
 - void AudioProcessorGraph::clear()
 - {
 -     nodes.clear();
 -     connections.clear();
 -     needsReorder = true;
 - }
 - 
 - AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const
 - {
 -     for (int i = nodes.size(); --i >= 0;)
 -         if (nodes.getUnchecked(i)->nodeId == nodeId)
 -             return nodes.getUnchecked(i);
 - 
 -     return nullptr;
 - }
 - 
 - AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, uint32 nodeId)
 - {
 -     CARLA_SAFE_ASSERT_RETURN (newProcessor != nullptr && newProcessor != this, nullptr);
 - 
 -     for (int i = nodes.size(); --i >= 0;)
 -     {
 -         CARLA_SAFE_ASSERT_RETURN (nodes.getUnchecked(i)->getProcessor() != newProcessor, nullptr);
 -     }
 - 
 -     if (nodeId == 0)
 -     {
 -         nodeId = ++lastNodeId;
 -     }
 -     else
 -     {
 -         // you can't add a node with an id that already exists in the graph..
 -         CARLA_SAFE_ASSERT_RETURN (getNodeForId (nodeId) == nullptr, nullptr);
 -         removeNode (nodeId);
 - 
 -         if (nodeId > lastNodeId)
 -             lastNodeId = nodeId;
 -     }
 - 
 -     Node* const n = new Node (nodeId, newProcessor);
 -     nodes.add (n);
 - 
 -     if (isPrepared)
 -         needsReorder = true;
 - 
 -     n->setParentGraph (this);
 -     return n;
 - }
 - 
 - bool AudioProcessorGraph::removeNode (const uint32 nodeId)
 - {
 -     disconnectNode (nodeId);
 - 
 -     for (int i = nodes.size(); --i >= 0;)
 -     {
 -         if (nodes.getUnchecked(i)->nodeId == nodeId)
 -         {
 -             nodes.remove (i);
 - 
 -             if (isPrepared)
 -                 needsReorder = true;
 - 
 -             return true;
 -         }
 -     }
 - 
 -     return false;
 - }
 - 
 - bool AudioProcessorGraph::removeNode (Node* node)
 - {
 -     CARLA_SAFE_ASSERT_RETURN(node != nullptr, false);
 - 
 -     return removeNode (node->nodeId);
 - }
 - 
 - //==============================================================================
 - const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const ChannelType ct,
 -                                                                                   const uint32 sourceNodeId,
 -                                                                                   const uint sourceChannelIndex,
 -                                                                                   const uint32 destNodeId,
 -                                                                                   const uint destChannelIndex) const
 - {
 -     const Connection c (ct, sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex);
 -     GraphRenderingOps::ConnectionSorter sorter;
 -     return connections [connections.indexOfSorted (sorter, &c)];
 - }
 - 
 - bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId,
 -                                        const uint32 possibleDestNodeId) const
 - {
 -     for (int i = connections.size(); --i >= 0;)
 -     {
 -         const Connection* const c = connections.getUnchecked(i);
 - 
 -         if (c->sourceNodeId == possibleSourceNodeId
 -              && c->destNodeId == possibleDestNodeId)
 -         {
 -             return true;
 -         }
 -     }
 - 
 -     return false;
 - }
 - 
 - bool AudioProcessorGraph::canConnect (ChannelType ct,
 -                                       const uint32 sourceNodeId,
 -                                       const uint sourceChannelIndex,
 -                                       const uint32 destNodeId,
 -                                       const uint destChannelIndex) const
 - {
 -     if (sourceNodeId == destNodeId)
 -         return false;
 - 
 -     const Node* const source = getNodeForId (sourceNodeId);
 - 
 -     if (source == nullptr
 -          || (ct != ChannelTypeMIDI && sourceChannelIndex >= source->processor->getTotalNumOutputChannels(ct))
 -          || (ct == ChannelTypeMIDI && ! source->processor->producesMidi()))
 -         return false;
 - 
 -     const Node* const dest = getNodeForId (destNodeId);
 - 
 -     if (dest == nullptr
 -          || (ct != ChannelTypeMIDI && destChannelIndex >= dest->processor->getTotalNumInputChannels(ct))
 -          || (ct == ChannelTypeMIDI && ! dest->processor->acceptsMidi()))
 -         return false;
 - 
 -     return getConnectionBetween (ct,
 -                                  sourceNodeId, sourceChannelIndex,
 -                                  destNodeId, destChannelIndex) == nullptr;
 - }
 - 
 - bool AudioProcessorGraph::addConnection (const ChannelType ct,
 -                                          const uint32 sourceNodeId,
 -                                          const uint sourceChannelIndex,
 -                                          const uint32 destNodeId,
 -                                          const uint destChannelIndex)
 - {
 -     if (! canConnect (ct, sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex))
 -         return false;
 - 
 -     GraphRenderingOps::ConnectionSorter sorter;
 -     connections.addSorted (sorter, new Connection (ct,
 -                                                    sourceNodeId, sourceChannelIndex,
 -                                                    destNodeId, destChannelIndex));
 - 
 -     if (isPrepared)
 -         needsReorder = true;
 - 
 -     return true;
 - }
 - 
 - void AudioProcessorGraph::removeConnection (const int index)
 - {
 -     connections.remove (index);
 - 
 -     if (isPrepared)
 -         needsReorder = true;
 - }
 - 
 - bool AudioProcessorGraph::removeConnection (const ChannelType ct,
 -                                             const uint32 sourceNodeId, const uint sourceChannelIndex,
 -                                             const uint32 destNodeId, const uint destChannelIndex)
 - {
 -     bool doneAnything = false;
 - 
 -     for (int i = connections.size(); --i >= 0;)
 -     {
 -         const Connection* const c = connections.getUnchecked(i);
 - 
 -         if (c->channelType == ct
 -              && c->sourceNodeId == sourceNodeId
 -              && c->destNodeId == destNodeId
 -              && c->sourceChannelIndex == sourceChannelIndex
 -              && c->destChannelIndex == destChannelIndex)
 -         {
 -             removeConnection (i);
 -             doneAnything = true;
 -         }
 -     }
 - 
 -     return doneAnything;
 - }
 - 
 - bool AudioProcessorGraph::disconnectNode (const uint32 nodeId)
 - {
 -     bool doneAnything = false;
 - 
 -     for (int i = connections.size(); --i >= 0;)
 -     {
 -         const Connection* const c = connections.getUnchecked(i);
 - 
 -         if (c->sourceNodeId == nodeId || c->destNodeId == nodeId)
 -         {
 -             removeConnection (i);
 -             doneAnything = true;
 -         }
 -     }
 - 
 -     return doneAnything;
 - }
 - 
 - bool AudioProcessorGraph::isConnectionLegal (const Connection* const c) const
 - {
 -     CARLA_SAFE_ASSERT_RETURN (c != nullptr, false);
 - 
 -     const Node* const source = getNodeForId (c->sourceNodeId);
 -     const Node* const dest   = getNodeForId (c->destNodeId);
 - 
 -     return source != nullptr
 -         && dest != nullptr
 -         && (c->channelType != ChannelTypeMIDI ? (c->sourceChannelIndex < source->processor->getTotalNumOutputChannels(c->channelType))
 -                                               : source->processor->producesMidi())
 -         && (c->channelType != ChannelTypeMIDI ? (c->destChannelIndex < dest->processor->getTotalNumInputChannels(c->channelType))
 -                                               : dest->processor->acceptsMidi());
 - }
 - 
 - bool AudioProcessorGraph::removeIllegalConnections()
 - {
 -     bool doneAnything = false;
 - 
 -     for (int i = connections.size(); --i >= 0;)
 -     {
 -         if (! isConnectionLegal (connections.getUnchecked(i)))
 -         {
 -             removeConnection (i);
 -             doneAnything = true;
 -         }
 -     }
 - 
 -     return doneAnything;
 - }
 - 
 - //==============================================================================
 - static void deleteRenderOpArray (Array<void*>& ops)
 - {
 -     for (int i = ops.size(); --i >= 0;)
 -         delete static_cast<GraphRenderingOps::AudioGraphRenderingOpBase*> (ops.getUnchecked(i));
 - }
 - 
 - void AudioProcessorGraph::clearRenderingSequence()
 - {
 -     Array<void*> oldOps;
 - 
 -     {
 -         const CarlaRecursiveMutexLocker cml (getCallbackLock());
 -         renderingOps.swapWith (oldOps);
 -     }
 - 
 -     deleteRenderOpArray (oldOps);
 - }
 - 
 - bool AudioProcessorGraph::isAnInputTo (const uint32 possibleInputId,
 -                                        const uint32 possibleDestinationId,
 -                                        const int recursionCheck) const
 - {
 -     if (recursionCheck > 0)
 -     {
 -         for (int i = connections.size(); --i >= 0;)
 -         {
 -             const AudioProcessorGraph::Connection* const c = connections.getUnchecked (i);
 - 
 -             if (c->destNodeId == possibleDestinationId
 -                  && (c->sourceNodeId == possibleInputId
 -                       || isAnInputTo (possibleInputId, c->sourceNodeId, recursionCheck - 1)))
 -                 return true;
 -         }
 -     }
 - 
 -     return false;
 - }
 - 
 - void AudioProcessorGraph::buildRenderingSequence()
 - {
 -     Array<void*> newRenderingOps;
 -     int numAudioRenderingBuffersNeeded = 2;
 -     int numCVRenderingBuffersNeeded = 0;
 -     int numMidiBuffersNeeded = 1;
 - 
 -     {
 -         const CarlaRecursiveMutexLocker cml (reorderMutex);
 - 
 -         Array<Node*> orderedNodes;
 - 
 -         {
 -             const GraphRenderingOps::ConnectionLookupTable table (connections);
 - 
 -             for (int i = 0; i < nodes.size(); ++i)
 -             {
 -                 Node* const node = nodes.getUnchecked(i);
 - 
 -                 node->prepare (getSampleRate(), getBlockSize(), this);
 - 
 -                 int j = 0;
 -                 for (; j < orderedNodes.size(); ++j)
 -                     if (table.isAnInputTo (node->nodeId, ((Node*) orderedNodes.getUnchecked(j))->nodeId))
 -                       break;
 - 
 -                 orderedNodes.insert (j, node);
 -             }
 -         }
 - 
 -         GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps);
 - 
 -         numAudioRenderingBuffersNeeded = calculator.getNumAudioBuffersNeeded();
 -         numCVRenderingBuffersNeeded = calculator.getNumCVBuffersNeeded();
 -         numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded();
 -     }
 - 
 -     {
 -         // swap over to the new rendering sequence..
 -         const CarlaRecursiveMutexLocker cml (getCallbackLock());
 - 
 -         audioAndCVBuffers->setRenderingBufferSize (numAudioRenderingBuffersNeeded,
 -                                                    numCVRenderingBuffersNeeded,
 -                                                    getBlockSize());
 - 
 -         for (int i = static_cast<int>(midiBuffers.size()); --i >= 0;)
 -             midiBuffers.getUnchecked(i)->clear();
 - 
 -         while (static_cast<int>(midiBuffers.size()) < numMidiBuffersNeeded)
 -             midiBuffers.add (new MidiBuffer());
 - 
 -         renderingOps.swapWith (newRenderingOps);
 -     }
 - 
 -     // delete the old ones..
 -     deleteRenderOpArray (newRenderingOps);
 - }
 - 
 - //==============================================================================
 - void AudioProcessorGraph::prepareToPlay (double sampleRate, int estimatedSamplesPerBlock)
 - {
 -     setRateAndBufferSizeDetails(sampleRate, estimatedSamplesPerBlock);
 - 
 -     audioAndCVBuffers->prepareInOutBuffers(jmax(1U, getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio)),
 -                                            jmax(1U, getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV)),
 -                                            estimatedSamplesPerBlock);
 - 
 -     currentMidiInputBuffer = nullptr;
 -     currentMidiOutputBuffer.clear();
 - 
 -     clearRenderingSequence();
 -     buildRenderingSequence();
 - 
 -     isPrepared = true;
 - }
 - 
 - void AudioProcessorGraph::releaseResources()
 - {
 -     isPrepared = false;
 - 
 -     for (int i = 0; i < nodes.size(); ++i)
 -         nodes.getUnchecked(i)->unprepare();
 - 
 -     audioAndCVBuffers->release();
 -     midiBuffers.clear();
 - 
 -     currentMidiInputBuffer = nullptr;
 -     currentMidiOutputBuffer.clear();
 - }
 - 
 - void AudioProcessorGraph::reset()
 - {
 -     const CarlaRecursiveMutexLocker cml (getCallbackLock());
 - 
 -     for (int i = 0; i < nodes.size(); ++i)
 -         nodes.getUnchecked(i)->getProcessor()->reset();
 - }
 - 
 - void AudioProcessorGraph::setNonRealtime (bool isProcessingNonRealtime) noexcept
 - {
 -     const CarlaRecursiveMutexLocker cml (getCallbackLock());
 - 
 -     AudioProcessor::setNonRealtime (isProcessingNonRealtime);
 - 
 -     for (int i = 0; i < nodes.size(); ++i)
 -         nodes.getUnchecked(i)->getProcessor()->setNonRealtime (isProcessingNonRealtime);
 - }
 - 
 - /*
 - void AudioProcessorGraph::processAudio (AudioSampleBuffer& audioBuffer, MidiBuffer& midiMessages)
 - {
 -     AudioSampleBuffer*& currentAudioInputBuffer  = audioAndCVBuffers->currentAudioInputBuffer;
 -     AudioSampleBuffer&  currentAudioOutputBuffer = audioAndCVBuffers->currentAudioOutputBuffer;
 -     AudioSampleBuffer&  renderingAudioBuffers    = audioAndCVBuffers->renderingAudioBuffers;
 -     AudioSampleBuffer&  renderingCVBuffers       = audioAndCVBuffers->renderingCVBuffers;
 - 
 -     const int numSamples = audioBuffer.getNumSamples();
 - 
 -     if (! audioAndCVBuffers->currentAudioOutputBuffer.setSizeRT(numSamples))
 -         return;
 -     if (! audioAndCVBuffers->renderingAudioBuffers.setSizeRT(numSamples))
 -         return;
 -     if (! audioAndCVBuffers->renderingCVBuffers.setSizeRT(numSamples))
 -         return;
 - 
 -     currentAudioInputBuffer = &audioBuffer;
 -     currentAudioOutputBuffer.clear();
 -     currentMidiInputBuffer = &midiMessages;
 -     currentMidiOutputBuffer.clear();
 - 
 -     for (int i = 0; i < renderingOps.size(); ++i)
 -     {
 -         GraphRenderingOps::AudioGraphRenderingOpBase* const op
 -             = (GraphRenderingOps::AudioGraphRenderingOpBase*) renderingOps.getUnchecked(i);
 - 
 -         op->perform (renderingAudioBuffers, renderingCVBuffers, midiBuffers, numSamples);
 -     }
 - 
 -     for (uint32_t i = 0; i < audioBuffer.getNumChannels(); ++i)
 -         audioBuffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples);
 - 
 -     midiMessages.clear();
 -     midiMessages.addEvents (currentMidiOutputBuffer, 0, audioBuffer.getNumSamples(), 0);
 - }
 - */
 - 
 - void AudioProcessorGraph::processAudioAndCV (AudioSampleBuffer& audioBuffer,
 -                                              const AudioSampleBuffer& cvInBuffer,
 -                                              AudioSampleBuffer& cvOutBuffer,
 -                                              MidiBuffer& midiMessages)
 - {
 -     AudioSampleBuffer*&       currentAudioInputBuffer  = audioAndCVBuffers->currentAudioInputBuffer;
 -     const AudioSampleBuffer*& currentCVInputBuffer     = audioAndCVBuffers->currentCVInputBuffer;
 -     AudioSampleBuffer&        currentAudioOutputBuffer = audioAndCVBuffers->currentAudioOutputBuffer;
 -     AudioSampleBuffer&        currentCVOutputBuffer    = audioAndCVBuffers->currentCVOutputBuffer;
 -     AudioSampleBuffer&        renderingAudioBuffers    = audioAndCVBuffers->renderingAudioBuffers;
 -     AudioSampleBuffer&        renderingCVBuffers       = audioAndCVBuffers->renderingCVBuffers;
 - 
 -     const int numSamples = audioBuffer.getNumSamples();
 - 
 -     if (! audioAndCVBuffers->currentAudioOutputBuffer.setSizeRT(numSamples))
 -         return;
 -     if (! audioAndCVBuffers->currentCVOutputBuffer.setSizeRT(numSamples))
 -         return;
 -     if (! audioAndCVBuffers->renderingAudioBuffers.setSizeRT(numSamples))
 -         return;
 -     if (! audioAndCVBuffers->renderingCVBuffers.setSizeRT(numSamples))
 -         return;
 - 
 -     currentAudioInputBuffer = &audioBuffer;
 -     currentCVInputBuffer = &cvInBuffer;
 -     currentMidiInputBuffer = &midiMessages;
 -     currentAudioOutputBuffer.clear();
 -     currentCVOutputBuffer.clear();
 -     currentMidiOutputBuffer.clear();
 - 
 -     for (int i = 0; i < renderingOps.size(); ++i)
 -     {
 -         GraphRenderingOps::AudioGraphRenderingOpBase* const op
 -             = (GraphRenderingOps::AudioGraphRenderingOpBase*) renderingOps.getUnchecked(i);
 - 
 -         op->perform (renderingAudioBuffers, renderingCVBuffers, midiBuffers, numSamples);
 -     }
 - 
 -     for (uint32_t i = 0; i < audioBuffer.getNumChannels(); ++i)
 -         audioBuffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples);
 - 
 -     for (uint32_t i = 0; i < cvOutBuffer.getNumChannels(); ++i)
 -         cvOutBuffer.copyFrom (i, 0, currentCVOutputBuffer, i, 0, numSamples);
 - 
 -     midiMessages.clear();
 -     midiMessages.addEvents (currentMidiOutputBuffer, 0, audioBuffer.getNumSamples(), 0);
 - }
 - 
 - bool AudioProcessorGraph::acceptsMidi() const                       { return true; }
 - bool AudioProcessorGraph::producesMidi() const                      { return true; }
 - 
 - /*
 - void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
 - {
 -     processAudio (buffer, midiMessages);
 - }
 - */
 - 
 - void AudioProcessorGraph::processBlockWithCV (AudioSampleBuffer& audioBuffer,
 -                                               const AudioSampleBuffer& cvInBuffer,
 -                                               AudioSampleBuffer& cvOutBuffer,
 -                                               MidiBuffer& midiMessages)
 - {
 -     processAudioAndCV (audioBuffer, cvInBuffer, cvOutBuffer, midiMessages);
 - }
 - 
 - void AudioProcessorGraph::reorderNowIfNeeded()
 - {
 -     if (needsReorder)
 -     {
 -         needsReorder = false;
 -         buildRenderingSequence();
 -     }
 - }
 - 
 - const CarlaRecursiveMutex& AudioProcessorGraph::getReorderMutex() const
 - {
 -     return reorderMutex;
 - }
 - 
 - //==============================================================================
 - AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType deviceType)
 -     : type (deviceType), graph (nullptr)
 - {
 - }
 - 
 - AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor()
 - {
 - }
 - 
 - const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const
 - {
 -     switch (type)
 -     {
 -         case audioOutputNode:   return "Audio Output";
 -         case audioInputNode:    return "Audio Input";
 -         case cvOutputNode:      return "CV Output";
 -         case cvInputNode:       return "CV Input";
 -         case midiOutputNode:    return "Midi Output";
 -         case midiInputNode:     return "Midi Input";
 -         default:                break;
 -     }
 - 
 -     return String();
 - }
 - 
 - void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int)
 - {
 -     CARLA_SAFE_ASSERT (graph != nullptr);
 - }
 - 
 - void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources()
 - {
 - }
 - 
 - void AudioProcessorGraph::AudioGraphIOProcessor::processAudioAndCV (AudioSampleBuffer& audioBuffer,
 -                                                                     const AudioSampleBuffer& cvInBuffer,
 -                                                                     AudioSampleBuffer& cvOutBuffer,
 -                                                                     MidiBuffer& midiMessages)
 - {
 -     CARLA_SAFE_ASSERT_RETURN(graph != nullptr,);
 - 
 -     switch (type)
 -     {
 -         case audioOutputNode:
 -         {
 -             AudioSampleBuffer&  currentAudioOutputBuffer =
 -                 graph->audioAndCVBuffers->currentAudioOutputBuffer;
 - 
 -             for (int i = jmin (currentAudioOutputBuffer.getNumChannels(),
 -                                audioBuffer.getNumChannels()); --i >= 0;)
 -             {
 -                 currentAudioOutputBuffer.addFrom (i, 0, audioBuffer, i, 0, audioBuffer.getNumSamples());
 -             }
 - 
 -             break;
 -         }
 - 
 -         case audioInputNode:
 -         {
 -             AudioSampleBuffer*& currentAudioInputBuffer =
 -                 graph->audioAndCVBuffers->currentAudioInputBuffer;
 - 
 -             for (int i = jmin (currentAudioInputBuffer->getNumChannels(),
 -                                audioBuffer.getNumChannels()); --i >= 0;)
 -             {
 -                 audioBuffer.copyFrom (i, 0, *currentAudioInputBuffer, i, 0, audioBuffer.getNumSamples());
 -             }
 - 
 -             break;
 -         }
 - 
 -         case cvOutputNode:
 -         {
 -             AudioSampleBuffer&  currentCVOutputBuffer =
 -                 graph->audioAndCVBuffers->currentCVOutputBuffer;
 - 
 -             for (int i = jmin (currentCVOutputBuffer.getNumChannels(),
 -                                cvInBuffer.getNumChannels()); --i >= 0;)
 -             {
 -                 currentCVOutputBuffer.addFrom (i, 0, cvInBuffer, i, 0, cvInBuffer.getNumSamples());
 -             }
 - 
 -             break;
 -         }
 - 
 -         case cvInputNode:
 -         {
 -             const AudioSampleBuffer*& currentCVInputBuffer =
 -                 graph->audioAndCVBuffers->currentCVInputBuffer;
 - 
 -             for (int i = jmin (currentCVInputBuffer->getNumChannels(),
 -                                cvOutBuffer.getNumChannels()); --i >= 0;)
 -             {
 -                 cvOutBuffer.copyFrom (i, 0, *currentCVInputBuffer, i, 0, cvOutBuffer.getNumSamples());
 -             }
 - 
 -             break;
 -         }
 - 
 -         case midiOutputNode:
 -             graph->currentMidiOutputBuffer.addEvents (midiMessages, 0, audioBuffer.getNumSamples(), 0);
 -             break;
 - 
 -         case midiInputNode:
 -             midiMessages.addEvents (*graph->currentMidiInputBuffer, 0, audioBuffer.getNumSamples(), 0);
 -             break;
 - 
 -         default:
 -             break;
 -     }
 - }
 - 
 - void AudioProcessorGraph::AudioGraphIOProcessor::processBlockWithCV (AudioSampleBuffer& audioBuffer,
 -                                                                      const AudioSampleBuffer& cvInBuffer,
 -                                                                      AudioSampleBuffer& cvOutBuffer,
 -                                                                      MidiBuffer& midiMessages)
 - {
 -     processAudioAndCV (audioBuffer, cvInBuffer, cvOutBuffer, midiMessages);
 - }
 - 
 - bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const
 - {
 -     return type == midiOutputNode;
 - }
 - 
 - bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const
 - {
 -     return type == midiInputNode;
 - }
 - 
 - bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const noexcept
 - {
 -     return type == audioInputNode || type == cvInputNode || type == midiInputNode;
 - }
 - 
 - bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const noexcept
 - {
 -     return type == audioOutputNode || type == cvOutputNode || type == midiOutputNode;
 - }
 - 
 - void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph)
 - {
 -     graph = newGraph;
 - 
 -     if (graph != nullptr)
 -     {
 -         setPlayConfigDetails (type == audioOutputNode
 -                                    ? graph->getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio)
 -                                    : 0,
 -                               type == audioInputNode
 -                                    ? graph->getTotalNumInputChannels(AudioProcessor::ChannelTypeAudio)
 -                                    : 0,
 -                               type == cvOutputNode
 -                                    ? graph->getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV)
 -                                    : 0,
 -                               type == cvInputNode
 -                                    ? graph->getTotalNumInputChannels(AudioProcessor::ChannelTypeCV)
 -                                    : 0,
 -                               type == midiOutputNode
 -                                    ? graph->getTotalNumOutputChannels(AudioProcessor::ChannelTypeMIDI)
 -                                    : 0,
 -                               type == midiInputNode
 -                                    ? graph->getTotalNumInputChannels(AudioProcessor::ChannelTypeMIDI)
 -                                    : 0,
 -                               getSampleRate(),
 -                               getBlockSize());
 -     }
 - }
 - 
 - }
 
 
  |