1 /**
2 * DSP utility functions. 
3 *
4 * Copyright: Copyright Auburn Sounds 2015-2016
5 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 * Authors:   Guillaume Piolat
7 */
8 module dplug.core.math;
9 
10 import std.math;
11 
12 version(LDC)
13 {
14     import ldc.intrinsics;
15 }
16 
17 immutable real TAU = PI * 2;
18 
19 /// Map linearly x from the range [a, b] to the range [c, d]
20 T linmap(T)(T value, T a, T b, T c, T d) pure nothrow @nogc
21 {
22     return c + (d - c) * (value - a) / (b - a);
23 }
24 
25 /// map [0..1] to [min..max] logarithmically
26 /// min and max must be all > 0, t in [0..1]
27 T logmap(T)(T t, T min, T max) pure nothrow @nogc
28 {
29     assert(min < max);
30     return min * exp(t * log(max / min));
31 }
32 
33 /// Hermite interpolation.
34 T hermite(T)(T frac_pos, T xm1, T x0, T x1, T x2) pure nothrow @nogc
35 {
36     T c = (x1 - xm1) * 0.5f;
37     T v = x0 - x1;
38     T w = c + v;
39     T a = w + v + (x2 - x0) * 0.5f;
40     T b_neg = w + a;
41     return ((((a * frac_pos) - b_neg) * frac_pos + c) * frac_pos + x0);
42 }
43 
44 /// Convert from dB to float.
45 T deciBelToFloat(T)(T dB) pure nothrow @nogc
46 {
47     static immutable T ln10_20 = cast(T)LN10 / 20;
48     return exp(dB * ln10_20);
49 }
50 
51 /// Convert from float to dB
52 T floatToDeciBel(T)(T x) pure nothrow @nogc
53 {
54     static immutable T f20_ln10 = 20 / cast(T)LN10;
55     return log(x) * f20_ln10;
56 }
57 
58 /// Is this integer odd?
59 bool isOdd(T)(T i) pure nothrow @nogc
60 {
61     return (i & 1) != 0;
62 }
63 
64 /// Is this integer even?
65 bool isEven(T)(T i) pure nothrow @nogc
66 {
67     return (i & 1) == 0;
68 }
69 
70 /// Returns: true of i is a power of 2.
71 bool isPowerOfTwo(int i) pure nothrow @nogc
72 {
73     assert(i >= 0);
74     return (i != 0) && ((i & (i - 1)) == 0);
75 }
76 
77 /// Computes next power of 2.
78 int nextPowerOf2(int i) pure nothrow @nogc
79 {
80     int v = i - 1;
81     v |= v >> 1;
82     v |= v >> 2;
83     v |= v >> 4;
84     v |= v >> 8;
85     v |= v >> 16;
86     v++;
87     return v;
88 }
89 
90 /// Computes next power of 2.
91 long nextPowerOf2(long i) pure nothrow @nogc
92 {
93     long v = i - 1;
94     v |= v >> 1;
95     v |= v >> 2;
96     v |= v >> 4;
97     v |= v >> 8;
98     v |= v >> 16;
99     v |= v >> 32;
100     v++;
101     return v;
102 }
103 
104 /// Returns: x so that (1 << x) >= i
105 int iFloorLog2(int i) pure nothrow @nogc
106 {
107     assert(i >= 1);
108     int result = 0;
109     while (i > 1)
110     {
111         i = i / 2;
112         result = result + 1;
113     }
114     return result;
115 }
116 
117 /// Mapping from MIDI notes to frequency.
118 float convertMIDINoteToFrequency(float note) pure nothrow @nogc
119 {
120     return 440.0f * pow(2.0, (note - 69.0f) / 12.0f);
121 }
122 
123 /// Mapping from frequency to MIDI notes.
124 float convertFrequencyToMIDINote(float frequency) pure nothrow @nogc
125 {
126     return 69.0f + 12.0f * log2(frequency / 440.0f);
127 }
128 
129 /// Fletcher and Munson equal-loudness curve
130 /// Reference: Xavier Serra thesis (1989).
131 T equalLoudnessCurve(T)(T frequency) pure nothrow @nogc
132 {
133     T x = cast(T)0.05 + 4000 / frequency;
134     return x * ( cast(T)10 ^^ x);
135 }
136 
137 /// Cardinal sine
138 T sinc(T)(T x) pure nothrow @nogc
139 {
140     if (cast(T)(1) + x * x == cast(T)(1))
141         return 1;
142     else
143         return sin(cast(T)PI * x) / (cast(T)PI * x);
144 }
145 
146 
147 /// Gets a factor for making exponential decay curves.
148 ///
149 /// Returns: Multiplier for this time constant and sampling rate.
150 ///
151 /// Params:
152 ///    timeConstantInSeconds = Time after which the amplitude is only 37% of the original.
153 ///    samplerate = Sampling rate.
154 double expDecayFactor(double timeConstantInSeconds, double samplerate) pure nothrow @nogc
155 {
156     // 1 - exp(-time * sampleRate) would yield innacuracies
157     return -expm1(-1.0 / (timeConstantInSeconds * samplerate));
158 }
159 
160 /// Give back a phase between -PI and PI
161 T normalizePhase(T)(T phase) nothrow @nogc
162 {
163     enum bool Assembly = D_InlineAsm_Any && !(is(Unqual!T == real));
164 
165     static if (Assembly)
166     {
167         T k_TAU = PI * 2;
168         T result = phase;
169         asm nothrow @nogc
170         {
171             fld k_TAU;    // TAU
172             fld result;    // phase | TAU
173             fprem1;       // normalized(phase) | TAU
174             fstp result;   // TAU
175             fstp ST(0);   //
176         }
177         return result;
178     }
179     else
180     {
181         T res = fmod(phase, cast(T)TAU);
182         if (res > PI)
183             res -= TAU;
184         if (res < -PI)
185             res += TAU;
186         return res;
187     }
188 }
189 
190 unittest
191 {
192     assert(approxEqual(normalizePhase!real(TAU), 0));
193 
194     assert(approxEqual(normalizePhase!float(0.1f), 0.1f));
195     assert(approxEqual(normalizePhase!float(TAU + 0.1f), 0.1f));
196 
197     assert(approxEqual(normalizePhase!double(-0.1f), -0.1f));
198     assert(approxEqual(normalizePhase!double(-TAU - 0.1f), -0.1f));
199 
200     bool approxEqual(T)(T a, T b) nothrow @nogc
201     {
202         return (a - b) < 1e-7;
203     }
204 }
205 
206 /// Quick and dirty sawtooth for testing purposes.
207 T rawSawtooth(T)(T x) pure nothrow @nogc
208 {
209     return normalizePhase(x) / (cast(T)PI);
210 }
211 
212 /// Quick and dirty triangle for testing purposes.
213 T rawTriangle(T)(T x) pure nothrow @nogc
214 {
215     return 1 - normalizePhase(x) / cast(T)PI_2;
216 }
217 
218 /// Quick and dirty square for testing purposes.
219 T rawSquare(T)(T x) pure nothrow @nogc
220 {
221     return normalizePhase(x) > 0 ? 1 : -1;
222 }
223 
224 T computeRMS(T)(T[] samples) pure nothrow @nogc
225 {
226     double sum = 0;
227     foreach(sample; samples)
228         sum += sample * sample;
229     return sqrt(sum / cast(int)samples.length);
230 }
231 
232 unittest
233 {
234     double[] d = [4, 5, 6];
235     computeRMS(d);
236 }
237 
238 
239 version(D_InlineAsm_X86)
240     private enum D_InlineAsm_Any = true;
241 else version(D_InlineAsm_X86_64)
242     private enum D_InlineAsm_Any = true;
243 else
244     private enum D_InlineAsm_Any = false;
245 
246 // Expose LDC intrinsics, but usable with DMD too.
247 
248 version(LDC)
249 {
250     // Note: function wouldn't work (depending on inlining), 
251     // it's much more reliable to use alias
252 
253     alias fast_exp = llvm_exp;
254     alias fast_log = llvm_log;
255     alias fast_pow = llvm_pow;
256 
257     alias fast_fabs = llvm_fabs;
258     alias fast_log2 = llvm_log2;
259     alias fast_êxp2 = llvm_exp2;
260     alias fast_log10 = llvm_log10;
261 
262     alias fast_floor = llvm_floor;
263     alias fast_ceil = llvm_ceil;
264     alias fast_trunc = llvm_trunc;
265     alias fast_round = llvm_round;
266 
267     alias fast_sqrt = llvm_sqrt;
268     alias fast_sin = llvm_sin;
269     alias fast_cos = llvm_cos;
270 }
271 else
272 {
273     alias fast_exp = exp;
274     alias fast_log = log;
275     alias fast_pow = pow;
276 
277     alias fast_fabs = fabs;
278     alias fast_log2 = log2;
279     alias fast_êxp2 = exp2;
280     alias fast_log10 = log10;
281 
282     alias fast_floor = floor;
283     alias fast_ceil = ceil;
284     alias fast_trunc = trunc;
285     alias fast_round = round;
286 
287     alias fast_sqrt = sqrt; // Undefined behaviour for operands below -0
288     alias fast_sin = sin;
289     alias fast_cos = cos;
290 }
291 
292 /// Linear interpolation, akin to GLSL's mix.
293 S lerp(S, T)(S a, S b, T t) pure nothrow @nogc 
294     if (is(typeof(t * b + (1 - t) * a) : S))
295 {
296     return t * b + (1 - t) * a;
297 }
298 
299 /// Same as GLSL smoothstep function.
300 /// See: http://en.wikipedia.org/wiki/Smoothstep
301 T smoothStep(T)(T a, T b, T t) pure nothrow @nogc 
302 {
303     if (t <= a)
304         return 0;
305     else if (t >= b)
306         return 1;
307     else
308     {
309         T x = (t - a) / (b - a);
310         return x * x * (3 - 2 * x);
311     }
312 }
313 
314 /// SSE approximation of reciprocal square root.
315 T inverseSqrt(T)(T x) pure nothrow @nogc if (is(T : float) || is(T: double))
316 {
317     version(AsmX86)
318     {
319         static if (is(T == float))
320         {
321             float result;
322 
323             asm pure nothrow @nogc 
324             {
325                 movss XMM0, x; 
326                 rsqrtss XMM0, XMM0; 
327                 movss result, XMM0; 
328             }
329             return result;
330         }
331         else
332             return 1 / sqrt(x);
333     }
334     else
335         return 1 / sqrt(x);
336 }
337 
338 unittest
339 {
340     assert(abs( inverseSqrt!float(1) - 1) < 1e-3 );
341     assert(abs( inverseSqrt!double(1) - 1) < 1e-3 );
342 }
343 
344 /// Computes a normalized frequency form a frequency.
345 float convertFrequencyToNormalizedFrequency(float frequencyHz, float samplingRate) pure nothrow @nogc
346 {
347     return frequencyHz / samplingRate;
348 }
349 
350 /// Computes a frequency.
351 float convertNormalizedFreqyencyToFrequency(float frequencyCyclesPerSample, float samplingRate) pure nothrow @nogc
352 {
353     return frequencyCyclesPerSample * samplingRate;
354 }