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(T) : DynamicsProcessor!T
18 {
19 public:
20 nothrow:
21 @nogc:
22     
23     override T getNextSample(const T 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 unittest
64 {
65     Expander!float expander = new Expander!float();
66 }
67 
68 class Gate(T) : Expander!T
69 {
70 public:
71 nothrow:
72 @nogc:
73     this()
74     {
75         _gate = true;
76     }
77 }
78 
79 unittest
80 {
81     Gate!float gate = new Gate!float();
82 }