1 /** 2 * Various envelope followers. 3 * 4 * Copyright: Copyright Guillaume Piolat 2015-2018. 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 * Authors: Guillaume Piolat 7 */ 8 deprecated module dplug.dsp.envelope; 9 10 import std.math; 11 import dplug.dsp.iir; 12 import dplug.dsp.smooth; 13 14 15 /// Simple envelope follower, filters the envelope with 24db/oct lowpass. 16 deprecated("Will be removed, please don't use this.") struct EnvelopeFollower 17 { 18 public: 19 20 // typical frequency would be is 10-30hz 21 void initialize(float cutoffInHz, float samplerate) nothrow @nogc 22 { 23 _coeff = biquadRBJLowPass(cutoffInHz, samplerate); 24 _delay0.initialize(); 25 _delay1.initialize(); 26 } 27 28 // takes on sample, return mean amplitude 29 float nextSample(float x) nothrow @nogc 30 { 31 float l = abs(x); 32 l = _delay0.nextSample(l, _coeff); 33 l = _delay1.nextSample(l, _coeff); 34 return l; 35 } 36 37 void nextBuffer(const(float)* input, float* output, int frames) nothrow @nogc 38 { 39 for(int i = 0; i < frames; ++i) 40 output[i] = abs(input[i]); 41 42 _delay0.nextBuffer(output, output, frames, _coeff); 43 _delay1.nextBuffer(output, output, frames, _coeff); 44 } 45 46 private: 47 BiquadCoeff _coeff; 48 BiquadDelay _delay0; 49 BiquadDelay _delay1; 50 } 51 52 /// Get the module of estimate of analytic signal. 53 /// Phase response depends a lot on input signal, it's not great for bass but gets 54 /// better in medium frequencies. 55 deprecated("Will be removed, please don't use this.") struct AnalyticSignal(T) if (is(T == float) || is(T == double)) 56 { 57 public: 58 void initialize(T samplerate) nothrow @nogc 59 { 60 _hilbert.initialize(samplerate); 61 } 62 63 T nextSample(T input) nothrow @nogc 64 { 65 T outSine, outCosine; 66 _hilbert.nextSample(input, outCosine, outSine); 67 return sqrt(input * input + outSine * outSine); 68 } 69 70 void nextBuffer(const(T)* input, T* output, int frames) nothrow @nogc 71 { 72 // FUTURE: pass maxFrames and allocate buffers 73 for (int i = 0; i < frames; ++i) 74 output[i] = nextSample(input[i]); 75 } 76 77 private: 78 HilbertTransformer!T _hilbert; 79 } 80 /** 81 * 82 * Copyright 1999, by Sean M. Costello 83 * 84 * hilbert is an implementation of an IIR Hilbert transformer. 85 * The structure is based on two 6th-order allpass filters in 86 * parallel, with a constant phase difference of 90 degrees 87 * (+- some small amount of error) between the two outputs. 88 * Allpass coefficients are calculated at i-time. 89 * 90 * "Feel free to use the code under whatever license you wish." - Sean Costello 91 */ 92 93 /// Estimate amplitude. 94 deprecated("Will be removed, please don't use this.") struct HilbertTransformer(T) if (is(T == float) || is(T == double)) 95 { 96 public: 97 void initialize(float sampleRate) nothrow @nogc 98 { 99 // pole values taken from Bernie Hutchins, "Musical Engineer's Handbook" 100 static immutable double[12] poles = 101 [ 102 0.3609, 2.7412, 11.1573, 44.7581, 179.6242, 798.4578, 103 1.2524, 5.5671, 22.3423, 89.6271, 364.7914, 2770.1114 104 ]; 105 106 float onedsr = 1 / sampleRate; 107 108 // calculate coefficients for allpass filters, based on sampling rate 109 for (int j = 0; j < 12; ++j) 110 { 111 const double polefreq = poles[j] * 15.0; 112 const double alpha = (2.0 * PI * polefreq); 113 const double beta = (1.0 - (alpha * 0.5 * onedsr)) / (1.0 + (alpha * 0.5 * onedsr)); 114 _coef[j] = -beta; 115 } 116 117 for (int j = 0; j < 12; ++j) 118 { 119 _xnm1[j] = 0; 120 _ynm1[j] = 0; 121 } 122 } 123 124 void nextSample(T input, out T out1, out T out2) nothrow @nogc 125 { 126 T yn1, yn2; 127 T xn1 = input; 128 129 /* 6th order allpass filter for sine output. Structure is 130 * 6 first-order allpass sections in series. Coefficients 131 * taken from arrays calculated at i-time. 132 */ 133 134 for (int j=0; j < 6; j++) 135 { 136 yn1 = _coef[j] * (xn1 - _ynm1[j]) + _xnm1[j]; 137 _xnm1[j] = xn1; 138 _ynm1[j] = yn1; 139 xn1 = yn1; 140 } 141 142 T xn2 = input; 143 144 /* 6th order allpass filter for cosine output. Structure is 145 * 6 first-order allpass sections in series. Coefficients 146 * taken from arrays calculated at i-time. 147 */ 148 for (int j = 6; j < 12; j++) 149 { 150 yn2 = _coef[j] * (xn2 - _ynm1[j]) + _xnm1[j]; 151 _xnm1[j] = xn2; 152 _ynm1[j] = yn2; 153 xn2 = yn2; 154 } 155 out1 = cast(T)yn2; 156 out2 = cast(T)yn1; 157 } 158 159 void nextBuffer(const(T)* input, T* output1, T* output2, int frames) nothrow @nogc 160 { 161 for (int i = 0; i < frames; ++i) 162 { 163 T yn1; 164 T xn1 = input[i]; 165 166 /* 6th order allpass filter for sine output. Structure is 167 * 6 first-order allpass sections in series. Coefficients 168 * taken from arrays calculated at i-time. 169 */ 170 for (int j=0; j < 6; j++) 171 { 172 yn1 = _coef[j] * (xn1 - _ynm1[j]) + _xnm1[j]; 173 _xnm1[j] = xn1; 174 _ynm1[j] = yn1; 175 xn1 = yn1; 176 } 177 output2[i] = cast(T)yn1; 178 } 179 180 for (int i = 0; i < frames; ++i) 181 { 182 T yn2; 183 T xn2 = input[i]; 184 185 /* 6th order allpass filter for cosine output. Structure is 186 * 6 first-order allpass sections in series. Coefficients 187 * taken from arrays calculated at i-time. 188 */ 189 for (int j = 6; j < 12; j++) 190 { 191 yn2 = _coef[j] * (xn2 - _ynm1[j]) + _xnm1[j]; 192 _xnm1[j] = xn2; 193 _ynm1[j] = yn2; 194 xn2 = yn2; 195 } 196 197 output1[i] = cast(T)yn2; 198 } 199 200 // Remove small signals 201 for (int j = 0; j < 12; ++j) 202 { 203 _xnm1[j] += 1e-10f; 204 _xnm1[j] -= 1e-10f; 205 _ynm1[j] += 1e-10f; 206 _ynm1[j] -= 1e-10f; 207 } 208 } 209 210 private: 211 T[12] _coef; 212 T[12] _xnm1; 213 T[12] _ynm1; 214 } 215 216 /// Sliding RMS computation 217 /// To use for coarse grained levels for visual display. 218 deprecated("Will be removed, please don't use this.") struct CoarseRMS 219 { 220 public: 221 void initialize(double sampleRate) nothrow @nogc 222 { 223 // In Reaper, default RMS window is 500 ms 224 _envelope.initialize(20, sampleRate); 225 226 _last = 0; 227 } 228 229 /// Process a chunk of samples and return a value in dB (could be -infinity) 230 void nextSample(float input) nothrow @nogc 231 { 232 _last = _envelope.nextSample(input * input); 233 } 234 235 void nextBuffer(float* input, int frames) nothrow @nogc 236 { 237 if (frames == 0) 238 return; 239 240 for (int i = 0; i < frames - 1; ++i) 241 _envelope.nextSample(input[i] * input[i]); 242 243 _last = _envelope.nextSample(input[frames - 1] * input[frames - 1]); 244 } 245 246 float RMS() nothrow @nogc 247 { 248 return sqrt(_last); 249 } 250 251 private: 252 EnvelopeFollower _envelope; 253 float _last; 254 } 255