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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_FadeInOutNode.cpp
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
14namespace fade_utils
15{
16 inline int timeToSample (int numSamples, TimeRange editTime, TimePosition t)
17 {
18 return (int) (((t - editTime.getStart()) / editTime.getLength() * numSamples) + 0.5);
19 }
20
21 inline void processSection (choc::buffer::ChannelArrayView<float> audio, TimeRange editTime,
22 TimeRange fadeIn, AudioFadeCurve::Type fadeInType,
23 TimeRange fadeOut, AudioFadeCurve::Type fadeOutType,
24 bool clearExtraSamples)
25 {
26 const auto numSamples = (int) audio.getNumFrames();
27
28 if (editTime.overlaps (fadeIn) && fadeIn.getLength() > 0s)
29 {
30 double alpha1 = 0;
31 auto startSamp = timeToSample (numSamples, editTime, fadeIn.getStart());
32
33 if (startSamp > 0)
34 {
35 if (clearExtraSamples)
36 audio.getStart ((choc::buffer::FrameCount) startSamp).clear();
37 }
38 else
39 {
40 alpha1 = (editTime.getStart() - fadeIn.getStart()) / fadeIn.getLength();
41 startSamp = 0;
42 }
43
44 int endSamp;
45 double alpha2;
46
47 if (editTime.getEnd() >= fadeIn.getEnd())
48 {
49 endSamp = timeToSample (numSamples, editTime, fadeIn.getEnd());
50 alpha2 = 1.0;
51 }
52 else
53 {
54 endSamp = numSamples;
55 alpha2 = std::max (0.0, (editTime.getEnd() - fadeIn.getStart()) / fadeIn.getLength());
56 }
57
58 if (endSamp > startSamp)
59 {
60 auto buffer = tracktion::graph::toAudioBuffer (audio);
61 AudioFadeCurve::applyCrossfadeSection (buffer,
62 startSamp, endSamp - startSamp,
63 fadeInType,
64 (float) alpha1,
65 (float) alpha2);
66 }
67 }
68
69 if (editTime.overlaps (fadeOut) && fadeOut.getLength() > 0s)
70 {
71 double alpha1 = 0;
72 auto startSamp = timeToSample (numSamples, editTime, fadeOut.getStart());
73
74 if (startSamp <= 0)
75 {
76 startSamp = 0;
77 alpha1 = (editTime.getStart() - fadeOut.getStart()) / fadeOut.getLength();
78 }
79
80 int endSamp;
81 double alpha2;
82
83 if (editTime.getEnd() >= fadeOut.getEnd())
84 {
85 endSamp = timeToSample (numSamples, editTime, fadeOut.getEnd());
86 alpha2 = 1.0;
87
88 if (clearExtraSamples && endSamp < numSamples)
89 audio.getEnd ((choc::buffer::FrameCount) endSamp);
90 }
91 else
92 {
93 endSamp = numSamples;
94 alpha2 = (editTime.getEnd() - fadeOut.getStart()) / fadeOut.getLength();
95 }
96
97 if (endSamp > startSamp)
98 {
99 auto buffer = tracktion::graph::toAudioBuffer (audio);
100 AudioFadeCurve::applyCrossfadeSection (buffer,
101 startSamp, endSamp - startSamp,
102 fadeOutType,
103 juce::jlimit (0.0f, 1.0f, (float) (1.0 - alpha1)),
104 juce::jlimit (0.0f, 1.0f, (float) (1.0 - alpha2)));
105 }
106 }
107 }
108}
109
110
111//==============================================================================
112//==============================================================================
113FadeInOutNode::FadeInOutNode (std::unique_ptr<tracktion::graph::Node> inputNode,
114 ProcessState& ps,
115 TimeRange in, TimeRange out,
116 AudioFadeCurve::Type fadeInType_, AudioFadeCurve::Type fadeOutType_,
117 bool clearSamplesOutsideFade)
118 : TracktionEngineNode (ps),
119 input (std::move (inputNode)),
120 fadeIn (in),
121 fadeOut (out),
122 fadeInType (fadeInType_),
123 fadeOutType (fadeOutType_),
124 clearExtraSamples (clearSamplesOutsideFade)
125{
126 jassert (! (fadeIn.isEmpty() && fadeOut.isEmpty()));
127
128 setOptimisations ({ tracktion::graph::ClearBuffers::no,
129 tracktion::graph::AllocateAudioBuffer::yes });
130}
131
132//==============================================================================
133void FadeInOutNode::setDynamicOffsetTime (TimeDuration newOffset)
134{
135 dynamicOffset = newOffset;
136}
137
138tracktion::graph::NodeProperties FadeInOutNode::getNodeProperties()
139{
140 auto props = input->getNodeProperties();
141 props.nodeID = 0;
142
143 return props;
144}
145
146std::vector<tracktion::graph::Node*> FadeInOutNode::getDirectInputNodes()
147{
148 return { input.get() };
149}
150
151bool FadeInOutNode::isReadyToProcess()
152{
153 return input->hasProcessed();
154}
155
156void FadeInOutNode::process (ProcessContext& pc)
157{
158 const auto editTimeRange = getEditTimeRange();
159
160 auto sourceBuffers = input->getProcessedOutput();
161 auto destAudioBlock = pc.buffers.audio;
162 auto& destMidiBlock = pc.buffers.midi;
163 jassert (sourceBuffers.audio.getSize() == destAudioBlock.getSize());
164
165 destMidiBlock.copyFrom (sourceBuffers.midi);
166
167 if (! renderingNeeded (editTimeRange))
168 {
169 // If we don't need to apply the fade, just pass through the buffer
170 setAudioOutput (input.get(), sourceBuffers.audio);
171 return;
172 }
173
174 // Otherwise copy the source in to the dest ready for fading
175 graph::copyIfNotAliased (destAudioBlock, sourceBuffers.audio);
176
177 fade_utils::processSection (destAudioBlock, editTimeRange,
178 fadeIn + dynamicOffset, fadeInType,
179 fadeOut + dynamicOffset, fadeOutType,
180 clearExtraSamples);
181}
182
183bool FadeInOutNode::renderingNeeded (const TimeRange timelineRange)
184{
185 if (! getPlayHead().isPlaying())
186 return false;
187
188 return (fadeIn + dynamicOffset).intersects (timelineRange)
189 || (fadeOut + dynamicOffset).intersects (timelineRange)
190 || (clearExtraSamples && (timelineRange.getStart() <= (fadeIn.getStart() + dynamicOffset)
191 || timelineRange.getEnd() >= (fadeOut.getEnd() + dynamicOffset)));
192}
193
194}} // namespace tracktion { inline namespace engine
Struct to describe a single iteration of a process call.
#define jassert(expression)
typedef int
T max(T... args)
T move(T... args)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Represents a duration in real-life time.
Type
A enumeration of the curve classes available.
Holds some really basic properties of a node.