1 /**
2 * Copyright 2017 Cut Through Recordings
3 * License: MIT License
4 * Author(s): Ethan Reker
5 */
6 module ddsp.effect.expander;
7 
8 import ddsp.util.functions;
9 import ddsp.effect.dynamics;
10 
11 import dplug.core.nogc;
12 
13 import std.algorithm;
14 import std.math;
15 
16 /// Basic Expander
17 class Expander : DynamicsProcessor
18 {
19 public:
20 nothrow:
21 @nogc:
22     
23     override float getNextSample(const float input)
24     {
25         detector.detect(input);
26         float detectorValue = floatToDecibel(detector.getEnvelope());
27         
28         return input * calcExpanderGain(detectorValue, _threshold, _ratio, _kneeWidth);
29     }
30     
31 private:
32 
33     bool _gate;
34     
35     /// This is the function that does most of the work with calculating compression
36     float calcExpanderGain(float detectorValue, float threshold, float ratio, float kneeWidth)
37     {
38         float ES = 1.0f / ratio - 1.0f;
39         
40         if(_gate)
41             ES = -1.0f;
42             
43         if(kneeWidth > 0 && detectorValue > (threshold - kneeWidth / 2.0f) && 
44            detectorValue < threshold + kneeWidth / 2.0f)
45         {
46             x[0] = threshold - kneeWidth / 2.0f;
47             x[1] = threshold + kneeWidth / 2.0f;
48             x[1] = clamp(x[1], -96.0f, 0.0f);
49             y[0] = ES;
50             y[1] = 0.0f;
51             
52             ES = lagrpol(x, y, 2, detectorValue);
53         }
54         
55         float yG = ES * (threshold - detectorValue);
56         
57         yG = clamp(yG, -96.0, 0);
58         
59         return pow(10.0f, yG / 20.0f);
60     }
61 }
62 
63 class Gate : Expander
64 {
65 public:
66 nothrow:
67 @nogc:
68     this()
69     {
70         _gate = true;
71     }
72 }