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