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 }