VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_audio_processors / processors / juce_AudioProcessorGraph.cpp
blob6a0bed1afceb435df8098fae6a562bb20b20c319
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
29 static void updateOnMessageThread (AsyncUpdater& updater)
31 if (MessageManager::getInstance()->isThisTheMessageThread())
32 updater.handleAsyncUpdate();
33 else
34 updater.triggerAsyncUpdate();
37 template <typename FloatType>
38 struct GraphRenderSequence
40 struct Context
42 FloatType** audioBuffers;
43 MidiBuffer* midiBuffers;
44 AudioPlayHead* audioPlayHead;
45 int numSamples;
48 void perform (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages, AudioPlayHead* audioPlayHead)
50 auto numSamples = buffer.getNumSamples();
51 auto maxSamples = renderingBuffer.getNumSamples();
53 if (numSamples > maxSamples)
55 // Being asked to render more samples than our buffers have, so divide the buffer into chunks
56 int chunkStartSample = 0;
57 while (chunkStartSample < numSamples)
59 auto chunkSize = jmin (maxSamples, numSamples - chunkStartSample);
61 AudioBuffer<FloatType> audioChunk (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), chunkStartSample, chunkSize);
62 midiChunk.clear();
63 midiChunk.addEvents (midiMessages, chunkStartSample, chunkSize, -chunkStartSample);
65 // Splitting up the buffer like this will cause the play head and host time to be
66 // invalid for all but the first chunk...
67 perform (audioChunk, midiChunk, audioPlayHead);
69 chunkStartSample += maxSamples;
72 return;
75 currentAudioInputBuffer = &buffer;
76 currentAudioOutputBuffer.setSize (jmax (1, buffer.getNumChannels()), numSamples);
77 currentAudioOutputBuffer.clear();
78 currentMidiInputBuffer = &midiMessages;
79 currentMidiOutputBuffer.clear();
82 const Context context { renderingBuffer.getArrayOfWritePointers(), midiBuffers.begin(), audioPlayHead, numSamples };
84 for (auto* op : renderOps)
85 op->perform (context);
88 for (int i = 0; i < buffer.getNumChannels(); ++i)
89 buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples);
91 midiMessages.clear();
92 midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
93 currentAudioInputBuffer = nullptr;
96 void addClearChannelOp (int index)
98 createOp ([=] (const Context& c) { FloatVectorOperations::clear (c.audioBuffers[index], c.numSamples); });
101 void addCopyChannelOp (int srcIndex, int dstIndex)
103 createOp ([=] (const Context& c) { FloatVectorOperations::copy (c.audioBuffers[dstIndex],
104 c.audioBuffers[srcIndex],
105 c.numSamples); });
108 void addAddChannelOp (int srcIndex, int dstIndex)
110 createOp ([=] (const Context& c) { FloatVectorOperations::add (c.audioBuffers[dstIndex],
111 c.audioBuffers[srcIndex],
112 c.numSamples); });
115 void addClearMidiBufferOp (int index)
117 createOp ([=] (const Context& c) { c.midiBuffers[index].clear(); });
120 void addCopyMidiBufferOp (int srcIndex, int dstIndex)
122 createOp ([=] (const Context& c) { c.midiBuffers[dstIndex] = c.midiBuffers[srcIndex]; });
125 void addAddMidiBufferOp (int srcIndex, int dstIndex)
127 createOp ([=] (const Context& c) { c.midiBuffers[dstIndex].addEvents (c.midiBuffers[srcIndex],
128 0, c.numSamples, 0); });
131 void addDelayChannelOp (int chan, int delaySize)
133 renderOps.add (new DelayChannelOp (chan, delaySize));
136 void addProcessOp (const AudioProcessorGraph::Node::Ptr& node,
137 const Array<int>& audioChannelsUsed, int totalNumChans, int midiBuffer)
139 renderOps.add (new ProcessOp (node, audioChannelsUsed, totalNumChans, midiBuffer));
142 void prepareBuffers (int blockSize)
144 renderingBuffer.setSize (numBuffersNeeded + 1, blockSize);
145 renderingBuffer.clear();
146 currentAudioOutputBuffer.setSize (numBuffersNeeded + 1, blockSize);
147 currentAudioOutputBuffer.clear();
149 currentAudioInputBuffer = nullptr;
150 currentMidiInputBuffer = nullptr;
151 currentMidiOutputBuffer.clear();
153 midiBuffers.clearQuick();
154 midiBuffers.resize (numMidiBuffersNeeded);
156 const int defaultMIDIBufferSize = 512;
158 midiChunk.ensureSize (defaultMIDIBufferSize);
160 for (auto&& m : midiBuffers)
161 m.ensureSize (defaultMIDIBufferSize);
164 void releaseBuffers()
166 renderingBuffer.setSize (1, 1);
167 currentAudioOutputBuffer.setSize (1, 1);
168 currentAudioInputBuffer = nullptr;
169 currentMidiInputBuffer = nullptr;
170 currentMidiOutputBuffer.clear();
171 midiBuffers.clear();
174 int numBuffersNeeded = 0, numMidiBuffersNeeded = 0;
176 AudioBuffer<FloatType> renderingBuffer, currentAudioOutputBuffer;
177 AudioBuffer<FloatType>* currentAudioInputBuffer = nullptr;
179 MidiBuffer* currentMidiInputBuffer = nullptr;
180 MidiBuffer currentMidiOutputBuffer;
182 Array<MidiBuffer> midiBuffers;
183 MidiBuffer midiChunk;
185 private:
186 //==============================================================================
187 struct RenderingOp
189 RenderingOp() noexcept {}
190 virtual ~RenderingOp() {}
191 virtual void perform (const Context&) = 0;
193 JUCE_LEAK_DETECTOR (RenderingOp)
196 OwnedArray<RenderingOp> renderOps;
198 //==============================================================================
199 template <typename LambdaType,
200 std::enable_if_t<std::is_rvalue_reference<LambdaType&&>::value, int> = 0>
201 void createOp (LambdaType&& fn)
203 struct LambdaOp : public RenderingOp
205 LambdaOp (LambdaType&& f) : function (std::forward<LambdaType> (f)) {}
206 void perform (const Context& c) override { function (c); }
208 LambdaType function;
211 renderOps.add (new LambdaOp (std::forward<LambdaType> (fn)));
214 //==============================================================================
215 struct DelayChannelOp : public RenderingOp
217 DelayChannelOp (int chan, int delaySize)
218 : channel (chan),
219 bufferSize (delaySize + 1),
220 writeIndex (delaySize)
222 buffer.calloc ((size_t) bufferSize);
225 void perform (const Context& c) override
227 auto* data = c.audioBuffers[channel];
229 for (int i = c.numSamples; --i >= 0;)
231 buffer[writeIndex] = *data;
232 *data++ = buffer[readIndex];
234 if (++readIndex >= bufferSize) readIndex = 0;
235 if (++writeIndex >= bufferSize) writeIndex = 0;
239 HeapBlock<FloatType> buffer;
240 const int channel, bufferSize;
241 int readIndex = 0, writeIndex;
243 JUCE_DECLARE_NON_COPYABLE (DelayChannelOp)
246 //==============================================================================
247 struct ProcessOp : public RenderingOp
249 ProcessOp (const AudioProcessorGraph::Node::Ptr& n,
250 const Array<int>& audioChannelsUsed,
251 int totalNumChans, int midiBuffer)
252 : node (n),
253 processor (*n->getProcessor()),
254 audioChannelsToUse (audioChannelsUsed),
255 totalChans (jmax (1, totalNumChans)),
256 midiBufferToUse (midiBuffer)
258 audioChannels.calloc ((size_t) totalChans);
260 while (audioChannelsToUse.size() < totalChans)
261 audioChannelsToUse.add (0);
264 void perform (const Context& c) override
266 processor.setPlayHead (c.audioPlayHead);
268 for (int i = 0; i < totalChans; ++i)
269 audioChannels[i] = c.audioBuffers[audioChannelsToUse.getUnchecked (i)];
271 auto numAudioChannels = [this]
273 if (const auto* proc = node->getProcessor())
274 if (proc->getTotalNumInputChannels() == 0 && proc->getTotalNumOutputChannels() == 0)
275 return 0;
277 return totalChans;
278 }();
280 AudioBuffer<FloatType> buffer (audioChannels, numAudioChannels, c.numSamples);
282 const ScopedLock lock (processor.getCallbackLock());
284 if (processor.isSuspended())
285 buffer.clear();
286 else
287 callProcess (buffer, c.midiBuffers[midiBufferToUse]);
290 void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
292 if (processor.isUsingDoublePrecision())
294 tempBufferDouble.makeCopyOf (buffer, true);
296 if (node->isBypassed())
297 node->processBlockBypassed (tempBufferDouble, midiMessages);
298 else
299 node->processBlock (tempBufferDouble, midiMessages);
301 buffer.makeCopyOf (tempBufferDouble, true);
303 else
305 if (node->isBypassed())
306 node->processBlockBypassed (buffer, midiMessages);
307 else
308 node->processBlock (buffer, midiMessages);
312 void callProcess (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
314 if (processor.isUsingDoublePrecision())
316 if (node->isBypassed())
317 node->processBlockBypassed (buffer, midiMessages);
318 else
319 node->processBlock (buffer, midiMessages);
321 else
323 tempBufferFloat.makeCopyOf (buffer, true);
325 if (node->isBypassed())
326 node->processBlockBypassed (tempBufferFloat, midiMessages);
327 else
328 node->processBlock (tempBufferFloat, midiMessages);
330 buffer.makeCopyOf (tempBufferFloat, true);
334 const AudioProcessorGraph::Node::Ptr node;
335 AudioProcessor& processor;
337 Array<int> audioChannelsToUse;
338 HeapBlock<FloatType*> audioChannels;
339 AudioBuffer<float> tempBufferFloat, tempBufferDouble;
340 const int totalChans, midiBufferToUse;
342 JUCE_DECLARE_NON_COPYABLE (ProcessOp)
346 //==============================================================================
347 //==============================================================================
348 template <typename RenderSequence>
349 struct RenderSequenceBuilder
351 RenderSequenceBuilder (AudioProcessorGraph& g, RenderSequence& s)
352 : graph (g), sequence (s), orderedNodes (createOrderedNodeList (graph))
354 audioBuffers.add (AssignedBuffer::createReadOnlyEmpty()); // first buffer is read-only zeros
355 midiBuffers .add (AssignedBuffer::createReadOnlyEmpty());
357 for (int i = 0; i < orderedNodes.size(); ++i)
359 createRenderingOpsForNode (*orderedNodes.getUnchecked(i), i);
360 markAnyUnusedBuffersAsFree (audioBuffers, i);
361 markAnyUnusedBuffersAsFree (midiBuffers, i);
364 graph.setLatencySamples (totalLatency);
366 s.numBuffersNeeded = audioBuffers.size();
367 s.numMidiBuffersNeeded = midiBuffers.size();
370 //==============================================================================
371 using Node = AudioProcessorGraph::Node;
372 using NodeID = AudioProcessorGraph::NodeID;
374 AudioProcessorGraph& graph;
375 RenderSequence& sequence;
377 const Array<Node*> orderedNodes;
379 struct AssignedBuffer
381 AudioProcessorGraph::NodeAndChannel channel;
383 static AssignedBuffer createReadOnlyEmpty() noexcept { return { { zeroNodeID(), 0 } }; }
384 static AssignedBuffer createFree() noexcept { return { { freeNodeID(), 0 } }; }
386 bool isReadOnlyEmpty() const noexcept { return channel.nodeID == zeroNodeID(); }
387 bool isFree() const noexcept { return channel.nodeID == freeNodeID(); }
388 bool isAssigned() const noexcept { return ! (isReadOnlyEmpty() || isFree()); }
390 void setFree() noexcept { channel = { freeNodeID(), 0 }; }
391 void setAssignedToNonExistentNode() noexcept { channel = { anonNodeID(), 0 }; }
393 private:
394 static NodeID anonNodeID() { return NodeID (0x7ffffffd); }
395 static NodeID zeroNodeID() { return NodeID (0x7ffffffe); }
396 static NodeID freeNodeID() { return NodeID (0x7fffffff); }
399 Array<AssignedBuffer> audioBuffers, midiBuffers;
401 enum { readOnlyEmptyBufferIndex = 0 };
403 struct Delay
405 NodeID nodeID;
406 int delay;
409 HashMap<uint32, int> delays;
410 int totalLatency = 0;
412 int getNodeDelay (NodeID nodeID) const noexcept
414 return delays[nodeID.uid];
417 int getInputLatencyForNode (NodeID nodeID) const
419 int maxLatency = 0;
421 for (auto&& c : graph.getConnections())
422 if (c.destination.nodeID == nodeID)
423 maxLatency = jmax (maxLatency, getNodeDelay (c.source.nodeID));
425 return maxLatency;
428 //==============================================================================
429 static void getAllParentsOfNode (const Node* child,
430 std::unordered_set<Node*>& parents,
431 const std::unordered_map<Node*, std::unordered_set<Node*>>& otherParents)
433 for (auto&& i : child->inputs)
435 auto* parentNode = i.otherNode;
437 if (parentNode == child)
438 continue;
440 if (parents.insert (parentNode).second)
442 auto parentParents = otherParents.find (parentNode);
444 if (parentParents != otherParents.end())
446 parents.insert (parentParents->second.begin(), parentParents->second.end());
447 continue;
450 getAllParentsOfNode (i.otherNode, parents, otherParents);
455 static auto createOrderedNodeList (const AudioProcessorGraph& graph)
457 Array<Node*> result;
459 std::unordered_map<Node*, std::unordered_set<Node*>> nodeParents;
461 for (auto* node : graph.getNodes())
463 int insertionIndex = 0;
465 for (; insertionIndex < result.size(); ++insertionIndex)
467 auto& parents = nodeParents[result.getUnchecked (insertionIndex)];
469 if (parents.find (node) != parents.end())
470 break;
473 result.insert (insertionIndex, node);
474 getAllParentsOfNode (node, nodeParents[node], nodeParents);
477 return result;
480 int findBufferForInputAudioChannel (Node& node, const int inputChan,
481 const int ourRenderingIndex, const int maxLatency)
483 auto& processor = *node.getProcessor();
484 auto numOuts = processor.getTotalNumOutputChannels();
486 auto sources = getSourcesForChannel (node, inputChan);
488 // Handle an unconnected input channel...
489 if (sources.isEmpty())
491 if (inputChan >= numOuts)
492 return readOnlyEmptyBufferIndex;
494 auto index = getFreeBuffer (audioBuffers);
495 sequence.addClearChannelOp (index);
496 return index;
499 // Handle an input from a single source..
500 if (sources.size() == 1)
502 // channel with a straightforward single input..
503 auto src = sources.getUnchecked(0);
505 int bufIndex = getBufferContaining (src);
507 if (bufIndex < 0)
509 // if not found, this is probably a feedback loop
510 bufIndex = readOnlyEmptyBufferIndex;
511 jassert (bufIndex >= 0);
514 if (inputChan < numOuts
515 && isBufferNeededLater (ourRenderingIndex, inputChan, src))
517 // can't mess up this channel because it's needed later by another node,
518 // so we need to use a copy of it..
519 auto newFreeBuffer = getFreeBuffer (audioBuffers);
520 sequence.addCopyChannelOp (bufIndex, newFreeBuffer);
521 bufIndex = newFreeBuffer;
524 auto nodeDelay = getNodeDelay (src.nodeID);
526 if (nodeDelay < maxLatency)
527 sequence.addDelayChannelOp (bufIndex, maxLatency - nodeDelay);
529 return bufIndex;
532 // Handle a mix of several outputs coming into this input..
533 int reusableInputIndex = -1;
534 int bufIndex = -1;
536 for (int i = 0; i < sources.size(); ++i)
538 auto src = sources.getReference(i);
539 auto sourceBufIndex = getBufferContaining (src);
541 if (sourceBufIndex >= 0 && ! isBufferNeededLater (ourRenderingIndex, inputChan, src))
543 // we've found one of our input chans that can be re-used..
544 reusableInputIndex = i;
545 bufIndex = sourceBufIndex;
547 auto nodeDelay = getNodeDelay (src.nodeID);
549 if (nodeDelay < maxLatency)
550 sequence.addDelayChannelOp (bufIndex, maxLatency - nodeDelay);
552 break;
556 if (reusableInputIndex < 0)
558 // can't re-use any of our input chans, so get a new one and copy everything into it..
559 bufIndex = getFreeBuffer (audioBuffers);
560 jassert (bufIndex != 0);
562 audioBuffers.getReference (bufIndex).setAssignedToNonExistentNode();
564 auto srcIndex = getBufferContaining (sources.getFirst());
566 if (srcIndex < 0)
567 sequence.addClearChannelOp (bufIndex); // if not found, this is probably a feedback loop
568 else
569 sequence.addCopyChannelOp (srcIndex, bufIndex);
571 reusableInputIndex = 0;
572 auto nodeDelay = getNodeDelay (sources.getFirst().nodeID);
574 if (nodeDelay < maxLatency)
575 sequence.addDelayChannelOp (bufIndex, maxLatency - nodeDelay);
578 for (int i = 0; i < sources.size(); ++i)
580 if (i != reusableInputIndex)
582 auto src = sources.getReference(i);
583 int srcIndex = getBufferContaining (src);
585 if (srcIndex >= 0)
587 auto nodeDelay = getNodeDelay (src.nodeID);
589 if (nodeDelay < maxLatency)
591 if (! isBufferNeededLater (ourRenderingIndex, inputChan, src))
593 sequence.addDelayChannelOp (srcIndex, maxLatency - nodeDelay);
595 else // buffer is reused elsewhere, can't be delayed
597 auto bufferToDelay = getFreeBuffer (audioBuffers);
598 sequence.addCopyChannelOp (srcIndex, bufferToDelay);
599 sequence.addDelayChannelOp (bufferToDelay, maxLatency - nodeDelay);
600 srcIndex = bufferToDelay;
604 sequence.addAddChannelOp (srcIndex, bufIndex);
609 return bufIndex;
612 int findBufferForInputMidiChannel (Node& node, int ourRenderingIndex)
614 auto& processor = *node.getProcessor();
615 auto sources = getSourcesForChannel (node, AudioProcessorGraph::midiChannelIndex);
617 // No midi inputs..
618 if (sources.isEmpty())
620 auto midiBufferToUse = getFreeBuffer (midiBuffers); // need to pick a buffer even if the processor doesn't use midi
622 if (processor.acceptsMidi() || processor.producesMidi())
623 sequence.addClearMidiBufferOp (midiBufferToUse);
625 return midiBufferToUse;
628 // One midi input..
629 if (sources.size() == 1)
631 auto src = sources.getReference (0);
632 auto midiBufferToUse = getBufferContaining (src);
634 if (midiBufferToUse >= 0)
636 if (isBufferNeededLater (ourRenderingIndex, AudioProcessorGraph::midiChannelIndex, src))
638 // can't mess up this channel because it's needed later by another node, so we
639 // need to use a copy of it..
640 auto newFreeBuffer = getFreeBuffer (midiBuffers);
641 sequence.addCopyMidiBufferOp (midiBufferToUse, newFreeBuffer);
642 midiBufferToUse = newFreeBuffer;
645 else
647 // probably a feedback loop, so just use an empty one..
648 midiBufferToUse = getFreeBuffer (midiBuffers); // need to pick a buffer even if the processor doesn't use midi
651 return midiBufferToUse;
654 // Multiple midi inputs..
655 int midiBufferToUse = -1;
656 int reusableInputIndex = -1;
658 for (int i = 0; i < sources.size(); ++i)
660 auto src = sources.getReference (i);
661 auto sourceBufIndex = getBufferContaining (src);
663 if (sourceBufIndex >= 0
664 && ! isBufferNeededLater (ourRenderingIndex, AudioProcessorGraph::midiChannelIndex, src))
666 // we've found one of our input buffers that can be re-used..
667 reusableInputIndex = i;
668 midiBufferToUse = sourceBufIndex;
669 break;
673 if (reusableInputIndex < 0)
675 // can't re-use any of our input buffers, so get a new one and copy everything into it..
676 midiBufferToUse = getFreeBuffer (midiBuffers);
677 jassert (midiBufferToUse >= 0);
679 auto srcIndex = getBufferContaining (sources.getUnchecked(0));
681 if (srcIndex >= 0)
682 sequence.addCopyMidiBufferOp (srcIndex, midiBufferToUse);
683 else
684 sequence.addClearMidiBufferOp (midiBufferToUse);
686 reusableInputIndex = 0;
689 for (int i = 0; i < sources.size(); ++i)
691 if (i != reusableInputIndex)
693 auto srcIndex = getBufferContaining (sources.getUnchecked(i));
695 if (srcIndex >= 0)
696 sequence.addAddMidiBufferOp (srcIndex, midiBufferToUse);
700 return midiBufferToUse;
703 void createRenderingOpsForNode (Node& node, const int ourRenderingIndex)
705 auto& processor = *node.getProcessor();
706 auto numIns = processor.getTotalNumInputChannels();
707 auto numOuts = processor.getTotalNumOutputChannels();
708 auto totalChans = jmax (numIns, numOuts);
710 Array<int> audioChannelsToUse;
711 auto maxLatency = getInputLatencyForNode (node.nodeID);
713 for (int inputChan = 0; inputChan < numIns; ++inputChan)
715 // get a list of all the inputs to this node
716 auto index = findBufferForInputAudioChannel (node, inputChan, ourRenderingIndex, maxLatency);
717 jassert (index >= 0);
719 audioChannelsToUse.add (index);
721 if (inputChan < numOuts)
722 audioBuffers.getReference (index).channel = { node.nodeID, inputChan };
725 for (int outputChan = numIns; outputChan < numOuts; ++outputChan)
727 auto index = getFreeBuffer (audioBuffers);
728 jassert (index != 0);
729 audioChannelsToUse.add (index);
731 audioBuffers.getReference (index).channel = { node.nodeID, outputChan };
734 auto midiBufferToUse = findBufferForInputMidiChannel (node, ourRenderingIndex);
736 if (processor.producesMidi())
737 midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, AudioProcessorGraph::midiChannelIndex };
739 delays.set (node.nodeID.uid, maxLatency + processor.getLatencySamples());
741 if (numOuts == 0)
742 totalLatency = maxLatency;
744 sequence.addProcessOp (node, audioChannelsToUse, totalChans, midiBufferToUse);
747 //==============================================================================
748 Array<AudioProcessorGraph::NodeAndChannel> getSourcesForChannel (Node& node, int inputChannelIndex)
750 Array<AudioProcessorGraph::NodeAndChannel> results;
751 AudioProcessorGraph::NodeAndChannel nc { node.nodeID, inputChannelIndex };
753 for (auto&& c : graph.getConnections())
754 if (c.destination == nc)
755 results.add (c.source);
757 return results;
760 static int getFreeBuffer (Array<AssignedBuffer>& buffers)
762 for (int i = 1; i < buffers.size(); ++i)
763 if (buffers.getReference (i).isFree())
764 return i;
766 buffers.add (AssignedBuffer::createFree());
767 return buffers.size() - 1;
770 int getBufferContaining (AudioProcessorGraph::NodeAndChannel output) const noexcept
772 int i = 0;
774 for (auto& b : output.isMIDI() ? midiBuffers : audioBuffers)
776 if (b.channel == output)
777 return i;
779 ++i;
782 return -1;
785 void markAnyUnusedBuffersAsFree (Array<AssignedBuffer>& buffers, const int stepIndex)
787 for (auto& b : buffers)
788 if (b.isAssigned() && ! isBufferNeededLater (stepIndex, -1, b.channel))
789 b.setFree();
792 bool isBufferNeededLater (int stepIndexToSearchFrom,
793 int inputChannelOfIndexToIgnore,
794 AudioProcessorGraph::NodeAndChannel output) const
796 while (stepIndexToSearchFrom < orderedNodes.size())
798 auto* node = orderedNodes.getUnchecked (stepIndexToSearchFrom);
800 if (output.isMIDI())
802 if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex
803 && graph.isConnected ({ { output.nodeID, AudioProcessorGraph::midiChannelIndex },
804 { node->nodeID, AudioProcessorGraph::midiChannelIndex } }))
805 return true;
807 else
809 for (int i = 0; i < node->getProcessor()->getTotalNumInputChannels(); ++i)
810 if (i != inputChannelOfIndexToIgnore && graph.isConnected ({ output, { node->nodeID, i } }))
811 return true;
814 inputChannelOfIndexToIgnore = -1;
815 ++stepIndexToSearchFrom;
818 return false;
821 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RenderSequenceBuilder)
824 //==============================================================================
825 AudioProcessorGraph::Connection::Connection (NodeAndChannel src, NodeAndChannel dst) noexcept
826 : source (src), destination (dst)
830 bool AudioProcessorGraph::Connection::operator== (const Connection& other) const noexcept
832 return source == other.source && destination == other.destination;
835 bool AudioProcessorGraph::Connection::operator!= (const Connection& c) const noexcept
837 return ! operator== (c);
840 bool AudioProcessorGraph::Connection::operator< (const Connection& other) const noexcept
842 if (source.nodeID != other.source.nodeID)
843 return source.nodeID < other.source.nodeID;
845 if (destination.nodeID != other.destination.nodeID)
846 return destination.nodeID < other.destination.nodeID;
848 if (source.channelIndex != other.source.channelIndex)
849 return source.channelIndex < other.source.channelIndex;
851 return destination.channelIndex < other.destination.channelIndex;
854 //==============================================================================
855 AudioProcessorGraph::Node::Node (NodeID n, std::unique_ptr<AudioProcessor> p) noexcept
856 : nodeID (n), processor (std::move (p))
858 jassert (processor != nullptr);
861 void AudioProcessorGraph::Node::prepare (double newSampleRate, int newBlockSize,
862 AudioProcessorGraph* graph, ProcessingPrecision precision)
864 const ScopedLock lock (processorLock);
866 if (! isPrepared)
868 setParentGraph (graph);
870 // try to align the precision of the processor and the graph
871 processor->setProcessingPrecision (processor->supportsDoublePrecisionProcessing() ? precision
872 : singlePrecision);
874 processor->setRateAndBufferSizeDetails (newSampleRate, newBlockSize);
875 processor->prepareToPlay (newSampleRate, newBlockSize);
877 // This may be checked from other threads that haven't taken the processorLock,
878 // so we need to leave it until the processor has been completely prepared
879 isPrepared = true;
883 void AudioProcessorGraph::Node::unprepare()
885 const ScopedLock lock (processorLock);
887 if (isPrepared)
889 isPrepared = false;
890 processor->releaseResources();
894 void AudioProcessorGraph::Node::setParentGraph (AudioProcessorGraph* const graph) const
896 const ScopedLock lock (processorLock);
898 if (auto* ioProc = dynamic_cast<AudioProcessorGraph::AudioGraphIOProcessor*> (processor.get()))
899 ioProc->setParentGraph (graph);
902 bool AudioProcessorGraph::Node::Connection::operator== (const Connection& other) const noexcept
904 return otherNode == other.otherNode
905 && thisChannel == other.thisChannel
906 && otherChannel == other.otherChannel;
909 //==============================================================================
910 bool AudioProcessorGraph::Node::isBypassed() const noexcept
912 if (processor != nullptr)
914 if (auto* bypassParam = processor->getBypassParameter())
915 return (bypassParam->getValue() != 0.0f);
918 return bypassed;
921 void AudioProcessorGraph::Node::setBypassed (bool shouldBeBypassed) noexcept
923 if (processor != nullptr)
925 if (auto* bypassParam = processor->getBypassParameter())
926 bypassParam->setValueNotifyingHost (shouldBeBypassed ? 1.0f : 0.0f);
929 bypassed = shouldBeBypassed;
932 //==============================================================================
933 struct AudioProcessorGraph::RenderSequenceFloat : public GraphRenderSequence<float> {};
934 struct AudioProcessorGraph::RenderSequenceDouble : public GraphRenderSequence<double> {};
936 //==============================================================================
937 AudioProcessorGraph::AudioProcessorGraph()
941 AudioProcessorGraph::~AudioProcessorGraph()
943 cancelPendingUpdate();
944 clearRenderingSequence();
945 clear();
948 const String AudioProcessorGraph::getName() const
950 return "Audio Graph";
953 //==============================================================================
954 void AudioProcessorGraph::topologyChanged()
956 sendChangeMessage();
958 if (isPrepared)
959 updateOnMessageThread (*this);
962 void AudioProcessorGraph::clear()
964 const ScopedLock sl (getCallbackLock());
966 if (nodes.isEmpty())
967 return;
969 nodes.clear();
970 topologyChanged();
973 AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (NodeID nodeID) const
975 for (auto* n : nodes)
976 if (n->nodeID == nodeID)
977 return n;
979 return {};
982 AudioProcessorGraph::Node::Ptr AudioProcessorGraph::addNode (std::unique_ptr<AudioProcessor> newProcessor, NodeID nodeID)
984 if (newProcessor == nullptr || newProcessor.get() == this)
986 jassertfalse;
987 return {};
990 if (nodeID == NodeID())
991 nodeID.uid = ++(lastNodeID.uid);
993 for (auto* n : nodes)
995 if (n->getProcessor() == newProcessor.get() || n->nodeID == nodeID)
997 jassertfalse; // Cannot add two copies of the same processor, or duplicate node IDs!
998 return {};
1002 if (lastNodeID < nodeID)
1003 lastNodeID = nodeID;
1005 newProcessor->setPlayHead (getPlayHead());
1007 Node::Ptr n (new Node (nodeID, std::move (newProcessor)));
1010 const ScopedLock sl (getCallbackLock());
1011 nodes.add (n.get());
1014 n->setParentGraph (this);
1015 topologyChanged();
1016 return n;
1019 AudioProcessorGraph::Node::Ptr AudioProcessorGraph::removeNode (NodeID nodeId)
1021 const ScopedLock sl (getCallbackLock());
1023 for (int i = nodes.size(); --i >= 0;)
1025 if (nodes.getUnchecked (i)->nodeID == nodeId)
1027 disconnectNode (nodeId);
1028 auto node = nodes.removeAndReturn (i);
1029 topologyChanged();
1030 return node;
1034 return {};
1037 AudioProcessorGraph::Node::Ptr AudioProcessorGraph::removeNode (Node* node)
1039 if (node != nullptr)
1040 return removeNode (node->nodeID);
1042 jassertfalse;
1043 return {};
1046 //==============================================================================
1047 void AudioProcessorGraph::getNodeConnections (Node& node, std::vector<Connection>& connections)
1049 for (auto& i : node.inputs)
1050 connections.push_back ({ { i.otherNode->nodeID, i.otherChannel }, { node.nodeID, i.thisChannel } });
1052 for (auto& o : node.outputs)
1053 connections.push_back ({ { node.nodeID, o.thisChannel }, { o.otherNode->nodeID, o.otherChannel } });
1056 std::vector<AudioProcessorGraph::Connection> AudioProcessorGraph::getConnections() const
1058 std::vector<Connection> connections;
1060 for (auto& n : nodes)
1061 getNodeConnections (*n, connections);
1063 std::sort (connections.begin(), connections.end());
1064 auto last = std::unique (connections.begin(), connections.end());
1065 connections.erase (last, connections.end());
1067 return connections;
1070 bool AudioProcessorGraph::isConnected (Node* source, int sourceChannel, Node* dest, int destChannel) const noexcept
1072 for (auto& o : source->outputs)
1073 if (o.otherNode == dest && o.thisChannel == sourceChannel && o.otherChannel == destChannel)
1074 return true;
1076 return false;
1079 bool AudioProcessorGraph::isConnected (const Connection& c) const noexcept
1081 if (auto* source = getNodeForId (c.source.nodeID))
1082 if (auto* dest = getNodeForId (c.destination.nodeID))
1083 return isConnected (source, c.source.channelIndex,
1084 dest, c.destination.channelIndex);
1086 return false;
1089 bool AudioProcessorGraph::isConnected (NodeID srcID, NodeID destID) const noexcept
1091 if (auto* source = getNodeForId (srcID))
1092 if (auto* dest = getNodeForId (destID))
1093 for (auto& out : source->outputs)
1094 if (out.otherNode == dest)
1095 return true;
1097 return false;
1100 bool AudioProcessorGraph::isAnInputTo (Node& src, Node& dst) const noexcept
1102 jassert (nodes.contains (&src));
1103 jassert (nodes.contains (&dst));
1105 return isAnInputTo (src, dst, nodes.size());
1108 bool AudioProcessorGraph::isAnInputTo (Node& src, Node& dst, int recursionCheck) const noexcept
1110 for (auto&& i : dst.inputs)
1111 if (i.otherNode == &src)
1112 return true;
1114 if (recursionCheck > 0)
1115 for (auto&& i : dst.inputs)
1116 if (isAnInputTo (src, *i.otherNode, recursionCheck - 1))
1117 return true;
1119 return false;
1122 bool AudioProcessorGraph::canConnect (Node* source, int sourceChannel, Node* dest, int destChannel) const noexcept
1124 bool sourceIsMIDI = sourceChannel == midiChannelIndex;
1125 bool destIsMIDI = destChannel == midiChannelIndex;
1127 if (sourceChannel < 0
1128 || destChannel < 0
1129 || source == dest
1130 || sourceIsMIDI != destIsMIDI)
1131 return false;
1133 if (source == nullptr
1134 || (! sourceIsMIDI && sourceChannel >= source->processor->getTotalNumOutputChannels())
1135 || (sourceIsMIDI && ! source->processor->producesMidi()))
1136 return false;
1138 if (dest == nullptr
1139 || (! destIsMIDI && destChannel >= dest->processor->getTotalNumInputChannels())
1140 || (destIsMIDI && ! dest->processor->acceptsMidi()))
1141 return false;
1143 return ! isConnected (source, sourceChannel, dest, destChannel);
1146 bool AudioProcessorGraph::canConnect (const Connection& c) const
1148 if (auto* source = getNodeForId (c.source.nodeID))
1149 if (auto* dest = getNodeForId (c.destination.nodeID))
1150 return canConnect (source, c.source.channelIndex,
1151 dest, c.destination.channelIndex);
1153 return false;
1156 bool AudioProcessorGraph::addConnection (const Connection& c)
1158 if (auto* source = getNodeForId (c.source.nodeID))
1160 if (auto* dest = getNodeForId (c.destination.nodeID))
1162 auto sourceChan = c.source.channelIndex;
1163 auto destChan = c.destination.channelIndex;
1165 if (canConnect (source, sourceChan, dest, destChan))
1167 source->outputs.add ({ dest, destChan, sourceChan });
1168 dest->inputs.add ({ source, sourceChan, destChan });
1169 jassert (isConnected (c));
1170 topologyChanged();
1171 return true;
1176 return false;
1179 bool AudioProcessorGraph::removeConnection (const Connection& c)
1181 if (auto* source = getNodeForId (c.source.nodeID))
1183 if (auto* dest = getNodeForId (c.destination.nodeID))
1185 auto sourceChan = c.source.channelIndex;
1186 auto destChan = c.destination.channelIndex;
1188 if (isConnected (source, sourceChan, dest, destChan))
1190 source->outputs.removeAllInstancesOf ({ dest, destChan, sourceChan });
1191 dest->inputs.removeAllInstancesOf ({ source, sourceChan, destChan });
1192 topologyChanged();
1193 return true;
1198 return false;
1201 bool AudioProcessorGraph::disconnectNode (NodeID nodeID)
1203 if (auto* node = getNodeForId (nodeID))
1205 std::vector<Connection> connections;
1206 getNodeConnections (*node, connections);
1208 if (! connections.empty())
1210 for (auto c : connections)
1211 removeConnection (c);
1213 return true;
1217 return false;
1220 bool AudioProcessorGraph::isLegal (Node* source, int sourceChannel, Node* dest, int destChannel) const noexcept
1222 return (sourceChannel == midiChannelIndex ? source->processor->producesMidi()
1223 : isPositiveAndBelow (sourceChannel, source->processor->getTotalNumOutputChannels()))
1224 && (destChannel == midiChannelIndex ? dest->processor->acceptsMidi()
1225 : isPositiveAndBelow (destChannel, dest->processor->getTotalNumInputChannels()));
1228 bool AudioProcessorGraph::isConnectionLegal (const Connection& c) const
1230 if (auto* source = getNodeForId (c.source.nodeID))
1231 if (auto* dest = getNodeForId (c.destination.nodeID))
1232 return isLegal (source, c.source.channelIndex, dest, c.destination.channelIndex);
1234 return false;
1237 bool AudioProcessorGraph::removeIllegalConnections()
1239 bool anyRemoved = false;
1241 for (auto* node : nodes)
1243 std::vector<Connection> connections;
1244 getNodeConnections (*node, connections);
1246 for (auto c : connections)
1247 if (! isConnectionLegal (c))
1248 anyRemoved = removeConnection (c) || anyRemoved;
1251 return anyRemoved;
1254 //==============================================================================
1255 void AudioProcessorGraph::clearRenderingSequence()
1257 std::unique_ptr<RenderSequenceFloat> oldSequenceF;
1258 std::unique_ptr<RenderSequenceDouble> oldSequenceD;
1261 const ScopedLock sl (getCallbackLock());
1262 std::swap (renderSequenceFloat, oldSequenceF);
1263 std::swap (renderSequenceDouble, oldSequenceD);
1267 bool AudioProcessorGraph::anyNodesNeedPreparing() const noexcept
1269 for (auto* node : nodes)
1270 if (! node->isPrepared)
1271 return true;
1273 return false;
1276 void AudioProcessorGraph::buildRenderingSequence()
1278 auto newSequenceF = std::make_unique<RenderSequenceFloat>();
1279 auto newSequenceD = std::make_unique<RenderSequenceDouble>();
1281 RenderSequenceBuilder<RenderSequenceFloat> builderF (*this, *newSequenceF);
1282 RenderSequenceBuilder<RenderSequenceDouble> builderD (*this, *newSequenceD);
1284 const ScopedLock sl (getCallbackLock());
1286 const auto currentBlockSize = getBlockSize();
1287 newSequenceF->prepareBuffers (currentBlockSize);
1288 newSequenceD->prepareBuffers (currentBlockSize);
1290 if (anyNodesNeedPreparing())
1292 renderSequenceFloat.reset();
1293 renderSequenceDouble.reset();
1295 for (auto* node : nodes)
1296 node->prepare (getSampleRate(), currentBlockSize, this, getProcessingPrecision());
1299 isPrepared = 1;
1301 std::swap (renderSequenceFloat, newSequenceF);
1302 std::swap (renderSequenceDouble, newSequenceD);
1305 void AudioProcessorGraph::handleAsyncUpdate()
1307 buildRenderingSequence();
1310 //==============================================================================
1311 void AudioProcessorGraph::prepareToPlay (double sampleRate, int estimatedSamplesPerBlock)
1314 const ScopedLock sl (getCallbackLock());
1315 setRateAndBufferSizeDetails (sampleRate, estimatedSamplesPerBlock);
1317 const auto newPrepareSettings = [&]
1319 PrepareSettings settings;
1320 settings.precision = getProcessingPrecision();
1321 settings.sampleRate = sampleRate;
1322 settings.blockSize = estimatedSamplesPerBlock;
1323 settings.valid = true;
1324 return settings;
1325 }();
1327 if (prepareSettings != newPrepareSettings)
1329 unprepare();
1330 prepareSettings = newPrepareSettings;
1334 clearRenderingSequence();
1336 updateOnMessageThread (*this);
1339 bool AudioProcessorGraph::supportsDoublePrecisionProcessing() const
1341 return true;
1344 void AudioProcessorGraph::unprepare()
1346 prepareSettings.valid = false;
1348 isPrepared = 0;
1350 for (auto* n : nodes)
1351 n->unprepare();
1354 void AudioProcessorGraph::releaseResources()
1356 const ScopedLock sl (getCallbackLock());
1358 cancelPendingUpdate();
1360 unprepare();
1362 if (renderSequenceFloat != nullptr)
1363 renderSequenceFloat->releaseBuffers();
1365 if (renderSequenceDouble != nullptr)
1366 renderSequenceDouble->releaseBuffers();
1369 void AudioProcessorGraph::reset()
1371 const ScopedLock sl (getCallbackLock());
1373 for (auto* n : nodes)
1374 n->getProcessor()->reset();
1377 void AudioProcessorGraph::setNonRealtime (bool isProcessingNonRealtime) noexcept
1379 const ScopedLock sl (getCallbackLock());
1381 AudioProcessor::setNonRealtime (isProcessingNonRealtime);
1383 for (auto* n : nodes)
1384 n->getProcessor()->setNonRealtime (isProcessingNonRealtime);
1387 double AudioProcessorGraph::getTailLengthSeconds() const { return 0; }
1388 bool AudioProcessorGraph::acceptsMidi() const { return true; }
1389 bool AudioProcessorGraph::producesMidi() const { return true; }
1390 void AudioProcessorGraph::getStateInformation (MemoryBlock&) {}
1391 void AudioProcessorGraph::setStateInformation (const void*, int) {}
1393 template <typename FloatType, typename SequenceType>
1394 static void processBlockForBuffer (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
1395 AudioProcessorGraph& graph,
1396 std::unique_ptr<SequenceType>& renderSequence,
1397 std::atomic<bool>& isPrepared)
1399 if (graph.isNonRealtime())
1401 while (! isPrepared)
1402 Thread::sleep (1);
1404 const ScopedLock sl (graph.getCallbackLock());
1406 if (renderSequence != nullptr)
1407 renderSequence->perform (buffer, midiMessages, graph.getPlayHead());
1409 else
1411 const ScopedLock sl (graph.getCallbackLock());
1413 if (isPrepared)
1415 if (renderSequence != nullptr)
1416 renderSequence->perform (buffer, midiMessages, graph.getPlayHead());
1418 else
1420 buffer.clear();
1421 midiMessages.clear();
1426 void AudioProcessorGraph::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
1428 if ((! isPrepared) && MessageManager::getInstance()->isThisTheMessageThread())
1429 handleAsyncUpdate();
1431 processBlockForBuffer<float> (buffer, midiMessages, *this, renderSequenceFloat, isPrepared);
1434 void AudioProcessorGraph::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
1436 if ((! isPrepared) && MessageManager::getInstance()->isThisTheMessageThread())
1437 handleAsyncUpdate();
1439 processBlockForBuffer<double> (buffer, midiMessages, *this, renderSequenceDouble, isPrepared);
1442 //==============================================================================
1443 AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType deviceType)
1444 : type (deviceType)
1448 AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor()
1452 const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const
1454 switch (type)
1456 case audioOutputNode: return "Audio Output";
1457 case audioInputNode: return "Audio Input";
1458 case midiOutputNode: return "MIDI Output";
1459 case midiInputNode: return "MIDI Input";
1460 default: break;
1463 return {};
1466 void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const
1468 d.name = getName();
1469 d.category = "I/O devices";
1470 d.pluginFormatName = "Internal";
1471 d.manufacturerName = "JUCE";
1472 d.version = "1.0";
1473 d.isInstrument = false;
1475 d.deprecatedUid = d.uniqueId = d.name.hashCode();
1477 d.numInputChannels = getTotalNumInputChannels();
1479 if (type == audioOutputNode && graph != nullptr)
1480 d.numInputChannels = graph->getTotalNumInputChannels();
1482 d.numOutputChannels = getTotalNumOutputChannels();
1484 if (type == audioInputNode && graph != nullptr)
1485 d.numOutputChannels = graph->getTotalNumOutputChannels();
1488 void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int)
1490 jassert (graph != nullptr);
1493 void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources()
1497 bool AudioProcessorGraph::AudioGraphIOProcessor::supportsDoublePrecisionProcessing() const
1499 return true;
1502 template <typename FloatType, typename SequenceType>
1503 static void processIOBlock (AudioProcessorGraph::AudioGraphIOProcessor& io, SequenceType& sequence,
1504 AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages)
1506 switch (io.getType())
1508 case AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode:
1510 auto&& currentAudioOutputBuffer = sequence.currentAudioOutputBuffer;
1512 for (int i = jmin (currentAudioOutputBuffer.getNumChannels(), buffer.getNumChannels()); --i >= 0;)
1513 currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
1515 break;
1518 case AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode:
1520 auto* currentInputBuffer = sequence.currentAudioInputBuffer;
1522 for (int i = jmin (currentInputBuffer->getNumChannels(), buffer.getNumChannels()); --i >= 0;)
1523 buffer.copyFrom (i, 0, *currentInputBuffer, i, 0, buffer.getNumSamples());
1525 break;
1528 case AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode:
1529 sequence.currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0);
1530 break;
1532 case AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode:
1533 midiMessages.addEvents (*sequence.currentMidiInputBuffer, 0, buffer.getNumSamples(), 0);
1534 break;
1536 default:
1537 break;
1541 void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
1543 jassert (graph != nullptr);
1544 processIOBlock (*this, *graph->renderSequenceFloat, buffer, midiMessages);
1547 void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
1549 jassert (graph != nullptr);
1550 processIOBlock (*this, *graph->renderSequenceDouble, buffer, midiMessages);
1553 double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const
1555 return 0;
1558 bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const
1560 return type == midiOutputNode;
1563 bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const
1565 return type == midiInputNode;
1568 bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const noexcept { return type == audioInputNode || type == midiInputNode; }
1569 bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const noexcept { return type == audioOutputNode || type == midiOutputNode; }
1571 #if ! JUCE_AUDIOPROCESSOR_NO_GUI
1572 bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const { return false; }
1573 AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return nullptr; }
1574 #endif
1576 int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; }
1577 int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; }
1578 void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { }
1580 const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return {}; }
1581 void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) {}
1583 void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (MemoryBlock&) {}
1584 void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) {}
1586 void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph)
1588 graph = newGraph;
1590 if (graph != nullptr)
1592 setPlayConfigDetails (type == audioOutputNode ? graph->getTotalNumOutputChannels() : 0,
1593 type == audioInputNode ? graph->getTotalNumInputChannels() : 0,
1594 getSampleRate(),
1595 getBlockSize());
1597 updateHostDisplay();
1601 } // namespace juce