mini_midi/
lib.rs

1use std::iter;
2
3/// A MIDI channel.
4#[derive(Debug)]
5pub enum Channel {
6    Channel1,
7    Channel2,
8    Channel3,
9    Channel4,
10    Channel5,
11    Channel6,
12    Channel7,
13    Channel8,
14    Channel9,
15    Channel10,
16    Channel11,
17    Channel12,
18    Channel13,
19    Channel14,
20    Channel15,
21    Channel16,
22}
23
24impl From<u8> for Channel {
25    fn from(value: u8) -> Self {
26        match value {
27            0 => Self::Channel1,
28            1 => Self::Channel2,
29            2 => Self::Channel3,
30            3 => Self::Channel4,
31            4 => Self::Channel5,
32            5 => Self::Channel6,
33            6 => Self::Channel7,
34            7 => Self::Channel8,
35            8 => Self::Channel9,
36            9 => Self::Channel10,
37            10 => Self::Channel11,
38            11 => Self::Channel12,
39            12 => Self::Channel13,
40            13 => Self::Channel14,
41            14 => Self::Channel15,
42            _ => Self::Channel16,
43        }
44    }
45}
46
47impl Into<u8> for &Channel {
48    fn into(self) -> u8 {
49        match self.as_ref() {
50            Channel::Channel1 => 0,
51            Channel::Channel2 => 1,
52            Channel::Channel3 => 2,
53            Channel::Channel4 => 3,
54            Channel::Channel5 => 4,
55            Channel::Channel6 => 5,
56            Channel::Channel7 => 6,
57            Channel::Channel8 => 7,
58            Channel::Channel9 => 8,
59            Channel::Channel10 => 9,
60            Channel::Channel11 => 10,
61            Channel::Channel12 => 11,
62            Channel::Channel13 => 12,
63            Channel::Channel14 => 13,
64            Channel::Channel15 => 14,
65            Channel::Channel16 => 15,
66        }
67    }
68}
69
70impl AsRef<Channel> for Channel {
71    fn as_ref(&self) -> &Channel {
72        self
73    }
74}
75
76/// A MIDI message.  
77/// Refer to [this table](https://midi.org/expanded-midi-1-0-messages-list)
78/// for more info.
79#[derive(Debug)]
80pub enum Message<'a> {
81    // TODO: Implement a struct for 7-bit values
82    // TODO: Implement an enum for notes
83    NoteOff(Channel, u8, u8),
84    NoteOn(Channel, u8, u8),
85    PolyphonicAftertouch(Channel, u8, u8),
86    /* TODO: Implement MIDI spec table 3
87    https://midi.org/midi-1-0-control-change-messages */
88    ControlOrModeChange(Channel, u8, u8),
89    ProgramChange(Channel, u8),
90    Aftertouch(Channel, u8),
91    PitchBendChange(Channel, u8, u8),
92    SystemExclusive(&'a [u8]),
93    // TODO: Determine whether to implement the quarter frame (242)
94    SongPositionPointer(u8, u8),
95    SongSelect(u8),
96    TuneRequest,
97    TimingClock,
98    Start,
99    Continue,
100    Stop,
101    ActiveSensing,
102    SystemReset,
103}
104
105impl<'a> From<&'a [u8]> for Message<'a> {
106    fn from(value: &'a [u8]) -> Self {
107        let first_byte = if value.len() >= 1 {
108            value[0]
109        } else {
110            panic!("A MIDI message can't be zero bytes long!");
111        };
112
113        let get_second_byte = || {
114            if value.len() >= 2 {
115                value[1]
116            } else {
117                // TODO: Add an error message
118                panic!("No second byte present!")
119            }
120        };
121
122        let get_third_byte = || {
123            if value.len() >= 3 {
124                value[2]
125            } else {
126                panic!("No third byte present!")
127            }
128        };
129
130        match first_byte {
131            128..=143 => Self::NoteOff(
132                Channel::from(first_byte - 128),
133                get_second_byte(),
134                get_third_byte(),
135            ),
136            144..=159 => Self::NoteOn(
137                Channel::from(first_byte - 144),
138                get_second_byte(),
139                get_third_byte(),
140            ),
141            160..=175 => Self::PolyphonicAftertouch(
142                Channel::from(first_byte - 145),
143                get_second_byte(),
144                get_third_byte(),
145            ),
146            176..=191 => Self::ControlOrModeChange(
147                Channel::from(first_byte - 176),
148                get_second_byte(),
149                get_third_byte(),
150            ),
151            192..=207 => Self::ProgramChange(Channel::from(first_byte - 192), get_second_byte()),
152            208..=223 => Self::Aftertouch(Channel::from(first_byte - 208), get_second_byte()),
153            224..=239 => Self::PitchBendChange(
154                Channel::from(first_byte),
155                get_second_byte(),
156                get_third_byte(),
157            ),
158            240 => Self::SystemExclusive(&value[1..value.len() - 1]),
159            242 => Self::SongPositionPointer(get_second_byte(), get_third_byte()),
160            243 => Self::SongSelect(get_second_byte()),
161            246 => Self::TuneRequest,
162            248 => Self::TimingClock,
163            250 => Self::Start,
164            251 => Self::Continue,
165            252 => Self::Stop,
166            254 => Self::ActiveSensing,
167            255 => Self::SystemReset,
168            // TODO: Add error handling
169            _ => panic!("Invalid MIDI message!"),
170        }
171    }
172}
173
174impl<'a> Into<Vec<u8>> for Message<'a> {
175    fn into(self) -> Vec<u8> {
176        fn c_to_u8(channel: &Channel) -> u8 {
177            Into::<u8>::into(channel)
178        }
179
180        match self {
181            Message::NoteOff(ref channel, note, velocity) => {
182                vec![128u8 + c_to_u8(channel), note, velocity]
183            }
184            Message::NoteOn(ref channel, note, velocity) => {
185                vec![144u8 + c_to_u8(channel), note, velocity]
186            }
187            Message::PolyphonicAftertouch(ref channel, note, pressure) => {
188                vec![160u8 + c_to_u8(channel), note, pressure]
189            }
190            Message::ControlOrModeChange(ref channel, byte2, byte3) => {
191                vec![176u8 + c_to_u8(channel), byte2, byte3]
192            }
193            Message::ProgramChange(ref channel, program) => {
194                vec![192u8 + c_to_u8(channel), program]
195            }
196            Message::Aftertouch(ref channel, pressure) => {
197                vec![208u8 + c_to_u8(channel), pressure]
198            }
199            Message::PitchBendChange(ref channel, lsb, msb) => {
200                vec![224u8 + c_to_u8(channel), lsb, msb]
201            }
202            Message::SystemExclusive(data) => iter::once(240u8)
203                .chain(data.to_owned())
204                .chain(iter::once(247u8))
205                .collect(),
206            Message::SongPositionPointer(lsb, msb) => {
207                vec![242u8, lsb, msb]
208            }
209            Message::SongSelect(song) => {
210                vec![243u8, song]
211            }
212            Message::TuneRequest => {
213                vec![246u8]
214            }
215            Message::TimingClock => {
216                vec![248u8]
217            }
218            Message::Start => {
219                vec![250u8]
220            }
221            Message::Continue => {
222                vec![251u8]
223            }
224            Message::Stop => {
225                vec![252u8]
226            }
227            Message::ActiveSensing => {
228                vec![254u8]
229            }
230            Message::SystemReset => {
231                vec![255u8]
232            }
233        }
234    }
235}