gled-model 2.34.0

The data model of gled: projects, scenes, effects, animations, curves and assets
Documentation
//! The physical controls of a controller, by the message they send, so the
//! controller window can name a fader or pad instead of its MIDI numbers.
//! Bindings still carry their own triggers; a binding belongs to the
//! control whose trigger it matches.

use serde::{Deserialize, Serialize};

use super::MidiTrigger;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct MidiControl {
    pub name: String,
    /// The message the control sends when pressed or moved.
    pub trigger: MidiTrigger,
}

impl MidiTrigger {
    /// Whether a message with `status` and `data1` is this trigger's.
    pub fn matches(&self, status: u8, data1: u8) -> bool {
        self.status == status && (!self.match_data1 || self.data1 == data1)
    }

    /// Whether this trigger is the release of a button that sends `press`:
    /// the Note Off of the same note, or the press note itself, which
    /// releases with velocity 0.
    pub fn releases(&self, press: &MidiTrigger) -> bool {
        let note_on = press.status & 0xF0 == 0x90;
        let same_channel = self.status & 0x0F == press.status & 0x0F;
        note_on && same_channel && self.status & 0xF0 == 0x80 && self.data1 == press.data1
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn trigger(status: u8, data1: u8, match_data1: bool) -> MidiTrigger {
        MidiTrigger {
            status,
            data1,
            match_data1,
        }
    }

    #[test]
    fn a_trigger_matches_its_message() {
        assert!(trigger(0x90, 24, true).matches(0x90, 24));
        assert!(!trigger(0x90, 24, true).matches(0x90, 25));
        assert!(!trigger(0x90, 24, true).matches(0x91, 24));
    }

    #[test]
    fn a_pitch_bend_trigger_matches_any_data1() {
        assert!(trigger(0xE2, 0, false).matches(0xE2, 93));
    }

    #[test]
    fn a_note_off_releases_the_note_on_of_its_note() {
        let press = trigger(0x90, 8, true);
        assert!(trigger(0x80, 8, true).releases(&press));
        assert!(!trigger(0x80, 9, true).releases(&press));
        assert!(!trigger(0x81, 8, true).releases(&press));
        assert!(!trigger(0x80, 8, true).releases(&trigger(0xB0, 8, true)));
    }
}