LMMS
Loading...
Searching...
No Matches
juce_AudioProcessorGraph.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
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.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29static void updateOnMessageThread (AsyncUpdater& updater)
30{
31 if (MessageManager::getInstance()->isThisTheMessageThread())
32 updater.handleAsyncUpdate();
33 else
34 updater.triggerAsyncUpdate();
35}
36
37template <typename FloatType>
39{
47
48 void perform (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages, AudioPlayHead* audioPlayHead)
49 {
50 auto numSamples = buffer.getNumSamples();
51 auto maxSamples = renderingBuffer.getNumSamples();
52
53 if (numSamples > maxSamples)
54 {
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)
58 {
59 auto chunkSize = jmin (maxSamples, numSamples - chunkStartSample);
60
61 AudioBuffer<FloatType> audioChunk (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), chunkStartSample, chunkSize);
62 midiChunk.clear();
63 midiChunk.addEvents (midiMessages, chunkStartSample, chunkSize, -chunkStartSample);
64
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);
68
69 chunkStartSample += maxSamples;
70 }
71
72 return;
73 }
74
76 currentAudioOutputBuffer.setSize (jmax (1, buffer.getNumChannels()), numSamples);
78 currentMidiInputBuffer = &midiMessages;
80
81 {
82 const Context context { renderingBuffer.getArrayOfWritePointers(), midiBuffers.begin(), audioPlayHead, numSamples };
83
84 for (auto* op : renderOps)
85 op->perform (context);
86 }
87
88 for (int i = 0; i < buffer.getNumChannels(); ++i)
89 buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples);
90
91 midiMessages.clear();
92 midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
94 }
95
96 void addClearChannelOp (int index)
97 {
98 createOp ([=] (const Context& c) { FloatVectorOperations::clear (c.audioBuffers[index], c.numSamples); });
99 }
100
101 void addCopyChannelOp (int srcIndex, int dstIndex)
102 {
103 createOp ([=] (const Context& c) { FloatVectorOperations::copy (c.audioBuffers[dstIndex],
104 c.audioBuffers[srcIndex],
105 c.numSamples); });
106 }
107
108 void addAddChannelOp (int srcIndex, int dstIndex)
109 {
110 createOp ([=] (const Context& c) { FloatVectorOperations::add (c.audioBuffers[dstIndex],
111 c.audioBuffers[srcIndex],
112 c.numSamples); });
113 }
114
115 void addClearMidiBufferOp (int index)
116 {
117 createOp ([=] (const Context& c) { c.midiBuffers[index].clear(); });
118 }
119
120 void addCopyMidiBufferOp (int srcIndex, int dstIndex)
121 {
122 createOp ([=] (const Context& c) { c.midiBuffers[dstIndex] = c.midiBuffers[srcIndex]; });
123 }
124
125 void addAddMidiBufferOp (int srcIndex, int dstIndex)
126 {
127 createOp ([=] (const Context& c) { c.midiBuffers[dstIndex].addEvents (c.midiBuffers[srcIndex],
128 0, c.numSamples, 0); });
129 }
130
131 void addDelayChannelOp (int chan, int delaySize)
132 {
133 renderOps.add (new DelayChannelOp (chan, delaySize));
134 }
135
137 const Array<int>& audioChannelsUsed, int totalNumChans, int midiBuffer)
138 {
139 renderOps.add (new ProcessOp (node, audioChannelsUsed, totalNumChans, midiBuffer));
140 }
141
142 void prepareBuffers (int blockSize)
143 {
144 renderingBuffer.setSize (numBuffersNeeded + 1, blockSize);
145 renderingBuffer.clear();
146 currentAudioOutputBuffer.setSize (numBuffersNeeded + 1, blockSize);
148
149 currentAudioInputBuffer = nullptr;
150 currentMidiInputBuffer = nullptr;
152
153 midiBuffers.clearQuick();
155
156 const int defaultMIDIBufferSize = 512;
157
158 midiChunk.ensureSize (defaultMIDIBufferSize);
159
160 for (auto&& m : midiBuffers)
161 m.ensureSize (defaultMIDIBufferSize);
162 }
163
165 {
166 renderingBuffer.setSize (1, 1);
167 currentAudioOutputBuffer.setSize (1, 1);
168 currentAudioInputBuffer = nullptr;
169 currentMidiInputBuffer = nullptr;
171 midiBuffers.clear();
172 }
173
175
178
181
184
185private:
186 //==============================================================================
188 {
190 virtual ~RenderingOp() {}
191 virtual void perform (const Context&) = 0;
192
194 };
195
197
198 //==============================================================================
199 template <typename LambdaType,
200 std::enable_if_t<std::is_rvalue_reference<LambdaType&&>::value, int> = 0>
201 void createOp (LambdaType&& fn)
202 {
203 struct LambdaOp : public RenderingOp
204 {
205 LambdaOp (LambdaType&& f) : function (std::forward<LambdaType> (f)) {}
206 void perform (const Context& c) override { function (c); }
207
208 LambdaType function;
209 };
210
211 renderOps.add (new LambdaOp (std::forward<LambdaType> (fn)));
212 }
213
214 //==============================================================================
216 {
217 DelayChannelOp (int chan, int delaySize)
218 : channel (chan),
219 bufferSize (delaySize + 1),
220 writeIndex (delaySize)
221 {
222 buffer.calloc ((size_t) bufferSize);
223 }
224
225 void perform (const Context& c) override
226 {
227 auto* data = c.audioBuffers[channel];
228
229 for (int i = c.numSamples; --i >= 0;)
230 {
232 *data++ = buffer[readIndex];
233
234 if (++readIndex >= bufferSize) readIndex = 0;
235 if (++writeIndex >= bufferSize) writeIndex = 0;
236 }
237 }
238
240 const int channel, bufferSize;
242
244 };
245
246 //==============================================================================
247 struct ProcessOp : public RenderingOp
248 {
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)
257 {
258 audioChannels.calloc ((size_t) totalChans);
259
260 while (audioChannelsToUse.size() < totalChans)
261 audioChannelsToUse.add (0);
262 }
263
264 void perform (const Context& c) override
265 {
266 processor.setPlayHead (c.audioPlayHead);
267
268 for (int i = 0; i < totalChans; ++i)
269 audioChannels[i] = c.audioBuffers[audioChannelsToUse.getUnchecked (i)];
270
271 auto numAudioChannels = [this]
272 {
273 if (const auto* proc = node->getProcessor())
274 if (proc->getTotalNumInputChannels() == 0 && proc->getTotalNumOutputChannels() == 0)
275 return 0;
276
277 return totalChans;
278 }();
279
280 AudioBuffer<FloatType> buffer (audioChannels, numAudioChannels, c.numSamples);
281
282 const ScopedLock lock (processor.getCallbackLock());
283
284 if (processor.isSuspended())
285 buffer.clear();
286 else
287 callProcess (buffer, c.midiBuffers[midiBufferToUse]);
288 }
289
290 void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
291 {
292 if (processor.isUsingDoublePrecision())
293 {
294 tempBufferDouble.makeCopyOf (buffer, true);
295
296 if (node->isBypassed())
297 node->processBlockBypassed (tempBufferDouble, midiMessages);
298 else
299 node->processBlock (tempBufferDouble, midiMessages);
300
301 buffer.makeCopyOf (tempBufferDouble, true);
302 }
303 else
304 {
305 if (node->isBypassed())
306 node->processBlockBypassed (buffer, midiMessages);
307 else
308 node->processBlock (buffer, midiMessages);
309 }
310 }
311
312 void callProcess (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
313 {
314 if (processor.isUsingDoublePrecision())
315 {
316 if (node->isBypassed())
317 node->processBlockBypassed (buffer, midiMessages);
318 else
319 node->processBlock (buffer, midiMessages);
320 }
321 else
322 {
323 tempBufferFloat.makeCopyOf (buffer, true);
324
325 if (node->isBypassed())
326 node->processBlockBypassed (tempBufferFloat, midiMessages);
327 else
328 node->processBlock (tempBufferFloat, midiMessages);
329
330 buffer.makeCopyOf (tempBufferFloat, true);
331 }
332 }
333
336
341
343 };
344};
345
346//==============================================================================
347//==============================================================================
348template <typename RenderSequence>
350{
353 {
354 audioBuffers.add (AssignedBuffer::createReadOnlyEmpty()); // first buffer is read-only zeros
356
357 for (int i = 0; i < orderedNodes.size(); ++i)
358 {
359 createRenderingOpsForNode (*orderedNodes.getUnchecked(i), i);
362 }
363
364 graph.setLatencySamples (totalLatency);
365
366 s.numBuffersNeeded = audioBuffers.size();
367 s.numMidiBuffersNeeded = midiBuffers.size();
368 }
369
370 //==============================================================================
373
375 RenderSequence& sequence;
376
378
380 {
382
383 static AssignedBuffer createReadOnlyEmpty() noexcept { return { { zeroNodeID(), 0 } }; }
384 static AssignedBuffer createFree() noexcept { return { { freeNodeID(), 0 } }; }
385
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()); }
389
390 void setFree() noexcept { channel = { freeNodeID(), 0 }; }
392
393 private:
394 static NodeID anonNodeID() { return NodeID (0x7ffffffd); }
395 static NodeID zeroNodeID() { return NodeID (0x7ffffffe); }
396 static NodeID freeNodeID() { return NodeID (0x7fffffff); }
397 };
398
400
402
403 struct Delay
404 {
406 int delay;
407 };
408
411
412 int getNodeDelay (NodeID nodeID) const noexcept
413 {
414 return delays[nodeID.uid];
415 }
416
418 {
419 int maxLatency = 0;
420
421 for (auto&& c : graph.getConnections())
422 if (c.destination.nodeID == nodeID)
423 maxLatency = jmax (maxLatency, getNodeDelay (c.source.nodeID));
424
425 return maxLatency;
426 }
427
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)
432 {
433 for (auto&& i : child->inputs)
434 {
435 auto* parentNode = i.otherNode;
436
437 if (parentNode == child)
438 continue;
439
440 if (parents.insert (parentNode).second)
441 {
442 auto parentParents = otherParents.find (parentNode);
443
444 if (parentParents != otherParents.end())
445 {
446 parents.insert (parentParents->second.begin(), parentParents->second.end());
447 continue;
448 }
449
450 getAllParentsOfNode (i.otherNode, parents, otherParents);
451 }
452 }
453 }
454
456 {
458
459 std::unordered_map<Node*, std::unordered_set<Node*>> nodeParents;
460
461 for (auto* node : graph.getNodes())
462 {
463 int insertionIndex = 0;
464
465 for (; insertionIndex < result.size(); ++insertionIndex)
466 {
467 auto& parents = nodeParents[result.getUnchecked (insertionIndex)];
468
469 if (parents.find (node) != parents.end())
470 break;
471 }
472
473 result.insert (insertionIndex, node);
474 getAllParentsOfNode (node, nodeParents[node], nodeParents);
475 }
476
477 return result;
478 }
479
480 int findBufferForInputAudioChannel (Node& node, const int inputChan,
481 const int ourRenderingIndex, const int maxLatency)
482 {
483 auto& processor = *node.getProcessor();
484 auto numOuts = processor.getTotalNumOutputChannels();
485
486 auto sources = getSourcesForChannel (node, inputChan);
487
488 // Handle an unconnected input channel...
489 if (sources.isEmpty())
490 {
491 if (inputChan >= numOuts)
493
494 auto index = getFreeBuffer (audioBuffers);
495 sequence.addClearChannelOp (index);
496 return index;
497 }
498
499 // Handle an input from a single source..
500 if (sources.size() == 1)
501 {
502 // channel with a straightforward single input..
503 auto src = sources.getUnchecked(0);
504
505 int bufIndex = getBufferContaining (src);
506
507 if (bufIndex < 0)
508 {
509 // if not found, this is probably a feedback loop
510 bufIndex = readOnlyEmptyBufferIndex;
511 jassert (bufIndex >= 0);
512 }
513
514 if (inputChan < numOuts
515 && isBufferNeededLater (ourRenderingIndex, inputChan, src))
516 {
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;
522 }
523
524 auto nodeDelay = getNodeDelay (src.nodeID);
525
526 if (nodeDelay < maxLatency)
527 sequence.addDelayChannelOp (bufIndex, maxLatency - nodeDelay);
528
529 return bufIndex;
530 }
531
532 // Handle a mix of several outputs coming into this input..
533 int reusableInputIndex = -1;
534 int bufIndex = -1;
535
536 for (int i = 0; i < sources.size(); ++i)
537 {
538 auto src = sources.getReference(i);
539 auto sourceBufIndex = getBufferContaining (src);
540
541 if (sourceBufIndex >= 0 && ! isBufferNeededLater (ourRenderingIndex, inputChan, src))
542 {
543 // we've found one of our input chans that can be re-used..
544 reusableInputIndex = i;
545 bufIndex = sourceBufIndex;
546
547 auto nodeDelay = getNodeDelay (src.nodeID);
548
549 if (nodeDelay < maxLatency)
550 sequence.addDelayChannelOp (bufIndex, maxLatency - nodeDelay);
551
552 break;
553 }
554 }
555
556 if (reusableInputIndex < 0)
557 {
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);
561
562 audioBuffers.getReference (bufIndex).setAssignedToNonExistentNode();
563
564 auto srcIndex = getBufferContaining (sources.getFirst());
565
566 if (srcIndex < 0)
567 sequence.addClearChannelOp (bufIndex); // if not found, this is probably a feedback loop
568 else
569 sequence.addCopyChannelOp (srcIndex, bufIndex);
570
571 reusableInputIndex = 0;
572 auto nodeDelay = getNodeDelay (sources.getFirst().nodeID);
573
574 if (nodeDelay < maxLatency)
575 sequence.addDelayChannelOp (bufIndex, maxLatency - nodeDelay);
576 }
577
578 for (int i = 0; i < sources.size(); ++i)
579 {
580 if (i != reusableInputIndex)
581 {
582 auto src = sources.getReference(i);
583 int srcIndex = getBufferContaining (src);
584
585 if (srcIndex >= 0)
586 {
587 auto nodeDelay = getNodeDelay (src.nodeID);
588
589 if (nodeDelay < maxLatency)
590 {
591 if (! isBufferNeededLater (ourRenderingIndex, inputChan, src))
592 {
593 sequence.addDelayChannelOp (srcIndex, maxLatency - nodeDelay);
594 }
595 else // buffer is reused elsewhere, can't be delayed
596 {
597 auto bufferToDelay = getFreeBuffer (audioBuffers);
598 sequence.addCopyChannelOp (srcIndex, bufferToDelay);
599 sequence.addDelayChannelOp (bufferToDelay, maxLatency - nodeDelay);
600 srcIndex = bufferToDelay;
601 }
602 }
603
604 sequence.addAddChannelOp (srcIndex, bufIndex);
605 }
606 }
607 }
608
609 return bufIndex;
610 }
611
612 int findBufferForInputMidiChannel (Node& node, int ourRenderingIndex)
613 {
614 auto& processor = *node.getProcessor();
616
617 // No midi inputs..
618 if (sources.isEmpty())
619 {
620 auto midiBufferToUse = getFreeBuffer (midiBuffers); // need to pick a buffer even if the processor doesn't use midi
621
622 if (processor.acceptsMidi() || processor.producesMidi())
623 sequence.addClearMidiBufferOp (midiBufferToUse);
624
625 return midiBufferToUse;
626 }
627
628 // One midi input..
629 if (sources.size() == 1)
630 {
631 auto src = sources.getReference (0);
632 auto midiBufferToUse = getBufferContaining (src);
633
634 if (midiBufferToUse >= 0)
635 {
637 {
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;
643 }
644 }
645 else
646 {
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
649 }
650
651 return midiBufferToUse;
652 }
653
654 // Multiple midi inputs..
655 int midiBufferToUse = -1;
656 int reusableInputIndex = -1;
657
658 for (int i = 0; i < sources.size(); ++i)
659 {
660 auto src = sources.getReference (i);
661 auto sourceBufIndex = getBufferContaining (src);
662
663 if (sourceBufIndex >= 0
664 && ! isBufferNeededLater (ourRenderingIndex, AudioProcessorGraph::midiChannelIndex, src))
665 {
666 // we've found one of our input buffers that can be re-used..
667 reusableInputIndex = i;
668 midiBufferToUse = sourceBufIndex;
669 break;
670 }
671 }
672
673 if (reusableInputIndex < 0)
674 {
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);
678
679 auto srcIndex = getBufferContaining (sources.getUnchecked(0));
680
681 if (srcIndex >= 0)
682 sequence.addCopyMidiBufferOp (srcIndex, midiBufferToUse);
683 else
684 sequence.addClearMidiBufferOp (midiBufferToUse);
685
686 reusableInputIndex = 0;
687 }
688
689 for (int i = 0; i < sources.size(); ++i)
690 {
691 if (i != reusableInputIndex)
692 {
693 auto srcIndex = getBufferContaining (sources.getUnchecked(i));
694
695 if (srcIndex >= 0)
696 sequence.addAddMidiBufferOp (srcIndex, midiBufferToUse);
697 }
698 }
699
700 return midiBufferToUse;
701 }
702
703 void createRenderingOpsForNode (Node& node, const int ourRenderingIndex)
704 {
705 auto& processor = *node.getProcessor();
706 auto numIns = processor.getTotalNumInputChannels();
707 auto numOuts = processor.getTotalNumOutputChannels();
708 auto totalChans = jmax (numIns, numOuts);
709
710 Array<int> audioChannelsToUse;
711 auto maxLatency = getInputLatencyForNode (node.nodeID);
712
713 for (int inputChan = 0; inputChan < numIns; ++inputChan)
714 {
715 // get a list of all the inputs to this node
716 auto index = findBufferForInputAudioChannel (node, inputChan, ourRenderingIndex, maxLatency);
717 jassert (index >= 0);
718
719 audioChannelsToUse.add (index);
720
721 if (inputChan < numOuts)
722 audioBuffers.getReference (index).channel = { node.nodeID, inputChan };
723 }
724
725 for (int outputChan = numIns; outputChan < numOuts; ++outputChan)
726 {
727 auto index = getFreeBuffer (audioBuffers);
728 jassert (index != 0);
729 audioChannelsToUse.add (index);
730
731 audioBuffers.getReference (index).channel = { node.nodeID, outputChan };
732 }
733
734 auto midiBufferToUse = findBufferForInputMidiChannel (node, ourRenderingIndex);
735
736 if (processor.producesMidi())
737 midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, AudioProcessorGraph::midiChannelIndex };
738
739 delays.set (node.nodeID.uid, maxLatency + processor.getLatencySamples());
740
741 if (numOuts == 0)
742 totalLatency = maxLatency;
743
744 sequence.addProcessOp (node, audioChannelsToUse, totalChans, midiBufferToUse);
745 }
746
747 //==============================================================================
749 {
751 AudioProcessorGraph::NodeAndChannel nc { node.nodeID, inputChannelIndex };
752
753 for (auto&& c : graph.getConnections())
754 if (c.destination == nc)
755 results.add (c.source);
756
757 return results;
758 }
759
761 {
762 for (int i = 1; i < buffers.size(); ++i)
763 if (buffers.getReference (i).isFree())
764 return i;
765
767 return buffers.size() - 1;
768 }
769
771 {
772 int i = 0;
773
774 for (auto& b : output.isMIDI() ? midiBuffers : audioBuffers)
775 {
776 if (b.channel == output)
777 return i;
778
779 ++i;
780 }
781
782 return -1;
783 }
784
785 void markAnyUnusedBuffersAsFree (Array<AssignedBuffer>& buffers, const int stepIndex)
786 {
787 for (auto& b : buffers)
788 if (b.isAssigned() && ! isBufferNeededLater (stepIndex, -1, b.channel))
789 b.setFree();
790 }
791
792 bool isBufferNeededLater (int stepIndexToSearchFrom,
793 int inputChannelOfIndexToIgnore,
795 {
796 while (stepIndexToSearchFrom < orderedNodes.size())
797 {
798 auto* node = orderedNodes.getUnchecked (stepIndexToSearchFrom);
799
800 if (output.isMIDI())
801 {
802 if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex
803 && graph.isConnected ({ { output.nodeID, AudioProcessorGraph::midiChannelIndex },
804 { node->nodeID, AudioProcessorGraph::midiChannelIndex } }))
805 return true;
806 }
807 else
808 {
809 for (int i = 0; i < node->getProcessor()->getTotalNumInputChannels(); ++i)
810 if (i != inputChannelOfIndexToIgnore && graph.isConnected ({ output, { node->nodeID, i } }))
811 return true;
812 }
813
814 inputChannelOfIndexToIgnore = -1;
815 ++stepIndexToSearchFrom;
816 }
817
818 return false;
819 }
820
822};
823
824//==============================================================================
829
830bool AudioProcessorGraph::Connection::operator== (const Connection& other) const noexcept
831{
832 return source == other.source && destination == other.destination;
833}
834
835bool AudioProcessorGraph::Connection::operator!= (const Connection& c) const noexcept
836{
837 return ! operator== (c);
838}
839
840bool AudioProcessorGraph::Connection::operator< (const Connection& other) const noexcept
841{
842 if (source.nodeID != other.source.nodeID)
843 return source.nodeID < other.source.nodeID;
844
845 if (destination.nodeID != other.destination.nodeID)
846 return destination.nodeID < other.destination.nodeID;
847
848 if (source.channelIndex != other.source.channelIndex)
849 return source.channelIndex < other.source.channelIndex;
850
851 return destination.channelIndex < other.destination.channelIndex;
852}
853
854//==============================================================================
855AudioProcessorGraph::Node::Node (NodeID n, std::unique_ptr<AudioProcessor> p) noexcept
856 : nodeID (n), processor (std::move (p))
857{
858 jassert (processor != nullptr);
859}
860
861void AudioProcessorGraph::Node::prepare (double newSampleRate, int newBlockSize,
863{
864 const ScopedLock lock (processorLock);
865
866 if (! isPrepared)
867 {
868 setParentGraph (graph);
869
870 // try to align the precision of the processor and the graph
871 processor->setProcessingPrecision (processor->supportsDoublePrecisionProcessing() ? precision
873
874 processor->setRateAndBufferSizeDetails (newSampleRate, newBlockSize);
875 processor->prepareToPlay (newSampleRate, newBlockSize);
876
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;
880 }
881}
882
884{
885 const ScopedLock lock (processorLock);
886
887 if (isPrepared)
888 {
889 isPrepared = false;
890 processor->releaseResources();
891 }
892}
893
895{
896 const ScopedLock lock (processorLock);
897
898 if (auto* ioProc = dynamic_cast<AudioProcessorGraph::AudioGraphIOProcessor*> (processor.get()))
899 ioProc->setParentGraph (graph);
900}
901
902bool AudioProcessorGraph::Node::Connection::operator== (const Connection& other) const noexcept
903{
904 return otherNode == other.otherNode
905 && thisChannel == other.thisChannel
906 && otherChannel == other.otherChannel;
907}
908
909//==============================================================================
911{
912 if (processor != nullptr)
913 {
914 if (auto* bypassParam = processor->getBypassParameter())
915 return (bypassParam->getValue() != 0.0f);
916 }
917
918 return bypassed;
919}
920
921void AudioProcessorGraph::Node::setBypassed (bool shouldBeBypassed) noexcept
922{
923 if (processor != nullptr)
924 {
925 if (auto* bypassParam = processor->getBypassParameter())
926 bypassParam->setValueNotifyingHost (shouldBeBypassed ? 1.0f : 0.0f);
927 }
928
929 bypassed = shouldBeBypassed;
930}
931
932//==============================================================================
935
936//==============================================================================
940
947
949{
950 return "Audio Graph";
951}
952
953//==============================================================================
961
963{
964 const ScopedLock sl (getCallbackLock());
965
966 if (nodes.isEmpty())
967 return;
968
969 nodes.clear();
971}
972
974{
975 for (auto* n : nodes)
976 if (n->nodeID == nodeID)
977 return n;
978
979 return {};
980}
981
982AudioProcessorGraph::Node::Ptr AudioProcessorGraph::addNode (std::unique_ptr<AudioProcessor> newProcessor, NodeID nodeID)
983{
984 if (newProcessor == nullptr || newProcessor.get() == this)
985 {
987 return {};
988 }
989
990 if (nodeID == NodeID())
991 nodeID.uid = ++(lastNodeID.uid);
992
993 for (auto* n : nodes)
994 {
995 if (n->getProcessor() == newProcessor.get() || n->nodeID == nodeID)
996 {
997 jassertfalse; // Cannot add two copies of the same processor, or duplicate node IDs!
998 return {};
999 }
1000 }
1001
1002 if (lastNodeID < nodeID)
1003 lastNodeID = nodeID;
1004
1005 newProcessor->setPlayHead (getPlayHead());
1006
1007 Node::Ptr n (new Node (nodeID, std::move (newProcessor)));
1008
1009 {
1010 const ScopedLock sl (getCallbackLock());
1011 nodes.add (n.get());
1012 }
1013
1014 n->setParentGraph (this);
1016 return n;
1017}
1018
1020{
1021 const ScopedLock sl (getCallbackLock());
1022
1023 for (int i = nodes.size(); --i >= 0;)
1024 {
1025 if (nodes.getUnchecked (i)->nodeID == nodeId)
1026 {
1027 disconnectNode (nodeId);
1028 auto node = nodes.removeAndReturn (i);
1030 return node;
1031 }
1032 }
1033
1034 return {};
1035}
1036
1038{
1039 if (node != nullptr)
1040 return removeNode (node->nodeID);
1041
1043 return {};
1044}
1045
1046//==============================================================================
1047void AudioProcessorGraph::getNodeConnections (Node& node, std::vector<Connection>& connections)
1048{
1049 for (auto& i : node.inputs)
1050 connections.push_back ({ { i.otherNode->nodeID, i.otherChannel }, { node.nodeID, i.thisChannel } });
1051
1052 for (auto& o : node.outputs)
1053 connections.push_back ({ { node.nodeID, o.thisChannel }, { o.otherNode->nodeID, o.otherChannel } });
1054}
1055
1056std::vector<AudioProcessorGraph::Connection> AudioProcessorGraph::getConnections() const
1057{
1058 std::vector<Connection> connections;
1059
1060 for (auto& n : nodes)
1061 getNodeConnections (*n, connections);
1062
1063 std::sort (connections.begin(), connections.end());
1064 auto last = std::unique (connections.begin(), connections.end());
1065 connections.erase (last, connections.end());
1066
1067 return connections;
1068}
1069
1070bool AudioProcessorGraph::isConnected (Node* source, int sourceChannel, Node* dest, int destChannel) const noexcept
1071{
1072 for (auto& o : source->outputs)
1073 if (o.otherNode == dest && o.thisChannel == sourceChannel && o.otherChannel == destChannel)
1074 return true;
1075
1076 return false;
1077}
1078
1080{
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);
1085
1086 return false;
1087}
1088
1089bool AudioProcessorGraph::isConnected (NodeID srcID, NodeID destID) const noexcept
1090{
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;
1096
1097 return false;
1098}
1099
1100bool AudioProcessorGraph::isAnInputTo (Node& src, Node& dst) const noexcept
1101{
1102 jassert (nodes.contains (&src));
1103 jassert (nodes.contains (&dst));
1104
1105 return isAnInputTo (src, dst, nodes.size());
1106}
1107
1108bool AudioProcessorGraph::isAnInputTo (Node& src, Node& dst, int recursionCheck) const noexcept
1109{
1110 for (auto&& i : dst.inputs)
1111 if (i.otherNode == &src)
1112 return true;
1113
1114 if (recursionCheck > 0)
1115 for (auto&& i : dst.inputs)
1116 if (isAnInputTo (src, *i.otherNode, recursionCheck - 1))
1117 return true;
1118
1119 return false;
1120}
1121
1122bool AudioProcessorGraph::canConnect (Node* source, int sourceChannel, Node* dest, int destChannel) const noexcept
1123{
1124 bool sourceIsMIDI = sourceChannel == midiChannelIndex;
1125 bool destIsMIDI = destChannel == midiChannelIndex;
1126
1127 if (sourceChannel < 0
1128 || destChannel < 0
1129 || source == dest
1130 || sourceIsMIDI != destIsMIDI)
1131 return false;
1132
1133 if (source == nullptr
1134 || (! sourceIsMIDI && sourceChannel >= source->processor->getTotalNumOutputChannels())
1135 || (sourceIsMIDI && ! source->processor->producesMidi()))
1136 return false;
1137
1138 if (dest == nullptr
1139 || (! destIsMIDI && destChannel >= dest->processor->getTotalNumInputChannels())
1140 || (destIsMIDI && ! dest->processor->acceptsMidi()))
1141 return false;
1142
1143 return ! isConnected (source, sourceChannel, dest, destChannel);
1144}
1145
1147{
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);
1152
1153 return false;
1154}
1155
1157{
1158 if (auto* source = getNodeForId (c.source.nodeID))
1159 {
1160 if (auto* dest = getNodeForId (c.destination.nodeID))
1161 {
1162 auto sourceChan = c.source.channelIndex;
1163 auto destChan = c.destination.channelIndex;
1164
1165 if (canConnect (source, sourceChan, dest, destChan))
1166 {
1167 source->outputs.add ({ dest, destChan, sourceChan });
1168 dest->inputs.add ({ source, sourceChan, destChan });
1169 jassert (isConnected (c));
1171 return true;
1172 }
1173 }
1174 }
1175
1176 return false;
1177}
1178
1180{
1181 if (auto* source = getNodeForId (c.source.nodeID))
1182 {
1183 if (auto* dest = getNodeForId (c.destination.nodeID))
1184 {
1185 auto sourceChan = c.source.channelIndex;
1186 auto destChan = c.destination.channelIndex;
1187
1188 if (isConnected (source, sourceChan, dest, destChan))
1189 {
1190 source->outputs.removeAllInstancesOf ({ dest, destChan, sourceChan });
1191 dest->inputs.removeAllInstancesOf ({ source, sourceChan, destChan });
1193 return true;
1194 }
1195 }
1196 }
1197
1198 return false;
1199}
1200
1202{
1203 if (auto* node = getNodeForId (nodeID))
1204 {
1205 std::vector<Connection> connections;
1206 getNodeConnections (*node, connections);
1207
1208 if (! connections.empty())
1209 {
1210 for (auto c : connections)
1212
1213 return true;
1214 }
1215 }
1216
1217 return false;
1218}
1219
1220bool AudioProcessorGraph::isLegal (Node* source, int sourceChannel, Node* dest, int destChannel) const noexcept
1221{
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()));
1226}
1227
1229{
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);
1233
1234 return false;
1235}
1236
1238{
1239 bool anyRemoved = false;
1240
1241 for (auto* node : nodes)
1242 {
1243 std::vector<Connection> connections;
1244 getNodeConnections (*node, connections);
1245
1246 for (auto c : connections)
1247 if (! isConnectionLegal (c))
1248 anyRemoved = removeConnection (c) || anyRemoved;
1249 }
1250
1251 return anyRemoved;
1252}
1253
1254//==============================================================================
1256{
1257 std::unique_ptr<RenderSequenceFloat> oldSequenceF;
1258 std::unique_ptr<RenderSequenceDouble> oldSequenceD;
1259
1260 {
1261 const ScopedLock sl (getCallbackLock());
1262 std::swap (renderSequenceFloat, oldSequenceF);
1263 std::swap (renderSequenceDouble, oldSequenceD);
1264 }
1265}
1266
1268{
1269 for (auto* node : nodes)
1270 if (! node->isPrepared)
1271 return true;
1272
1273 return false;
1274}
1275
1277{
1278 auto newSequenceF = std::make_unique<RenderSequenceFloat>();
1279 auto newSequenceD = std::make_unique<RenderSequenceDouble>();
1280
1281 RenderSequenceBuilder<RenderSequenceFloat> builderF (*this, *newSequenceF);
1282 RenderSequenceBuilder<RenderSequenceDouble> builderD (*this, *newSequenceD);
1283
1284 const ScopedLock sl (getCallbackLock());
1285
1286 const auto currentBlockSize = getBlockSize();
1287 newSequenceF->prepareBuffers (currentBlockSize);
1288 newSequenceD->prepareBuffers (currentBlockSize);
1289
1291 {
1292 renderSequenceFloat.reset();
1293 renderSequenceDouble.reset();
1294
1295 for (auto* node : nodes)
1296 node->prepare (getSampleRate(), currentBlockSize, this, getProcessingPrecision());
1297 }
1298
1299 isPrepared = 1;
1300
1301 std::swap (renderSequenceFloat, newSequenceF);
1302 std::swap (renderSequenceDouble, newSequenceD);
1303}
1304
1309
1310//==============================================================================
1311void AudioProcessorGraph::prepareToPlay (double sampleRate, int estimatedSamplesPerBlock)
1312{
1313 {
1314 const ScopedLock sl (getCallbackLock());
1315 setRateAndBufferSizeDetails (sampleRate, estimatedSamplesPerBlock);
1316
1317 const auto newPrepareSettings = [&]
1318 {
1319 PrepareSettings settings;
1320 settings.precision = getProcessingPrecision();
1321 settings.sampleRate = sampleRate;
1322 settings.blockSize = estimatedSamplesPerBlock;
1323 settings.valid = true;
1324 return settings;
1325 }();
1326
1327 if (prepareSettings != newPrepareSettings)
1328 {
1329 unprepare();
1330 prepareSettings = newPrepareSettings;
1331 }
1332 }
1333
1335
1336 updateOnMessageThread (*this);
1337}
1338
1340{
1341 return true;
1342}
1343
1345{
1346 prepareSettings.valid = false;
1347
1348 isPrepared = 0;
1349
1350 for (auto* n : nodes)
1351 n->unprepare();
1352}
1353
1355{
1356 const ScopedLock sl (getCallbackLock());
1357
1359
1360 unprepare();
1361
1362 if (renderSequenceFloat != nullptr)
1363 renderSequenceFloat->releaseBuffers();
1364
1365 if (renderSequenceDouble != nullptr)
1366 renderSequenceDouble->releaseBuffers();
1367}
1368
1370{
1371 const ScopedLock sl (getCallbackLock());
1372
1373 for (auto* n : nodes)
1374 n->getProcessor()->reset();
1375}
1376
1377void AudioProcessorGraph::setNonRealtime (bool isProcessingNonRealtime) noexcept
1378{
1379 const ScopedLock sl (getCallbackLock());
1380
1381 AudioProcessor::setNonRealtime (isProcessingNonRealtime);
1382
1383 for (auto* n : nodes)
1384 n->getProcessor()->setNonRealtime (isProcessingNonRealtime);
1385}
1386
1388bool AudioProcessorGraph::acceptsMidi() const { return true; }
1389bool AudioProcessorGraph::producesMidi() const { return true; }
1392
1393template <typename FloatType, typename SequenceType>
1394static void processBlockForBuffer (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
1395 AudioProcessorGraph& graph,
1396 std::unique_ptr<SequenceType>& renderSequence,
1397 std::atomic<bool>& isPrepared)
1398{
1399 if (graph.isNonRealtime())
1400 {
1401 while (! isPrepared)
1402 Thread::sleep (1);
1403
1404 const ScopedLock sl (graph.getCallbackLock());
1405
1406 if (renderSequence != nullptr)
1407 renderSequence->perform (buffer, midiMessages, graph.getPlayHead());
1408 }
1409 else
1410 {
1411 const ScopedLock sl (graph.getCallbackLock());
1412
1413 if (isPrepared)
1414 {
1415 if (renderSequence != nullptr)
1416 renderSequence->perform (buffer, midiMessages, graph.getPlayHead());
1417 }
1418 else
1419 {
1420 buffer.clear();
1421 midiMessages.clear();
1422 }
1423 }
1424}
1425
1427{
1428 if ((! isPrepared) && MessageManager::getInstance()->isThisTheMessageThread())
1430
1431 processBlockForBuffer<float> (buffer, midiMessages, *this, renderSequenceFloat, isPrepared);
1432}
1433
1435{
1436 if ((! isPrepared) && MessageManager::getInstance()->isThisTheMessageThread())
1438
1439 processBlockForBuffer<double> (buffer, midiMessages, *this, renderSequenceDouble, isPrepared);
1440}
1441
1442//==============================================================================
1447
1451
1453{
1454 switch (type)
1455 {
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;
1461 }
1462
1463 return {};
1464}
1465
1467{
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;
1474
1475 d.deprecatedUid = d.uniqueId = d.name.hashCode();
1476
1477 d.numInputChannels = getTotalNumInputChannels();
1478
1479 if (type == audioOutputNode && graph != nullptr)
1480 d.numInputChannels = graph->getTotalNumInputChannels();
1481
1482 d.numOutputChannels = getTotalNumOutputChannels();
1483
1484 if (type == audioInputNode && graph != nullptr)
1485 d.numOutputChannels = graph->getTotalNumOutputChannels();
1486}
1487
1489{
1490 jassert (graph != nullptr);
1491}
1492
1496
1501
1502template <typename FloatType, typename SequenceType>
1503static void processIOBlock (AudioProcessorGraph::AudioGraphIOProcessor& io, SequenceType& sequence,
1504 AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages)
1505{
1506 switch (io.getType())
1507 {
1509 {
1510 auto&& currentAudioOutputBuffer = sequence.currentAudioOutputBuffer;
1511
1512 for (int i = jmin (currentAudioOutputBuffer.getNumChannels(), buffer.getNumChannels()); --i >= 0;)
1513 currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
1514
1515 break;
1516 }
1517
1519 {
1520 auto* currentInputBuffer = sequence.currentAudioInputBuffer;
1521
1522 for (int i = jmin (currentInputBuffer->getNumChannels(), buffer.getNumChannels()); --i >= 0;)
1523 buffer.copyFrom (i, 0, *currentInputBuffer, i, 0, buffer.getNumSamples());
1524
1525 break;
1526 }
1527
1529 sequence.currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0);
1530 break;
1531
1533 midiMessages.addEvents (*sequence.currentMidiInputBuffer, 0, buffer.getNumSamples(), 0);
1534 break;
1535
1536 default:
1537 break;
1538 }
1539}
1540
1542{
1543 jassert (graph != nullptr);
1544 processIOBlock (*this, *graph->renderSequenceFloat, buffer, midiMessages);
1545}
1546
1548{
1549 jassert (graph != nullptr);
1550 processIOBlock (*this, *graph->renderSequenceDouble, buffer, midiMessages);
1551}
1552
1557
1562
1567
1570
1571#if ! JUCE_AUDIOPROCESSOR_NO_GUI
1574#endif
1575
1579
1582
1585
1587{
1588 graph = newGraph;
1589
1590 if (graph != nullptr)
1591 {
1592 setPlayConfigDetails (type == audioOutputNode ? graph->getTotalNumOutputChannels() : 0,
1593 type == audioInputNode ? graph->getTotalNumInputChannels() : 0,
1594 getSampleRate(),
1595 getBlockSize());
1596
1598 }
1599}
1600
1601} // namespace juce
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
#define noexcept
Definition DistrhoDefines.h:72
virtual void setNonRealtime(bool isNonRealtime) noexcept
Definition AudioProcessor.cpp:72
Definition juce_Array.h:56
int size() const noexcept
Definition juce_Array.h:215
void add(const ElementType &newElement)
Definition juce_Array.h:418
ElementType & getReference(int index) noexcept
Definition juce_Array.h:267
Definition juce_AsyncUpdater.h:39
virtual void handleAsyncUpdate()=0
void triggerAsyncUpdate()
Definition juce_AsyncUpdater.cpp:62
void cancelPendingUpdate() noexcept
Definition juce_AsyncUpdater.cpp:74
Definition juce_AudioSampleBuffer.h:34
Definition juce_AudioPlayHead.h:39
Definition juce_AudioProcessorEditor.h:43
Definition juce_AudioProcessorGraph.h:318
void setStateInformation(const void *data, int sizeInBytes) override
Definition juce_AudioProcessorGraph.cpp:1584
const IODeviceType type
Definition juce_AudioProcessorGraph.h:385
AudioProcessorGraph * graph
Definition juce_AudioProcessorGraph.h:386
int getNumPrograms() override
Definition juce_AudioProcessorGraph.cpp:1576
bool supportsDoublePrecisionProcessing() const override
Definition juce_AudioProcessorGraph.cpp:1497
bool isInput() const noexcept
Definition juce_AudioProcessorGraph.cpp:1568
IODeviceType getType() const noexcept
Definition juce_AudioProcessorGraph.h:340
bool acceptsMidi() const override
Definition juce_AudioProcessorGraph.cpp:1558
const String getProgramName(int) override
Definition juce_AudioProcessorGraph.cpp:1580
bool producesMidi() const override
Definition juce_AudioProcessorGraph.cpp:1563
double getTailLengthSeconds() const override
Definition juce_AudioProcessorGraph.cpp:1553
void prepareToPlay(double newSampleRate, int estimatedSamplesPerBlock) override
Definition juce_AudioProcessorGraph.cpp:1488
IODeviceType
Definition juce_AudioProcessorGraph.h:323
@ midiInputNode
Definition juce_AudioProcessorGraph.h:330
@ audioInputNode
Definition juce_AudioProcessorGraph.h:324
@ audioOutputNode
Definition juce_AudioProcessorGraph.h:327
@ midiOutputNode
Definition juce_AudioProcessorGraph.h:333
void setCurrentProgram(int) override
Definition juce_AudioProcessorGraph.cpp:1578
AudioProcessorEditor * createEditor() override
Definition juce_AudioProcessorGraph.cpp:1573
void getStateInformation(juce::MemoryBlock &destData) override
Definition juce_AudioProcessorGraph.cpp:1583
void setParentGraph(AudioProcessorGraph *)
Definition juce_AudioProcessorGraph.cpp:1586
~AudioGraphIOProcessor() override
Definition juce_AudioProcessorGraph.cpp:1448
void releaseResources() override
Definition juce_AudioProcessorGraph.cpp:1493
bool hasEditor() const override
Definition juce_AudioProcessorGraph.cpp:1572
const String getName() const override
Definition juce_AudioProcessorGraph.cpp:1452
int getCurrentProgram() override
Definition juce_AudioProcessorGraph.cpp:1577
AudioGraphIOProcessor(IODeviceType)
Definition juce_AudioProcessorGraph.cpp:1443
void fillInPluginDescription(PluginDescription &) const override
Definition juce_AudioProcessorGraph.cpp:1466
bool isOutput() const noexcept
Definition juce_AudioProcessorGraph.cpp:1569
void changeProgramName(int, const String &) override
Definition juce_AudioProcessorGraph.cpp:1581
void processBlock(AudioBuffer< float > &, MidiBuffer &) override
Definition juce_AudioProcessorGraph.cpp:1541
Definition juce_AudioProcessorGraph.h:101
CriticalSection processorLock
Definition juce_AudioProcessorGraph.h:179
bool isPrepared
Definition juce_AudioProcessorGraph.h:149
ReferenceCountedObjectPtr< Node > Ptr
Definition juce_AudioProcessorGraph.h:129
Array< Connection > outputs
Definition juce_AudioProcessorGraph.h:148
std::unique_ptr< AudioProcessor > processor
Definition juce_AudioProcessorGraph.h:147
Array< Connection > inputs
Definition juce_AudioProcessorGraph.h:148
void prepare(double newSampleRate, int newBlockSize, AudioProcessorGraph *, ProcessingPrecision)
Definition juce_AudioProcessorGraph.cpp:861
void setBypassed(bool shouldBeBypassed) noexcept
Definition juce_AudioProcessorGraph.cpp:921
void setParentGraph(AudioProcessorGraph *) const
Definition juce_AudioProcessorGraph.cpp:894
bool isBypassed() const noexcept
Definition juce_AudioProcessorGraph.cpp:910
AudioProcessor * getProcessor() const noexcept
Definition juce_AudioProcessorGraph.h:110
std::atomic< bool > bypassed
Definition juce_AudioProcessorGraph.h:150
void unprepare()
Definition juce_AudioProcessorGraph.cpp:883
Node(NodeID, std::unique_ptr< AudioProcessor >) noexcept
Definition juce_AudioProcessorGraph.cpp:855
const NodeID nodeID
Definition juce_AudioProcessorGraph.h:107
friend class AudioProcessorGraph
Definition juce_AudioProcessorGraph.h:133
Definition juce_AudioProcessorGraph.h:48
std::unique_ptr< RenderSequenceFloat > renderSequenceFloat
Definition juce_AudioProcessorGraph.h:443
void reset() override
Definition juce_AudioProcessorGraph.cpp:1369
void releaseResources() override
Definition juce_AudioProcessorGraph.cpp:1354
bool supportsDoublePrecisionProcessing() const override
Definition juce_AudioProcessorGraph.cpp:1339
NodeID lastNodeID
Definition juce_AudioProcessorGraph.h:439
bool isLegal(Node *src, int sourceChannel, Node *dest, int destChannel) const noexcept
Definition juce_AudioProcessorGraph.cpp:1220
bool removeIllegalConnections()
Definition juce_AudioProcessorGraph.cpp:1237
static void getNodeConnections(Node &, std::vector< Connection > &)
Definition juce_AudioProcessorGraph.cpp:1047
bool producesMidi() const override
Definition juce_AudioProcessorGraph.cpp:1389
bool isAnInputTo(Node &source, Node &destination) const noexcept
Definition juce_AudioProcessorGraph.cpp:1100
Node::Ptr removeNode(NodeID)
Definition juce_AudioProcessorGraph.cpp:1019
bool disconnectNode(NodeID)
Definition juce_AudioProcessorGraph.cpp:1201
bool isConnectionLegal(const Connection &) const
Definition juce_AudioProcessorGraph.cpp:1228
void handleAsyncUpdate() override
Definition juce_AudioProcessorGraph.cpp:1305
bool isConnected(const Connection &) const noexcept
Definition juce_AudioProcessorGraph.cpp:1079
bool canConnect(const Connection &) const
Definition juce_AudioProcessorGraph.cpp:1146
bool anyNodesNeedPreparing() const noexcept
Definition juce_AudioProcessorGraph.cpp:1267
void processBlock(AudioBuffer< float > &, MidiBuffer &) override
Definition juce_AudioProcessorGraph.cpp:1426
bool acceptsMidi() const override
Definition juce_AudioProcessorGraph.cpp:1388
PrepareSettings prepareSettings
Definition juce_AudioProcessorGraph.h:446
double getTailLengthSeconds() const override
Definition juce_AudioProcessorGraph.cpp:1387
AudioProcessorGraph()
Definition juce_AudioProcessorGraph.cpp:937
~AudioProcessorGraph() override
Definition juce_AudioProcessorGraph.cpp:941
std::unique_ptr< RenderSequenceDouble > renderSequenceDouble
Definition juce_AudioProcessorGraph.h:444
const String getName() const override
Definition juce_AudioProcessorGraph.cpp:948
ReferenceCountedArray< Node > nodes
Definition juce_AudioProcessorGraph.h:438
Node * getNodeForId(NodeID) const
Definition juce_AudioProcessorGraph.cpp:973
void buildRenderingSequence()
Definition juce_AudioProcessorGraph.cpp:1276
void clearRenderingSequence()
Definition juce_AudioProcessorGraph.cpp:1255
Node::Ptr addNode(std::unique_ptr< AudioProcessor > newProcessor, NodeID nodeId={})
Definition juce_AudioProcessorGraph.cpp:982
void setNonRealtime(bool) noexcept override
Definition juce_AudioProcessorGraph.cpp:1377
@ midiChannelIndex
Definition juce_AudioProcessorGraph.h:78
void setStateInformation(const void *data, int sizeInBytes) override
Definition juce_AudioProcessorGraph.cpp:1391
void unprepare()
Definition juce_AudioProcessorGraph.cpp:1344
void clear()
Definition juce_AudioProcessorGraph.cpp:962
void prepareToPlay(double, int) override
Definition juce_AudioProcessorGraph.cpp:1311
bool addConnection(const Connection &)
Definition juce_AudioProcessorGraph.cpp:1156
std::vector< Connection > getConnections() const
Definition juce_AudioProcessorGraph.cpp:1056
bool removeConnection(const Connection &)
Definition juce_AudioProcessorGraph.cpp:1179
void getStateInformation(juce::MemoryBlock &) override
Definition juce_AudioProcessorGraph.cpp:1390
std::atomic< bool > isPrepared
Definition juce_AudioProcessorGraph.h:450
void topologyChanged()
Definition juce_AudioProcessorGraph.cpp:954
Definition juce_AudioProcessor.h:46
int getTotalNumInputChannels() const noexcept
Definition juce_AudioProcessor.h:733
bool isNonRealtime() const noexcept
Definition juce_AudioProcessor.h:930
void updateHostDisplay(const ChangeDetails &details=ChangeDetails::getDefaultFlags())
Definition juce_AudioProcessor.cpp:425
ProcessingPrecision getProcessingPrecision() const noexcept
Definition juce_AudioProcessor.h:681
ProcessingPrecision
Definition juce_AudioProcessor.h:76
@ singlePrecision
Definition juce_AudioProcessor.h:77
const CriticalSection & getCallbackLock() const noexcept
Definition juce_AudioProcessor.h:860
void setPlayConfigDetails(int numIns, int numOuts, double sampleRate, int blockSize)
Definition juce_AudioProcessor.cpp:343
double getSampleRate() const noexcept
Definition juce_AudioProcessor.h:803
int getTotalNumOutputChannels() const noexcept
Definition juce_AudioProcessor.h:747
AudioPlayHead * getPlayHead() const noexcept
Definition juce_AudioProcessor.h:718
int getBlockSize() const noexcept
Definition juce_AudioProcessor.h:814
void setRateAndBufferSizeDetails(double sampleRate, int blockSize) noexcept
Definition juce_AudioProcessor.cpp:370
void sendChangeMessage()
Definition juce_ChangeBroadcaster.cpp:65
Definition juce_HashMap.h:104
Definition juce_HeapBlock.h:87
Definition juce_MemoryBlock.h:33
static MessageManager * getInstance()
Definition juce_MessageManager.cpp:47
Definition juce_MidiBuffer.h:145
void clear() noexcept
Definition juce_MidiBuffer.cpp:110
void addEvents(const MidiBuffer &otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd)
Definition juce_MidiBuffer.cpp:155
Definition juce_OwnedArray.h:51
Definition juce_PluginDescription.h:43
Definition juce_String.h:53
static void JUCE_CALLTYPE sleep(int milliseconds)
Definition juce_posix_SharedCode.h:44
unsigned * m
Definition inflate.c:1559
unsigned d
Definition inflate.c:940
int g
Definition inflate.c:1573
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
unsigned f
Definition inflate.c:1572
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate)=0
JSAMPIMAGE data
Definition jpeglib.h:945
#define JUCE_LEAK_DETECTOR(OwnerClass)
Definition juce_LeakedObjectDetector.h:138
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE(className)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
float out
Definition lilv_test.c:1461
Definition carla_juce.cpp:31
CriticalSection::ScopedLockType ScopedLock
Definition juce_CriticalSection.h:186
static void processBlockForBuffer(AudioBuffer< FloatType > &buffer, MidiBuffer &midiMessages, AudioProcessorGraph &graph, std::unique_ptr< SequenceType > &renderSequence, std::atomic< bool > &isPrepared)
Definition juce_AudioProcessorGraph.cpp:1394
constexpr Type jmin(Type a, Type b)
Definition juce_MathsFunctions.h:106
constexpr Type jmax(Type a, Type b)
Definition juce_MathsFunctions.h:94
jack_client_t client jack_client_t client jack_client_t client jack_client_t JackInfoShutdownCallback function
Definition juce_linux_JackAudio.cpp:63
static void updateOnMessageThread(AsyncUpdater &updater)
Definition juce_AudioProcessorGraph.cpp:29
static void processIOBlock(AudioProcessorGraph::AudioGraphIOProcessor &io, SequenceType &sequence, AudioBuffer< FloatType > &buffer, MidiBuffer &midiMessages)
Definition juce_AudioProcessorGraph.cpp:1503
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Definition juce_MathsFunctions.h:279
Definition juce_AudioProcessorGraph.h:190
NodeAndChannel source
Definition juce_AudioProcessorGraph.h:204
NodeAndChannel destination
Definition juce_AudioProcessorGraph.h:207
Definition juce_AudioProcessorGraph.h:140
int otherChannel
Definition juce_AudioProcessorGraph.h:142
int thisChannel
Definition juce_AudioProcessorGraph.h:142
Node * otherNode
Definition juce_AudioProcessorGraph.h:141
Definition juce_AudioProcessorGraph.h:85
bool isMIDI() const noexcept
Definition juce_AudioProcessorGraph.h:89
Definition juce_AudioProcessorGraph.h:61
uint32 uid
Definition juce_AudioProcessorGraph.h:65
Definition juce_AudioProcessorGraph.h:420
ProcessingPrecision precision
Definition juce_AudioProcessorGraph.h:421
double sampleRate
Definition juce_AudioProcessorGraph.h:422
int blockSize
Definition juce_AudioProcessorGraph.h:423
bool valid
Definition juce_AudioProcessorGraph.h:424
Definition juce_AudioProcessorGraph.cpp:934
Definition juce_AudioProcessorGraph.cpp:933
Definition juce_AudioProcessorGraph.cpp:41
AudioPlayHead * audioPlayHead
Definition juce_AudioProcessorGraph.cpp:44
int numSamples
Definition juce_AudioProcessorGraph.cpp:45
MidiBuffer * midiBuffers
Definition juce_AudioProcessorGraph.cpp:43
FloatType ** audioBuffers
Definition juce_AudioProcessorGraph.cpp:42
Definition juce_AudioProcessorGraph.cpp:216
int writeIndex
Definition juce_AudioProcessorGraph.cpp:241
DelayChannelOp(int chan, int delaySize)
Definition juce_AudioProcessorGraph.cpp:217
HeapBlock< FloatType > buffer
Definition juce_AudioProcessorGraph.cpp:239
const int channel
Definition juce_AudioProcessorGraph.cpp:240
const int bufferSize
Definition juce_AudioProcessorGraph.cpp:240
int readIndex
Definition juce_AudioProcessorGraph.cpp:241
void perform(const Context &c) override
Definition juce_AudioProcessorGraph.cpp:225
Definition juce_AudioProcessorGraph.cpp:248
void callProcess(AudioBuffer< double > &buffer, MidiBuffer &midiMessages)
Definition juce_AudioProcessorGraph.cpp:312
const int midiBufferToUse
Definition juce_AudioProcessorGraph.cpp:340
AudioBuffer< float > tempBufferFloat
Definition juce_AudioProcessorGraph.cpp:339
const AudioProcessorGraph::Node::Ptr node
Definition juce_AudioProcessorGraph.cpp:334
Array< int > audioChannelsToUse
Definition juce_AudioProcessorGraph.cpp:337
ProcessOp(const AudioProcessorGraph::Node::Ptr &n, const Array< int > &audioChannelsUsed, int totalNumChans, int midiBuffer)
Definition juce_AudioProcessorGraph.cpp:249
void perform(const Context &c) override
Definition juce_AudioProcessorGraph.cpp:264
AudioProcessor & processor
Definition juce_AudioProcessorGraph.cpp:335
HeapBlock< FloatType * > audioChannels
Definition juce_AudioProcessorGraph.cpp:338
AudioBuffer< float > tempBufferDouble
Definition juce_AudioProcessorGraph.cpp:339
const int totalChans
Definition juce_AudioProcessorGraph.cpp:340
void callProcess(AudioBuffer< float > &buffer, MidiBuffer &midiMessages)
Definition juce_AudioProcessorGraph.cpp:290
Definition juce_AudioProcessorGraph.cpp:188
virtual ~RenderingOp()
Definition juce_AudioProcessorGraph.cpp:190
RenderingOp() noexcept
Definition juce_AudioProcessorGraph.cpp:189
virtual void perform(const Context &)=0
Definition juce_AudioProcessorGraph.cpp:39
void addCopyChannelOp(int srcIndex, int dstIndex)
Definition juce_AudioProcessorGraph.cpp:101
void addCopyMidiBufferOp(int srcIndex, int dstIndex)
Definition juce_AudioProcessorGraph.cpp:120
void addClearMidiBufferOp(int index)
Definition juce_AudioProcessorGraph.cpp:115
void createOp(LambdaType &&fn)
Definition juce_AudioProcessorGraph.cpp:201
MidiBuffer * currentMidiInputBuffer
Definition juce_AudioProcessorGraph.cpp:179
void addAddMidiBufferOp(int srcIndex, int dstIndex)
Definition juce_AudioProcessorGraph.cpp:125
void addDelayChannelOp(int chan, int delaySize)
Definition juce_AudioProcessorGraph.cpp:131
AudioBuffer< FloatType > renderingBuffer
Definition juce_AudioProcessorGraph.cpp:176
Array< MidiBuffer > midiBuffers
Definition juce_AudioProcessorGraph.cpp:182
void addProcessOp(const AudioProcessorGraph::Node::Ptr &node, const Array< int > &audioChannelsUsed, int totalNumChans, int midiBuffer)
Definition juce_AudioProcessorGraph.cpp:136
AudioBuffer< FloatType > * currentAudioInputBuffer
Definition juce_AudioProcessorGraph.cpp:177
void prepareBuffers(int blockSize)
Definition juce_AudioProcessorGraph.cpp:142
int numMidiBuffersNeeded
Definition juce_AudioProcessorGraph.cpp:174
MidiBuffer midiChunk
Definition juce_AudioProcessorGraph.cpp:183
MidiBuffer currentMidiOutputBuffer
Definition juce_AudioProcessorGraph.cpp:180
void releaseBuffers()
Definition juce_AudioProcessorGraph.cpp:164
void perform(AudioBuffer< FloatType > &buffer, MidiBuffer &midiMessages, AudioPlayHead *audioPlayHead)
Definition juce_AudioProcessorGraph.cpp:48
void addClearChannelOp(int index)
Definition juce_AudioProcessorGraph.cpp:96
void addAddChannelOp(int srcIndex, int dstIndex)
Definition juce_AudioProcessorGraph.cpp:108
AudioBuffer< FloatType > currentAudioOutputBuffer
Definition juce_AudioProcessorGraph.cpp:176
OwnedArray< RenderingOp > renderOps
Definition juce_AudioProcessorGraph.cpp:196
int numBuffersNeeded
Definition juce_AudioProcessorGraph.cpp:174
Definition juce_AudioProcessorGraph.cpp:380
bool isReadOnlyEmpty() const noexcept
Definition juce_AudioProcessorGraph.cpp:386
static NodeID freeNodeID()
Definition juce_AudioProcessorGraph.cpp:396
void setAssignedToNonExistentNode() noexcept
Definition juce_AudioProcessorGraph.cpp:391
static NodeID anonNodeID()
Definition juce_AudioProcessorGraph.cpp:394
bool isFree() const noexcept
Definition juce_AudioProcessorGraph.cpp:387
static AssignedBuffer createReadOnlyEmpty() noexcept
Definition juce_AudioProcessorGraph.cpp:383
AudioProcessorGraph::NodeAndChannel channel
Definition juce_AudioProcessorGraph.cpp:381
static AssignedBuffer createFree() noexcept
Definition juce_AudioProcessorGraph.cpp:384
void setFree() noexcept
Definition juce_AudioProcessorGraph.cpp:390
bool isAssigned() const noexcept
Definition juce_AudioProcessorGraph.cpp:388
static NodeID zeroNodeID()
Definition juce_AudioProcessorGraph.cpp:395
Definition juce_AudioProcessorGraph.cpp:404
NodeID nodeID
Definition juce_AudioProcessorGraph.cpp:405
int delay
Definition juce_AudioProcessorGraph.cpp:406
Definition juce_AudioProcessorGraph.cpp:350
int findBufferForInputMidiChannel(Node &node, int ourRenderingIndex)
Definition juce_AudioProcessorGraph.cpp:612
int getInputLatencyForNode(NodeID nodeID) const
Definition juce_AudioProcessorGraph.cpp:417
RenderSequence & sequence
Definition juce_AudioProcessorGraph.cpp:375
int getBufferContaining(AudioProcessorGraph::NodeAndChannel output) const noexcept
Definition juce_AudioProcessorGraph.cpp:770
AudioProcessorGraph::NodeID NodeID
Definition juce_AudioProcessorGraph.cpp:372
static auto createOrderedNodeList(const AudioProcessorGraph &graph)
Definition juce_AudioProcessorGraph.cpp:455
AudioProcessorGraph & graph
Definition juce_AudioProcessorGraph.cpp:374
int getNodeDelay(NodeID nodeID) const noexcept
Definition juce_AudioProcessorGraph.cpp:412
@ readOnlyEmptyBufferIndex
Definition juce_AudioProcessorGraph.cpp:401
int totalLatency
Definition juce_AudioProcessorGraph.cpp:410
Array< AssignedBuffer > audioBuffers
Definition juce_AudioProcessorGraph.cpp:399
void markAnyUnusedBuffersAsFree(Array< AssignedBuffer > &buffers, const int stepIndex)
Definition juce_AudioProcessorGraph.cpp:785
static void getAllParentsOfNode(const Node *child, std::unordered_set< Node * > &parents, const std::unordered_map< Node *, std::unordered_set< Node * > > &otherParents)
Definition juce_AudioProcessorGraph.cpp:429
void createRenderingOpsForNode(Node &node, const int ourRenderingIndex)
Definition juce_AudioProcessorGraph.cpp:703
Array< AssignedBuffer > midiBuffers
Definition juce_AudioProcessorGraph.cpp:399
static int getFreeBuffer(Array< AssignedBuffer > &buffers)
Definition juce_AudioProcessorGraph.cpp:760
RenderSequenceBuilder(AudioProcessorGraph &g, RenderSequence &s)
Definition juce_AudioProcessorGraph.cpp:351
Array< AudioProcessorGraph::NodeAndChannel > getSourcesForChannel(Node &node, int inputChannelIndex)
Definition juce_AudioProcessorGraph.cpp:748
bool isBufferNeededLater(int stepIndexToSearchFrom, int inputChannelOfIndexToIgnore, AudioProcessorGraph::NodeAndChannel output) const
Definition juce_AudioProcessorGraph.cpp:792
const Array< Node * > orderedNodes
Definition juce_AudioProcessorGraph.cpp:377
AudioProcessorGraph::Node Node
Definition juce_AudioProcessorGraph.cpp:371
int findBufferForInputAudioChannel(Node &node, const int inputChan, const int ourRenderingIndex, const int maxLatency)
Definition juce_AudioProcessorGraph.cpp:480
HashMap< uint32, int > delays
Definition juce_AudioProcessorGraph.cpp:409
const char const char const char const char char * fn
Definition swell-functions.h:168
int n
Definition crypt.c:458
uch * p
Definition crypt.c:594
return c
Definition crypt.c:175
b
Definition crypt.c:628
int result
Definition process.c:1455
#define const
Definition zconf.h:137