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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Delay.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
14DelayPlugin::DelayPlugin (PluginCreationInfo info) : Plugin (info)
15{
16 feedbackDb = addParam ("feedback", TRANS("Feedback"), { getMinDelayFeedbackDb(), 0.0f },
17 [] (float value) { return juce::Decibels::toString (value); },
18 [] (const juce::String& s) { return dbStringToDb (s); });
19
20 mixProportion = addParam ("mix proportion", TRANS("Mix proportion"), { 0.0f, 1.0f },
21 [] (float value) { return juce::String (juce::roundToInt (value * 100.0f)) + "% wet"; },
22 [] (const juce::String& s) { return s.getFloatValue() / 100.0f; });
23
24 auto um = getUndoManager();
25
26 feedbackValue.referTo (state, IDs::feedback, um, -6.0f);
27 mixValue.referTo (state, IDs::mix, um, 0.3f);
28 lengthMs.referTo (state, IDs::length, um, 150);
29
30 feedbackDb->attachToCurrentValue (feedbackValue);
31 mixProportion->attachToCurrentValue (mixValue);
32}
33
34DelayPlugin::~DelayPlugin()
35{
36 notifyListenersOfDeletion();
37
38 feedbackDb->detachFromCurrentValue();
39 mixProportion->detachFromCurrentValue();
40}
41
42const char* DelayPlugin::xmlTypeName = "delay";
43
44void DelayPlugin::initialise (const PluginInitialisationInfo& info)
45{
46 const int lengthInSamples = (int) (lengthMs * info.sampleRate / 1000.0);
47 delayBuffer.ensureMaxBufferSize (lengthInSamples);
48 delayBuffer.clearBuffer();
49}
50
51void DelayPlugin::deinitialise()
52{
53 delayBuffer.releaseBuffer();
54}
55
56void DelayPlugin::reset()
57{
58 delayBuffer.clearBuffer();
59}
60
61void DelayPlugin::applyToBuffer (const PluginRenderContext& fc)
62{
63 if (fc.destBuffer == nullptr)
64 return;
65
66 SCOPED_REALTIME_CHECK
67
68 const float feedbackGain = feedbackDb->getCurrentValue() > getMinDelayFeedbackDb()
69 ? dbToGain (feedbackDb->getCurrentValue()) : 0.0f;
70
71 const AudioFadeCurve::CrossfadeLevels wetDry (mixProportion->getCurrentValue());
72
73 const int lengthInSamples = (int) (lengthMs * sampleRate / 1000.0);
74 delayBuffer.ensureMaxBufferSize (lengthInSamples);
75
76 const int offset = delayBuffer.bufferPos;
77
78 clearChannels (*fc.destBuffer, 2, -1, fc.bufferStartSample, fc.bufferNumSamples);
79
80 for (int chan = std::min (2, fc.destBuffer->getNumChannels()); --chan >= 0;)
81 {
82 float* const d = fc.destBuffer->getWritePointer (chan, fc.bufferStartSample);
83 float* const buf = (float*) delayBuffer.buffers[chan].getData();
84
85 for (int i = 0; i < fc.bufferNumSamples; ++i)
86 {
87 float* const b = buf + ((i + offset) % lengthInSamples);
88
89 float in = d[i];
90 d[i] = wetDry.gain2 * in + wetDry.gain1 * *b;
91 in += *b * feedbackGain;
93 *b = in;
94 }
95 }
96
97 delayBuffer.bufferPos = (delayBuffer.bufferPos + fc.bufferNumSamples) % lengthInSamples;
98
99 zeroDenormalisedValuesIfNeeded (*fc.destBuffer);
100}
101
102void DelayPlugin::restorePluginStateFromValueTree (const juce::ValueTree& v)
103{
104 copyPropertiesToCachedValues (v, feedbackValue, mixValue, lengthMs);
105
106 for (auto p : getAutomatableParameters())
107 p->updateFromAttachedValue();
108}
109
110#if TRACKTION_UNIT_TESTS && ENGINE_UNIT_TESTS_DELAY_PLUGIN
111
112//==============================================================================
113//==============================================================================
114class DelayPluginTests : public juce::UnitTest
115{
116public:
117 DelayPluginTests() : juce::UnitTest ("DelayPlugin", "Tracktion") {}
118
119 //==============================================================================
120 void runTest() override
121 {
122 runRestoreStateTests();
123 }
124
125private:
126 void runRestoreStateTests()
127 {
128 auto edit = Edit::createSingleTrackEdit (*Engine::getEngines()[0]);
129
130 beginTest ("Delay plugin instantiation");
131 {
132 Plugin::Ptr pluginPtr = edit->getPluginCache().createNewPlugin (DelayPlugin::xmlTypeName, {});
133 auto delay = dynamic_cast<DelayPlugin*> (pluginPtr.get());
134 expect (delay != nullptr);
135 }
136
137 beginTest ("Restore feedback parameter from ValueTree");
138 {
139 Plugin::Ptr pluginPtr = edit->getPluginCache().createNewPlugin (DelayPlugin::xmlTypeName, {});
140 auto delay = dynamic_cast<DelayPlugin*> (pluginPtr.get());
141 expect (delay != nullptr);
142
143 float desiredValue = -30.0f;
144
145 auto preset = createValueTree (IDs::PLUGIN,
146 IDs::type, delay->getPluginName(),
147 IDs::feedback, desiredValue);
148
149 pluginPtr->restorePluginStateFromValueTree (preset);
150 pluginPtr->flushPluginStateToValueTree();
151
152 auto feedbackParam = delay->feedbackDb;
153
154 expectWithinAbsoluteError (feedbackParam->getCurrentExplicitValue(), desiredValue, 0.001f);
155 expectWithinAbsoluteError (feedbackParam->getCurrentValue(), desiredValue, 0.001f);
156 expectWithinAbsoluteError (feedbackParam->getCurrentValue(), feedbackParam->getCurrentExplicitValue(), 0.001f);
157 expect (! pluginPtr->state.hasProperty (IDs::parameters), "State has erroneous parameters property");
158 }
159 }
160};
161
162static DelayPluginTests delayPluginTests;
163
164#endif // TRACKTION_UNIT_TESTS
165
166}} // namespace tracktion { inline namespace engine
Type * getWritePointer(int channelNumber) noexcept
int getNumChannels() const noexcept
static String toString(Type decibels, int decimalPlaces=2, Type minusInfinityDb=Type(defaultMinusInfinitydB), bool shouldIncludeSuffix=true, StringRef customMinusInfinityString={})
float getFloatValue() const noexcept
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
void expectWithinAbsoluteError(ValueType actual, ValueType expected, ValueType maxAbsoluteError, String failureMessage=String())
virtual void runTest()=0
#define TRANS(stringLiteral)
#define JUCE_UNDENORMALISE(x)
typedef int
T min(T... args)
int roundToInt(const FloatType value) noexcept
Passed into Plugins when they are being initialised, to give them useful contextual information that ...
Calculates the two gain multipliers to use for mixing between two sources, given a position alpha fro...
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.