1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#[warn(nonstandard_style, rust_2018_idioms, future_incompatible)]

pub enum Behavior {
    Omni,
    CableOmni(u8),
    ChannelOmni(u8),
    Specific { cable: u8, channel: u8 },
}

impl Default for Behavior {
    fn default() -> Behavior {
        Behavior::Omni
    }
}

#[derive(Debug)]
pub enum Message {
    NoteOn { note: u8, velocity: u8 },
    NoteOff { note: u8, velocity: u8 },
}

pub fn parse_midi(cable: u8, bytes: &[u8], behavior: Behavior) -> Option<Message> {
    if bytes.is_empty() {
        return None;
    }

    let channel = bytes[0] & 0xF;

    // check if we should short-circuit due to our behavior
    match behavior {
        Behavior::Omni => {}
        Behavior::CableOmni(c) => {
            if c != cable {
                return None;
            }
        }
        Behavior::ChannelOmni(c) => {
            if c != channel {
                return None;
            }
        }
        Behavior::Specific {
            cable: cable_,
            channel: channel_,
        } => {
            if (cable_ != cable) || (channel_ != channel) {
                return None;
            }
        }
    }

    if (bytes[0] & 0xF0 == 0x80) && (bytes.len() == 3) {
        let note = bytes[1];
        let velocity = bytes[2];
        return Some(Message::NoteOff { note, velocity });
    }

    if (bytes[0] & 0xF0 == 0x90) && (bytes.len() == 3) {
        let note = bytes[1];
        let velocity = bytes[2];
        if velocity != 0 {
            return Some(Message::NoteOn { note, velocity });
        } else {
            return Some(Message::NoteOff { note, velocity });
        }
    }

    None
}