synth_utils/
mono_midi_receiver.rs1use heapless::Vec;
13
14use midi_convert::{
15 midi_types::{MidiMessage, Value7},
16 MidiByteStreamParser,
17};
18
19pub struct MonoMidiReceiver {
21 parser: MidiByteStreamParser,
22
23 channel: u8,
25
26 note_num: u8,
28
29 velocity: f32,
31
32 pitch_bend: f32,
34
35 mod_wheel: f32,
37
38 volume: f32,
40
41 vcf_cutoff: f32,
43
44 vcf_resonance: f32,
46
47 portamento_time: f32,
49
50 portamento_enabled: bool,
51 sustain_enabled: bool,
52
53 gate: bool,
54 rising_gate: bool,
55 falling_gate: bool,
56
57 retrigger_mode: RetriggerMode,
58 note_priority: NotePriority,
59
60 held_down_notes: Vec<u8, HELD_DOWN_NOTE_BUFFER_LEN>,
62}
63
64impl MonoMidiReceiver {
65 pub fn new(channel: u8) -> Self {
73 Self {
74 parser: MidiByteStreamParser::new(),
75
76 channel: channel.min(15),
77
78 note_num: 0,
79
80 pitch_bend: 0.0_f32,
81
82 velocity: 0.0_f32,
83 mod_wheel: 0.0_f32,
84 volume: 0.0_f32,
85 vcf_cutoff: 0.0_f32,
86 vcf_resonance: 0.0_f32,
87 portamento_time: 0.0_f32,
88
89 portamento_enabled: true,
90 sustain_enabled: true,
91
92 gate: false,
93 rising_gate: false,
94 falling_gate: false,
95
96 retrigger_mode: RetriggerMode::NoRetrigger,
97 note_priority: NotePriority::Last,
98
99 held_down_notes: Vec::new(),
100 }
101 }
102
103 pub fn parse(&mut self, byte: u8) {
121 match self.parser.parse(byte) {
122 Some(MidiMessage::NoteOn(ch, note, vel)) if u8::from(ch) == self.channel => {
123 if 0 == u8::from(vel) {
125 self.handle_note_off(note.into());
126 } else {
127 self.handle_note_on(note.into(), vel);
128 };
129 }
130 Some(MidiMessage::NoteOff(ch, note, _)) if u8::from(ch) == self.channel => {
131 self.handle_note_off(note.into());
132 }
133 Some(MidiMessage::PitchBendChange(ch, val_u14)) if u8::from(ch) == self.channel => {
134 self.pitch_bend = f32::from(val_u14);
135 }
136 Some(MidiMessage::ControlChange(ch, cc, val7)) if u8::from(ch) == self.channel => {
137 match u8::from(cc) {
138 CC_MOD_WHEEL => self.mod_wheel = value7_to_f32(val7),
139 CC_VOLUME => self.volume = value7_to_f32(val7),
140 CC_VCF_CUTOFF => self.vcf_cutoff = value7_to_f32(val7),
141 CC_VCF_RESONANCE => self.vcf_resonance = value7_to_f32(val7),
142 CC_PORTAMENTO_TIME => self.portamento_time = value7_to_f32(val7),
143 CC_PORTAMENTO_SWITCH => {
144 self.portamento_enabled = U7_HALF_SCALE <= u8::from(val7)
145 }
146 CC_SUSTAIN_SWITCH => self.sustain_enabled = U7_HALF_SCALE <= u8::from(val7),
147 CC_ALL_CONTROLLERS_OFF => self.reset_controllers(),
148 CC_ALL_NOTES_OFF => {
149 self.held_down_notes.clear();
150 self.gate = false;
151 self.rising_gate = false;
152 self.falling_gate = false;
153 }
154 _ => (), }
156 }
157 _ => (), }
159 }
160
161 fn handle_note_on(&mut self, note: u8, velocity: Value7) {
163 self.velocity = value7_to_f32(velocity);
164
165 self.held_down_notes.push(note).ok();
166
167 self.note_num = self.choose_next_note();
168
169 self.gate = true;
170 self.falling_gate = false;
171
172 if (self.retrigger_mode == RetriggerMode::AllowRetrigger)
173 | (self.held_down_notes.len() == 1)
174 {
175 self.rising_gate = true;
176 }
177 }
178
179 fn handle_note_off(&mut self, note: u8) {
181 self.held_down_notes.retain(|n| *n != note);
183
184 if self.held_down_notes.is_empty() {
185 self.gate = false;
186 self.rising_gate = false;
187 self.falling_gate = true;
188 } else {
189 self.note_num = self.choose_next_note();
191 }
192 }
193
194 fn choose_next_note(&self) -> u8 {
198 match self.note_priority {
199 NotePriority::Last => *self.held_down_notes.last().unwrap_or(&0),
200 NotePriority::High => *self.held_down_notes.iter().max().unwrap_or(&0),
201 NotePriority::Low => *self.held_down_notes.iter().min().unwrap_or(&0),
202 }
203 }
204
205 pub fn note_num(&self) -> u8 {
207 self.note_num
208 }
209
210 pub fn pitch_bend(&self) -> f32 {
215 self.pitch_bend
216 }
217
218 pub fn velocity(&self) -> f32 {
220 self.velocity
221 }
222
223 pub fn mod_wheel(&self) -> f32 {
225 self.mod_wheel
226 }
227
228 pub fn volume(&self) -> f32 {
230 self.volume
231 }
232
233 pub fn vcf_cutoff(&self) -> f32 {
235 self.vcf_cutoff
236 }
237
238 pub fn vcf_resonance(&self) -> f32 {
240 self.vcf_resonance
241 }
242
243 pub fn portamento_time(&self) -> f32 {
245 self.portamento_time
246 }
247
248 pub fn portamento_enabled(&self) -> bool {
250 self.portamento_enabled
251 }
252
253 pub fn sustain_enabled(&self) -> bool {
255 self.sustain_enabled
256 }
257
258 pub fn gate(&self) -> bool {
260 self.gate
261 }
262
263 pub fn rising_gate(&mut self) -> bool {
270 if self.rising_gate {
271 self.rising_gate = false;
272 true
273 } else {
274 false
275 }
276 }
277
278 pub fn falling_gate(&mut self) -> bool {
280 if self.falling_gate {
281 self.falling_gate = false;
282 true
283 } else {
284 false
285 }
286 }
287
288 pub fn set_retrigger_mode(&mut self, mode: RetriggerMode) {
290 self.retrigger_mode = mode;
291 }
292
293 pub fn set_note_priority(&mut self, priority: NotePriority) {
295 self.note_priority = priority;
296 }
297
298 fn reset_controllers(&mut self) {
300 self.pitch_bend = 0.0_f32;
301 self.mod_wheel = 0.0_f32;
302 self.volume = 0.0_f32;
303 self.vcf_cutoff = 0.0_f32;
304 self.vcf_resonance = 0.0_f32;
305 self.portamento_time = 0.0_f32;
306 self.portamento_enabled = true;
307 self.sustain_enabled = true;
308 }
309}
310
311#[derive(PartialEq, Eq)]
320pub enum RetriggerMode {
321 AllowRetrigger,
322 NoRetrigger,
323}
324
325pub enum NotePriority {
335 Last,
336 High,
337 Low,
338}
339
340fn value7_to_f32(val7: Value7) -> f32 {
342 u8::from(val7) as f32 / 127.0_f32
343}
344
345const CC_MOD_WHEEL: u8 = 0x01;
347const CC_VOLUME: u8 = 0x07;
348const CC_VCF_CUTOFF: u8 = 0x47;
349const CC_VCF_RESONANCE: u8 = 0x4A;
350const CC_SUSTAIN_SWITCH: u8 = 0x40;
351const CC_PORTAMENTO_SWITCH: u8 = 0x41;
352const CC_PORTAMENTO_TIME: u8 = 0x05;
353const CC_ALL_CONTROLLERS_OFF: u8 = 0x79;
354const CC_ALL_NOTES_OFF: u8 = 0x7B;
355
356const U7_HALF_SCALE: u8 = 1 << 6;
358
359const HELD_DOWN_NOTE_BUFFER_LEN: usize = 32;
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn should_listen_on_correct_channel() {
370 let mut mr = MonoMidiReceiver::new(1);
371 mr.parse(0x91); mr.parse(42); mr.parse(127);
374
375 assert_eq!(mr.note_num(), 42);
376 }
377
378 #[test]
379 fn should_not_list_on_wrong_channel() {
380 let mut mr = MonoMidiReceiver::new(1);
381 assert_eq!(mr.note_num(), 0);
382
383 mr.parse(0x92); mr.parse(43); mr.parse(127);
386
387 assert_eq!(mr.note_num(), 0);
389 }
390
391 #[test]
392 fn should_handle_running_status() {
393 let mut mr = MonoMidiReceiver::new(1);
394 mr.parse(0x91);
395 mr.parse(42);
396 mr.parse(127);
397 mr.parse(43);
399 mr.parse(127);
400
401 assert_eq!(mr.note_num(), 43);
403 }
404
405 #[test]
406 fn gate_goes_on_with_note_on() {
407 let mut mr = MonoMidiReceiver::new(1);
408 mr.parse(0x91);
409 mr.parse(42);
410 mr.parse(127);
411 assert!(mr.gate());
412 }
413
414 #[test]
415 fn gate_goes_off_with_note_off() {
416 let mut mr = MonoMidiReceiver::new(1);
417 mr.parse(0x91);
418 mr.parse(42);
419 mr.parse(127);
420
421 mr.parse(0x81); mr.parse(42);
423 mr.parse(0);
424 assert!(!mr.gate());
425 }
426
427 #[test]
428 fn gate_stays_on_if_any_notes_left_on() {
429 let mut mr = MonoMidiReceiver::new(1);
430 mr.parse(0x91);
431 mr.parse(42);
432 mr.parse(127);
433 mr.parse(43);
434 mr.parse(127);
435 mr.parse(44);
436 mr.parse(127);
437
438 mr.parse(0x81);
439 mr.parse(42);
440 mr.parse(0);
441 mr.parse(44);
442 mr.parse(0);
443
444 assert!(mr.gate());
446 }
447
448 #[test]
449 fn gate_turns_off_when_all_notes_are_off() {
450 let mut mr = MonoMidiReceiver::new(1);
451 mr.parse(0x91);
452 mr.parse(42);
453 mr.parse(127);
454 mr.parse(43);
455 mr.parse(127);
456 mr.parse(44);
457 mr.parse(127);
458
459 mr.parse(0x81);
460 mr.parse(42);
461 mr.parse(0);
462 mr.parse(43);
463 mr.parse(0);
464 mr.parse(44);
465 mr.parse(0);
466
467 assert!(!mr.gate());
468 }
469
470 #[test]
471 fn channel_clamps_if_too_big() {
472 let mut mr = MonoMidiReceiver::new(200); mr.parse(0x9F); mr.parse(11);
476 mr.parse(127);
477
478 assert_eq!(mr.note_num(), 11);
479 }
480
481 #[test]
482 fn velocity_of_0_is_treated_as_note_off() {
483 let mut mr = MonoMidiReceiver::new(1);
484 mr.parse(0x91);
485 mr.parse(42);
486 mr.parse(0); assert!(!mr.gate());
488 }
489
490 #[test]
491 fn velocity_of_0_turns_existing_note_off() {
492 let mut mr = MonoMidiReceiver::new(1);
493 mr.parse(0x91);
494 mr.parse(42);
495 mr.parse(5); assert!(mr.gate());
497
498 mr.parse(42);
499 mr.parse(0); assert!(!mr.gate());
501 }
502
503 #[test]
504 fn rising_gate_is_self_clearing() {
505 let mut mr = MonoMidiReceiver::new(1);
506 mr.parse(0x91);
507 mr.parse(42);
508 mr.parse(1); assert!(mr.rising_gate());
510 assert!(!mr.rising_gate());
512 }
513
514 #[test]
515 fn can_retrigger_when_retrigger_mode_is_on() {
516 let mut mr = MonoMidiReceiver::new(1);
517
518 mr.set_retrigger_mode(RetriggerMode::AllowRetrigger);
519
520 mr.parse(0x91);
521 mr.parse(42);
522 mr.parse(1);
523 assert!(mr.rising_gate());
524
525 mr.parse(43); mr.parse(1);
527 assert!(mr.rising_gate());
528 }
529
530 #[test]
531 fn can_not_retrigger_when_retrigger_mode_is_off() {
532 let mut mr = MonoMidiReceiver::new(1);
533
534 mr.set_retrigger_mode(RetriggerMode::NoRetrigger);
535
536 mr.parse(0x91);
537 mr.parse(42);
538 mr.parse(1);
539 assert!(mr.rising_gate());
540
541 mr.parse(43); mr.parse(1);
543 assert!(!mr.rising_gate());
545 }
546
547 #[test]
548 fn note_priority_last_gets_the_last_note() {
549 let mut mr = MonoMidiReceiver::new(1);
550
551 mr.set_note_priority(NotePriority::Last);
552
553 mr.parse(0x91);
554 mr.parse(42);
555 mr.parse(1);
556 mr.parse(43);
557 mr.parse(1);
558 mr.parse(44);
559 mr.parse(1);
560 assert_eq!(mr.note_num(), 44);
561 }
562
563 #[test]
564 fn note_priority_high_gets_the_highest_note() {
565 let mut mr = MonoMidiReceiver::new(1);
566
567 mr.set_note_priority(NotePriority::High);
568
569 mr.parse(0x91);
570 mr.parse(42);
571 mr.parse(1);
572 mr.parse(43);
573 mr.parse(1);
574 mr.parse(44);
575 mr.parse(1);
576 mr.parse(66); mr.parse(1);
578 mr.parse(10);
579 mr.parse(1);
580 assert_eq!(mr.note_num(), 66);
581 }
582
583 #[test]
584 fn note_priority_low_gets_the_lowest_note() {
585 let mut mr = MonoMidiReceiver::new(1);
586
587 mr.set_note_priority(NotePriority::Low);
588
589 mr.parse(0x91);
590 mr.parse(42);
591 mr.parse(1);
592 mr.parse(5); mr.parse(1);
594 mr.parse(44);
595 mr.parse(1);
596 mr.parse(66);
597 mr.parse(1);
598 mr.parse(10);
599 mr.parse(1);
600 assert_eq!(mr.note_num(), 5);
601 }
602
603 #[test]
604 fn note_off_keeps_the_last_note() {
605 let mut mr = MonoMidiReceiver::new(1);
606 mr.parse(0x91);
607 mr.parse(42);
608 mr.parse(1);
609
610 mr.parse(0x81); mr.parse(42);
612 mr.parse(0);
613
614 assert_eq!(mr.note_num(), 42);
616 }
617}