73namespace tracktion {
inline namespace graph
81template<
typename NodeType,
typename... Args>
94 choc::buffer::ChannelArrayView<float> view;
95 choc::buffer::ChannelArrayBuffer<float> data;
104 Node* node =
nullptr;
111 return n1.id < n2.id;
117 return n1.node == n2.node && n1.id == n2.id;
147 bool enableNodeMemorySharing =
false;
153 bool hasAudio =
false;
154 bool hasMidi =
false;
155 int numberOfChannels = 0;
156 int latencyNumSamples = 0;
197 void cacheProperty (
size_t key, T value);
200 T* getCachedProperty (
size_t key);
220 virtual ~Node() =
default;
247 choc::buffer::ChannelArrayView<float> audio;
276 return TransformResult::none;
304 choc::buffer::FrameCount numSamples;
324 void* internal =
nullptr;
325 int numOutputNodes = -1;
326 virtual size_t getAllocatedBytes()
const;
327 void enablePreProcess (
bool);
376 choc::buffer::Size audioBufferSize;
377 choc::buffer::ChannelArrayBuffer<float> audioBuffer;
378 choc::buffer::ChannelArrayView<float> audioView, allocatedView;
382 NodeOptimisations nodeOptimisations;
387 std::function<NodeBuffer (choc::buffer::Size)> allocateAudioBuffer =
nullptr;
388 std::function<void (NodeBuffer&&)> deallocateAudioBuffer =
nullptr;
403template<
typename Visitor>
404void visitNodes (Node&, Visitor&&,
bool preordering);
415 bfsReversePreordering
434 bool needToTransformAgain =
false;
436 auto allNodes = getNodes (rootNode, VertexOrdering::postordering);
439 for (
auto node : allNodes)
441 const auto res = node->transform (rootNode, allNodes, cache);
443 if (res == TransformResult::none)
446 needToTransformAgain =
true;
449 if (res == TransformResult::nodesDeleted)
453 assert (res == TransformResult::connectionsMade);
456 if (! needToTransformAgain)
469 audioBufferSize = choc::buffer::Size::create ((choc::buffer::ChannelCount) props.numberOfChannels,
470 (choc::buffer::FrameCount) info.blockSize);
472 if (info.allocateAudioBuffer)
474 allocateAudioBuffer = info.allocateAudioBuffer;
475 deallocateAudioBuffer = info.deallocateAudioBuffer;
477 else if (nodeOptimisations.allocate == AllocateAudioBuffer::yes)
479 audioBuffer.resize (audioBufferSize);
488 if (retainCount == 0)
495 for (
auto& n : directInputNodes)
506 assert (! isBeingProcessed);
507 isBeingProcessed =
true;
509 for (
auto n : directInputNodes)
510 assert (n->hasProcessed());
513 preProcess (numSamples, referenceSampleRange);
516 if (allocateAudioBuffer)
518 auto nodeBuffer = allocateAudioBuffer (audioBufferSize);
519 audioBuffer = std::move (nodeBuffer.data);
520 allocatedView = std::move (nodeBuffer.view);
521 assert (audioBufferSize == allocatedView.getSize());
524 if (nodeOptimisations.clear == ClearBuffers::yes)
530 const auto numChannelsBeforeProcessing = audioBuffer.getNumChannels();
531 const auto numSamplesBeforeProcessing = audioBuffer.getNumFrames();
535 jassert (numChannelsBeforeProcessing == 0 || numSamples <= audioBuffer.getNumFrames());
537 if (allocatedView.getSize() == audioBufferSize)
540 audioView = allocatedView;
545 audioView = ((nodeOptimisations.allocate == AllocateAudioBuffer::yes ? audioBuffer.getView()
546 : referencedViewToUse ? referencedViewToUse->getFirstChannels (audioBufferSize.numChannels)
547 : choc::buffer::ChannelArrayView<float> { {}, audioBufferSize }));
550 audioView = audioView.getStart (numSamples);
552 auto destAudioView = audioView;
553 ProcessContext pc { numSamples, referenceSampleRange, { destAudioView, midiBuffer } };
557 jassert (numChannelsBeforeProcessing == audioBuffer.getNumChannels());
558 jassert (numSamplesBeforeProcessing == audioBuffer.getNumFrames());
562 for (
auto& n : directInputNodes)
566 jassert (destAudioView.getSize() == audioView.getSize());
569 isBeingProcessed =
false;
587 jassert (node->hasProcessed());
594inline size_t Node::getAllocatedBytes()
const
596 return audioBuffer.getView().data.getBytesNeeded (audioBuffer.getSize())
597 + (
size_t (midiBuffer.size()) *
sizeof (tracktion_engine::MidiMessageArray::MidiMessageWithSource));
602 nodeOptimisations = newOptimisations;
613 referencedViewToUse = view;
620 assert (sourceNode == node);
628 audioView = newAudioView;
646 if (deallocateAudioBuffer)
647 deallocateAudioBuffer ({ std::move (allocatedView), std::move (audioBuffer) });
657 template<
typename Visitor>
658 static void visit (
std::vector<Node*>& visitedNodes,
Node& visitingNode, Visitor&& visitor,
bool preordering)
666 visitor (visitingNode);
670 visit (visitedNodes, *n, visitor, preordering);
675 visitor (visitingNode);
682 template<
typename Visitor>
688 visitor (visitingNode);
694 for (
auto n : inputs)
699 visitor (visitingNode);
703 for (
auto n : inputs)
704 visit (visitedNodes, *n, visitor);
709template<
typename Visitor>
713 detail::VisitNodesWithRecord::visit (visitedNodes, node, visitor, preordering);
716template<
typename Visitor>
717inline void visitNodesBFS (
Node& node, Visitor&& visitor)
720 detail::VisitNodesWithRecordBFS::visit (visitedNodes, node, visitor);
725 if (vertexOrdering == VertexOrdering::bfsPreordering
726 || vertexOrdering == VertexOrdering::bfsReversePreordering)
729 detail::VisitNodesWithRecordBFS::visit (visitedNodes, node, [](
auto&){});
731 if (vertexOrdering == VertexOrdering::bfsReversePreordering)
737 bool preordering = vertexOrdering == VertexOrdering::preordering
738 || vertexOrdering == VertexOrdering::reversePreordering;
741 detail::VisitNodesWithRecord::visit (visitedNodes, node, [](
auto&){}, preordering);
743 if (vertexOrdering == VertexOrdering::reversePreordering
744 || vertexOrdering == VertexOrdering::reversePostordering)
754 for (
auto internalNode : n.getInternalNodes())
755 addNodesRecursive (nodeMap, *internalNode);
763 addNodesRecursive (nodeMap, *n);
774 assert (rootNode !=
nullptr);
775 auto orderedNodes = transformNodes (*rootNode);
776 auto sortedNodes = createNodeMap (orderedNodes);
779 for (
auto node : orderedNodes)
780 node->numOutputNodes = 0;
782 for (
auto node : orderedNodes)
783 for (
auto inputNode : node->getDirectInputNodes())
784 ++inputNode->numOutputNodes;
787 nodeGraph->rootNode = std::move (rootNode);
788 nodeGraph->orderedNodes = std::move (orderedNodes);
789 nodeGraph->sortedNodes = std::move (sortedNodes);
795inline void TransformCache::cacheProperty (
size_t key, T value)
797 cache[key] = std::move (value);
801inline T* TransformCache::getCachedProperty (
size_t key)
803 if (
auto found = cache.find (key); found != cache.end())
Main graph Node processor class.
virtual std::vector< Node * > getDirectInputNodes()
Should return all the inputs directly feeding in to this node.
virtual std::vector< Node * > getInternalNodes()
Can return Nodes that are internal to this Node but don't make up the main graph constructed from get...
virtual void prepareToPlay(const PlaybackInitialisationInfo &)
Called once before playback begins for each node.
void process(choc::buffer::FrameCount numSamples, juce::Range< int64_t > referenceSampleRange)
Call to process the node, which will in turn call the process method with the buffers to fill.
void setAudioOutput(Node *sourceNode, const choc::buffer::ChannelArrayView< float > &)
This can be called during your process function to set a view to the output.
virtual NodeProperties getNodeProperties()=0
Should return the properties of the node.
virtual bool isReadyToProcess()=0
Should return true when this node is ready to be processed.
void retain()
Retains the buffers so they won't be deallocated after the Node has processed.
virtual void process(ProcessContext &)=0
Called when the node is to be processed.
virtual void prefetchBlock(juce::Range< int64_t >)
Called before once on all Nodes before they are processed.
void prepareForNextBlock(juce::Range< int64_t > referenceSampleRange)
Call before processing the next block, used to reset the process status.
void setOptimisations(NodeOptimisations)
This can be called to provide some hints about allocating or playing back a Node to improve efficienc...
void setBufferViewToUse(Node *sourceNode, const choc::buffer::ChannelArrayView< float > &)
This can be called during prepareToPlay to set a BufferView to use which can improve efficiency.
bool hasProcessed() const
Returns true if this node has processed and its outputs can be retrieved.
void release()
Releases the buffers allowing internal storage to be deallocated.
void initialise(const PlaybackInitialisationInfo &)
Call once after the graph has been constructed to initialise buffers etc.
AudioAndMidiBuffer getProcessedOutput()
Returns the processed audio and MIDI output.
virtual TransformResult transform(Node &, const std::vector< Node * > &, TransformCache &)
Called after construction to give the node a chance to modify its topology.
virtual void preProcess(choc::buffer::FrameCount, juce::Range< int64_t >)
Called when the node is to be processed, just before process.
Contains the buffers for a processing operation.
Struct to describe a single iteration of a process call.
void ignoreUnused(Types &&...) noexcept
void visitNodes(Node &, Visitor &&, bool preordering)
Should call the visitor for any direct inputs to the node exactly once.
VertexOrdering
Specifies the ordering algorithm.
std::unique_ptr< Node > makeNode(Args &&... args)
Creates a node of the given type and returns it as the base Node class.
TransformResult
Enum to signify the result of the transform function.
@ connectionsMade
No transform has been made.
@ nodesDeleted
New connections have been made.
std::unique_ptr< NodeGraph > createNodeGraph(std::unique_ptr< Node >)
Transforms a Node and then returns a NodeGraph of it ready to be initialised.
A Node and its ID cached for quick lookup (without having to traverse the graph).
Holds a view over some data and optionally some storage for that data.
Holds a graph in an order ready for processing and a sorted map for quick lookups.
Holds some hints that might be used by the Node or players to improve efficiency.
Holds some really basic properties of a node.
Passed into Nodes when they are being initialised, to give them useful contextual information that th...