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