1pub extern crate pitch_calc;
5pub use pitch_calc::{Letter, LetterOctave};
6
7pub const MIDI_INPUT_PORT_PREFIX: &'static str = "ReMOTE SL";
9
10#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
12pub enum InputPort {
13 A,
15 B,
17 C,
19}
20
21#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
23pub enum Event {
24 Control(Control),
25 Key(State, LetterOctave, u8),
26}
27
28#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
30pub enum State {
31 On,
33 Off
35}
36
37#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
39#[repr(u8)]
40pub enum Oct { A, B, C, D, E, F, G, H }
41
42#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
44pub enum ButtonRow {
45 TopLeft,
46 BottomLeft,
47 TopRight,
48 BottomRight,
49}
50
51#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
53pub enum Axis { X, Y }
54
55#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
57pub enum Side {
58 Left,
59 Right,
60}
61
62#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
64pub enum Page {
65 Up,
66 Down,
67}
68
69#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
71pub enum LeftButton { A, B, C, D }
72
73#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
75pub enum RightButton { A, B, C }
76
77#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
79pub enum Playback {
80 Previous,
81 Next,
82 Stop,
83 Play,
84 Loop,
85 Record,
86}
87
88#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
90pub enum Control {
91
92 RotaryDial(Oct, i8),
96
97 RotarySlider(Oct, u8),
101
102 VerticalSlider(Oct, u8),
106
107 PressurePad(Oct, u8),
111
112 Button(ButtonRow, Oct, State),
114
115 TouchPad(Axis, u8),
119
120 Pitch(i8),
124
125 Mod(u8),
129
130 Page(Side, Page, State),
132
133 LeftButton(LeftButton, State),
135
136 RightButton(RightButton, State),
138
139 Playback(Playback, State),
141}
142
143
144impl Oct {
145
146 fn from_u8(n: u8) -> Option<Self> {
148 match n {
149 0 => Some(Oct::A),
150 1 => Some(Oct::B),
151 2 => Some(Oct::C),
152 3 => Some(Oct::D),
153 4 => Some(Oct::E),
154 5 => Some(Oct::F),
155 6 => Some(Oct::G),
156 7 => Some(Oct::H),
157 _ => None,
158 }
159 }
160
161}
162
163
164impl InputPort {
165
166 pub fn from_name(name: &str) -> Option<Self> {
168 if name.starts_with(MIDI_INPUT_PORT_PREFIX) {
169 match name.chars().last() {
170 Some('0') => Some(InputPort::A),
171 Some('1') => Some(InputPort::B),
172 Some('2') => Some(InputPort::C),
173 _ => None,
174 }
175 } else {
176 None
177 }
178 }
179
180}
181
182
183impl Event {
184
185 pub fn from_midi(port: InputPort, msg: &[u8]) -> Option<Self> {
187 match port {
188
189 InputPort::A => match msg.len() {
191 3 => match (msg[0], msg[1], msg[2]) {
192
193 (224, 0, pitch) => Some(Control::Pitch(pitch as i8 - 64).into()),
195
196 (176, 1, modulation) => Some(Control::Mod(modulation).into()),
198
199 (state, step, velocity) => {
201 let letter_octave = pitch_calc::Step(step as f32).to_letter_octave();
202 let note = match state {
203 144 => Some(State::On),
204 128 => Some(State::Off),
205 _ => None,
206 };
207 note.map(|note| Event::Key(note, letter_octave, velocity))
208 },
209
210 },
211 _ => None,
212 },
213
214 InputPort::B => match msg.len() {
216 3 => match (msg[0], msg[1], msg[2]) {
217
218 (176, n @ 56...63, value) => {
220 let oct = Oct::from_u8(n - 56).unwrap();
221 let value = if value > 64 { -(value as i8 - 64) } else { value as i8 };
222 Some(Control::RotaryDial(oct, value).into())
223 },
224
225 (176, n @ 8...15, value) => {
227 let oct = Oct::from_u8(n - 8).unwrap();
228 Some(Control::RotarySlider(oct, value).into())
229 },
230
231 (176, n @ 16...23, value) => {
233 let oct = Oct::from_u8(n - 16).unwrap();
234 Some(Control::VerticalSlider(oct, value).into())
235 },
236
237 (144, n @ 36...43, velocity) => {
239 let oct = Oct::from_u8(n - 36).unwrap();
240 Some(Control::PressurePad(oct, velocity).into())
241 },
242
243 (176, axis @ 68...69, value) => {
245 let axis = if axis == 68 { Axis::X } else { Axis::Y };
246 Some(Control::TouchPad(axis, value).into())
247 },
248
249
250 (176, n @ 24...31, state) => {
256 let oct = Oct::from_u8(n - 24).unwrap();
257 let state = if state == 0 { State::Off } else { State::On };
258 Some(Control::Button(ButtonRow::TopLeft, oct, state).into())
259 },
260
261 (176, n @ 32...39, state) => {
263 let oct = Oct::from_u8(n - 32).unwrap();
264 let state = if state == 0 { State::Off } else { State::On };
265 Some(Control::Button(ButtonRow::BottomLeft, oct, state).into())
266 },
267
268 (176, n @ 40...47, state) => {
270 let oct = Oct::from_u8(n - 40).unwrap();
271 let state = if state == 0 { State::Off } else { State::On };
272 Some(Control::Button(ButtonRow::TopRight, oct, state).into())
273 },
274
275 (176, n @ 48...55, state) => {
277 let oct = Oct::from_u8(n - 48).unwrap();
278 let state = if state == 0 { State::Off } else { State::On };
279 Some(Control::Button(ButtonRow::BottomRight, oct, state).into())
280 },
281
282 (176, n @ 88...91, state) => {
284 let (side, page) = match n {
285 88 => (Side::Left, Page::Up),
286 89 => (Side::Left, Page::Down),
287 90 => (Side::Right, Page::Up),
288 91 => (Side::Right, Page::Down),
289 _ => unreachable!(),
290 };
291 let state = if state == 0 { State::Off } else { State::On };
292 Some(Control::Page(side, page, state).into())
293 },
294
295 (176, n @ 80...83, state) => {
297 let button = match n {
298 80 => LeftButton::A,
299 81 => LeftButton::B,
300 82 => LeftButton::C,
301 83 => LeftButton::D,
302 _ => unreachable!(),
303 };
304 let state = if state == 0 { State::Off } else { State::On };
305 Some(Control::LeftButton(button, state).into())
306 },
307
308 (176, n @ 85...87, state) => {
310 let button = match n {
311 85 => RightButton::A,
312 86 => RightButton::B,
313 87 => RightButton::C,
314 _ => unreachable!(),
315 };
316 let state = if state == 0 { State::Off } else { State::On };
317 Some(Control::RightButton(button, state).into())
318 },
319
320 (176, n @ 72...77, state) => {
322 let playback = match n {
323 72 => Playback::Previous,
324 73 => Playback::Next,
325 74 => Playback::Stop,
326 75 => Playback::Play,
327 76 => Playback::Record,
328 77 => Playback::Loop,
329 _ => unreachable!(),
330 };
331 let state = if state == 0 { State::Off } else { State::On };
332 Some(Control::Playback(playback, state).into())
333 },
334
335 _ => None,
336
337 },
338 _ => None,
339 },
340
341 InputPort::C => {
343 None
344 },
345 }
346 }
347
348}
349
350
351impl From<Control> for Event {
352 fn from(control: Control) -> Self {
353 Event::Control(control)
354 }
355}