Skip to main content

gled_model/asset/midi_controller/
control.rs

1//! The physical controls of a controller, by the message they send, so the
2//! controller window can name a fader or pad instead of its MIDI numbers.
3//! Bindings still carry their own triggers; a binding belongs to the
4//! control whose trigger it matches.
5
6use serde::{Deserialize, Serialize};
7
8use super::MidiTrigger;
9
10#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
11pub struct MidiControl {
12    pub name: String,
13    /// The message the control sends when pressed or moved.
14    pub trigger: MidiTrigger,
15}
16
17impl MidiTrigger {
18    /// Whether a message with `status` and `data1` is this trigger's.
19    pub fn matches(&self, status: u8, data1: u8) -> bool {
20        self.status == status && (!self.match_data1 || self.data1 == data1)
21    }
22
23    /// Whether this trigger is the release of a button that sends `press`:
24    /// the Note Off of the same note, or the press note itself, which
25    /// releases with velocity 0.
26    pub fn releases(&self, press: &MidiTrigger) -> bool {
27        let note_on = press.status & 0xF0 == 0x90;
28        let same_channel = self.status & 0x0F == press.status & 0x0F;
29        note_on && same_channel && self.status & 0xF0 == 0x80 && self.data1 == press.data1
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    fn trigger(status: u8, data1: u8, match_data1: bool) -> MidiTrigger {
38        MidiTrigger {
39            status,
40            data1,
41            match_data1,
42        }
43    }
44
45    #[test]
46    fn a_trigger_matches_its_message() {
47        assert!(trigger(0x90, 24, true).matches(0x90, 24));
48        assert!(!trigger(0x90, 24, true).matches(0x90, 25));
49        assert!(!trigger(0x90, 24, true).matches(0x91, 24));
50    }
51
52    #[test]
53    fn a_pitch_bend_trigger_matches_any_data1() {
54        assert!(trigger(0xE2, 0, false).matches(0xE2, 93));
55    }
56
57    #[test]
58    fn a_note_off_releases_the_note_on_of_its_note() {
59        let press = trigger(0x90, 8, true);
60        assert!(trigger(0x80, 8, true).releases(&press));
61        assert!(!trigger(0x80, 9, true).releases(&press));
62        assert!(!trigger(0x81, 8, true).releases(&press));
63        assert!(!trigger(0x80, 8, true).releases(&trigger(0xB0, 8, true)));
64    }
65}