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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Envelope.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2018
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7*/
8
9
10namespace tracktion { inline namespace engine
11{
12
13//==============================================================================
14// An ADSR Envelope with exponential curves with the same API as the juce::ADSR
16{
17public:
19
20 //==============================================================================
23 {
25 float attack = 0.1f;
26
28 float decay = 0.1f;
29
31 float sustain = 1.0f;
32
34 float release = 0.1f;
35 };
36
44 void setParameters (const Parameters& newParameters)
45 {
46 setAttackTime (newParameters.attack);
47 setDecayTime (newParameters.decay);
48 setSustainLevel (newParameters.sustain);
49 setReleaseTime (newParameters.release);
50 }
51
52 inline void setSampleRate (double d) { sampleRate = d; }
53
54 //==============================================================================
56 inline void reset()
57 {
58 currentState = State::idle;
59 calculateReleaseTime();
60 envelopeVal = 0.0;
61 }
62
64 inline void noteOn() { currentState = State::attack; }
65
67 inline void noteOff()
68 {
69 if (envelopeVal > 0)
70 currentState = State::release;
71 else
72 currentState = State::idle;
73 }
74
76 inline bool isActive() const { return currentState != State::idle; }
77
78 //==============================================================================
83 inline float getNextSample()
84 {
85 switch (currentState)
86 {
87 case State::idle:
88 {
89 envelopeVal = 0.0f;
90 break;
91 }
92 case State::attack:
93 {
94 envelopeVal = attackOffset + envelopeVal * attackCoeff;
95
96 if (envelopeVal >= 1.0f || attackTime <= 0.0f)
97 {
98 envelopeVal = 1.0f;
99 currentState = State::decay;
100 break;
101 }
102 break;
103 }
104 case State::decay:
105 {
106 envelopeVal = decayOffset + envelopeVal * decayCoeff;
107
108 if (envelopeVal <= sustainLevel || decayTime <= 0.0f)
109 {
110 envelopeVal = sustainLevel;
111 currentState = State::sustain;
112 break;
113 }
114 break;
115 }
116 case State::sustain:
117 {
118 envelopeVal = sustainLevel;
119 break;
120 }
121 case State::release:
122 {
123 envelopeVal = releaseOffset + envelopeVal * releaseCoeff;
124
125 if (envelopeVal <= 0.0f || releaseTime <= 0.0f)
126 {
127 envelopeVal = 0.0f;
128 currentState = State::idle;
129 break;
130 }
131 break;
132 }
133 }
134
135 return envelopeVal;
136 }
137
143 template<typename FloatType>
144 void applyEnvelopeToBuffer (juce::AudioBuffer<FloatType>& buffer, int startSample, int numSamples)
145 {
146 jassert (startSample + numSamples <= buffer.getNumSamples());
147
148 auto numChannels = buffer.getNumChannels();
149
150 while (--numSamples >= 0)
151 {
152 auto env = getNextSample();
153
154 for (int i = 0; i < numChannels; ++i)
155 buffer.getWritePointer (i)[startSample] *= env;
156
157 ++startSample;
158 }
159 }
160
161 void setAnalog (bool a)
162 {
163 if (a != analog)
164 {
165 analog = a;
166 if (analog)
167 {
168 attackTCO = std::exp (-0.5f);
169 decayTCO = std::exp (-5.0f);
170 releaseTCO = std::exp (-5.0f);
171 }
172 else
173 {
174 attackTCO = decayTCO = releaseTCO = std::pow (10.0f, -96.0f / 20.0f);
175 }
176 }
177 }
178
179private:
180 double sampleRate = 44100.0;
181
182 float envelopeVal = 0.0f;
183
184 float attackTime = 0.1f, decayTime = 0.1f, releaseTime = 0.1f;
185 float sustainLevel = 0.0f;
186
187 float attackCoeff = 0.0f, attackOffset = 0.0f, attackTCO {std::exp (-0.5f)};
188 float decayCoeff = 0.0f, decayOffset = 0.0f, decayTCO {std::exp (-5.0f)};
189 float releaseCoeff = 0.0f, releaseOffset = 0.0f, releaseTCO {std::exp (-5.0f)};
190
191 bool analog = true;
192
193 enum class State { idle, attack, decay, sustain, release };
194 State currentState = State::idle;
195
196 void calculateAttackTime();
197 void calculateDecayTime();
198 void calculateReleaseTime();
199
200 inline void setAttackTime (float d)
201 {
202 if (! almostEqual (attackTime, d))
203 {
204 attackTime = d;
205 calculateAttackTime();
206 }
207 }
208
209 inline void setDecayTime (float d)
210 {
211 if (! almostEqual (decayTime, d))
212 {
213 decayTime = d;
214 calculateDecayTime();
215 }
216 }
217
218 inline void setReleaseTime (float d)
219 {
220 if (! almostEqual (releaseTime, d))
221 {
222 releaseTime = d;
223 calculateReleaseTime();
224 }
225 }
226
227 inline void setSustainLevel (float d)
228 {
229 if (! almostEqual (sustainLevel, d))
230 {
231 sustainLevel = d;
232 calculateDecayTime();
233 if (currentState != State::release)
234 calculateReleaseTime();
235 }
236 }
237};
238
239//==============================================================================
240// An ADSR Envelope with linear curves with the same API as the juce::ADSR
242{
243public:
244 enum class State { idle, attack, decay, sustain, release };
245
246 //==============================================================================
249 {
251 float attack = 0.1f;
252
254 float decay = 0.1f;
255
257 float sustain = 1.0f;
258
260 float release = 0.1f;
261 };
262
268 void setParameters (const Parameters& newParameters)
269 {
270 currentParameters = newParameters;
271
272 sustainLevel = newParameters.sustain;
273 calculateRates (newParameters);
274 }
275
276 void setSampleRate (double sr) { sampleRate = sr; }
277
278 float getEnvelopeValue() { return envelopeVal; }
279 State getState() { return currentState; }
280
281 void noteOn()
282 {
283 if (attackRate > 0.0f)
284 {
285 currentState = State::attack;
286 }
287 else if (decayRate > 0.0f)
288 {
289 currentState = State::decay;
290 envelopeVal = 1.0f;
291 }
292 else if (sustainLevel > 0.0f)
293 {
294 currentState = State::sustain;
295 envelopeVal = sustainLevel;
296 }
297 else if (releaseRate > 0.0f)
298 {
299 currentState = State::release;
300 }
301 else
302 {
303 currentState = State::idle;
304 envelopeVal = 0.0f;
305 }
306 }
307
308 void noteOff()
309 {
310 if (releaseRate > 0)
311 {
312 currentState = State::release;
313 }
314 else
315 {
316 currentState = State::idle;
317 envelopeVal = 0.0f;
318 }
319 }
320
321 void reset()
322 {
323 currentState = State::idle;
324 envelopeVal = 0.0f;
325 }
326
327 //==============================================================================
332 {
333 if (currentState == State::idle)
334 return 0.0f;
335
336 if (currentState == State::attack)
337 {
338 if (attackRate > 0.0f)
339 envelopeVal += attackRate;
340 else
341 envelopeVal = 1.0f;
342
343 if (envelopeVal >= 1.0f)
344 {
345 envelopeVal = 1.0f;
346
347 if (decayRate > 0.0f)
348 currentState = State::decay;
349 else
350 currentState = State::sustain;
351 }
352 }
353 else if (currentState == State::decay)
354 {
355 if (decayRate > 0.0f)
356 envelopeVal -= decayRate;
357 else
358 envelopeVal = sustainLevel;
359
360 if (envelopeVal <= sustainLevel)
361 {
362 envelopeVal = sustainLevel;
363 currentState = State::sustain;
364 }
365 }
366 else if (currentState == State::sustain)
367 {
368 envelopeVal = sustainLevel;
369 }
370 else if (currentState == State::release)
371 {
372 if (releaseRate > 0.0f)
373 envelopeVal -= releaseRate;
374 else
375 envelopeVal = 0.0f;
376
377 if (envelopeVal <= 0.0f)
378 reset();
379 }
380
381 return envelopeVal;
382 }
383
388 template<typename FloatType>
389 void applyEnvelopeToBuffer (juce::AudioBuffer<FloatType>& buffer, int startSample, int numSamples)
390 {
391 jassert (startSample + numSamples <= buffer.getNumSamples());
392
393 auto numChannels = buffer.getNumChannels();
394
395 while (--numSamples >= 0)
396 {
397 auto env = getNextSample();
398
399 for (int i = 0; i < numChannels; ++i)
400 buffer.getWritePointer (i)[startSample] *= env;
401
402 ++startSample;
403 }
404 }
405
406protected:
407 void calculateRates (const Parameters& parameters)
408 {
409 // need to call setSampleRate() first!
410 jassert (sampleRate > 0.0);
411
412 attackRate = (parameters.attack > 0.0f ? static_cast<float> (1.0f / (parameters.attack * sampleRate)) : 0.0f);
413 decayRate = (parameters.decay > 0.0f ? static_cast<float> (1.0f / (parameters.decay * sampleRate)) : 0.0f);
414 releaseRate = (parameters.release > 0.0f ? static_cast<float> (1.0f / (parameters.release * sampleRate)) : 0.0f);
415 }
416
417 State currentState = State::idle;
418 Parameters currentParameters;
419 double sampleRate = 44100.0;
420 float envelopeVal = 0.0f;
421
422 float sustainLevel = 0.0f;
423 float attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
424};
425
426}} // namespace tracktion { inline namespace engine
Type * getWritePointer(int channelNumber) noexcept
int getNumChannels() const noexcept
int getNumSamples() const noexcept
void noteOff()
Starts the release phase of the envelope.
void reset()
Resets the envelope to an idle state.
bool isActive() const
Returns true if the envelope is in its attack, decay, sustain or release stage.
float getNextSample()
Returns the next sample value for an ADSR object.
void noteOn()
Starts the attack phase of the envelope.
void applyEnvelopeToBuffer(juce::AudioBuffer< FloatType > &buffer, int startSample, int numSamples)
This method will conveniently apply the next numSamples number of envelope values to an AudioBuffer.
void setParameters(const Parameters &newParameters)
Sets the parameters that will be used by an ADSR object.
Holds the parameters being used by an ADSR object.
void applyEnvelopeToBuffer(juce::AudioBuffer< FloatType > &buffer, int startSample, int numSamples)
This method will conveniently apply the next numSamples number of envelope values to an AudioBuffer.
void setParameters(const Parameters &newParameters)
Sets the parameters that will be used by an ADSR object.
float getNextSample()
Returns the next sample value for an ADSR object.
Holds the parameters being used by an ADSR object.
T exp(T... args)
#define jassert(expression)
bool almostEqual(FloatingPointType firstValue, FloatingPointType secondValue, FloatingPointType precision=(FloatingPointType) 0.00001)
Checks to see if two values are equal within a given precision.
T pow(T... args)