1 /** 2 Attack and release basic smoother. 3 4 Copyright: Guillaume Piolat 2015-2022. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module ar; 8 9 import std.math: isFinite; 10 import dplug.core.math; 11 import dplug.core.ringbuf; 12 import dplug.core.nogc; 13 import dplug.core.vec; 14 15 struct AttackRelease(T) if (is(T == float) || is(T == double)) 16 { 17 public: 18 /// time: the time constant of the smoother. 19 /// threshold: absolute difference below which we consider current value and target equal 20 void initialize(float sampleRate, float timeAttackSecs, float timeReleaseSecs, T initialValue) nothrow @nogc 21 { 22 assert(isFinite(initialValue)); 23 _sampleRate = sampleRate; 24 _current = cast(T)(initialValue); 25 setAttackTime(timeAttackSecs); 26 setReleaseTime(timeReleaseSecs); 27 } 28 29 /// Changes attack time (given in seconds). 30 void setAttackTime(float timeAttackSecs) nothrow @nogc 31 { 32 _expFactorAttack = cast(T)(expDecayFactor(timeAttackSecs, _sampleRate)); 33 } 34 35 /// Changes release time (given in seconds). 36 void setReleaseTime(float timeReleaseSecs) nothrow @nogc 37 { 38 _expFactorRelease = cast(T)(expDecayFactor(timeReleaseSecs, _sampleRate)); 39 } 40 41 /// Advance smoothing and return the next smoothed sample with respect 42 /// to tau time and samplerate. 43 T nextSample(T target) nothrow @nogc 44 { 45 T diff = target - _current; 46 if (diff != 0) 47 { 48 if (fast_fabs(diff) < 1e-10f) // to avoid subnormal, and excess churn 49 { 50 _current = target; 51 } 52 else 53 { 54 double expFactor = (diff > 0) ? _expFactorAttack : _expFactorRelease; 55 double temp = _current + diff * expFactor; // Is double-precision really needed here? 56 T newCurrent = cast(T)(temp); 57 _current = newCurrent; 58 } 59 } 60 return _current; 61 } 62 63 void nextBuffer(const(T)* input, T* output, int frames) 64 { 65 for (int i = 0; i < frames; ++i) 66 { 67 output[i] = nextSample(input[i]); 68 } 69 } 70 71 void nextBuffer(T input, T* output, int frames) 72 { 73 for (int i = 0; i < frames; ++i) 74 { 75 output[i] = nextSample(input); 76 } 77 } 78 79 private: 80 T _current; 81 T _expFactorAttack; 82 T _expFactorRelease; 83 float _sampleRate; 84 } 85