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