tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_LevelMeasurerProcessingNode.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
18{
19public:
21 : input (std::move (inputNode)),
22 meterPlugin (levelMeterPlugin)
23 {
24 }
25
27 {
28 if (isInitialised && ! meterPlugin.baseClassNeedsInitialising())
29 meterPlugin.baseClassDeinitialise();
30 }
31
33 {
34 return input->getNodeProperties();
35 }
36
38 {
39 return { input.get() };
40 }
41
42 bool isReadyToProcess() override
43 {
44 return input->hasProcessed();
45 }
46
48 {
49 initialisePlugin();
50
51 const auto inputProps = input->getNodeProperties();
52 const auto nodeProps = getNodeProperties();
53
54 if (info.enableNodeMemorySharing && input->numOutputNodes == 1)
55 {
56 const auto inputNumChannels = inputProps.numberOfChannels;
57 const auto desiredNumChannels = nodeProps.numberOfChannels;
58
59 if (inputNumChannels >= desiredNumChannels)
60 {
61 canUseSourceBuffers = true;
62 setOptimisations ({ tracktion::graph::ClearBuffers::no,
63 tracktion::graph::AllocateAudioBuffer::no });
64 }
65 }
66
67 const int latencyAtRoot = info.nodeGraph.rootNode->getNodeProperties().latencyNumSamples;
68 const int latencyAtInput = inputProps.latencyNumSamples;
69
70 const int numSamplesLatencyToIntroduce = latencyAtRoot - latencyAtInput;
71
72 if (numSamplesLatencyToIntroduce <= 0)
73 return;
74
76 latencyProcessor->setLatencyNumSamples (numSamplesLatencyToIntroduce);
77 latencyProcessor->prepareToPlay (info.sampleRate, info.blockSize, nodeProps.numberOfChannels);
78
79 tempAudioBuffer.resize ({ (choc::buffer::ChannelCount) nodeProps.numberOfChannels,
80 (choc::buffer::FrameCount) info.blockSize });
81 }
82
83 void preProcess (choc::buffer::FrameCount, juce::Range<int64_t>) override
84 {
85 if (canUseSourceBuffers)
86 setBufferViewToUse (input.get(), input->getProcessedOutput().audio);
87 }
88
89 void process (ProcessContext& pc) override
90 {
91 // Copy the input buffers to the outputs without applying latency
92 auto sourceBuffers = input->getProcessedOutput();
93 jassert (sourceBuffers.audio.getNumChannels() == pc.buffers.audio.getNumChannels());
94
95 // If the source only outputs to this node, we can steal its data
96 if (input->numOutputNodes == 1)
97 {
98 pc.buffers.midi.swapWith (sourceBuffers.midi);
99 setAudioOutput (input.get(), sourceBuffers.audio);
100 }
101 else
102 {
103 pc.buffers.midi.copyFrom (sourceBuffers.midi);
104 copyIfNotAliased (pc.buffers.audio, sourceBuffers.audio);
105 }
106
107 // If we have no latency, simply process the meter
108 if (! latencyProcessor)
109 {
110 processLevelMeasurer (meterPlugin.measurer, sourceBuffers.audio, pc.buffers.midi);
111 return;
112 }
113
114 // Otherwise, pass the buffers through the latency processor and process the meter
115 const auto numFrames = sourceBuffers.audio.getNumFrames();
116
117 latencyProcessor->writeAudio (sourceBuffers.audio);
118 latencyProcessor->writeMIDI (sourceBuffers.midi);
119
120 tempMidiBuffer.clear();
121
122 auto tempBlock = tempAudioBuffer.getStart (numFrames);
123 latencyProcessor->readAudioOverwriting (tempBlock);
124 latencyProcessor->readMIDI (tempMidiBuffer, (int) numFrames);
125
126 processLevelMeasurer (meterPlugin.measurer, tempBlock, tempMidiBuffer);
127 }
128
129private:
131 LevelMeterPlugin& meterPlugin;
132 Plugin::Ptr pluginPtr { meterPlugin };
133 bool isInitialised = false, canUseSourceBuffers = false;
134
136
137 choc::buffer::ChannelArrayBuffer<float> tempAudioBuffer;
138 MidiMessageArray tempMidiBuffer;
139
140 //==============================================================================
141 void initialisePlugin()
142 {
143 // N.B. This is deliberately zeroed as it (correctly) assumes the LevelMeterPlugin doesn't need the info during initialisation
144 meterPlugin.baseClassInitialise ({ 0_tp, 0.0, 0 });
145 isInitialised = true;
146 }
147
148 void processLevelMeasurer (LevelMeasurer& measurer, choc::buffer::ChannelArrayView<float> block, MidiMessageArray& midi)
149 {
150 auto buffer = tracktion::graph::toAudioBuffer (block);
151 measurer.processBuffer (buffer, 0, buffer.getNumSamples());
152
153 measurer.setShowMidi (meterPlugin.showMidiActivity);
154 measurer.processMidi (midi, nullptr);
155 }
156};
157
158}} // namespace tracktion { inline namespace engine
A Node that introduces latency to balance the latency at the root Node and its position in the graph.
void process(ProcessContext &pc) override
Called when the node is to be processed.
bool isReadyToProcess() override
Should return true when this node is ready to be processed.
void preProcess(choc::buffer::FrameCount, juce::Range< int64_t >) override
Called when the node is to be processed, just before process.
void prepareToPlay(const tracktion::graph::PlaybackInitialisationInfo &info) override
Called once before playback begins for each node.
tracktion::graph::NodeProperties getNodeProperties() override
Should return the properties of the node.
std::vector< tracktion::graph::Node * > getDirectInputNodes() override
Should return all the inputs directly feeding in to this node.
Main graph Node processor class.
void setAudioOutput(Node *sourceNode, const choc::buffer::ChannelArrayView< float > &)
This can be called during your process function to set a view to the output.
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.
Struct to describe a single iteration of a process call.
T get(T... args)
T is_pointer_v
#define jassert(expression)
Holds some really basic properties of a node.
Passed into Nodes when they are being initialised, to give them useful contextual information that th...