VCV Rack API v2
Loading...
Searching...
No Matches
midi.hpp
Go to the documentation of this file.
1#pragma once
2#include <algorithm>
3#include <dsp/common.hpp>
4#include <dsp/filter.hpp>
5#include <dsp/digital.hpp>
6#include <midi.hpp>
7#include <jansson.h>
8
9
10namespace rack {
11namespace dsp {
12
13
17template <int CHANNELS>
19 uint8_t channels;
20 int8_t vels[CHANNELS];
21 int8_t notes[CHANNELS];
22 bool gates[CHANNELS];
23 int8_t keyPressures[CHANNELS];
25 int8_t ccs[128];
26 int16_t pw;
27 bool clk;
28 bool start;
29 bool stop;
30 bool cont;
31 int64_t frame = -1;
32
34 reset();
35 }
36
37 void reset() {
38 channels = CHANNELS;
39 for (int c = 0; c < CHANNELS; c++) {
40 vels[c] = 100;
41 notes[c] = 60;
42 gates[c] = false;
43 keyPressures[c] = -1;
44 }
45 channelPressure = -1;
46 for (int i = 0; i < 128; i++) {
47 ccs[i] = -1;
48 }
49 pw = 0x2000;
50 clk = false;
51 start = false;
52 stop = false;
53 cont = false;
54 }
55
56 void panic() {
57 reset();
58 // Send all note off commands
59 for (int note = 0; note <= 127; note++) {
60 // Note off
62 m.setStatus(0x8);
63 m.setNote(note);
64 m.setValue(0);
65 m.setFrame(frame);
66 onMessage(m);
67 }
68 }
69
70 void setChannels(uint8_t channels) {
71 if (this->channels == channels)
72 return;
73 // Disable notes when channels decreases
74 for (uint8_t c = channels; c < this->channels; c++) {
75 setNoteGate(notes[c], false, c);
76 }
77 this->channels = channels;
78 }
79
81 void setVelocity(int8_t vel, int c) {
82 vels[c] = vel;
83 }
84
85 void setNoteGate(int8_t note, bool gate, int c) {
86 bool changedNote = gate && gates[c] && (note != notes[c]);
87 bool enabledGate = gate && !gates[c];
88 bool disabledGate = !gate && gates[c];
89 if (changedNote || disabledGate) {
90 // Note off
92 m.setStatus(0x8);
93 m.setNote(notes[c]);
94 m.setValue(vels[c]);
95 m.setFrame(frame);
96 onMessage(m);
97 }
98 if (changedNote || enabledGate) {
99 // Note on
101 m.setStatus(0x9);
102 m.setNote(note);
103 m.setValue(vels[c]);
104 m.setFrame(frame);
105 onMessage(m);
106 }
107 notes[c] = note;
108 gates[c] = gate;
109 }
110
111 void setKeyPressure(int8_t val, int c) {
112 if (keyPressures[c] == val)
113 return;
114 keyPressures[c] = val;
115 // Polyphonic key pressure
117 m.setStatus(0xa);
118 m.setNote(notes[c]);
119 m.setValue(val);
120 m.setFrame(frame);
121 onMessage(m);
122 }
123
124 void setChannelPressure(int8_t val) {
125 if (channelPressure == val)
126 return;
127 channelPressure = val;
128 // Channel pressure
130 m.setSize(2);
131 m.setStatus(0xd);
132 m.setNote(val);
133 m.setFrame(frame);
134 onMessage(m);
135 }
136
137 void setCc(int8_t cc, int id) {
138 if (ccs[id] == cc)
139 return;
140 ccs[id] = cc;
141 // Continuous controller
143 m.setStatus(0xb);
144 m.setNote(id);
145 m.setValue(cc);
146 m.setFrame(frame);
147 onMessage(m);
148 }
149
150 void setModWheel(int8_t cc) {
151 setCc(cc, 0x01);
152 }
153
154 void setVolume(int8_t cc) {
155 setCc(cc, 0x07);
156 }
157
158 void setBalance(int8_t cc) {
159 setCc(cc, 0x08);
160 }
161
162 void setPan(int8_t cc) {
163 setCc(cc, 0x0a);
164 }
165
166 void setSustainPedal(int8_t cc) {
167 setCc(cc, 0x40);
168 }
169
170 void setPitchWheel(int16_t pw) {
171 if (this->pw == pw)
172 return;
173 this->pw = pw;
174 // Pitch wheel
176 m.setStatus(0xe);
177 m.setNote(pw & 0x7f);
178 m.setValue((pw >> 7) & 0x7f);
179 m.setFrame(frame);
180 onMessage(m);
181 }
182
183 void setClock(bool clk) {
184 if (this->clk == clk)
185 return;
186 this->clk = clk;
187 if (clk) {
188 // Timing clock
190 m.setSize(1);
191 m.setStatus(0xf);
192 m.setChannel(0x8);
193 m.setFrame(frame);
194 onMessage(m);
195 }
196 }
197
198 void setStart(bool start) {
199 if (this->start == start)
200 return;
201 this->start = start;
202 if (start) {
203 // Start
205 m.setSize(1);
206 m.setStatus(0xf);
207 m.setChannel(0xa);
208 m.setFrame(frame);
209 onMessage(m);
210 }
211 }
212
213 void setContinue(bool cont) {
214 if (this->cont == cont)
215 return;
216 this->cont = cont;
217 if (cont) {
218 // Continue
220 m.setSize(1);
221 m.setStatus(0xf);
222 m.setChannel(0xb);
223 m.setFrame(frame);
224 onMessage(m);
225 }
226 }
227
228 void setStop(bool stop) {
229 if (this->stop == stop)
230 return;
231 this->stop = stop;
232 if (stop) {
233 // Stop
235 m.setSize(1);
236 m.setStatus(0xf);
237 m.setChannel(0xc);
238 m.setFrame(frame);
239 onMessage(m);
240 }
241 }
242
243 void setFrame(int64_t frame) {
244 this->frame = frame;
245 }
246
247 virtual void onMessage(const midi::Message& message) {}
248};
249
250
254template <uint8_t MAX_CHANNELS>
256 // Settings
257
259 uint8_t channels;
260
269
272
282
285
287 float pwRange;
288
290 bool smooth;
291
294
295 // States
296
298 int64_t clock;
299
301 bool pedal;
302
303 uint8_t notes[MAX_CHANNELS];
304 bool gates[MAX_CHANNELS];
305 uint8_t velocities[MAX_CHANNELS];
306 uint8_t aftertouches[MAX_CHANNELS];
307 std::vector<uint8_t> heldNotes;
309
313 int16_t pws[MAX_CHANNELS];
316 uint8_t mods[MAX_CHANNELS];
320
327
329 heldNotes.reserve(128);
330 reset();
331 }
332
334 void reset() {
335 channels = 1;
337 retriggerOnResume = false;
340 pwRange = 2.f;
341 smooth = true;
342 clockDivision = 24;
343 setFilterLambda(30.f);
344 panic();
345 }
346
348 void panic() {
349 clock = 0;
350 pedal = false;
351 for (uint8_t c = 0; c < MAX_CHANNELS; c++) {
352 // Middle C
353 notes[c] = 60;
354 gates[c] = false;
355 velocities[c] = 0;
356 aftertouches[c] = 0;
357 pws[c] = 0;
358 mods[c] = 0;
359 pwFilters[c].reset();
360 modFilters[c].reset();
361 }
362 rotateIndex = -1;
363 heldNotes.clear();
364 }
365
366 void processFilters(float deltaTime) {
367 uint8_t wheelChannels = getWheelChannels();
368 for (uint8_t c = 0; c < wheelChannels; c++) {
369 float pw = pws[c] / 8191.f;
370 pw = math::clamp(pw, -1.f, 1.f);
371 if (smooth)
372 pw = pwFilters[c].process(deltaTime, pw);
373 else
374 pwFilters[c].out = pw;
375
376 float mod = mods[c] / 127.f;
377 mod = math::clamp(mod, 0.f, 1.f);
378 if (smooth)
379 mod = modFilters[c].process(deltaTime, mod);
380 else
381 modFilters[c].out = mod;
382 }
383 }
384
385 void processPulses(float deltaTime) {
386 clockPulse.process(deltaTime);
387 clockDividerPulse.process(deltaTime);
388 startPulse.process(deltaTime);
389 stopPulse.process(deltaTime);
390 continuePulse.process(deltaTime);
391 for (uint8_t c = 0; c < channels; c++) {
392 retriggerPulses[c].process(deltaTime);
393 }
394 }
395
396 void processMessage(const midi::Message& msg) {
397 // DEBUG("MIDI: %ld %s", msg.getFrame(), msg.toString().c_str());
398
399 switch (msg.getStatus()) {
400 // note off
401 case 0x8: {
402 releaseNote(msg.getNote(), msg.getChannel(), msg.getValue());
403 } break;
404 // note on
405 case 0x9: {
406 uint8_t velocity = msg.getValue();
407 if (velocity > 0) {
408 pressNote(msg.getNote(), msg.getChannel(), velocity);
409 }
410 else {
411 // Note-on event with velocity 0 is an alternative for note-off event.
412 releaseNote(msg.getNote(), msg.getChannel(), -1);
413 }
414 } break;
415 // key pressure
416 case 0xa: {
417 // Set the aftertouches with the same note
418 // TODO Should we handle the MPE case differently?
419 for (uint8_t c = 0; c < MAX_CHANNELS; c++) {
420 if (notes[c] == msg.getNote())
421 aftertouches[c] = msg.getValue();
422 }
423 } break;
424 // cc
425 case 0xb: {
426 processCC(msg);
427 } break;
428 // channel pressure
429 case 0xd: {
430 if (channels > 1 && polyMode == MPE_MODE) {
431 // Set the channel aftertouch
432 aftertouches[msg.getChannel()] = msg.getNote();
433 }
434 else {
435 // Set all aftertouches
436 for (uint8_t c = 0; c < MAX_CHANNELS; c++) {
437 aftertouches[c] = msg.getNote();
438 }
439 }
440 } break;
441 // pitch wheel
442 case 0xe: {
443 uint8_t c = (channels > 1 && polyMode == MPE_MODE) ? msg.getChannel() : 0;
444 int16_t pw = msg.getValue();
445 pw <<= 7;
446 pw |= msg.getNote();
447 pw -= 8192;
448 pws[c] = pw;
449 } break;
450 case 0xf: {
451 processSystem(msg);
452 } break;
453 default: break;
454 }
455 }
456
457 void processCC(const midi::Message& msg) {
458 switch (msg.getNote()) {
459 // mod
460 case 0x01: {
461 uint8_t c = (channels > 1 && polyMode == MPE_MODE) ? msg.getChannel() : 0;
462 mods[c] = msg.getValue();
463 } break;
464 // sustain
465 case 0x40: {
466 if (msg.getValue() >= 64)
467 pressPedal();
468 else
469 releasePedal();
470 } break;
471 // all notes off (panic)
472 case 0x7b: {
473 if (msg.getValue() == 0) {
474 panic();
475 }
476 } break;
477 default: break;
478 }
479 }
480
481 void processSystem(const midi::Message& msg) {
482 switch (msg.getChannel()) {
483 // Song Position Pointer
484 case 0x2: {
485 int64_t pos = int64_t(msg.getNote()) | (int64_t(msg.getValue()) << 7);
486 clock = pos * 6;
487 } break;
488 // Timing
489 case 0x8: {
490 clockPulse.trigger(1e-3);
491 if (clock % clockDivision == 0) {
492 clockDividerPulse.trigger(1e-3);
493 }
494 clock++;
495 } break;
496 // Start
497 case 0xa: {
498 startPulse.trigger(1e-3);
499 clock = 0;
500 } break;
501 // Continue
502 case 0xb: {
503 continuePulse.trigger(1e-3);
504 } break;
505 // Stop
506 case 0xc: {
507 stopPulse.trigger(1e-3);
508 } break;
509 default: break;
510 }
511 }
512
513 uint8_t assignChannel(uint8_t note) {
514 if (channels <= 1)
515 return 0;
516
517 if (polyMode == REUSE_MODE) {
518 // Try to find channel with the same note
519 for (uint8_t c = 0; c < channels; c++) {
520 if (notes[c] == note)
521 return c;
522 }
523 }
525 // Find next available channel
526 for (uint8_t i = 0; i < channels; i++) {
527 rotateIndex++;
528 if (rotateIndex >= channels)
529 rotateIndex = 0;
530 if (!gates[rotateIndex])
531 return rotateIndex;
532 }
533 // No notes are available. Advance rotateIndex once more.
534 rotateIndex++;
535 if (rotateIndex >= channels)
536 rotateIndex = 0;
537 return rotateIndex;
538 }
539 if (polyMode == RESET_MODE) {
540 for (uint8_t c = 0; c < channels; c++) {
541 if (!gates[c])
542 return c;
543 }
544 return channels - 1;
545 }
546 if (polyMode == MPE_MODE) {
547 // This case is handled by querying the MIDI message channel.
548 return 0;
549 }
550 return 0;
551 }
552
553 void pressNote(uint8_t note, uint8_t channel, uint8_t velocity) {
554 // Remove existing similar note
555 heldNotes.erase(std::remove(heldNotes.begin(), heldNotes.end(), note), heldNotes.end());
556 // Push note to end
557 heldNotes.push_back(note);
558 // Handle polyphony modes
559 if (channels > 1) {
560 if (polyMode == MPE_MODE) {
561 // Output channel equals MIDI channel
562 }
563 else {
564 channel = assignChannel(note);
565 }
566 }
567 // Handle monophonic modes
568 else {
569 channel = 0;
571 // Always play note
572 }
574 if (heldNotes.size() > 1)
575 return;
576 }
578 uint8_t minNote = *std::min_element(heldNotes.begin(), heldNotes.end());
579 if (note != minNote)
580 return;
581 }
583 uint8_t maxNote = *std::max_element(heldNotes.begin(), heldNotes.end());
584 if (note != maxNote)
585 return;
586 }
587 }
588 // Set note
589 notes[channel] = note;
590 gates[channel] = true;
591 velocities[channel] = velocity;
592 retriggerPulses[channel].trigger(1e-3);
593 }
594
596 void releaseNote(uint8_t note, int8_t channel, int8_t velocity) {
597 // Remove the note
598 heldNotes.erase(std::remove(heldNotes.begin(), heldNotes.end(), note), heldNotes.end());
599 // Hold note if pedal is pressed
600 if (pedal)
601 return;
602 // Find output channel of released note, if any
603 if (channels > 1 && polyMode == MPE_MODE) {
604 // Each MPE channel must be monophonic so the output channel must be the released note channel
605 }
606 else {
607 // Find channel of active note
608 channel = -1;
609 for (uint8_t c = 0; c < channels; c++) {
610 if (gates[c] && notes[c] == note) {
611 channel = c;
612 break;
613 }
614 }
615 }
616 if (channel < 0) {
617 // Released note is not active on any channel
618 return;
619 }
620 // Deactivate note
621 gates[channel] = false;
622 refreshHeld();
623 // Set velocity
624 if (releaseVelocityEnabled && velocity >= 0) {
625 velocities[channel] = velocity;
626 }
627 }
628
630 void refreshHeld() {
631 // Monophonic
632 if (channels <= 1) {
633 // Reactivate note if at least one is held
634 if (!heldNotes.empty()) {
636 notes[0] = heldNotes.back();
637 }
639 notes[0] = heldNotes.front();
640 }
642 notes[0] = *std::min_element(heldNotes.begin(), heldNotes.end());
643 }
645 notes[0] = *std::max_element(heldNotes.begin(), heldNotes.end());
646 }
647 gates[0] = true;
648 if (retriggerOnResume) {
649 retriggerPulses[0].trigger(1e-3);
650 }
651 }
652 else {
653 gates[0] = false;
654 }
655 }
656 // Polyphonic
657 else {
658 // Deactivate notes that are not held
659 for (uint8_t c = 0; c < channels; c++) {
660 if (!gates[c])
661 continue;
662 // Check if note is still held
663 bool held = std::find(heldNotes.begin(), heldNotes.end(), notes[c]) != heldNotes.end();
664 if (!held)
665 gates[c] = false;
666 }
667 }
668 }
669
670 void pressPedal() {
671 if (pedal)
672 return;
673 pedal = true;
674 }
675
677 if (!pedal)
678 return;
679 pedal = false;
680 refreshHeld();
681 }
682
683 uint8_t getChannels() {
684 return channels;
685 }
686
687 void setChannels(uint8_t channels) {
688 if (channels == this->channels)
689 return;
690 this->channels = channels;
691 panic();
692 }
693
695 if (monoMode == this->monoMode)
696 return;
697 this->monoMode = monoMode;
698 panic();
699 }
700
702 if (polyMode == this->polyMode)
703 return;
704 this->polyMode = polyMode;
705 panic();
706 }
707
708 float getPitchVoltage(uint8_t channel) {
709 uint8_t wheelChannel = (channels > 1 && polyMode == MPE_MODE) ? channel : 0;
710 return (notes[channel] - 60.f + pwFilters[wheelChannel].out * pwRange) / 12.f;
711 }
712
714 void setFilterLambda(float lambda) {
715 for (uint8_t c = 0; c < MAX_CHANNELS; c++) {
716 pwFilters[c].setLambda(lambda);
717 modFilters[c].setLambda(lambda);
718 }
719 }
720
722 float getPw(uint8_t channel) {
723 return pwFilters[channel].out;
724 }
725
727 float getMod(uint8_t channel) {
728 return modFilters[channel].out;
729 }
730
733 return (channels > 1 && polyMode == MPE_MODE) ? MAX_CHANNELS : 1;
734 }
735
736 json_t* toJson() {
737 json_t* rootJ = json_object();
738 json_object_set_new(rootJ, "channels", json_integer(channels));
739 json_object_set_new(rootJ, "monoMode", json_integer(monoMode));
740 json_object_set_new(rootJ, "retriggerOnResume", json_boolean(retriggerOnResume));
741 json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
742 json_object_set_new(rootJ, "releaseVelocityEnabled", json_boolean(releaseVelocityEnabled));
743 json_object_set_new(rootJ, "pwRange", json_real(pwRange));
744 json_object_set_new(rootJ, "smooth", json_boolean(smooth));
745 json_object_set_new(rootJ, "clockDivision", json_integer(clockDivision));
746 if (channels > 1 && polyMode == MPE_MODE) {
747 // Saving/restoring pitch and mod doesn't make much sense for MPE.
748 }
749 else {
750 json_object_set_new(rootJ, "lastPw", json_integer(pws[0]));
751 json_object_set_new(rootJ, "lastMod", json_integer(mods[0]));
752 }
753 // Assume all filter lambdas are the same
754 json_object_set_new(rootJ, "filterLambda", json_real(pwFilters[0].lambda));
755 return rootJ;
756 }
757
758 void fromJson(json_t* rootJ) {
759 json_t* channelsJ = json_object_get(rootJ, "channels");
760 if (channelsJ)
761 setChannels(json_integer_value(channelsJ));
762
763 json_t* monoModeJ = json_object_get(rootJ, "monoMode");
764 if (monoModeJ)
765 monoMode = (MonoMode) json_integer_value(monoModeJ);
766
767 json_t* retriggerOnResumeJ = json_object_get(rootJ, "retriggerOnResume");
768 if (retriggerOnResumeJ)
769 retriggerOnResume = json_boolean_value(retriggerOnResumeJ);
770
771 json_t* polyModeJ = json_object_get(rootJ, "polyMode");
772 if (polyModeJ)
773 polyMode = (PolyMode) json_integer_value(polyModeJ);
774
775 json_t* releaseVelocityEnabledJ = json_object_get(rootJ, "releaseVelocityEnabled");
776 if (releaseVelocityEnabledJ)
777 releaseVelocityEnabled = json_boolean_value(releaseVelocityEnabledJ);
778
779 json_t* pwRangeJ = json_object_get(rootJ, "pwRange");
780 if (pwRangeJ)
781 pwRange = json_number_value(pwRangeJ);
782
783 json_t* smoothJ = json_object_get(rootJ, "smooth");
784 if (smoothJ)
785 smooth = json_boolean_value(smoothJ);
786
787 json_t* clockDivisionJ = json_object_get(rootJ, "clockDivision");
788 if (clockDivisionJ)
789 clockDivision = json_integer_value(clockDivisionJ);
790
791 json_t* lastPwJ = json_object_get(rootJ, "lastPw");
792 if (lastPwJ)
793 pws[0] = json_integer_value(lastPwJ);
794
795 // In Rack <2.5.3, `lastPitch` was used from 0 to 16383.
796 json_t* lastPitchJ = json_object_get(rootJ, "lastPitch");
797 if (lastPitchJ)
798 pws[0] = json_integer_value(lastPitchJ) - 8192;
799
800 json_t* lastModJ = json_object_get(rootJ, "lastMod");
801 if (lastModJ)
802 mods[0] = json_integer_value(lastModJ);
803
804 // Added in Rack 2.5.3
805 json_t* filterLambdaJ = json_object_get(rootJ, "filterLambda");
806 if (filterLambdaJ)
807 setFilterLambda(json_number_value(filterLambdaJ));
808 }
809};
810
811
812} // namespace dsp
813} // namespace rack
Digital signal processing routines and classes.
Definition approx.hpp:6
TExponentialFilter ExponentialFilter
Definition filter.hpp:88
int clamp(int x, int a, int b)
Limits x between a and b.
Definition math.hpp:32
Root namespace for the Rack API.
Definition AudioDisplay.hpp:9
bool gates[CHANNELS]
Definition midi.hpp:22
void reset()
Definition midi.hpp:37
void setSustainPedal(int8_t cc)
Definition midi.hpp:166
void setChannelPressure(int8_t val)
Definition midi.hpp:124
void setContinue(bool cont)
Definition midi.hpp:213
void panic()
Definition midi.hpp:56
bool stop
Definition midi.hpp:29
bool cont
Definition midi.hpp:30
MidiGenerator()
Definition midi.hpp:33
void setPitchWheel(int16_t pw)
Definition midi.hpp:170
void setCc(int8_t cc, int id)
Definition midi.hpp:137
void setBalance(int8_t cc)
Definition midi.hpp:158
bool start
Definition midi.hpp:28
void setPan(int8_t cc)
Definition midi.hpp:162
int8_t notes[CHANNELS]
Definition midi.hpp:21
int16_t pw
Definition midi.hpp:26
void setNoteGate(int8_t note, bool gate, int c)
Definition midi.hpp:85
void setClock(bool clk)
Definition midi.hpp:183
void setFrame(int64_t frame)
Definition midi.hpp:243
void setVelocity(int8_t vel, int c)
Must be called before setNoteGate().
Definition midi.hpp:81
int8_t channelPressure
Definition midi.hpp:24
void setStop(bool stop)
Definition midi.hpp:228
bool clk
Definition midi.hpp:27
void setChannels(uint8_t channels)
Definition midi.hpp:70
void setVolume(int8_t cc)
Definition midi.hpp:154
void setKeyPressure(int8_t val, int c)
Definition midi.hpp:111
int8_t ccs[128]
Definition midi.hpp:25
void setModWheel(int8_t cc)
Definition midi.hpp:150
uint8_t channels
Definition midi.hpp:19
int8_t keyPressures[CHANNELS]
Definition midi.hpp:23
void setStart(bool start)
Definition midi.hpp:198
int64_t frame
Definition midi.hpp:31
int8_t vels[CHANNELS]
Definition midi.hpp:20
virtual void onMessage(const midi::Message &message)
Definition midi.hpp:247
dsp::PulseGenerator retriggerPulses[MAX_CHANNELS]
Definition midi.hpp:323
uint8_t velocities[MAX_CHANNELS]
Definition midi.hpp:305
bool smooth
Enables pitch-wheel and mod-wheel exponential smoothing.
Definition midi.hpp:290
void setFilterLambda(float lambda)
Sets exponential smoothing filter lambda speed.
Definition midi.hpp:714
void setChannels(uint8_t channels)
Definition midi.hpp:687
bool gates[MAX_CHANNELS]
Definition midi.hpp:304
uint8_t channels
Actual number of output polyphonic channels.
Definition midi.hpp:259
void releasePedal()
Definition midi.hpp:676
void releaseNote(uint8_t note, int8_t channel, int8_t velocity)
-1 velocity means unset.
Definition midi.hpp:596
dsp::PulseGenerator clockDividerPulse
Definition midi.hpp:322
float getPw(uint8_t channel)
Returns pitch wheel value, from -1 to 1.
Definition midi.hpp:722
dsp::PulseGenerator stopPulse
Definition midi.hpp:325
void pressNote(uint8_t note, uint8_t channel, uint8_t velocity)
Definition midi.hpp:553
uint8_t getWheelChannels()
Returns number of polyphonic channels for pitch and mod wheels.
Definition midi.hpp:732
dsp::ExponentialFilter pwFilters[MAX_CHANNELS]
Smoothing filters for wheel values.
Definition midi.hpp:318
void setPolyMode(PolyMode polyMode)
Definition midi.hpp:701
uint8_t assignChannel(uint8_t note)
Definition midi.hpp:513
PolyMode polyMode
Definition midi.hpp:281
void setMonoMode(MonoMode monoMode)
Definition midi.hpp:694
bool releaseVelocityEnabled
Set Velocity output from Note Off velocity.
Definition midi.hpp:284
uint8_t aftertouches[MAX_CHANNELS]
Definition midi.hpp:306
MonoMode monoMode
Definition midi.hpp:268
int64_t clock
Clock index from song start.
Definition midi.hpp:298
float getMod(uint8_t channel)
Returns mod wheel value, from 0 to 1.
Definition midi.hpp:727
float getPitchVoltage(uint8_t channel)
Definition midi.hpp:708
dsp::PulseGenerator startPulse
Definition midi.hpp:324
void panic()
Resets performance state.
Definition midi.hpp:348
std::vector< uint8_t > heldNotes
Definition midi.hpp:307
bool pedal
Whether sustain pedal is held.
Definition midi.hpp:301
void processFilters(float deltaTime)
Definition midi.hpp:366
MidiParser()
Definition midi.hpp:328
json_t * toJson()
Definition midi.hpp:736
float pwRange
Number of semitones to bend up/down by pitch wheel.
Definition midi.hpp:287
dsp::PulseGenerator clockPulse
Definition midi.hpp:321
uint8_t mods[MAX_CHANNELS]
Mod wheel values, from 0 to 127.
Definition midi.hpp:316
dsp::PulseGenerator continuePulse
Definition midi.hpp:326
int16_t pws[MAX_CHANNELS]
Pitch wheel values, from -8192 to 8191.
Definition midi.hpp:313
uint32_t clockDivision
Number of 24 PPQN clocks between clock divider pulses.
Definition midi.hpp:293
MonoMode
Definition midi.hpp:261
@ NUM_MONO_MODES
Definition midi.hpp:266
@ FIRST_PRIORITY_MODE
Definition midi.hpp:263
@ HIGHEST_PRIORITY_MODE
Definition midi.hpp:265
@ LOWEST_PRIORITY_MODE
Definition midi.hpp:264
@ LAST_PRIORITY_MODE
Definition midi.hpp:262
void pressPedal()
Definition midi.hpp:670
int8_t rotateIndex
Definition midi.hpp:308
dsp::ExponentialFilter modFilters[MAX_CHANNELS]
Definition midi.hpp:319
uint8_t notes[MAX_CHANNELS]
Definition midi.hpp:303
void refreshHeld()
Deactivates all notes that are not held, and reactivates notes that are.
Definition midi.hpp:630
PolyMode
Method for assigning notes to polyphony channels.
Definition midi.hpp:274
@ ROTATE_MODE
Definition midi.hpp:275
@ REUSE_MODE
Definition midi.hpp:276
@ RESET_MODE
Definition midi.hpp:277
@ NUM_POLY_MODES
Definition midi.hpp:279
@ MPE_MODE
Definition midi.hpp:278
void processCC(const midi::Message &msg)
Definition midi.hpp:457
void processMessage(const midi::Message &msg)
Definition midi.hpp:396
void reset()
Resets settings and performance state.
Definition midi.hpp:334
void processPulses(float deltaTime)
Definition midi.hpp:385
void fromJson(json_t *rootJ)
Definition midi.hpp:758
void processSystem(const midi::Message &msg)
Definition midi.hpp:481
bool retriggerOnResume
In monophonic mode, generate Retrigger pulse when the active note is released and another takes over.
Definition midi.hpp:271
uint8_t getChannels()
Definition midi.hpp:683
When triggered, holds a high value for a specified time before going low again.
Definition digital.hpp:167
Definition midi.hpp:16
void setSize(int size)
Definition midi.hpp:31
void setFrame(int64_t frame)
Definition midi.hpp:85
void setValue(uint8_t value)
Definition midi.hpp:73
void setStatus(uint8_t status)
Definition midi.hpp:51
uint8_t getNote() const
Definition midi.hpp:57
void setNote(uint8_t note)
Definition midi.hpp:62
void setChannel(uint8_t channel)
Definition midi.hpp:40
uint8_t getChannel() const
Definition midi.hpp:35
uint8_t getStatus() const
Definition midi.hpp:46
uint8_t getValue() const
Definition midi.hpp:68