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