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(¬es[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