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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_TracktionNodePlayer.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
14namespace tracktion { inline namespace engine
15{
16
17//==============================================================================
18//==============================================================================
23{
24public:
26 TracktionNodePlayer (ProcessState& processStateToUse)
27 : playHeadState (processStateToUse.playHeadState),
28 processState (processStateToUse)
29 {
30 }
31
33 TracktionNodePlayer (ProcessState& processStateToUse,
35 juce::AudioWorkgroup audioWorkgroup = {})
36 : playHeadState (processStateToUse.playHeadState),
37 processState (processStateToUse),
38 nodePlayer (std::move (poolCreator), std::move (audioWorkgroup))
39 {
40 }
41
44 ProcessState& processStateToUse,
45 double sampleRate, int blockSize,
47 : TracktionNodePlayer (processStateToUse, std::move (poolCreator))
48 {
49 nodePlayer.setNode (std::move (node), sampleRate, blockSize);
50 }
51
56 void setNumThreads (size_t numThreads)
57 {
58 nodePlayer.setNumThreads (numThreads);
59 }
60
61 tracktion::graph::Node* getNode()
62 {
63 return nodePlayer.getNode();
64 }
65
66 void setNode (std::unique_ptr<tracktion::graph::Node> newNode)
67 {
68 nodePlayer.setNode (std::move (newNode));
69 }
70
71 void setNode (std::unique_ptr<tracktion::graph::Node> newNode, double sampleRateToUse, int blockSizeToUse)
72 {
73 nodePlayer.setNode (std::move (newNode), sampleRateToUse, blockSizeToUse);
74 }
75
76 void prepareToPlay (double sampleRateToUse, int blockSizeToUse)
77 {
78 nodePlayer.prepareToPlay (sampleRateToUse, blockSizeToUse);
79 }
80
85 {
86 int numMisses = 0;
87 playHeadState.playHead.setReferenceSampleRange (pc.referenceSampleRange);
88
89 // Check to see if the timeline needs to be processed in two halves due to looping
90 const auto splitTimelineRange = referenceSampleRangeToSplitTimelineRange (playHeadState.playHead, pc.referenceSampleRange);
91
92 if (splitTimelineRange.isSplit)
93 {
94 const auto firstRangeLength = splitTimelineRange.timelineRange1.getLength();
95
96 numMisses += processReferenceRange (pc, pc.referenceSampleRange.withLength (firstRangeLength));
97 numMisses += processReferenceRange (pc, pc.referenceSampleRange.withStart (pc.referenceSampleRange.getStart() + firstRangeLength));
98 }
99 else
100 {
101 numMisses += processReferenceRange (pc, pc.referenceSampleRange);
102 }
103
104 return numMisses;
105 }
106
109 {
110 nodePlayer.clearNode();
111 }
112
114 double getSampleRate() const
115 {
116 return nodePlayer.getSampleRate();
117 }
118
120 void enablePooledMemoryAllocations (bool enablePooledMemory)
121 {
122 nodePlayer.enablePooledMemoryAllocations (enablePooledMemory);
123 }
124
125 /* @internal. */
126 void enableNodeMemorySharing (bool enableNodeMemorySharing)
127 {
128 nodePlayer.enableNodeMemorySharing (enableNodeMemorySharing);
129 }
130
131private:
132 tracktion::graph::PlayHeadState& playHeadState;
133 ProcessState& processState;
134 MidiMessageArray scratchMidi;
136
137 tracktion::graph::Node::ProcessContext getSubProcessContext (const tracktion::graph::Node::ProcessContext& pc, juce::Range<int64_t> subReferenceSampleRange)
138 {
139 jassert (! pc.referenceSampleRange.isEmpty());
140 jassert (pc.referenceSampleRange.contains (subReferenceSampleRange));
141
142 const auto originalReferenceLength = (double) pc.referenceSampleRange.getLength();
143 const juce::Range<double> proportion ((subReferenceSampleRange.getStart() - pc.referenceSampleRange.getStart()) / originalReferenceLength,
144 (subReferenceSampleRange.getEnd() - pc.referenceSampleRange.getStart()) / originalReferenceLength);
145
146 const auto startSample = (choc::buffer::FrameCount) std::llround (proportion.getStart() * pc.numSamples);
147 const auto endSample = (choc::buffer::FrameCount) std::llround (proportion.getEnd() * pc.numSamples);
148 const choc::buffer::FrameRange sampleRange { startSample, endSample };
149
150 auto destAudio = pc.buffers.audio.getFrameRange (sampleRange);
151
152 return { sampleRange.size(), subReferenceSampleRange, { destAudio, pc.buffers.midi } };
153 }
154
155 int processReferenceRange (const tracktion::graph::Node::ProcessContext& pc, juce::Range<int64_t> referenceSampleRange)
156 {
157 return processTempoChanges (getSubProcessContext (pc, referenceSampleRange));
158 }
159
160 int processTempoChanges (const tracktion::graph::Node::ProcessContext& pc)
161 {
162 int numMisses = 0;
163 playHeadState.playHead.setReferenceSampleRange (pc.referenceSampleRange);
164
165 const auto sampleRate = nodePlayer.getSampleRate();
166 processState.update (sampleRate, pc.referenceSampleRange, ProcessState::UpdateContinuityFlags::no);
167 const auto timeRange = processState.editTimeRange;
168
169 if (auto tempoPosition = processState.getTempoSequencePosition())
170 {
171 double startProportion = 0.0;
172 auto lastEventPosition = timeRange.getStart();
173
174 for (;;)
175 {
176 const auto nextTempoChangePosition = tempoPosition->getTimeOfNextChange();
177
178 if (nextTempoChangePosition == lastEventPosition)
179 break;
180
181 if (! timeRange.contains (nextTempoChangePosition))
182 break;
183
184 const double proportion = (nextTempoChangePosition - timeRange.getStart()) / timeRange.getLength();
185 const auto numSamples = static_cast<decltype(pc.numSamples)> (std::llround (pc.numSamples * proportion));
186 lastEventPosition = nextTempoChangePosition;
187
188 // Min chunk size of 128 samples to avoid large jitter
189 if (numSamples < 128)
190 continue;
191
192 processSubRange (pc, { startProportion, proportion });
193 startProportion = proportion;
194 }
195
196 // Process any remaining buffer proportion
197 if (startProportion < 1.0)
198 processSubRange (pc, { startProportion, 1.0 });
199 }
200 else
201 {
202 processSubRange (pc, { 0.0, 1.0 });
203 }
204
205 return numMisses;
206 }
207
208 int processSubRange (const tracktion::graph::Node::ProcessContext& pc, juce::Range<double> proportion)
209 {
210 assert (pc.numSamples > 0);
211 assert (proportion.getStart() >= 0.0);
212 assert (proportion.getEnd() <= 1.0);
213 const auto sampleRate = nodePlayer.getSampleRate();
214
215 const auto startReferenceSample = pc.referenceSampleRange.getStart() + (int64_t) std::llround (proportion.getStart() * pc.referenceSampleRange.getLength());
216 const auto endReferenceSample = pc.referenceSampleRange.getStart() + (int64_t) std::llround (proportion.getEnd() * pc.referenceSampleRange.getLength());
217 const juce::Range<int64_t> referenceRange (startReferenceSample, endReferenceSample);
218
219 const auto startSample = (choc::buffer::FrameCount) std::llround (proportion.getStart() * pc.numSamples);
220 const auto endSample = (choc::buffer::FrameCount) std::llround (proportion.getEnd() * pc.numSamples);
221 const choc::buffer::FrameRange sampleRange { startSample, endSample };
222
223 if (sampleRange.size() == 0)
224 return 0;
225
226 auto destAudio = pc.buffers.audio.getFrameRange (sampleRange);
227 scratchMidi.clear();
228
229 tracktion::graph::Node::ProcessContext pc2 { sampleRange.size(), referenceRange, { destAudio, scratchMidi } };
230 processState.update (sampleRate, referenceRange, ProcessState::UpdateContinuityFlags::yes);
231 const auto numMisses = nodePlayer.process (pc2);
232
233 // Merge back MIDI from end of block
234 const auto offset = TimeDuration::fromSamples (startReferenceSample - pc.referenceSampleRange.getStart(), sampleRate);
235 pc.buffers.midi.mergeFromWithOffset (scratchMidi, offset.inSeconds());
236
237 return numMisses;
238 }
239};
240
241}} // namespace tracktion { inline namespace engine
assert
constexpr ValueType getStart() const noexcept
constexpr bool isEmpty() const noexcept
constexpr ValueType getEnd() const noexcept
constexpr Range withLength(const ValueType newLength) const noexcept
constexpr ValueType getLength() const noexcept
constexpr bool contains(const ValueType position) const noexcept
constexpr Range withStart(const ValueType newStart) const noexcept
Plays back a Node with PlayHeadState and ProcessState.
void setNumThreads(size_t numThreads)
Sets the number of threads to use for rendering.
TracktionNodePlayer(ProcessState &processStateToUse)
Creates an NodePlayer to process a Node.
void clearNode()
Clears the Node currently playing.
TracktionNodePlayer(ProcessState &processStateToUse, tracktion::graph::LockFreeMultiThreadedNodePlayer::ThreadPoolCreator poolCreator, juce::AudioWorkgroup audioWorkgroup={})
Creates an NodePlayer to process a Node.
TracktionNodePlayer(std::unique_ptr< tracktion::graph::Node > node, ProcessState &processStateToUse, double sampleRate, int blockSize, tracktion::graph::LockFreeMultiThreadedNodePlayer::ThreadPoolCreator poolCreator)
Creates an NodePlayer to process a Node.
double getSampleRate() const
Returns the current sample rate.
int process(const tracktion::graph::Node::ProcessContext &pc)
Processes a block of audio and MIDI data.
void setNode(std::unique_ptr< Node >)
Sets the Node to process.
void enablePooledMemoryAllocations(bool)
Enables or disables the use on an AudioBufferPool to reduce memory consumption.
int process(const Node::ProcessContext &)
Process a block of the Node.
void setNumThreads(size_t)
Sets the number of threads to use for rendering.
void prepareToPlay(double sampleRateToUse, int blockSizeToUse)
Prepares the current Node to be played.
Main graph Node processor class.
Struct to describe a single iteration of a process call.
Determines how this block releates to other previous render blocks and if the play head has jumped in...
void setReferenceSampleRange(juce::Range< int64_t > sampleRange)
Sets the reference sample count, adjusting the timeline if the play head is playing.
#define jassert(expression)
typedef double
SplitTimelineRange referenceSampleRangeToSplitTimelineRange(const PlayHead &playHead, juce::Range< int64_t > referenceSampleRange)
Converts a reference sample range to a TimelinePositionWindow which could have two time ranges if the...
T llround(T... args)
typedef int64_t
Holds the state of a process call.