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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_PluginAudioNode.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
11#pragma once
12
13#include "tracktion_AudioNode.h"
14
15
16namespace tracktion { inline namespace engine
17{
18
19//==============================================================================
21{
22public:
23 PluginAudioNode (const Plugin::Ptr& p, AudioNode* in, bool denormalisationNoise)
24 : plugin (p), input (in), applyAntiDenormalisationNoise (denormalisationNoise)
25 {
26 jassert (input != nullptr);
27 jassert (plugin != nullptr);
28 }
29
30 ~PluginAudioNode() override
31 {
32 input = nullptr;
34 }
35
36 void getAudioNodeProperties (AudioNodeProperties& info) override
37 {
38 if (input != nullptr)
39 {
40 input->getAudioNodeProperties (info);
41 }
42 else
43 {
44 info.hasAudio = false;
45 info.hasMidi = false;
46 info.numberOfChannels = 0;
47 }
48
49 info.numberOfChannels = std::max (info.numberOfChannels, plugin->getNumOutputChannelsGivenInputs (info.numberOfChannels));
50 info.hasAudio = info.hasAudio || plugin->producesAudioWhenNoAudioInput();
51 info.hasMidi = info.hasMidi || plugin->takesMidiInput();
52 hasAudioInput = info.hasAudio;
53 hasMidiInput = info.hasMidi;
54 }
55
56 void visitNodes (const VisitorFn& v) override
57 {
58 v (*this);
59
60 if (input != nullptr)
61 input->visitNodes (v);
62 }
63
64 Plugin::Ptr getPlugin() const override { return plugin; }
65
66 bool purgeSubNodes (bool keepAudio, bool keepMidi) override
67 {
68 const bool pluginWantsMidi = plugin->takesMidiInput() || plugin->isSynth();
69
70 return (input != nullptr && input->purgeSubNodes (keepAudio, keepMidi || pluginWantsMidi))
71 || pluginWantsMidi
72 || ! plugin->noTail();
73 }
74
76 {
77 if (! hasInitialised)
78 {
79 hasInitialised = true;
80 plugin->baseClassInitialise ({ TimePosition::fromSeconds (info.startTime), info.sampleRate, info.blockSizeSamples });
81 latencySeconds = plugin->getLatencySeconds();
82
83 if (input != nullptr)
84 input->prepareAudioNodeToPlay (info);
85 }
86 }
87
88 bool isReadyToRender() override
89 {
90 if (input != nullptr)
91 return input->isReadyToRender();
92
93 return true;
94 }
95
96 double getLatencySeconds() const noexcept
97 {
98 return latencySeconds;
99 }
100
102 {
103 if (hasInitialised)
104 {
105 hasInitialised = false;
106
107 if (input != nullptr)
108 input->releaseAudioNodeResources();
109
110 plugin->baseClassDeinitialise();
111 }
112 }
113
114 void renderAdding (const AudioRenderContext& rc) override
115 {
116 if (plugin->isEnabled() && (rc.isRendering || (! plugin->isFrozen())))
117 {
118 callRenderOver (rc);
119 }
120 else
121 {
122 if (rc.didPlayheadJump())
123 plugin->reset();
124
125 if (input != nullptr)
126 input->renderAdding (rc);
127 }
128 }
129
130 void renderOver (const AudioRenderContext& rc) override
131 {
132 if (rc.didPlayheadJump())
133 plugin->reset();
134
135 if (plugin->isEnabled() && (rc.isRendering || (! plugin->isFrozen())))
136 {
137 if (latencySeconds > 0)
138 {
139 AudioRenderContext rc2 (rc);
140 rc2.streamTime = rc2.streamTime + latencySeconds;
141
142 input->renderOver (rc2);
143 renderPlugin (rc2);
144 }
145 else
146 {
147 input->renderOver (rc);
148 renderPlugin (rc);
149 }
150 }
151 else
152 {
153 input->renderOver (rc);
154 }
155 }
156
157 virtual void renderPlugin (const AudioRenderContext& rc)
158 {
159 SCOPED_REALTIME_CHECK
160
161 if (applyAntiDenormalisationNoise)
162 rc.addAntiDenormalisationNoise();
163
164 if (! rc.isContiguousWithPreviousBlock())
165 plugin->updateParameterStreams (TimePosition::fromSeconds (rc.getEditTime().editRange1.getStart()));
166
167 plugin->applyToBufferWithAutomation (createPluginRenderContext (rc));
168 }
169
170 void prepareForNextBlock (const AudioRenderContext& rc) override
171 {
172 plugin->prepareForNextBlock (TimePosition::fromSeconds (rc.getEditTime().editRange1.getStart()));
173 input->prepareForNextBlock (rc);
174 }
175
176protected:
177 Plugin::Ptr plugin;
179
180 bool hasAudioInput = false, hasMidiInput = false, applyAntiDenormalisationNoise = false, hasInitialised = false;
181 double latencySeconds = 0.0;
182
183 PluginRenderContext createPluginRenderContext (const AudioRenderContext& rc)
184 {
185 return { rc.destBuffer, rc.destBufferChannels, rc.bufferStartSample, rc.bufferNumSamples,
186 rc.bufferForMidiMessages, rc.midiBufferOffset,
187 timeRangeFromSeconds (rc.getEditTime().editRange1),
188 rc.playhead.isPlaying(), rc.playhead.isUserDragging(), rc.isRendering,
189 false };
190 }
191
193};
194
195}} // namespace tracktion { inline namespace engine
Base class for nodes in an audio playback graph.
void prepareAudioNodeToPlay(const PlaybackInitialisationInfo &info) override
tells the node to initialise itself ready for playing from the given time.
void releaseAudioNodeResources() override
tells the node that play has stopped, and it can free up anything it no longer needs.
bool purgeSubNodes(bool keepAudio, bool keepMidi) override
Tells the node to delete any sub-nodes that don't produce the required type of output.
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
T max(T... args)
Holds some really basic properties of a node.
Passed into AudioNodes when they are being initialised, to give them useful contextual information th...
TimeRange timeRangeFromSeconds(SourceRangeType)
Creates a TimeRange from a range of seconds.
bool isRendering
True if the rendering is happening as part of an offline render rather than live playback.