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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_LatencyPlugin.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//==============================================================================
14//==============================================================================
18{
19public:
20 //==============================================================================
22 DelayRegister() = default;
23
27 void setMaxSize (int maxSize)
28 {
29 if (++maxSize != maxBufferSize)
30 {
31 bufferIndex = 0;
32 buffer.malloc ((size_t) maxSize);
33 maxBufferSize = maxSize;
34 bufferSize = std::min (bufferSize, maxBufferSize);
35 }
36
37 clear();
38 }
39
41 void clear() noexcept
42 {
43 buffer.clear ((size_t) maxBufferSize);
44 }
45
47 void setSize (int newSize)
48 {
49 lastBufferSize = bufferSize;
50 jassert (newSize < maxBufferSize);
51
52 if (newSize < bufferSize)
53 bufferIndex = 0;
54
55 bufferSize = std::min (maxBufferSize, newSize);
56 }
57
59 int getSize() const
60 {
61 return bufferSize;
62 }
63
65 void processSamples (float* samples, int numSamples)
66 {
67 for (int i = 0; i < numSamples; ++i)
68 {
69 ++bufferWritePos;
70 bufferWritePos = bufferWritePos % maxBufferSize;
71 buffer[bufferWritePos] = samples[i];
72
73 int bufferReadPos = bufferWritePos - bufferSize;
74
75 if (bufferReadPos < 0)
76 bufferReadPos += maxBufferSize;
77
78 samples[i] = buffer[bufferReadPos];
79 }
80 }
81
83 void processSamplesSmoothed (float* samples, int numSamples)
84 {
85 if (bufferSize == lastBufferSize)
86 {
87 processSamples (samples, numSamples);
88 return;
89 }
90
91 const float gainDelta = 1.0f / numSamples;
92 float newGain = 0.0f;
93 float oldGain = 1.0f;
94
95 for (int i = 0; i < numSamples; ++i)
96 {
97 ++bufferWritePos;
98 bufferWritePos = bufferWritePos % maxBufferSize;
99 buffer[bufferWritePos] = samples[i];
100
101 int bufferReadPos = bufferWritePos - bufferSize;
102 int bufferReadOld = bufferWritePos - lastBufferSize;
103
104 if (bufferReadPos < 0) bufferReadPos += maxBufferSize;
105 if (bufferReadOld < 0) bufferReadOld += maxBufferSize;
106
107 const float newSample = buffer[(int) bufferReadPos];
108 const float oldSample = buffer[(int) bufferReadOld];
109
110 samples[i] = (newSample * newGain) + (oldSample * oldGain);
111
112 newGain += gainDelta;
113 oldGain -= gainDelta;
114 }
115
116 lastBufferSize = bufferSize;
117 }
118
119private:
120 //==============================================================================
122 int bufferIndex = 0, maxBufferSize = 0, bufferWritePos = 0;
123 int bufferSize = 0, lastBufferSize = 0;
124
126};
127
128//==============================================================================
129//==============================================================================
130const char* LatencyPlugin::xmlTypeName ("latencyTester");
131
132LatencyPlugin::LatencyPlugin (PluginCreationInfo info)
133 : Plugin (info)
134{
135 delayCompensator[0] = std::make_unique<DelayRegister>();
136 delayCompensator[1] = std::make_unique<DelayRegister>();
137
138 latencyTimeSeconds.setConstrainer ([] (float in) { return juce::jlimit (0.0f, 5.0f, in); });
139 latencyTimeSeconds.referTo (state, IDs::time, getUndoManager(), 0.0f);
140 applyLatency.referTo (state, IDs::apply, getUndoManager(), true);
141
142 playbackRestartTimer.setCallback ([this]
143 {
144 edit.restartPlayback();
145 playbackRestartTimer.stopTimer();
146 });
147}
148
149LatencyPlugin::~LatencyPlugin()
150{
151 notifyListenersOfDeletion();
152}
153
154juce::ValueTree LatencyPlugin::create()
155{
156 return createValueTree (IDs::PLUGIN,
157 IDs::type, xmlTypeName);
158}
159
160void LatencyPlugin::initialise (const PluginInitialisationInfo&)
161{
162 jassert (sampleRate > 0.0);
163
164 const int maxDelaySamples = (int) std::ceil (5.0 * sampleRate);
165
166 for (int i = juce::numElementsInArray (delayCompensator); --i >= 0;)
167 delayCompensator[i]->setMaxSize (maxDelaySamples);
168}
169
171{
172 for (int i = juce::numElementsInArray (delayCompensator); --i >= 0;)
173 delayCompensator[i]->clear();
174}
175
177{
178 if (! applyLatency.get())
179 return;
180
181 if (rc.destBuffer == nullptr)
182 return;
183
184 const int delayCompensationSamples = juce::roundToInt (latencyTimeSeconds.get() * (float) sampleRate);
185
186 if (delayCompensationSamples != 0)
187 {
188 const int numChannels = std::min (rc.destBuffer->getNumChannels(),
189 juce::numElementsInArray (delayCompensator));
190 float* const* samples = rc.destBuffer->getArrayOfWritePointers();
191
192 for (int i = 0; i < numChannels; ++i)
193 {
194 delayCompensator[i]->setSize (delayCompensationSamples);
195 delayCompensator[i]->processSamples (samples[i] + rc.bufferStartSample, rc.bufferNumSamples);
196 }
197 }
198}
199
200void LatencyPlugin::restorePluginStateFromValueTree (const juce::ValueTree& v)
201{
202 if (v.hasProperty (IDs::time))
203 latencyTimeSeconds = v[IDs::time];
204
205 if (v.hasProperty (IDs::apply))
206 applyLatency = v[IDs::apply];
207}
208
209void LatencyPlugin::valueTreePropertyChanged (juce::ValueTree& v, const juce::Identifier& i)
210{
211 if (v == state && i == IDs::time)
212 playbackRestartTimer.startTimer (50);
213
214 Plugin::valueTreePropertyChanged (v, i);
215}
216
217}} // namespace tracktion { inline namespace engine
T ceil(T... args)
int getNumChannels() const noexcept
Type *const * getArrayOfWritePointers() noexcept
Type get() const noexcept
void clear(SizeType numElements) noexcept
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
Type get() const noexcept
Returns the current value of the property.
DelayRegister()=default
Creates an empty DelayRegister.
void setMaxSize(int maxSize)
Sets the max size of the register.
void processSamplesSmoothed(float *samples, int numSamples)
Processes a number of samples, crossfading the current and last read positions to avoid glitches.
void processSamples(float *samples, int numSamples)
Processes a number of samples, replacing the contents with delayed samples.
void setSize(int newSize)
Sets the size of delay in samples.
void applyToBuffer(const PluginRenderContext &) override
Process the next block of data.
void deinitialise() override
Called after play stops to release resources.
T is_pointer_v
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
typedef int
T min(T... args)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
int roundToInt(const FloatType value) noexcept
constexpr int numElementsInArray(Type(&)[N]) noexcept
Passed into Plugins when they are being initialised, to give them useful contextual information that ...
The context passed to plugin render methods to provide it with buffers to fill.
int bufferNumSamples
The number of samples to write into the audio buffer.
juce::AudioBuffer< float > * destBuffer
The target audio buffer which needs to be filled.
int bufferStartSample
The index of the start point in the audio buffer from which data must be written.