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