iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
ADSREnvelope.h
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9 */
10
11#pragma once
12
13#include "IPlugPlatform.h"
14#include "IPlugUtilities.h"
15
16#include <functional>
17#include <cmath>
18
19BEGIN_IPLUG_NAMESPACE
20
21template <typename T>
23{
24public:
25 enum EStage
26 {
27 kReleasedToEndEarly = -3,
28 kReleasedToRetrigger = -2,
29 kIdle = -1,
30 kAttack,
31 kDecay,
32 kSustain,
33 kRelease
34 };
35
36 static constexpr T EARLY_RELEASE_TIME = 20.; // ms
37 static constexpr T RETRIGGER_RELEASE_TIME = 3.; // ms
38 static constexpr T MIN_ENV_TIME_MS = 0.022675736961451; // 1 sample @44100
39 static constexpr T MAX_ENV_TIME_MS = 60000.;
40 static constexpr T ENV_VALUE_LOW = 0.000001; // -120dB
41 static constexpr T ENV_VALUE_HIGH = 0.999;
42
43private:
44#if DEBUG_ENV
45 bool mEnableDBGMSG = false;
46#endif
47
48 const char* mName;
49 T mEarlyReleaseIncr = 0.;
50 T mRetriggerReleaseIncr = 0.;
51 T mAttackIncr = 0.;
52 T mDecayIncr = 0.;
53 T mReleaseIncr = 0.;
54 T mSampleRate;
55 T mEnvValue = 0.; // current normalized value of the envelope
56 int mStage = kIdle; // the current stage
57 T mLevel = 0.; // envelope depth from velocity
58 T mReleaseLevel = 0.; // the level when the env is released
59 T mNewStartLevel = 0.; // envelope depth from velocity when retriggering
60 T mPrevResult = 0.; // last value BEFORE velocity scaling
61 T mPrevOutput = 0.; // last value AFTER velocity scaling
62 T mScalar = 1.; // for key-follow scaling
63 bool mReleased = true;
64 bool mSustainEnabled = true; // when false env is AD only
65
66 std::function<void()> mResetFunc = nullptr; // reset func
67 std::function<void()> mEndReleaseFunc = nullptr; // end release func
68
69public:
74 ADSREnvelope(const char* name = "", std::function<void()> resetFunc = nullptr, bool sustainEnabled = true)
75 : mName(name)
76 , mResetFunc(resetFunc)
77 , mSustainEnabled(sustainEnabled)
78 {
79 SetSampleRate(44100.);
80 }
81
85 void SetStageTime(int stage, T timeMS)
86 {
87 switch(stage)
88 {
89 case kAttack:
90 mAttackIncr = CalcIncrFromTimeLinear(Clip(timeMS, MIN_ENV_TIME_MS, MAX_ENV_TIME_MS), mSampleRate);
91 break;
92 case kDecay:
93 mDecayIncr = CalcIncrFromTimeExp(Clip(timeMS, MIN_ENV_TIME_MS, MAX_ENV_TIME_MS), mSampleRate);
94 break;
95 case kRelease:
96 mReleaseIncr = CalcIncrFromTimeExp(Clip(timeMS, MIN_ENV_TIME_MS, MAX_ENV_TIME_MS), mSampleRate);
97 break;
98 default:
99 //error
100 break;
101 }
102 }
103
105 bool GetBusy() const
106 {
107 return mStage != kIdle;
108 }
109
111 bool GetReleased() const
112 {
113 return mReleased;
114 }
115
118 {
119 return mPrevOutput;
120 }
121
125 inline void Start(T level, T timeScalar = 1.)
126 {
127 mStage = kAttack;
128 mEnvValue = 0.;
129 mLevel = level;
130 mScalar = 1./timeScalar;
131 mReleased = false;
132 }
133
135 inline void Release()
136 {
137 mStage = kRelease;
138 mReleaseLevel = mPrevResult;
139 mEnvValue = 1.;
140 mReleased = true;
141 }
142
146 inline void Retrigger(T newStartLevel, T timeScalar = 1.)
147 {
148 mEnvValue = 1.;
149 mNewStartLevel = newStartLevel;
150 mScalar = 1./timeScalar;
151 mReleaseLevel = mPrevResult;
152 mStage = kReleasedToRetrigger;
153 mReleased = false;
154
155 #if DEBUG_ENV
156 if (mEnableDBGMSG) DBGMSG("retrigger\n");
157 #endif
158 }
159
162 inline void Kill(bool hard)
163 {
164 if(hard)
165 {
166 if (mStage != kIdle)
167 {
168 mReleaseLevel = 0.;
169 mStage = kIdle;
170 mEnvValue = 0.;
171 }
172
173 #if DEBUG_ENV
174 if (mEnableDBGMSG) DBGMSG("hard kill\n");
175 #endif
176 }
177 else
178 {
179 if (mStage != kIdle)
180 {
181 mReleaseLevel = mPrevResult;
182 mStage = kReleasedToEndEarly;
183 mEnvValue = 1.;
184 }
185
186 #if DEBUG_ENV
187 if (mEnableDBGMSG) DBGMSG("soft kill\n");
188 #endif
189 }
190 }
191
195 void SetSampleRate(T sr)
196 {
197 mSampleRate = sr;
198 mEarlyReleaseIncr = CalcIncrFromTimeLinear(EARLY_RELEASE_TIME, sr);
199 mRetriggerReleaseIncr = CalcIncrFromTimeLinear(RETRIGGER_RELEASE_TIME, sr);
200 }
201
205 void SetResetFunc(std::function<void()> func) { mResetFunc = func; }
206
210 void SetEndReleaseFunc(std::function<void()> func) { mEndReleaseFunc = func; }
211
214 inline T Process(T sustainLevel = 0.)
215 {
216 T result = 0.;
217
218 switch(mStage)
219 {
220 case kIdle:
221 result = mEnvValue;
222 break;
223 case kAttack:
224 mEnvValue += (mAttackIncr * mScalar);
225 if (mEnvValue > ENV_VALUE_HIGH || mAttackIncr == 0.)
226 {
227 mStage = kDecay;
228 mEnvValue = 1.;
229 }
230 result = mEnvValue;
231 break;
232 case kDecay:
233 mEnvValue -= ((mDecayIncr*mEnvValue) * mScalar);
234 result = (mEnvValue * (1.-sustainLevel)) + sustainLevel;
235 if (mEnvValue < ENV_VALUE_LOW)
236 {
237 if(mSustainEnabled)
238 {
239 mStage = kSustain;
240 mEnvValue = 1.;
241 result = sustainLevel;
242 }
243 else
244 Release();
245 }
246 break;
247 case kSustain:
248 result = sustainLevel;
249 break;
250 case kRelease:
251 mEnvValue -= ((mReleaseIncr*mEnvValue) * mScalar);
252 if(mEnvValue < ENV_VALUE_LOW || mReleaseIncr == 0.)
253 {
254 mStage = kIdle;
255 mEnvValue = 0.;
256
257 if(mEndReleaseFunc)
258 mEndReleaseFunc();
259 }
260 result = mEnvValue * mReleaseLevel;
261 break;
262 case kReleasedToRetrigger:
263 mEnvValue -= mRetriggerReleaseIncr;
264 if(mEnvValue < ENV_VALUE_LOW)
265 {
266 mStage = kAttack;
267 mLevel = mNewStartLevel;
268 mEnvValue = 0.;
269 mPrevResult = 0.;
270 mReleaseLevel = 0.;
271
272 if(mResetFunc)
273 mResetFunc();
274 }
275 result = mEnvValue * mReleaseLevel;
276 break;
277 case kReleasedToEndEarly:
278 mEnvValue -= mEarlyReleaseIncr;
279 if(mEnvValue < ENV_VALUE_LOW)
280 {
281 mStage = kIdle;
282 mLevel = 0.;
283 mEnvValue = 0.;
284 mPrevResult = 0.;
285 mReleaseLevel = 0.;
286 if(mEndReleaseFunc)
287 mEndReleaseFunc();
288 }
289 result = mEnvValue * mReleaseLevel;
290 break;
291 default:
292 result = mEnvValue;
293 break;
294 }
295
296 mPrevResult = result;
297 mPrevOutput = (result * mLevel);
298 return mPrevOutput;
299 }
300
301private:
302 inline T CalcIncrFromTimeLinear(T timeMS, T sr) const
303 {
304 if (timeMS <= 0.) return 0.;
305 else return (1./sr) / (timeMS/1000.);
306 }
307
308 inline T CalcIncrFromTimeExp(T timeMS, T sr) const
309 {
310 T r;
311
312 if (timeMS <= 0.0) return 0.;
313 else
314 {
315 r = -std::expm1(1000.0 * std::log(0.001) / (sr * timeMS));
316 if (!(r < 1.0)) r = 1.0;
317
318 return r;
319 }
320 }
321};
322
323END_IPLUG_NAMESPACE
Include to get consistently named preprocessor macros for different platforms and logging functionali...
Utility functions and macros.
bool GetBusy() const
Definition: ADSREnvelope.h:105
void SetEndReleaseFunc(std::function< void()> func)
Sets a function to call when the envelope gets released, called when the ramp is at zero WARNING: don...
Definition: ADSREnvelope.h:210
bool GetReleased() const
Definition: ADSREnvelope.h:111
void SetResetFunc(std::function< void()> func)
Sets a function to call when the envelope gets retriggered, called when the fade out ramp is at zero,...
Definition: ADSREnvelope.h:205
void Kill(bool hard)
Kill the envelope.
Definition: ADSREnvelope.h:162
T GetPrevOutput() const
Definition: ADSREnvelope.h:117
void Start(T level, T timeScalar=1.)
Trigger/Start the envelope.
Definition: ADSREnvelope.h:125
void SetSampleRate(T sr)
Set the sample rate for processing, with updates the early release time and retrigger release time co...
Definition: ADSREnvelope.h:195
void Release()
Release the envelope.
Definition: ADSREnvelope.h:135
ADSREnvelope(const char *name="", std::function< void()> resetFunc=nullptr, bool sustainEnabled=true)
Constructs an ADSREnvelope object.
Definition: ADSREnvelope.h:74
T Process(T sustainLevel=0.)
Process the envelope, returning the value according to the current envelope stage.
Definition: ADSREnvelope.h:214
void SetStageTime(int stage, T timeMS)
Sets the time for a particular envelope stage.
Definition: ADSREnvelope.h:85
void Retrigger(T newStartLevel, T timeScalar=1.)
Retrigger the envelope.
Definition: ADSREnvelope.h:146
BEGIN_IPLUG_NAMESPACE T Clip(T x, T lo, T hi)
Clips the value x between lo and hi.