1 /*
2  * Copyright (C) 2019-2020 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of ZLFO
5  *
6  * ZLFO is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * ZLFO is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Affero Public License
17  * along with ZLFO.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 /**
21  * \file
22  *
23  * Math module.
24  */
25 
26 #ifndef __Z_LFO_MATH_H__
27 #define __Z_LFO_MATH_H__
28 
29 #include PLUGIN_CONFIG
30 
31 #include <float.h>
32 #include <math.h>
33 
34 #include "common.h"
35 
36 static inline float
sync_rate_to_float(SyncRate rate,SyncRateType type)37 sync_rate_to_float (
38   SyncRate     rate,
39   SyncRateType type)
40 {
41   float r = 0.01f;
42   switch (rate)
43     {
44     case SYNC_1_128:
45       r = 1.f / 128.f;
46       break;
47     case SYNC_1_64:
48       r = 1.f / 64.f;
49       break;
50     case SYNC_1_32:
51       r = 1.f / 32.f;
52       break;
53     case SYNC_1_16:
54       r = 1.f / 16.f;
55       break;
56     case SYNC_1_8:
57       r = 1.f / 8.f;
58       break;
59     case SYNC_1_4:
60       r = 1.f / 4.f;
61       break;
62     case SYNC_1_2:
63       r = 1.f / 2.f;
64       break;
65     case SYNC_1_1:
66       r = 1.f;
67       break;
68     case SYNC_2_1:
69       r = 2.f;
70       break;
71     case SYNC_4_1:
72       r = 4.f;
73       break;
74     case SYNC_8_1:
75       r = 8.f;
76       break;
77     case SYNC_16_1:
78       r = 16.f;
79       break;
80     case SYNC_32_1:
81       r = 32.f;
82       break;
83     case SYNC_64_1:
84       r = 64.f;
85       break;
86     case SYNC_128_1:
87       r = 128.f;
88       break;
89     default:
90       break;
91     }
92 
93   switch (type)
94     {
95     case SYNC_TYPE_NORMAL:
96       break;
97     case SYNC_TYPE_DOTTED:
98       r *= 1.5f;
99       break;
100     case SYNC_TYPE_TRIPLET:
101       r *= (2.f / 3.f);
102       break;
103     default:
104       break;
105     }
106 
107   return r;
108 }
109 
110 /**
111  * Returns the number to use for dividing by the
112  * grid step.
113  *
114  * Eg., when grid step is HALF, this returns 2, the
115  * bottom half of "1/2".
116  */
117 static inline int
grid_step_to_divisor(GridStep step)118 grid_step_to_divisor (
119   GridStep step)
120 {
121   int ret = 0;
122   switch (step)
123     {
124     case GRID_STEP_FULL:
125       ret = 1;
126       break;
127     case GRID_STEP_HALF:
128       ret = 2;
129       break;
130     case GRID_STEP_FOURTH:
131       ret = 4;
132       break;
133     case GRID_STEP_EIGHTH:
134       ret = 8;
135       break;
136     case GRID_STEP_SIXTEENTH:
137       ret = 16;
138       break;
139     case GRID_STEP_THIRTY_SECOND:
140       ret = 32;
141       break;
142     default:
143       break;
144     }
145 
146   return ret;
147 }
148 
149 /**
150  * Gets the y value for a node at the given X coord.
151  *
152  * See https://stackoverflow.com/questions/17623152/how-map-tween-a-number-based-on-a-dynamic-curve
153  * @param x X-coordinate.
154  * @param curviness Curviness variable (1.0 is
155  *   a straight line, 0.0 is full curved).
156  * @param start_higher Start at higher point.
157  */
158 static inline double
get_y_normalized(double x,double curviness,CurveAlgorithm algo,int start_higher,int curve_up)159 get_y_normalized (
160   double x,
161   double curviness,
162   CurveAlgorithm algo,
163   int    start_higher,
164   int    curve_up)
165 {
166   if (!start_higher)
167     x = 1.0 - x;
168   if (curve_up)
169     x = 1.0 - x;
170 
171   double val;
172   switch (algo)
173     {
174     case CURVE_ALGORITHM_EXPONENT:
175       val =
176         pow (x, curviness);
177       break;
178     case CURVE_ALGORITHM_SUPERELLIPSE:
179       val =
180         pow (
181           1.0 - pow (x, curviness),
182           (1.0 / curviness));
183       break;
184     }
185   if (curve_up)
186     {
187       val = 1.0 - val;
188     }
189   return val;
190 
191   fprintf (
192     stderr, "This line should not be reached");
193 }
194 
195 static inline float
get_frames_per_beat(float bpm,float samplerate)196 get_frames_per_beat (
197   float    bpm,
198   float    samplerate)
199 {
200   return 60.0f / bpm * samplerate;
201 }
202 
203 static inline float
get_effective_freq(int freerunning,float freq,HostPosition * host_pos,float sync_rate_float)204 get_effective_freq (
205   int            freerunning,
206   float          freq,
207   HostPosition * host_pos,
208   float          sync_rate_float)
209 {
210   /* if beat_unit is 0 that means we don't know the
211    * time info yet */
212   if (freerunning || host_pos->beat_unit == 0)
213     {
214       if (!freerunning && host_pos->beat_unit == 0)
215         {
216           fprintf (
217             stderr,
218             "Host did not send time info. Beat "
219             "unit is unknown.\n");
220         }
221       return freq;
222     }
223   else /* synced */
224     {
225       /* bpm / (60 * BU * sync note) */
226       return
227         host_pos->bpm /
228           (60.f *
229            host_pos->beat_unit * sync_rate_float);
230     }
231 }
232 
233 static inline uint32_t
get_period_size(int freerunning,HostPosition * host_pos,float effective_freq,float sync_rate_float,float frames_per_beat,float samplerate)234 get_period_size (
235   int            freerunning,
236   HostPosition * host_pos,
237   float          effective_freq,
238   float          sync_rate_float,
239   float          frames_per_beat,
240   float          samplerate)
241 {
242   /* if beat_unit is 0 that means we don't know the
243    * time info yet */
244   if (freerunning || host_pos->beat_unit == 0)
245     {
246       if (!freerunning && host_pos->beat_unit == 0)
247         {
248           fprintf (
249             stderr,
250             "Host did not send time info. Beat "
251             "unit is unknown.\n");
252         }
253       return
254         (uint32_t) (samplerate / effective_freq);
255     }
256   else /* synced */
257     {
258       return
259         (uint32_t)
260         (frames_per_beat * host_pos->beat_unit *
261          sync_rate_float);
262     }
263 }
264 
265 static inline uint32_t
get_current_sample(int freerunning,HostPosition * host_pos,uint32_t period_size)266 get_current_sample (
267   int            freerunning,
268   HostPosition * host_pos,
269   uint32_t       period_size)
270 {
271   if (freerunning)
272     {
273       return 0;
274     }
275   else if (host_pos->beat_unit == 0)
276     {
277       /* if beat_unit is 0 that means we don't
278        * know the time info yet */
279       fprintf (
280         stderr,
281         "Host did not send time info. Beat "
282         "unit is unknown.\n");
283       return 0;
284     }
285   else /* synced */
286     {
287       return host_pos->frame % period_size;
288     }
289 }
290 
291 static inline int
float_array_contains_nonzero(const float * arr,size_t size)292 float_array_contains_nonzero (
293   const float * arr,
294   size_t        size)
295 {
296   for (size_t i = 0; i < size; i++)
297     {
298       if (arr[i] > 0.0001f ||
299           arr[i] < - 0.0001f)
300         return 1;
301     }
302   return 0;
303 }
304 
305 /**
306  * @param sine_multipler Position to save the sine
307  *   multiplier in.
308  * @param saw_multipler Position to save the saw
309  *   multiplier in.
310  */
311 static void
recalc_vars(int freerunning,float * sine_multiplier,float * saw_multiplier,long * period_size,long * current_sample,HostPosition * host_pos,float effective_freq,float sync_rate_float,float samplerate)312 recalc_vars (
313   int     freerunning,
314   float * sine_multiplier,
315   float * saw_multiplier,
316   long *  period_size,
317   long *  current_sample,
318   HostPosition * host_pos,
319   float   effective_freq,
320   float   sync_rate_float,
321   float   samplerate)
322 {
323   float frames_per_beat =
324     get_frames_per_beat (host_pos->bpm, samplerate);
325 
326   /*
327    * F = frequency
328    * X = samples processed
329    * SR = sample rate
330    *
331    * First, get the radians.
332    * ? radians =
333    *   (2 * PI) radians per LFO cycle *
334    *   F cycles per second *
335    *   (1 / SR samples per second) *
336    *   X samples
337    *
338    * Then the LFO value is the sine of
339    * (radians % (2 * PI)).
340    *
341    * This multiplier handles the part known by now
342    * and the first part of the calculation should
343    * become:
344    * ? radians = X samples * sine_multiplier
345    */
346   *sine_multiplier =
347     (effective_freq / samplerate) * 2.f * PI;
348 
349   /*
350    * F = frequency
351    * X = samples processed
352    * SR = sample rate
353    *
354    * First, get the value.
355    * ? value =
356    *   (1 value per LFO cycle *
357    *    F cycles per second *
358    *    1 / SR samples per second *
359    *    X samples) % 1
360    *
361    * Then the LFO value is value * 2 - 1 (to make
362    * it start from -1 and end at 1).
363    *
364    * This multiplier handles the part known by now and the
365    * first part of the calculation should become:
366    * ? value = ((X samples * saw_multiplier) % 1) * 2 - 1
367    */
368   *saw_multiplier = effective_freq / samplerate;
369 
370   *period_size =
371     get_period_size (
372       freerunning, host_pos, effective_freq,
373       sync_rate_float, frames_per_beat, samplerate);
374   if (current_sample)
375     {
376       *current_sample =
377         get_current_sample (
378           freerunning, host_pos, *period_size);
379     }
380 }
381 
382 static inline long
invert_and_shift_xval(long base,long max_val,int hinvert,float shift)383 invert_and_shift_xval (
384   long    base,
385   long    max_val,
386   int     hinvert,
387   float   shift)
388 {
389   long ret = base;
390 
391   /* invert horizontally */
392   if (hinvert)
393     {
394       ret = max_val - ret;
395       while (ret >= max_val)
396         ret -= max_val;
397     }
398 
399   /* shift */
400   if (shift >= 0.5f)
401     {
402       /* add the samples to shift from 0 to
403        * (half the period width) */
404       ret +=
405         (long)
406         /* shift ratio */
407         (((shift - 0.5f) * 2.f) *
408         /* half a period */
409         (max_val / 2.f));
410 
411       /* readjust */
412       ret = ret % max_val;
413     }
414   else
415     {
416       /* subtract the samples to shift between
417        * 0 and (half the period width) */
418       ret -=
419         (long)
420         /* shift ratio */
421         (((0.5f - shift) * 2.f) *
422         /* half a period */
423         (max_val / 2.f));
424 
425       /* readjust */
426       while (ret < 0)
427         {
428           ret += max_val;
429         }
430     }
431 
432   return ret;
433 }
434 
435 /**
436  * This will return -1 if the next index is
437  * the copy of the first one at the end.
438  */
439 static inline int
get_next_idx(NodeIndexElement * elements,int num_nodes,float ratio)440 get_next_idx (
441   NodeIndexElement * elements,
442   int                num_nodes,
443   float              ratio)
444 {
445   float max_pos = 2.f;
446   int max_idx = 0;
447   for (int i = 0; i < num_nodes; i++)
448     {
449       if (elements[i].pos < max_pos &&
450           elements[i].pos >= ratio)
451         {
452           max_pos = elements[i].pos;
453           max_idx = elements[i].index;
454         }
455     }
456 
457   /* if no match, the next index is the copy of
458    * the first one */
459   if (max_pos > 1.9f)
460     {
461       return -1;
462     }
463 
464   return max_idx;
465 }
466 
467 static inline int
get_prev_idx(NodeIndexElement * elements,int num_nodes,float ratio)468 get_prev_idx (
469   NodeIndexElement * elements,
470   int                num_nodes,
471   float              ratio)
472 {
473   float min_pos = -1.f;
474   int min_idx = 0;
475   for (int i = 0; i < num_nodes; i++)
476     {
477       if (elements[i].pos > min_pos &&
478           elements[i].pos <
479             (ratio + 0.0001f))
480         {
481           min_pos = elements[i].pos;
482           min_idx = elements[i].index;
483         }
484     }
485   return min_idx;
486 }
487 
488 static int
pos_cmp(const void * a,const void * b)489 pos_cmp (const void * a, const void * b)
490 {
491   float pos_a = (*(NodeIndexElement*)a).pos;
492   float pos_b = (*(NodeIndexElement*)b).pos;
493   return pos_a > pos_b;
494 }
495 
496 static inline void
sort_node_indices_by_pos(float nodes[16][3],NodeIndexElement * elements,int num_nodes)497 sort_node_indices_by_pos (
498   float              nodes[16][3],
499   NodeIndexElement * elements,
500   int                num_nodes)
501 {
502   for (int i = 0; i < num_nodes; i++)
503     {
504       /* set index and position */
505       elements[i].index = i;
506       elements[i].pos = nodes[i][0];
507     }
508 
509   qsort (
510     elements, (size_t) num_nodes,
511     sizeof (NodeIndexElement), pos_cmp);
512 }
513 
514 #endif
515