launchy/launch_control/
input.rs

1use super::{Button, Template};
2
3#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
4/// One of the 16 knobs on the Launch COntrol
5pub struct Knob(u8);
6
7impl Knob {
8    /// Create a new knob from the given index, where indices 0-7 correspond to the top row and
9    /// indices 8-15 corresopnd to the bottom row
10    pub fn new(index: u8) -> Self {
11        assert!(index < 16);
12        Self(index)
13    }
14
15    /// Create a new knob in the upper row. `index` must be below 8
16    pub fn upper(index: u8) -> Self {
17        Self::new(index)
18    }
19    /// Create a new knob in the lower row. `index` must be below 8
20    pub fn lower(index: u8) -> Self {
21        Self::new(index + 8)
22    }
23}
24
25#[derive(Debug, Eq, PartialEq, Hash, Clone)]
26pub enum Message {
27    /// When a button is pressed
28    Press { template: Template, button: Button },
29
30    /// When a button is released
31    Release { template: Template, button: Button },
32
33    /// When the user presses a pad button, then changes the template, and then releases the
34    /// button, this message will be fired on release. The Launch Control provides no
35    /// information which button has been released, nor the template it was pressed or released
36    /// in
37    StalePadRelease,
38
39    /// Same meaning as StalePadRelease, but for control buttons instead of pads
40    StaleControlButtonRelease,
41
42    /// When a template is changed using the non-programmatically-accessible template buttons on
43    /// the Launch Control
44    TemplateChanged { template: Template },
45
46    /// When a knob has been moved
47    KnobChanged {
48        template: Template,
49        knob: Knob,
50        value: u8,
51    },
52}
53
54/// The Launch Control input connection creator.
55pub struct Input;
56
57impl Input {
58    fn decode_short_message(data: &[u8]) -> Message {
59        let status = data[0] & 0xF0;
60        let template = Template(data[0] & 0x0F);
61        let note = data[1];
62        let velocity = data[2];
63
64        // why am I not sending the template on Stale*Release messages? That's because the device
65        // doesn't provide it. the lower 4 bits are always zero on those Stale message, so I can't
66        // put it into the Message
67
68        match [status, note, velocity] {
69            // Pad buttons press + release
70            [0x90, button @ 9..=12, 127] => Message::Press {
71                template,
72                button: Button::pad(button - 9),
73            },
74            [0x80, button @ 9..=12, 0] => Message::Release {
75                template,
76                button: Button::pad(button - 9),
77            },
78            [0x90, button @ 25..=28, 127] => Message::Press {
79                template,
80                button: Button::pad(button - 25 + 4),
81            },
82            [0x80, button @ 25..=28, 0] => Message::Release {
83                template,
84                button: Button::pad(button - 25 + 4),
85            },
86            [0x80, 0, 0] => Message::StalePadRelease,
87
88            // Control buttons press + release
89            [0xB0, button @ 114..=117, 127] => Message::Press {
90                template,
91                button: Button::control(button - 114),
92            },
93            [0xB0, button @ 114..=117, 0] => Message::Release {
94                template,
95                button: Button::control(button - 114),
96            },
97            [0xB0, 0, 0] => Message::StaleControlButtonRelease,
98
99            // Knob changes
100            [0xB0, knob @ 21..=28, value] => Message::KnobChanged {
101                template,
102                knob: Knob::upper(knob - 21),
103                value,
104            },
105            [0xB0, knob @ 41..=48, value] => Message::KnobChanged {
106                template,
107                knob: Knob::lower(knob - 41),
108                value,
109            },
110
111            _ => panic!("Unexpected short message {:?}", data),
112        }
113    }
114
115    fn decode_sysex_message(data: &[u8]) -> Message {
116        match *data {
117            [240, 0, 32, 41, 2, 10, 119, template, 247] => Message::TemplateChanged {
118                template: Template(template),
119            },
120            _ => panic!("Unexpected sysex message {:?}", data),
121        }
122    }
123}
124
125impl crate::InputDevice for Input {
126    const MIDI_CONNECTION_NAME: &'static str = "Launchy Launch Control input";
127    const MIDI_DEVICE_KEYWORD: &'static str = "Launch Control";
128    type Message = Message;
129
130    fn decode_message(_timestamp: u64, data: &[u8]) -> Message {
131        match data.len() {
132            3 => Self::decode_short_message(data),
133            _ => Self::decode_sysex_message(data),
134        }
135    }
136}