1 /*!
2  * @file midilfo.cpp
3  * @brief Implements the MidiLfo MIDI worker class for the LFO Module.
4  *
5  *
6  *      Copyright 2009 - 2017 <qmidiarp-devel@lists.sourceforge.net>
7  *
8  *      This program is free software; you can redistribute it and/or modify
9  *      it under the terms of the GNU General Public License as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      This program is distributed in the hope that it will be useful,
14  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *      GNU General Public License for more details.
17  *
18  *      You should have received a copy of the GNU General Public License
19  *      along with this program; if not, write to the Free Software
20  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  *      MA 02110-1301, USA.
22  *
23  */
24 #include <cmath>
25 #ifdef __FreeBSD__
26 #include <sys/types.h>
27 #endif
28 #include "midilfo.h"
29 
30 
MidiLfo()31 MidiLfo::MidiLfo()
32 {
33     amp = 64;
34     offs = 0;
35     freq = 8;
36     size = 4;
37     res = 4;
38     maxNPoints = 16;
39     old_res = 0;
40     waveFormIndex = 0;
41     recordMode = false;
42     isRecording = false;
43     recValue = 0;
44     int l1 = 0;
45     int lt = 0;
46     int step = TPQN / res;
47     cwmin = 0;
48 
49     customWave.resize(8192);
50     muteMask.resize(8192);
51     data.reserve(8192);
52     frame.resize(32);
53 
54     Sample sample;
55     sample.value = 63;
56     sample.tick = 0;
57     for (l1 = 0; l1 < size * res; l1++) {
58         sample.tick = lt;
59         sample.muted = false;
60         customWave[l1] = sample;
61         data[l1] = sample;
62         if (l1 < 32) frame[l1] = sample;
63         muteMask[l1] = false;
64         lt+=step;
65     }
66     updateWaveForm(waveFormIndex);
67     getData(&data);
68     lastMouseLoc = 0;
69     lastMouseY = 0;
70     frameSize = 1;
71 
72     lastMute = false;
73 }
74 
getNextFrame(int tick)75 void MidiLfo::getNextFrame(int tick)
76 {
77     //this function is called by engine and returns one sample
78     //if res <= LFO_FRAMELIMIT. If res > LFO_FRAMELIMIT, a frame is output
79     //The FRAMELIMIT avoids excessive cursor updating
80 
81     if ((uint)framePtr >= data.size()) return;
82 
83     Sample sample;
84     const int step = TPQN / res;
85     const int npoints = size * res;
86     int lt, l1;
87     int framelimit;
88     int index;
89 
90     gotKbdTrig = false;
91 
92     if (isRecording) framelimit = 32; else framelimit = LFO_FRAMELIMIT;
93     frameSize = res / framelimit;
94     if (!frameSize) frameSize = 1;
95 
96     if (restartFlag) setFramePtr(0);
97     if (!framePtr) grooveTick = newGrooveTick;
98 
99     l1 = 0;
100     lt = nextTick;
101     do {
102         if (reverse) {
103             index = (frameSize - 1 - l1 + framePtr) % npoints;
104         }
105         else {
106             index = (l1 + framePtr) % npoints;
107         }
108         sample = data.at(index);
109 
110         if (isRecording) {
111             if (frameSize < 2) {
112                 sample.value = recValue;
113             }
114             else {
115             /* We do linear interpolation of points within frames if
116              * frameSize is > 0 to get a smooth recording at high resolutions
117              * interpolation is linear between lastSampleValue and current recValue
118              */
119                 sample.value = lastSampleValue
120                             + (double)(recValue - lastSampleValue) / res * framelimit
121                             * ((double)l1 + .5);
122             }
123             customWave[index] = sample;
124             dataChanged = true;
125         }
126         sample.tick = lt;
127         if (seqFinished) sample.muted = true;
128         frame[l1] = sample;
129         lt+=step;
130         l1++;
131     } while ((l1 < frameSize) && (l1 < npoints));
132 
133 
134     reflect = pingpong;
135 
136     if ((!framePtr && !reverse)
137         || (framePtr == npoints - l1 && reverse)) applyPendingParChanges();
138 
139     if (curLoopMode == 6) {
140         framePtr = (rand() % npoints) / l1;
141         framePtr *= l1;
142     }
143     else {
144         if (reverse) {
145             framePtr-=l1;
146             if (framePtr < 0) {
147                 if (!enableLoop) seqFinished = true;
148                 framePtr = npoints - l1;
149                 if (reflect  || !backward) {
150                     reverse = false;
151                     framePtr = 0;
152                 }
153             }
154         }
155         else {
156             framePtr+=l1;
157             if (framePtr >= npoints) {
158                 if (!enableLoop) seqFinished = true;
159                 framePtr = 0;
160                 if (reflect || backward) {
161                     reverse = true;
162                     framePtr = npoints - l1;
163                 }
164             }
165         }
166     }
167     int cur_grv_sft = 0.01 * (grooveTick * (step - 1));
168     /* pairwise application of new groove shift */
169     if (!(framePtr % 2)) {
170         cur_grv_sft = -cur_grv_sft;
171         grooveTick = newGrooveTick;
172     }
173     if (res > 16) cur_grv_sft = 0;
174 
175     lastSampleValue = recValue;
176 
177     nextTick = lt + cur_grv_sft;
178     if (nextTick < (tick - lt)) nextTick = tick;
179     sample.value = -1;
180     sample.tick = nextTick;
181     frame[l1] = sample;
182 
183     if (!trigByKbd && !(framePtr % 2) && !grooveTick) {
184         /* round-up to current resolution (quantize) */
185         nextTick/= (step * frameSize);
186         nextTick*= (step * frameSize);
187     }
188 
189     if (seqFinished) framePtr = 0;
190 
191 }
192 
getData(std::vector<Sample> * p_data)193 void MidiLfo::getData(std::vector<Sample> *p_data)
194 {
195     //this function returns the full LFO wave
196 
197     Sample sample;
198     const int step = TPQN / res;
199     const int npoints = size * res;
200     int val = 0;
201     int lt = 0;
202     bool cl = false;
203     std::vector<Sample> tmpdata;
204 
205     tmpdata.clear();
206 
207     switch(waveFormIndex) {
208         case 0: //sine
209             for (int l1 = 0; l1 < npoints; l1++) {
210                 sample.value = clip((-cos((double)(l1 * 6.28 /
211                 res * freq / 32)) + 1) * amp / 2 + offs, 0, 127, &cl);
212                 sample.tick = lt;
213                 sample.muted = muteMask.at(l1);
214                 tmpdata.push_back(sample);
215                 lt += step;
216             }
217         break;
218         case 1: //sawtooth up
219             val = 0;
220             for (int l1 = 0; l1 < npoints; l1++) {
221                 sample.value = clip(val * amp / res / 32
222                 + offs, 0, 127, &cl);
223                 sample.tick = lt;
224                 sample.muted = muteMask.at(l1);
225                 tmpdata.push_back(sample);
226                 lt += step;
227                 val += freq;
228                 val %= res * 32;
229             }
230         break;
231         case 2: //triangle
232             val = 0;
233             for (int l1 = 0; l1 < npoints; l1++) {
234                 int tempval = val - res * 16;
235                 if (tempval < 0 ) tempval = -tempval;
236                 sample.value = clip((res * 16 - tempval) * amp
237                         / res / 16 + offs, 0, 127, &cl);
238                 sample.tick = lt;
239                 sample.muted = muteMask.at(l1);
240                 tmpdata.push_back(sample);
241                 lt += step;
242                 val += freq;
243                 val %= res * 32;
244             }
245         break;
246         case 3: //sawtooth down
247             val = 0;
248             for (int l1 = 0; l1 < npoints; l1++) {
249                 sample.value = clip((res * 32 - val)
250                         * amp / res / 32 + offs, 0, 127, &cl);
251                 sample.tick = lt;
252                 sample.muted = muteMask.at(l1);
253                 tmpdata.push_back(sample);
254                 lt+=step;
255                 val += freq;
256                 val %= res * 32;
257             }
258         break;
259         case 4: //square
260             for (int l1 = 0; l1 < npoints; l1++) {
261                 sample.value = clip(amp * ((l1 * freq / 16
262                         / res) % 2 == 0) + offs, 0, 127, &cl);
263                 sample.tick = lt;
264                 sample.muted = muteMask.at(l1);
265                 tmpdata.push_back(sample);
266                 lt+=step;
267             }
268         break;
269         case 5: //custom
270             for (int l1 = 0; l1 < npoints; l1++) {
271                 tmpdata.push_back(customWave[l1]);
272             }
273             lt = step * npoints;
274         break;
275         default:
276         break;
277     }
278     sample.value = -1;
279     sample.tick = lt;
280     tmpdata.push_back(sample);
281     data = tmpdata;
282     *p_data = data;
283 }
284 
updateWaveForm(int val)285 void MidiLfo::updateWaveForm(int val)
286 {
287     waveFormIndex = val;
288 }
289 
updateFrequency(int val)290 void MidiLfo::updateFrequency(int val)
291 {
292     freq = val;
293 }
294 
updateAmplitude(int val)295 void MidiLfo::updateAmplitude(int val)
296 {
297     amp = val;
298 }
299 
updateOffset(int val)300 void MidiLfo::updateOffset(int val)
301 {
302     if (isRecording) return;
303     if (waveFormIndex == 5) updateCustomWaveOffset(val);
304     offs = val;
305 }
306 
updateResolution(int val)307 void MidiLfo::updateResolution(int val)
308 {
309     res = val;
310     resizeAll();
311 }
312 
updateSize(int val)313 void MidiLfo::updateSize(int val)
314 {
315     size = val;
316     resizeAll();
317 }
318 
updateLoop(int val)319 void MidiLfo::updateLoop(int val)
320 {
321     backward = val&1;
322     pingpong = val&2;
323     enableLoop = !(val&4);
324     curLoopMode = val;
325     if (seqFinished) {
326         seqFinished = false;
327         setFramePtr(0);
328     }
329 }
330 
setCustomWavePoint(double mouseX,double mouseY,bool newpt)331 int MidiLfo::setCustomWavePoint(double mouseX, double mouseY, bool newpt)
332 {
333     Sample sample;
334     int loc = mouseX * (res * size);
335     int Y = mouseY * 128;
336 
337     if (newpt || (lastMouseLoc >= (res * size))) {
338     // the mouse was just clicked so we can directly set the point
339         lastMouseLoc = loc;
340         lastMouseY = Y;
341     }
342 
343     if (loc == lastMouseLoc) lastMouseY = Y;
344 
345     do {
346     //if the mouse was moved, we interpolate potentially missing points after
347     //the last mouse position
348         if (loc > lastMouseLoc) {
349             lastMouseY += (double)(lastMouseY - Y) / (lastMouseLoc - loc) + .5;
350             lastMouseLoc++;
351         }
352         if (loc < lastMouseLoc) {
353             lastMouseY -= (double)(lastMouseY - Y) / (lastMouseLoc - loc) - .5;
354             lastMouseLoc--;
355         }
356         sample = customWave[lastMouseLoc];
357         sample.value = lastMouseY;
358         customWave[lastMouseLoc] = sample;
359     } while (lastMouseLoc != loc);
360 
361     newCustomOffset();
362     return (loc);
363 }
364 
mouseEvent(double mouseX,double mouseY,int buttons,int pressed)365 int MidiLfo::mouseEvent(double mouseX, double mouseY, int buttons, int pressed)
366 {
367     int ix = 0;
368     if (buttons == 2) {
369         if (pressed == 1) {
370             lastMute = toggleMutePoint(mouseX);
371             ix = lastMute;
372         }
373         else if (pressed == 0)
374             ix = setMutePoint(mouseX, lastMute);
375     }
376     else if ((pressed != 2) && (buttons == 1)) {
377         if (waveFormIndex < 5) copyToCustom();
378         ix = setCustomWavePoint(mouseX, mouseY, pressed);
379     }
380     dataChanged = true;
381     return (ix);
382 }
383 
resizeAll()384 void MidiLfo::resizeAll()
385 {
386     const int step = TPQN / res;
387     const int npoints = res * size;
388     Sample sample;
389 
390     framePtr%=npoints;
391 
392     if (maxNPoints < npoints) {
393         int lt = 0;
394         for (int l1 = 0; l1 < npoints; l1++) {
395             if (l1 >= maxNPoints)
396                 muteMask[l1] = muteMask[l1 % maxNPoints];
397             sample = customWave[l1 % maxNPoints];
398             sample.tick = lt;
399             sample.muted = muteMask[l1];
400             customWave[l1] = sample;
401             lt+=step;
402         }
403         maxNPoints = npoints;
404     }
405     nPoints = npoints;
406     dataChanged = true;
407 }
408 
copyToCustom()409 void MidiLfo::copyToCustom()
410 {
411     updateWaveForm(5);
412     for (int l1 = 0; l1 < nPoints; l1++)
413         customWave[l1] = data[l1];
414 
415 }
416 
newCustomOffset()417 void MidiLfo::newCustomOffset()
418 {
419     int min = 127;
420     const int npoints = res * size;
421     for (int l1 = 0; l1 < npoints; l1++) {
422         int value = customWave[l1].value;
423         if (value < min) min = value;
424     }
425     cwmin = min;
426 #ifdef APPBUILD
427     offs = min;
428 #endif
429 }
430 
flipWaveVertical()431 void MidiLfo::flipWaveVertical()
432 {
433     Sample sample;
434     int min = 127;
435     int max = 0;
436     const int npoints = res * size;
437 
438     if (waveFormIndex < 5) {
439         copyToCustom();
440     }
441 
442     for (int l1 = 0; l1 < npoints; l1++) {
443         int value = customWave[l1].value;
444         if (value < min) min = value;
445         if (value > max) max = value;
446     }
447 
448     for (int l1 = 0; l1 < npoints; l1++) {
449         sample = customWave[l1];
450         sample.value = min + max - sample.value;
451         customWave[l1] = sample;
452     }
453     cwmin = min;
454 #ifdef APPBUILD
455     offs = min;
456 #endif
457 }
458 
updateCustomWaveOffset(int o)459 void MidiLfo::updateCustomWaveOffset(int o)
460 {
461     Sample sample;
462     const int count = res * size;
463     int l1 = 0;
464     bool cl = false;
465 
466     while ((!cl) && (l1 < count)) {
467         sample.value = clip(customWave[l1].value + o - cwmin,
468                             0, 127, &cl);
469         l1++;
470         }
471 
472     if (cl) return;
473 
474     for (l1 = 0; l1 < count; l1++) {
475         sample = customWave[l1];
476         sample.value += o - cwmin;
477         customWave[l1] = sample;
478     }
479     cwmin = o;
480 }
481 
toggleMutePoint(double mouseX)482 bool MidiLfo::toggleMutePoint(double mouseX)
483 {
484     Sample sample;
485     bool m;
486     int loc = mouseX * (res * size);
487 
488     m = muteMask.at(loc);
489     muteMask[loc] = !m;
490     if (waveFormIndex == 5) {
491         sample = customWave[loc];
492         sample.muted = !m;
493         customWave[loc] = sample;
494     }
495     lastMouseLoc = loc;
496     return(!m);
497 }
498 
setMutePoint(double mouseX,bool on)499 int MidiLfo::setMutePoint(double mouseX, bool on)
500 {
501     Sample sample;
502     int loc = mouseX * (res * size);
503 
504     if (lastMouseLoc >= (res * size)) lastMouseLoc = loc;
505 
506     do {
507         if (waveFormIndex == 5) {
508             sample = customWave[lastMouseLoc];
509             sample.muted = on;
510             customWave[lastMouseLoc] = sample;
511         }
512         muteMask[lastMouseLoc] = on;
513         if (loc > lastMouseLoc) lastMouseLoc++;
514         if (loc < lastMouseLoc) lastMouseLoc--;
515     } while (lastMouseLoc != loc);
516 
517     return (loc);
518 }
519 
setFramePtr(int idx)520 void MidiLfo::setFramePtr(int idx)
521 {
522     framePtr = idx;
523     if (!idx) {
524         reverse = curLoopMode&1;
525         seqFinished = (enableNoteOff && !noteCount);
526         restartFlag = false;
527         if (reverse) framePtr = res * size - 1;
528     }
529 }
530 
setRecordMode(bool on)531 void MidiLfo::setRecordMode(bool on)
532 {
533     if (!on) {
534         isRecording = false;
535         newCustomOffset();
536         dataChanged = true;
537     }
538     recordMode = on;
539 }
540 
record(int value)541 void MidiLfo::record(int value)
542 {
543     recValue = value;
544     isRecording = true;
545 }
546 
handleEvent(MidiEvent inEv,int tick)547 bool MidiLfo::handleEvent(MidiEvent inEv, int tick)
548 {
549 
550     if (!recordMode && (inEv.type == EV_CONTROLLER)) return(true);
551     if (inEv.channel != chIn && chIn != OMNI) return(true);
552     if ((inEv.type == EV_CONTROLLER) && (inEv.data != ccnumberIn)) return(true);
553 
554     if ((inEv.type == EV_CONTROLLER) && recordMode) {
555         record(inEv.value);
556         return (false);
557     }
558     if (inEv.type != EV_NOTEON) return (true);
559 
560     if (((inEv.data < indexIn[0]) || (inEv.data > indexIn[1]))
561         || ((inEv.value < rangeIn[0]) || (inEv.value > rangeIn[1]))) {
562         return(true);
563     }
564 
565     if (inEv.value) {
566         /*This is a NOTE ON event*/
567         if (restartByKbd && (!noteCount || trigLegato)) restartFlag = true;
568         seqFinished = false;
569         noteCount++;
570         if (trigByKbd && ((noteCount == 1) || trigLegato)) {
571             nextTick = tick + 2; //schedDelayTicks;
572             gotKbdTrig = true;
573         }
574      }
575     else {
576         /*This is a NOTE OFF event*/
577         if (enableNoteOff && (noteCount == 1)) seqFinished = true;
578         if (noteCount) noteCount--;
579     }
580     return(false);
581 }
582 
applyPendingParChanges()583 void MidiLfo::applyPendingParChanges()
584 {
585     if (!parChangesPending) return;
586 
587     int olddefer = deferChanges;
588     deferChanges = false;
589     setMuted(isMutedDefer);
590     deferChanges = olddefer;
591     parChangesPending = false;
592     needsGUIUpdate = true;
593 }
594 
setNextTick(int tick)595 void MidiLfo::setNextTick(int tick)
596 {
597     int tickres = TPQN/res;
598     int pos = (tick/tickres) % nPoints;
599 
600     reverse = false;
601     if (pingpong) reverse = (((tick/tickres) / nPoints) % 2);
602 
603     if (backward) reverse = !reverse;
604     if (reverse) pos = nPoints - pos;
605 
606     setFramePtr(pos);
607     nextTick = (tick/tickres) * tickres;
608 }
609