1 /*!
2  * @file midiarp.cpp
3  * @brief Implements the MidiArp MIDI worker class for the Arpeggiator 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.
tcl_record_argnull12  *
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 <cstdlib>
25 #include <cstdio>
26 #include <iostream>
27 
28 #include "midiarp.h"
29 
30 /*
31 #include "lockfree.h"
32 
33 // this class must be implemented in such way
34 // so that its copy (= operator) is realtime safe
35 class MidiEngine
36 {
37 public:
38     int foo;
39 };
40 */
41 
42 MidiArp::MidiArp()
43 {
44     /*
45     // this simulates access from the two threads
46     // lock congestion is simulated by attempt to update
47     // when LockedData destructor is not called
48     {
49         LockFreeStore<MidiEngine> store; // store for the two instances of MidiEngine
50         const MidiEngine * rdata(store); // pointer to the read-only store, to be used from the seq/jack driver thread
51 
52         // write to the writable store
53         {
54             LockedData<MidiEngine> ldata(store); // lock the writable store, destructor will unlock it
55             ldata->foo = 1;                       // write access the writable store (lock obtained)
56             qWarning("gui: %d", ldata->foo);      // read access to the writable store (lock obtained)
57         } // ldata gets out of the scope and its destructor is called. this results in unlock of the writable store
58 
59         // attempt update the read-only store and report whether the update succeeded
60         // should succeed because the writable store is not locked
61         qWarning("update %s", store.try_lockfree_update() ? "complete" : "not complete (gui thread locked)");
62         // print the data in the read-only store
63         qWarning("seq: %d", rdata->foo);
64 
65         // write to the writable store
66         {
67             LockedData<MidiEngine> ldata(store);
68             ldata->foo = 2;
69             qWarning("gui: %d", ldata->foo);
70 
71             // attempt update the read-only store and report whether the update succeeded
72             // should fail because the writable store is locked
73             qWarning("update %s", store.try_lockfree_update() ? "complete" : "not complete (gui thread locked)");
74             qWarning("seq: %d (data locked by gui)", rdata->foo);
75         }
76 
77         // print the data in the read-only store before the update
78         qWarning("seq: %d (before update)", rdata->foo);
79         // attempt update the read-only store and report whether the update succeeded
80         // should succeed because the writable store is not locked
81         qWarning("update %s", store.try_lockfree_update() ? "complete" : "not complete (gui thread locked)");
82         // print the data in the read-only store after the update
83         qWarning("seq: %d (after update)", rdata->foo);
84     }
85 */
86     noteBufPtr = 0;
87     releaseNoteCount = 0;
88     purgeReleaseFlag = false;
89     stepWidth = 1.0;     // stepWidth relative to global queue stepWidth
90     minStepWidth = 1.0;
91     maxOctave = 0;
92     minOctave = 0;
93 
94     octMode = 0;
95     octLow = 0;
96     octHigh = 0;
97     octOfs = 0;
98     octIncr = 0;
99 
100     nSteps = 1.0;
101     len = 0.5;       // note length
102     vel = 0.8;  // velocity relative to global velocity
103     noteIndex[0] = 0;
104     patternIndex = 0;
105     patternLen = 0;
106     semitone = 0;
107     patternMaxIndex = 0;
108     noteOfs = 0;
109     arpTick = 0;
110     returnTick = 0;
111     chordMode = false;
112     randomTick = 0;
113     randomVelocity = 0;
114     randomLength = 0;
115     randomTickAmp = 0;
116     randomVelocityAmp = 0;
117     randomLengthAmp = 0;
118     hasNewNotes = false;
119     repeatPatternThroughChord = 1;
120     attack_time = 0.0;
121     release_time = 0.0;
122     sustain = false;
123     sustainBufferCount = 0;
124     latch_mode = false;
125     latchBufferCount = 0;
126     lastLatchTick = 0;
127     trigDelayTicks = 4;
128 }
129 
130 bool MidiArp::handleEvent(MidiEvent inEv, int tick, int keep_rel)
131 {
132     if (inEv.channel != chIn && chIn != OMNI) return(true);
133     if ((inEv.type == EV_CONTROLLER) &&
134         ((inEv.data == CT_ALLNOTESOFF) || (inEv.data == CT_ALLSOUNDOFF))) {
135         clearNoteBuffer();
136         return(true); // In case we receive all notes off we still forward
137     }
138     if ((inEv.type == EV_CONTROLLER) && (inEv.data == CT_FOOTSW)) {
139         setSustain((inEv.value == 127), tick);
140         return(false);
141     }
142 
143     if (inEv.type != EV_NOTEON) return(true);
144     if (((inEv.data < indexIn[0]) || (inEv.data > indexIn[1]))
145         || ((inEv.value < rangeIn[0]) || (inEv.value > rangeIn[1]))) {
146         return(true);
147     }
148 
149     if (inEv.value) {
150         // This is a NOTE ON event
151         if (!getPressedNoteCount() || trigLegato) {
152             purgeLatchBuffer(tick);
153             if (restartByKbd) restartFlag = true;
154             // if we have been triggered, remove pending release notes
155             if (trigByKbd && release_time > 0) purgeReleaseNotes(noteBufPtr);
156         }
157 
158         addNote(inEv.data, inEv.value, tick);
159 
160         if (repeatPatternThroughChord == 2) noteOfs = noteCount - 1;
161 
162         if ((trigByKbd && (getPressedNoteCount() == 1))
163                     || trigLegato) {
164             initArpTick(tick + trigDelayTicks);
165             gotKbdTrig = true;
166         }
167     }
168     else {
169         // This is a NOTE OFF event
170 
171         if (!noteCount) {
172             return(false);
173         }
174         if (sustain) {
175             if (sustainBufferCount == MAXNOTES - 1) purgeSustainBuffer(tick);
176             sustainBuffer[sustainBufferCount] = inEv.data;
177             sustainBufferCount++;
178             return(false);
179         }
180 
181         if (latch_mode && keep_rel) {
182             if (latchBufferCount == MAXNOTES - 1) purgeLatchBuffer(tick);
183             latchBuffer[latchBufferCount] = inEv.data;
184             latchBufferCount++;
185             if (latchBufferCount != noteCount) {
186                 if ((uint)tick > (uint)(lastLatchTick + 30)
187                     && (latchBufferCount > 1)) purgeLatchBuffer(tick);
188                 lastLatchTick = tick;
189             }
190             return(false);
191         }
192 
193         releaseNote(inEv.data, tick, keep_rel);
194     }
195 
196     return(false);
197 }
198 
199 void MidiArp::addNote(int note, int vel, int tick)
200 {
201         // modify buffer that is not accessed by arpeggio output
202     int bufPtr = (noteBufPtr) ? 0 : 1;
203     int index = 0;
204 
205     if (!noteCount || (note > notes[bufPtr][0][noteCount - 1])
206             || (repeatPatternThroughChord == 4) )
207         index = noteCount;
208     else {
209         while (index < MAXNOTES && note > notes[bufPtr][0][index]) index++;
210 
211         for (int l3 = 0; l3 < 4; l3++) {
212             for (int l2 = noteCount; l2 > index; l2--) {
213                 notes[bufPtr][l3][l2] = notes[bufPtr][l3][l2 - 1];
214             }
215         }
216     }
217     notes[bufPtr][0][index] = note;
218     notes[bufPtr][1][index] = vel;
219     notes[bufPtr][2][index] = tick;
220     notes[bufPtr][3][index] = 0;
221     noteCount++;
222 
223     copyNoteBuffer();
224 }
225 
226 void MidiArp::releaseNote(int note, int tick, bool keep_rel)
227 {
228     // modify buffer that is not accessed by arpeggio output
229     int bufPtr = (noteBufPtr) ? 0 : 1;
230     if ((!keep_rel) || (!release_time)) {
231         //definitely remove from buffer
232         if (note == notes[bufPtr][0][noteCount - 1]
233                 && (repeatPatternThroughChord != 4)) {
234             //note is on top of buffer: only decrement noteCount
235             noteCount--;
236             if (repeatPatternThroughChord == 2) noteOfs = noteCount - 1;
237         }
238         else {
239             //note is not on top: take out the note and pull down all above
240             int index = 0;
241             while ((index < MAXNOTES) && (index < noteCount)
242                 && (note != notes[bufPtr][0][index])) index++;
243             deleteNoteAt(index, bufPtr);
244         }
245     }
246     else tagAsReleased(note, tick, bufPtr);
247 
248     copyNoteBuffer();
249 }
250 
251 void MidiArp::removeNote(int *noteptr, int tick, int keep_rel)
252 {
253     int bufPtr, note ;
254     note = *noteptr;
255 
256     // modify buffer that is not accessed by arpeggio output
257     bufPtr = (noteBufPtr) ? 0 : 1;
258     if (!noteCount) {
259         return;
260     }
261     if (!keep_rel || (!release_time)) {
262         // definitely remove from buffer, do NOT check for doubles
263         if (note == notes[bufPtr][0][noteCount - 1]
264                 && (repeatPatternThroughChord != 4)) {
265             // note is on top of buffer: only decrement noteCount
266             noteCount--;
267             if (tick == -1) releaseNoteCount--;
268             if ((repeatPatternThroughChord == 2) && (noteOfs)) noteOfs--;
269         }
270         else {
271             // note is not on top: take out the note and pull down all above
272             int index = 0;
273             if (tick != -1) {
274                 while ((index < noteCount)
275                         && (note != notes[bufPtr][0][index])) index++;
276             }
277             else {
278                 while ((index < noteCount)
279                         && ((note != notes[bufPtr][0][index])
280                         || (!notes[bufPtr][3][index]))) index++;
281             }
282 
283 
284             if (note == notes[bufPtr][0][index]) {
285                 deleteNoteAt(index, bufPtr);
286                 if (tick == -1) releaseNoteCount--;
287                 for (int l2 = index; l2 < noteCount; l2++) {
288                     old_attackfn[l2] = old_attackfn[l2 + 1];
289                 }
290             }
291         }
292     }
293     else tagAsReleased(note, tick, bufPtr);
294 
295     copyNoteBuffer();
296 }
297 
298 void MidiArp::deleteNoteAt(int index, int bufPtr)
299 {
300     for (int l3 = 0; l3 < 4; l3++) {
301         for (int l2 = index; l2 < noteCount - 1; l2++) {
302             notes[bufPtr][l3][l2] = notes[bufPtr][l3][l2 + 1];
303         }
304     }
305     noteCount--;
306 }
307 
308 void MidiArp::tagAsReleased(int note, int tick, int bufPtr)
309 {
310     //mark as released but keep with note off time tick
311     int l1 = 0;
312     while ((l1 < noteCount)
313         && ((note != notes[bufPtr][0][l1]) || (notes[bufPtr][3][l1]))) {
314         l1++;
315     }
316     if (note == notes[bufPtr][0][l1]) {
317         notes[bufPtr][3][l1] = 1;
318         notes[bufPtr][2][l1] = tick;
319         releaseNoteCount++;
320     }
321 }
322 
323 void MidiArp::copyNoteBuffer()
324 {
325     int newBufPtr = noteBufPtr;
326     noteBufPtr++;
327     noteBufPtr%=2;
328 
329     for (int l2 = 0; l2 < noteCount; l2++) {
330         for (int l3 = 0; l3 < 4; l3++) {
331             notes[newBufPtr][l3][l2] = notes[noteBufPtr][l3][l2];
332         }
333     }
334 }
335 
336 void MidiArp::getNote(int *tick, int note[], int velocity[], int *length)
337 {
338     char c;
339     int l1, tmpIndex[MAXCHORD], chordIndex, grooveTmp;
340     int current_octave = 0;
341     bool outOfRange = false;
342     bool gotCC, pause;
343 
344 
345     chordIndex = 0;
346     tmpIndex[0] = 0;
347     tmpIndex[1] = -1;
348     gotCC = false;
349     pause = false;
350 
351     if (purgeReleaseFlag) {
352         purgeLatchBuffer(arpTick);
353         purgeReleaseNotes(noteBufPtr);
354         purgeReleaseFlag = false;
355     }
356     if (restartFlag) advancePatternIndex(true);
357 
358     if (!patternIndex) initLoop();
359 
360     framePtr++;
361     if (framePtr >= nPoints) framePtr = 0;
362 
363     chordSemitone[0] = semitone;
364     do {
365         if (patternLen)
366             c = (pattern.at(patternIndex));
367         else
368             c = ' ';
369 
370         if (c != ' ') {
371             if (isdigit(c) || (c == 'p')) {
372                 tmpIndex[chordIndex] = c - '0' + noteOfs;
373                 if ((chordIndex < MAXNOTES - 1) && chordMode) {
374                     chordIndex++;
375                     chordSemitone[chordIndex] = semitone;
376                 }
377                 gotCC = false;
378                 pause = (c == 'p');
379             }
380             else {
381                 gotCC = true;
382 
383                 switch(c) {
384                     case '(':
385                         chordMode = true;
386                         break;
387                     case ')':
388                         // mark end of chord
389                         tmpIndex[chordIndex] = -1;
390                         chordMode = false;
391                         gotCC = false;
392                         break;
393                     case 't':
394                         semitone++;
395                         break;
396                     case 'g':
397                         semitone--;
398                         break;
399                     case '+':
400                         semitone+=12;
401                         break;
402                     case '-':
403                         semitone-=12;
404                         break;
405                     case '=':
406                         semitone = 0;
407                         break;
408                     case '>':
409                         stepWidth *= .5;
410                         break;
411                     case '<':
412                         stepWidth *= 2.0;
413                         break;
414                     case '.':
415                         stepWidth = 1.0;
416                         break;
417                     case '/':
418                         vel += 0.2;
419                         break;
420                     case '\\':
421                         vel -= 0.2;
422                         break;
423                     case 'd':
424                         len *= 2.0;
425                         break;
426                     case 'h':
427                         len *= .5;
428                         break;
429                 }
430                 chordSemitone[chordIndex] = semitone;
431             }
432         }
433         current_octave = octOfs;
434     } while (advancePatternIndex(false) && (gotCC || chordMode || c == ' '));
435 
436     l1 = 0;
437     if (noteCount) do {
438         noteIndex[l1] = (noteCount) ? tmpIndex[l1] % noteCount : 0;
439         note[l1] = clip(notes[noteBufPtr][0][noteIndex[l1]] + current_octave * 12
440                 + chordSemitone[l1], 0, 127, &outOfRange);
441         if (outOfRange) checkOctaveAtEdge(false);
442 
443         grooveTmp = (framePtr % 2) ? grooveVelocity : -grooveVelocity;
444 
445         double releasefn = 0;
446         if ((release_time > 0) && (notes[noteBufPtr][3][noteIndex[l1]])) {
447             releasefn = 1.0 - (double)(arpTick
448                     - notes[noteBufPtr][2][noteIndex[l1]])
449                     / (release_time * (double)TPQN * 2);
450 
451             if (releasefn < 0.0) releasefn = 0.0;
452         }
453         else releasefn = 1.0;
454 
455         double attackfn = 0;
456         if (attack_time > 0) {
457             if (!notes[noteBufPtr][3][noteIndex[l1]]) {
458                 attackfn = (double)(arpTick
459                     - notes[noteBufPtr][2][noteIndex[l1]])
460                     / (attack_time * (double)TPQN * 2);
461 
462                 if (attackfn > 1.0) attackfn = 1.0;
463                 old_attackfn[noteIndex[l1]] = attackfn;
464             }
465             else attackfn = old_attackfn[noteIndex[l1]];
466         }
467         else attackfn = 1.0;
468 
469         velocity[l1] = clip((double)notes[noteBufPtr][1][noteIndex[l1]]
470                 * vel * (1.0 + 0.005 * (double)(randomVelocity + grooveTmp))
471                 * releasefn * attackfn, 0, 127, &outOfRange);
472 
473         if ((release_time > 0.) && (notes[noteBufPtr][3][noteIndex[l1]]) && (!velocity[l1])) {
474             removeNote(&notes[noteBufPtr][0][noteIndex[l1]], -1, 0);
475         }
476         else {
477             l1++;
478         }
479     } while (  (l1 < MAXCHORD - 1)
480             && (tmpIndex[l1] >= 0)
481             && ((l1 < noteCount) || (tmpIndex[l1] == 0))
482             && (noteCount));
483 
484     note[l1] = -1; // mark end of array
485     grooveTmp = (framePtr % 2) ? grooveLength : -grooveLength;
486     *length = clip(len * stepWidth * (double)TPQN
487             * (1.0 + 0.005 * (double)(randomLength + grooveTmp)), 2,
488             1000000,  &outOfRange);
489 
490     if (!framePtr) grooveTick = newGrooveTick;
491     grooveTmp = TPQN * stepWidth * grooveTick * 0.01;
492     /* pairwise application of new groove shift */
493     if (!(framePtr % 2)) {
494         grooveTmp = -grooveTmp;
495         grooveTick = newGrooveTick;
496     }
497     arpTick += stepWidth * TPQN + grooveTmp;
498 
499     if (!trigByKbd && !framePtr && !grooveTick) {
500         /* round-up to current resolution (quantize) */
501         arpTick/= (TPQN * minStepWidth);
502         arpTick*= (TPQN * minStepWidth);
503     }
504 
505     *tick = arpTick + clip(stepWidth * 0.25 * (double)randomTick, 0,
506             1000, &outOfRange);
507 
508     if (!(patternLen && noteCount) || pause || isMuted) {
509         velocity[0] = 0;
510     }
511 }
512 
513 void MidiArp::checkOctaveAtEdge(bool reset)
514 {
515     if (!octMode) return;
516     if (!octHigh && !octLow) {
517         octOfs = 0;
518         return;
519     }
520 
521     if (reset) {
522         octOfs = octLow;
523         if (octMode == 2) {
524             octOfs = octHigh;
525             octIncr = -1;
526         }
527         else {
528             octOfs = octLow;
529             octIncr = 1;
530         }
531         return;
532     }
533     if (octOfs > octHigh) {
534         if (octMode == 3){
535             octIncr = - octIncr;
536             octOfs--;
537             octOfs--;
538         }
539         else {
540             octOfs = octLow;
541         }
542     }
543     if (octOfs < octLow) {
544         if (octMode == 3) {
545             octIncr = - octIncr;
546             octOfs++;
547             octOfs++;
548         }
549         else {
550             octOfs = octHigh;
551         }
552     }
553 }
554 
555 bool MidiArp::advancePatternIndex(bool reset)
556 {
557     if (patternLen) {
558         patternIndex++;
559     }
560 
561     if ((patternIndex >= patternLen) || reset) {
562         patternIndex = 0;
563         restartFlag = false;
564         applyPendingParChanges();
565 
566         switch (repeatPatternThroughChord) {
567             case 1:
568             case 4:
569                 noteOfs++;
570                 if ((noteCount - 1 < patternMaxIndex + noteOfs) || reset) {
571                     noteOfs = 0;
572                     octOfs+=octIncr;
573                     checkOctaveAtEdge(reset);
574                 }
575                 break;
576             case 2:
577                 noteOfs--;
578                 if ((noteCount -1 < patternMaxIndex) ||
579                     (noteOfs < patternMaxIndex) || reset) {
580                     noteOfs = noteCount - 1;
581                     octOfs+=octIncr;
582                     checkOctaveAtEdge(reset);
583                 }
584                 break;
585             case 3:
586                 if (noteCount) noteOfs = rand() % noteCount;
587                 break;
588             default:
589                 noteOfs = 0;
590         }
591         return(false);
592     }
593     return(true);
594 }
595 
596 void MidiArp::initLoop()
597 {
598     stepWidth = 1.0;
599     len = 0.5;
600     vel = 0.8;
601     semitone = 0;
602     framePtr = 0;
603 }
604 
605 void MidiArp::getNextFrame(int askedTick)
606 {
607     gotKbdTrig = false;
608 
609     newRandomValues();
610 
611     int l1 = 0;
612     //allow 8 ticks of tolerance for echo tick for external sync
613     if ((askedTick + 8) >= nextTick) {
614         returnTick = nextTick;
615         getNote(&nextTick, nextNote, nextVelocity, &nextLength);
616         while ((l1 < MAXCHORD - 1) && (nextNote[l1] >= 0)) {
617             returnNote[l1] = nextNote[l1];
618             returnVelocity[l1] = nextVelocity[l1];
619             l1++;
620         }
621         returnLength = nextLength;
622         hasNewNotes = true;
623     }
624     else hasNewNotes = false;
625 
626     returnNote[l1] = -1; // mark end of chord
627 }
628 
629 void MidiArp::foldReleaseTicks(int tick)
630 {
631     int bufPtr, l2;
632 
633     bufPtr = (noteBufPtr) ? 0 : 1;
634 
635     if (tick <= 0) {
636         purgeReleaseNotes(bufPtr);
637         return;
638     }
639 
640     for (l2 = 0; l2 < noteCount; l2++) {
641             notes[bufPtr][2][l2] -= tick;
642     }
643 
644     copyNoteBuffer();
645     lastLatchTick -= tick;
646 }
647 
648 void MidiArp::initArpTick(int tick)
649 {
650     arpTick = tick;
651     returnVelocity[0] = 0;
652     nextTick  = tick;
653     nextVelocity[0] = 0;
654     noteIndex[0] = -1;
655     patternIndex = 0;
656     framePtr = 0;
657 }
658 
659 std::string MidiArp::stripPattern(const std::string& p_pattern)
660 {
661     std::string p = p_pattern;
662     patternLen = 0;
663     if (!p.length()) return (p);
664 
665     char c = p[p.length() - 1];
666     while (!isdigit(c) && (c != 'p') && (c != ')')) {
667         p = p.substr(0, p.length() - 1);
668         if (p.length() < 1) break;
669         c = p[p.length() - 1];
670     }
671 
672     patternLen = p.length();
673 
674     return (p);
675 }
676 
677 
678 void MidiArp::updatePattern(const std::string& p_pattern)
679 {
680     int l1;
681     char c;
682 
683     pattern = p_pattern;
684     patternMaxIndex = 0;
685     minStepWidth = 1.0;
686     minOctave = 0;
687     maxOctave = 0;
688 
689     double stepwd = 1.0;
690     double nsteps = 0.;
691     int chordindex = 0;
692     bool chordmd = false;
693     int oct = 0;
694     int npoints = 0;
695 
696     pattern = stripPattern(pattern);
697     // determine some useful properties of the arp pattern,
698     // number of octaves, step width and number of steps in beats and
699     // number of points
700 
701     for (l1 = 0; l1 < patternLen; l1++) {
702         c = pattern[l1];
703 
704         if (isdigit(c)) {
705             if (!chordindex) {
706                 nsteps += stepwd;
707                 npoints++;
708                 if (chordmd) chordindex++;
709             }
710             if (isdigit(c) && (c  - '0' > patternMaxIndex))
711                 patternMaxIndex = c - '0';
712         }
713         switch(c) {
714             case '(':
715                 chordmd = true;
716                 chordindex = 0;
717                 break;
718 
719             case ')':
720                 chordmd = false;
721                 chordindex = 0;
722                 break;
723 
724             case '>':
725                 stepwd *= .5;
726                 if (stepwd < minStepWidth)
727                     minStepWidth *= .5;
728                 break;
729 
730             case '<':
731                 stepwd *= 2.0;
732                 break;
733 
734             case '.':
735                 stepwd = 1.0;
736                 break;
737 
738             case 'p':
739                 if (!chordmd) {
740                     nsteps += stepwd;
741                	    npoints++;
742                 }
743                 break;
744 
745             case '+':
746                 oct++;
747                 if (oct > maxOctave)
748                     maxOctave++;
749                 break;
750 
751             case '-':
752                 oct--;
753                 if (oct < minOctave)
754                     minOctave--;
755                 break;
756 
757             case '=':
758                 oct=0;
759                 break;
760 
761             default:
762                 ;
763         }
764 
765     }
766 
767     patternIndex = 0;
768     framePtr = 0;
769     noteOfs = 0;
770     nSteps = nsteps;
771     nPoints = npoints;
772 }
773 
774 void MidiArp::newRandomValues()
775 {
776     randomTick = (double)randomTickAmp * (0.5 - (double)rand()
777             / (double)RAND_MAX);
778     randomVelocity = (double)randomVelocityAmp * (0.5 - (double)rand()
779             / (double)RAND_MAX);
780     randomLength = (double)randomLengthAmp * (0.5 - (double)rand()
781             / (double)RAND_MAX);
782 }
783 
784 void MidiArp::updateRandomTickAmp(int val)
785 {
786     randomTickAmp = val;
787 }
788 
789 void MidiArp::updateOctaveMode(int val)
790 {
791     octMode = val;
792     octOfs = 0;
793 
794     switch (val) {
795         case 0:
796             octIncr = 0;
797         break;
798 
799         case 1:
800             octIncr = 1;
801         break;
802 
803         case 2:
804             octIncr = -1;
805         break;
806 
807         case 3:
808             octIncr = 1;
809         break;
810     }
811 }
812 
813 void MidiArp::updateRandomVelocityAmp(int val)
814 {
815     randomVelocityAmp = val;
816 }
817 
818 void MidiArp::updateRandomLengthAmp(int val)
819 {
820     randomLengthAmp = val;
821 }
822 
823 void MidiArp::updateAttackTime(int val)
824 {
825     attack_time = (double)val;
826 }
827 
828 void MidiArp::updateReleaseTime(int val)
829 {
830     if (release_time > 0 && val == 0) purgeReleaseFlag = true;
831 
832     release_time = (double)val;
833 }
834 
835 void MidiArp::clearNoteBuffer()
836 {
837     noteCount = 0;
838     latchBufferCount = 0;
839     releaseNoteCount = 0;
840 }
841 
842 int MidiArp::getPressedNoteCount()
843 {
844     int c = noteCount - latchBufferCount - releaseNoteCount;
845     return(c);
846 }
847 
848 void MidiArp::setSustain(bool on, int sustick)
849 {
850     sustain = on;
851     if (!sustain) {
852         purgeSustainBuffer(sustick);
853         if (latch_mode) purgeLatchBuffer(sustick);
854     }
855 }
856 
857 void MidiArp::purgeSustainBuffer(int sustick)
858 {
859     for (int l1 = 0; l1 < sustainBufferCount; l1++) {
860         int buf = sustainBuffer[l1];
861         removeNote(&buf, sustick, 1);
862     }
863     sustainBufferCount = 0;
864 }
865 
866 void MidiArp::setLatchMode(bool on)
867 {
868     latch_mode = on;
869     if (!latch_mode) purgeLatchBuffer(arpTick);
870 }
871 
872 void MidiArp::purgeLatchBuffer(int latchtick)
873 {
874     for (int l1 = 0; l1 < latchBufferCount; l1++) {
875         int buf = latchBuffer[l1];
876         removeNote(&buf, latchtick, 1);
877     }
878     latchBufferCount = 0;
879 }
880 
881 void MidiArp::purgeReleaseNotes(int bufptr)
882 {
883     for (int l1 = noteCount - 1; l1 >= 0; l1--) {
884         if (notes[bufptr][3][l1])
885             deleteNoteAt(l1, bufptr);
886             releaseNoteCount--;
887     }
888 }
889 
890 void MidiArp::applyPendingParChanges()
891 {
892     if (!parChangesPending) return;
893 
894     int olddefer = deferChanges;
895     deferChanges = false;
896     setMuted(isMutedDefer);
897     deferChanges = olddefer;
898     parChangesPending = false;
899     needsGUIUpdate = true;
900 }
901 
902 void MidiArp::setNextTick(int tick)
903 {
904     if (nSteps == 0) return;
905 
906     returnTick = tick / (int)(nSteps*TPQN) * (int)(nSteps*TPQN);
907     patternIndex = 0;
908     framePtr = 0;
909     arpTick = returnTick;
910     nextTick = returnTick;
911 }
912