1 /*!
2  * @file midiseq.cpp
3  * @brief Implements the MidiSeq MIDI worker class for the Seq 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 #include "midiseq.h"
26 
27 
MidiSeq()28 MidiSeq::MidiSeq()
29 {
30     recordMode = false;
31     currentRecStep = 0;
32     loopMarker = 0;
33 
34     nOctaves = 4;
35     baseOctave = 3;
36 
37     vel = 0;
38     velDefer = 0;
39     transp = 0;
40     transpDefer = 0;
41     size = 4;
42     res = 4;
43     maxNPoints = 16;
44     notelength = 180;
45     notelengthDefer = 180;
46     lastMute = false;
47     lastMouseLoc = 0;
48 
49     customWave.resize(2048);
50     muteMask.resize(2048);
51     data.reserve(2048);
52 
53     int lt = 0;
54     int l1 = 0;
55     int step = TPQN / res;
56     Sample sample;
57     sample.value = 60;
58     for (l1 = 0; l1 < 2048; l1++) {
59         sample.tick = lt;
60         sample.muted = false;
61         customWave[l1] = sample;
62         data[l1] = sample;
63         muteMask[l1] = false;
64         lt+=step;
65     }
66     returnNote = sample;
67 }
68 
handleEvent(MidiEvent inEv,int tick)69 bool MidiSeq::handleEvent(MidiEvent inEv, int tick)
70 {
71     if (inEv.type != EV_NOTEON) return(true);
72     if (inEv.channel != chIn && chIn != OMNI) return(true);
73     if ((inEv.data < 36) || (inEv.data >= 84)) return(true);
74 
75     if (inEv.value) {
76         /*This is a NOTE ON event*/
77         if (recordMode) {
78             recordNote(inEv.data);
79             return(false);
80         }
81         if (((inEv.data < indexIn[0]) || (inEv.data > indexIn[1]))
82             || ((inEv.value < rangeIn[0]) || (inEv.value > rangeIn[1]))) {
83             return(true);
84         }
85         if (enableNoteIn) {
86             updateTranspose(inEv.data - 60);
87             needsGUIUpdate = true;
88         }
89         if (restartByKbd && (!noteCount || trigLegato)) restartFlag = true;
90         if (enableVelIn) {
91             updateVelocity(inEv.value);
92             needsGUIUpdate = true;
93         }
94         seqFinished = false;
95         noteCount++;
96         if (trigByKbd && ((noteCount == 1) || trigLegato)) {
97             nextTick = tick + 2; //schedDelayTicks;
98             gotKbdTrig = true;
99         }
100     }
101     else {
102         /*This is a NOTE OFF event*/
103         if (enableNoteOff && (noteCount == 1)) seqFinished = true;
104         if (noteCount) noteCount--;
105     }
106 
107     return(false);
108 }
109 
getNextFrame(int tick)110 void MidiSeq::getNextFrame(int tick)
111 {
112     const int frame_nticks = TPQN / res;
113     Sample sample;
114     int cur_grv_sft;
115 
116     gotKbdTrig = false;
117     if (restartFlag) setFramePtr(0);
118     if (!framePtr) grooveTick = newGrooveTick;
119 
120     sample = customWave[framePtr];
121     advancePatternIndex();
122 
123     if (nextTick < (tick - frame_nticks)) nextTick = tick;
124 
125     sample.value+=transp;
126     sample.tick = nextTick;
127 
128 
129     cur_grv_sft = 0.01 * (grooveTick * (frame_nticks - 1));
130 
131     /* pairwise application of new groove shift */
132     if (!(framePtr % 2)) {
133         cur_grv_sft = -cur_grv_sft;
134         grooveTick = newGrooveTick;
135     }
136     nextTick += frame_nticks + cur_grv_sft;
137 
138     if (!trigByKbd && !(framePtr % 2)) {
139         /* round-up to current resolution (quantize) */
140         nextTick/=frame_nticks;
141         nextTick*=frame_nticks;
142     }
143 
144     if (seqFinished) {
145         sample.muted = true;
146         framePtr = 0;
147     }
148     returnNote = sample;
149 }
150 
advancePatternIndex()151 void MidiSeq::advancePatternIndex()
152 {
153     const int npoints = res * size;
154     int pivot = abs(loopMarker);
155     reflect = pingpong;
156 
157     if (curLoopMode == 6) {
158         if (pivot)
159             framePtr = rand() % pivot;
160         else
161             framePtr = rand() % npoints;
162         return;
163     }
164 
165     if (reverse) {
166         if (!pivot) pivot = npoints;
167         if (framePtr == pivot - 1) applyPendingParChanges();
168         framePtr--;
169         if (framePtr == -1) {
170             if (!enableLoop) seqFinished = true;
171             if (reflect  || !backward) {
172                 reverse = false;
173                 framePtr = 0;
174             }
175             else framePtr = pivot - 1;
176         }
177         else if (framePtr == pivot - 1) {
178             if (!enableLoop) seqFinished = true;
179             if (loopMarker < 0) reflect = true;
180             if (loopMarker > 0) reflect = false;
181             if (reflect) {
182                 reverse = false;
183                 framePtr = pivot;
184             }
185             else framePtr = npoints - 1;
186         }
187     }
188     else {
189         if (!framePtr) applyPendingParChanges();
190         framePtr++;
191         if (framePtr == npoints) {
192             if (!enableLoop) seqFinished = true;
193 
194             if (reflect || backward) {
195                 reverse = true;
196                 framePtr = npoints - 1;
197             }
198             else framePtr = pivot;
199         }
200         else if ((framePtr == pivot)) {
201             if (!pivot) pivot = npoints;
202             if (!enableLoop) seqFinished = true;
203             if (loopMarker > 0) reflect = true;
204             if (loopMarker < 0) reflect = false;
205             if (reflect) {
206                 reverse = true;
207                 framePtr = pivot - 1;
208             }
209             else framePtr = 0;
210         }
211     }
212 }
213 
getData(std::vector<Sample> * p_data)214 void MidiSeq::getData(std::vector<Sample> * p_data)
215 {
216     Sample sample;
217     int lt = 0;
218     int l1 = 0;
219     const int step = TPQN / res;
220     const int npoints = res * size;
221 
222     data.resize(npoints);
223 
224     lt = step * npoints;
225     for (l1 = 0; l1 < npoints; l1++) data[l1] = customWave[l1];
226     sample.value = -1;
227     sample.tick = lt;
228     data.push_back(sample);
229 
230     *p_data = data;
231 }
232 
updateResolution(int val)233 void MidiSeq::updateResolution(int val)
234 {
235     res = val;
236     resizeAll();
237 }
238 
updateSize(int val)239 void MidiSeq::updateSize(int val)
240 {
241     size = val;
242     resizeAll();
243 }
244 
updateLoop(int val)245 void MidiSeq::updateLoop(int val)
246 {
247     backward = val&1;
248     pingpong = val&2;
249     enableLoop = !(val&4);
250     curLoopMode = val;
251     if (seqFinished) {
252         seqFinished = false;
253         setFramePtr(0);
254     }
255 }
256 
updateNoteLength(int val)257 void MidiSeq::updateNoteLength(int val)
258 {
259     notelengthDefer = val;
260     if (deferChanges) {
261         parChangesPending = true;
262     }
263     else notelength = val;
264 }
265 
updateVelocity(int val)266 void MidiSeq::updateVelocity(int val)
267 {
268     velDefer = val;
269     if (deferChanges) {
270         parChangesPending = true;
271     }
272     else vel = val;
273 }
274 
updateTranspose(int val)275 void MidiSeq::updateTranspose(int val)
276 {
277     transpDefer = val;
278     if (deferChanges) {
279         parChangesPending = true;
280     }
281     else transp = val;
282 }
283 
recordNote(int val)284 void MidiSeq::recordNote(int val)
285 {
286         setRecordedNote(val);
287         currentRecStep++;
288         currentRecStep %= (res * size);
289         dataChanged = true;
290 }
291 
setCustomWavePoint(double mouseX,double mouseY)292 int MidiSeq::setCustomWavePoint(double mouseX, double mouseY)
293 {
294     currentRecStep = mouseX * res * size;
295     setRecordedNote(12 * (mouseY * nOctaves + baseOctave));
296     return (currentRecStep);
297 }
298 
mouseEvent(double mouseX,double mouseY,int buttons,int pressed)299 int MidiSeq::mouseEvent(double mouseX, double mouseY, int buttons, int pressed)
300 {
301     int ix = 0;
302 
303     if ((mouseY < 0) && (pressed != 2)) {
304         if (mouseX < 0) mouseX = 0;
305         if (buttons == 2) mouseX = - mouseX;
306         setLoopMarkerMouse(mouseX);
307         return (0);
308     }
309 
310     if ((mouseX > 1) || (mouseX < 0) || (mouseY > 1) || (mouseY < 0)) return (0);
311 
312     if (buttons == 2) {
313         if (pressed == 1) {
314             lastMute = toggleMutePoint(mouseX);
315             ix = lastMute;
316         }
317         else if (pressed == 0)
318             ix = setMutePoint(mouseX, lastMute);
319     }
320     else if (pressed != 2) {
321         ix = setCustomWavePoint(mouseX, mouseY);
322     }
323     dataChanged = true;
324 
325     return (ix);
326 }
327 
setLoopMarkerMouse(double mouseX)328 void MidiSeq::setLoopMarkerMouse(double mouseX)
329 {
330     const int npoints = res * size;
331     if (mouseX > 0) setLoopMarker(mouseX * (double)npoints + .5);
332     else setLoopMarker(mouseX * (double)npoints - .5);
333 }
334 
setLoopMarker(int ix)335 void MidiSeq::setLoopMarker(int ix)
336 {
337     const int npoints = res * size;
338     loopMarker = ix;
339     if (abs(loopMarker) >= npoints) loopMarker = 0;
340     if (!loopMarker) nPoints = npoints;
341     else nPoints = abs(loopMarker);
342 }
343 
updateDispVert(int mode)344 void MidiSeq::updateDispVert(int mode)
345 {
346     switch (mode) {
347         case 0:
348             nOctaves = 4;
349             baseOctave = 3;
350         break;
351         case 1:
352             nOctaves = 2;
353             baseOctave = 5;
354         break;
355         case 2:
356             nOctaves = 2;
357             baseOctave = 4;
358         break;
359         case 3:
360             nOctaves = 2;
361             baseOctave = 3;
362         break;
363         default:
364             nOctaves = 4;
365             baseOctave = 3;
366     }
367 }
368 
setRecordMode(int on)369 void MidiSeq::setRecordMode(int on)
370 {
371     recordMode = on;
372 }
373 
setRecordedNote(int note)374 void MidiSeq::setRecordedNote(int note)
375 {
376     Sample sample;
377 
378     sample = customWave[currentRecStep];
379     sample.value = note;
380     sample.tick = currentRecStep * TPQN / res;
381     customWave[currentRecStep] = sample;
382 }
383 
resizeAll()384 void MidiSeq::resizeAll()
385 {
386     const int step = TPQN / res;
387     const int npoints = res * size;
388     Sample sample;
389 
390     framePtr%=npoints;
391     currentRecStep%=npoints;
392 
393     if (maxNPoints < npoints) {
394         int lt = 0;
395         for (int l1 = 0; l1 < npoints; l1++) {
396             if (l1 >= maxNPoints)
397                 muteMask[l1] = muteMask[l1 % maxNPoints];
398             sample = customWave[l1 % maxNPoints];
399             sample.tick = lt;
400             sample.muted = muteMask[l1];
401             customWave[l1] = sample;
402             lt+=step;
403         }
404         maxNPoints = npoints;
405     }
406 
407     if (!loopMarker) nPoints = npoints;
408     if (abs(loopMarker) >= npoints) loopMarker = 0;
409     dataChanged = true;
410 }
411 
toggleMutePoint(double mouseX)412 bool MidiSeq::toggleMutePoint(double mouseX)
413 {
414     Sample sample;
415     bool m;
416     int loc = mouseX * (res * size);
417 
418     m = muteMask[loc];
419     muteMask[loc] = !m;
420     sample = customWave[loc];
421     sample.muted = !m;
422     customWave[loc] = sample;
423     return(!m);
424 }
425 
setMutePoint(double mouseX,bool on)426 int MidiSeq::setMutePoint(double mouseX, bool on)
427 {
428     Sample sample;
429     int loc = mouseX * (res * size);
430 
431     sample = customWave[loc];
432     sample.muted = on;
433     customWave[loc] = sample;
434     muteMask[loc] = on;
435     return (loc);
436 }
437 
setFramePtr(int ix)438 void MidiSeq::setFramePtr(int ix)
439 {
440     framePtr=ix;
441 
442     if (!ix) {
443         seqFinished = (enableNoteOff && !noteCount);
444         restartFlag = false;
445         if (backward) {
446             reverse = true;
447             if (loopMarker) framePtr = abs(loopMarker) - 1;
448             else framePtr = res * size - 1;
449         }
450         else reverse = false;
451 
452         reflect = pingpong;
453     }
454 }
455 
applyPendingParChanges()456 void MidiSeq::applyPendingParChanges()
457 {
458     if (!parChangesPending) return;
459 
460     int olddefer = deferChanges;
461     deferChanges = false;
462 
463     setMuted(isMutedDefer);
464     updateNoteLength(notelengthDefer);
465     updateVelocity(velDefer);
466     updateTranspose(transpDefer);
467 
468     deferChanges = olddefer;
469     parChangesPending = false;
470     needsGUIUpdate = true;
471 
472 }
473 
setNextTick(int tick)474 void MidiSeq::setNextTick(int tick)
475 {
476     int tickres = TPQN/res;
477     int pos = (tick/tickres) % nPoints;
478 
479     reverse = false;
480     if (pingpong || (loopMarker > 0)) reverse = (((tick/tickres) / nPoints) % 2);
481 
482     if (backward) reverse = !reverse;
483     if (reverse) pos = nPoints - pos;
484 
485     setFramePtr(pos);
486     nextTick = (tick/tickres) * tickres;
487 }
488