midi_control/
message.rs

1//
2// (c) 2020 Hubert Figuière
3//
4// License: LGPL-3.0-or-later
5
6//! MIDI messages
7//!
8
9use crate::consts;
10use crate::note::MidiNote;
11use crate::sysex;
12
13/// The MIDI Channel
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15#[repr(u8)]
16pub enum Channel {
17    Ch1,
18    Ch2,
19    Ch3,
20    Ch4,
21    Ch5,
22    Ch6,
23    Ch7,
24    Ch8,
25    Ch9,
26    Ch10,
27    Ch11,
28    Ch12,
29    Ch13,
30    Ch14,
31    Ch15,
32    Ch16,
33    Invalid,
34}
35
36impl Channel {
37    /// Convert the MIDI command byte to a Channel
38    pub fn from_midi_cmd(v: u8) -> Channel {
39        Self::from(v & consts::MIDI_CHANNEL_MASK)
40    }
41}
42
43impl From<u8> for Channel {
44    /// Convert a byte value 0-15 to the Channel.
45    /// If you want to extract it from the MIDI event,
46    /// use Channel::from_midi_cmd()
47    fn from(v: u8) -> Channel {
48        use Channel::*;
49        match v {
50            0 => Ch1,
51            1 => Ch2,
52            2 => Ch3,
53            3 => Ch4,
54            4 => Ch5,
55            5 => Ch6,
56            6 => Ch7,
57            7 => Ch8,
58            8 => Ch9,
59            9 => Ch10,
60            10 => Ch11,
61            11 => Ch12,
62            12 => Ch13,
63            13 => Ch14,
64            14 => Ch15,
65            15 => Ch16,
66            _ => Invalid,
67        }
68    }
69}
70
71/// Parameters of a key related event
72///
73/// This include note on and off and poly key pressure Value is
74/// velocity for notes and pressure amount for pressure events.
75#[derive(Clone, Debug, Eq, PartialEq)]
76pub struct KeyEvent {
77    /// The note to which this applies
78    pub key: MidiNote,
79    /// The velocity for notes or pressure value for pressure.
80    pub value: u8,
81}
82
83/// Parameters for a control change
84#[derive(Debug, Eq, PartialEq)]
85pub struct ControlEvent {
86    /// Control number
87    pub control: u8,
88    /// Value of the control
89    pub value: u8,
90}
91
92/// The type of SysEx message
93#[derive(Debug, Eq, PartialEq)]
94pub enum SysExType {
95    /// Manufacturer ID.
96    Manufacturer(sysex::ManufacturerId),
97    /// Non RealTime Universal SysEx
98    /// Device ID, [sub id 1, sub id 2]
99    NonRealTime(u8, [u8; 2]),
100    /// Realtime Universal SysEx
101    /// Device ID, [sub id 1, sub id 2]
102    RealTime(u8, [u8; 2]),
103}
104
105/// SysEx message
106#[derive(Debug, Eq, PartialEq)]
107pub struct SysExEvent {
108    /// Type of SysEx message
109    pub r#type: SysExType,
110    /// The raw data for the sysex. This include the terminating byte.
111    pub data: Vec<u8>,
112}
113
114impl SysExEvent {
115    /// Create a new SysEx message with a manufacturer ID
116    /// * data is the data in the message, including the EOX
117    pub fn new_manufacturer(manufacturer: sysex::ManufacturerId, data: &[u8]) -> SysExEvent {
118        SysExEvent {
119            r#type: SysExType::Manufacturer(manufacturer),
120            data: Vec::from(data),
121        }
122    }
123
124    /// Create a non realtime Universal System Exclusive message
125    /// * device is the device. Use consts::usysex::ALL_CALL if you want all device to listen
126    /// * subids is the two bytes for the message type.
127    /// * data incude the rest of the data including EOX
128    pub fn new_non_realtime(device: u8, subids: [u8; 2], data: &[u8]) -> SysExEvent {
129        SysExEvent {
130            r#type: SysExType::NonRealTime(device, subids),
131            data: Vec::from(data),
132        }
133    }
134
135    /// Create a realtime Universal System Exclusive message
136    /// * device is the device. Use consts::usysex::ALL_CALL if you want all device to listen
137    /// * subids is the two bytes for the message type.
138    /// * data incude the rest of the data including EOX
139    pub fn new_realtime(device: u8, subids: [u8; 2], data: &[u8]) -> SysExEvent {
140        SysExEvent {
141            r#type: SysExType::RealTime(device, subids),
142            data: Vec::from(data),
143        }
144    }
145
146    /// Get the SysEx type
147    pub fn get_type(&self) -> &SysExType {
148        &self.r#type
149    }
150
151    /// Get the data from the SysEx
152    pub fn get_data(&self) -> &Vec<u8> {
153        &self.data
154    }
155}
156
157/// MIDI messages are what is being sent or received on the MIDI system
158///
159#[derive(Debug, Eq, PartialEq)]
160pub enum MidiMessage {
161    /// We don't know that message.
162    Invalid,
163    /// Note on.
164    NoteOn(Channel, KeyEvent),
165    /// Note off.
166    NoteOff(Channel, KeyEvent),
167    /// Pressure for notes (aftertouch).
168    PolyKeyPressure(Channel, KeyEvent),
169    /// Control value changed.
170    ControlChange(Channel, ControlEvent),
171    /// Program change.
172    ProgramChange(Channel, u8),
173    /// Channel pressure.
174    ChannelPressure(Channel, u8),
175    /// Pitch bending. LSB and MSB of the change.
176    PitchBend(Channel, u8, u8),
177    /// System extension event.
178    SysEx(SysExEvent),
179}
180
181impl MidiMessage {
182    /// Return the channel of the MIDI command
183    /// This is a convenience helper to avoid having to destructure.
184    /// Note: a SysEx message doesn't have a channel.
185    pub fn get_channel(&self) -> Channel {
186        use MidiMessage::*;
187        match *self {
188            Invalid | SysEx(_) => Channel::Invalid,
189            NoteOn(ch, _)
190            | NoteOff(ch, _)
191            | PolyKeyPressure(ch, _)
192            | ControlChange(ch, _)
193            | ProgramChange(ch, _)
194            | ChannelPressure(ch, _)
195            | PitchBend(ch, _, _) => ch,
196        }
197    }
198
199    /// Construct a SysEx message from the raw data.
200    /// Will return an Invalid message of the data doesn't start
201    /// with the SYSEX byte.
202    fn sysex_message_from(data: &[u8]) -> MidiMessage {
203        use consts::system_event::sysex::*;
204
205        if data[0] != consts::SYSEX {
206            return MidiMessage::Invalid;
207        }
208
209        let idx;
210        let manufacturer = data[1];
211        let r#type = match manufacturer {
212            NON_REAL_TIME => {
213                idx = 5;
214                SysExType::NonRealTime(data[2], [data[3], data[4]])
215            }
216            REAL_TIME => {
217                idx = 5;
218                SysExType::RealTime(data[2], [data[3], data[4]])
219            }
220            _ => {
221                let (_, d) = data.split_at(1);
222                let manufacturer = sysex::ManufacturerId::from_raw(d).unwrap();
223                idx = 1 + manufacturer.raw_len();
224                SysExType::Manufacturer(manufacturer)
225            }
226        };
227        MidiMessage::SysEx(SysExEvent {
228            r#type,
229            data: data.split_at(idx).1.to_vec(),
230        })
231    }
232}
233
234impl From<MidiMessage> for Vec<u8> {
235    /// Convert the MidiMessage into a raw buffer suited to be sent,
236    /// to the MIDI device.
237    /// An empty vector mean nothing could be made.
238    fn from(message: MidiMessage) -> Vec<u8> {
239        use MidiMessage::*;
240        match message {
241            NoteOff(ch, e) => vec![consts::NOTE_OFF | ch as u8, e.key, e.value],
242            NoteOn(ch, e) => vec![consts::NOTE_ON | ch as u8, e.key, e.value],
243            PolyKeyPressure(ch, e) => {
244                vec![consts::POLYPHONIC_KEY_PRESSURE | ch as u8, e.key, e.value]
245            }
246            ControlChange(ch, e) => vec![consts::CONTROL_CHANGE | ch as u8, e.control, e.value],
247            ProgramChange(ch, p) => vec![consts::PROGRAM_CHANGE | ch as u8, p, 0],
248            ChannelPressure(ch, p) => vec![consts::CHANNEL_KEY_PRESSURE | ch as u8, p, 0],
249            PitchBend(ch, lsb, msb) => vec![consts::PITCH_BEND_CHANGE | ch as u8, lsb, msb],
250            SysEx(ref e) => {
251                let out_size = match e.r#type {
252                    SysExType::Manufacturer(m) => 1 + m.raw_len() + e.data.len(),
253                    SysExType::NonRealTime(_, _) | SysExType::RealTime(_, _) => 5 + e.data.len(),
254                };
255                let mut vec = Vec::with_capacity(out_size);
256                vec.push(consts::SYSEX);
257                use SysExType::*;
258                match e.r#type {
259                    Manufacturer(ref b) => b.push_to(&mut vec),
260                    NonRealTime(d, id) => {
261                        vec.push(consts::system_event::sysex::NON_REAL_TIME);
262                        vec.push(d);
263                        vec.extend_from_slice(&id);
264                    }
265                    RealTime(d, id) => {
266                        vec.push(consts::system_event::sysex::REAL_TIME);
267                        vec.push(d);
268                        vec.extend_from_slice(&id);
269                    }
270                }
271                vec.extend_from_slice(&e.data);
272                vec
273            }
274            Invalid => vec![],
275        }
276    }
277}
278
279impl From<&[u8]> for MidiMessage {
280    /// Create a MidiMessage from raw data as received from the MIDI driver.
281    fn from(data: &[u8]) -> MidiMessage {
282        if data.len() < 3 {
283            MidiMessage::Invalid
284        } else {
285            let (event, channel) = if data[0] < consts::SYSEX {
286                (
287                    data[0] & consts::EVENT_TYPE_MASK,
288                    data[0] & consts::MIDI_CHANNEL_MASK,
289                )
290            } else {
291                (data[0], 0u8)
292            };
293            match event {
294                consts::NOTE_OFF => {
295                    // Note Off
296                    MidiMessage::NoteOff(
297                        Channel::from(channel),
298                        KeyEvent {
299                            key: data[1],
300                            value: data[2],
301                        },
302                    )
303                }
304                consts::NOTE_ON => {
305                    // Note On
306                    MidiMessage::NoteOn(
307                        Channel::from(channel),
308                        KeyEvent {
309                            key: data[1],
310                            value: data[2],
311                        },
312                    )
313                }
314                consts::POLYPHONIC_KEY_PRESSURE => MidiMessage::PolyKeyPressure(
315                    Channel::from(channel),
316                    KeyEvent {
317                        key: data[1],
318                        value: data[2],
319                    },
320                ),
321                consts::CONTROL_CHANGE => MidiMessage::ControlChange(
322                    Channel::from(channel),
323                    ControlEvent {
324                        control: data[1],
325                        value: data[2],
326                    },
327                ),
328                consts::PROGRAM_CHANGE => {
329                    MidiMessage::ProgramChange(Channel::from(channel), data[1])
330                }
331                consts::CHANNEL_KEY_PRESSURE => {
332                    MidiMessage::ChannelPressure(Channel::from(channel), data[1])
333                }
334                consts::PITCH_BEND_CHANGE => {
335                    MidiMessage::PitchBend(Channel::from(channel), data[1], data[2])
336                }
337                consts::SYSEX => MidiMessage::sysex_message_from(data),
338                // TODO handle other system message
339                _ => MidiMessage::Invalid,
340            }
341        }
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348
349    #[test]
350    pub fn test_midi_channel() {
351        let ch = Channel::from_midi_cmd(128);
352        assert_eq!(ch, Channel::Ch1);
353
354        let ch = Channel::from_midi_cmd(137);
355        assert_eq!(ch, Channel::Ch10);
356    }
357
358    #[test]
359    pub fn test_midi_from_to_raw() {
360        // Invalid
361        let raw = vec![0u8];
362        let msg = MidiMessage::Invalid;
363        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
364        let out: Vec<u8> = msg.into();
365        assert!(out.len() == 0);
366
367        // NoteOn
368        let raw = vec![144u8, 59u8, 88u8];
369        let msg = MidiMessage::NoteOn(Channel::Ch1, KeyEvent { key: 59, value: 88 });
370        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
371        let out: Vec<u8> = msg.into();
372        assert_eq!(out, raw);
373
374        // NoteOff
375        let raw = vec![128u8, 60u8, 0u8];
376        let msg = MidiMessage::NoteOff(Channel::Ch1, KeyEvent { key: 60, value: 0 });
377        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
378        let out: Vec<u8> = msg.into();
379        assert_eq!(out, raw);
380
381        // ControlChange
382        let raw = vec![176, 114, 65];
383        let msg = MidiMessage::ControlChange(
384            Channel::Ch1,
385            ControlEvent {
386                control: 114,
387                value: 65,
388            },
389        );
390        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
391        let out: Vec<u8> = msg.into();
392        assert_eq!(out, raw);
393
394        // PitchBend
395        let raw = vec![224, 0, 76];
396        let msg = MidiMessage::PitchBend(Channel::Ch1, 0, 76);
397        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
398        let out: Vec<u8> = msg.into();
399        assert_eq!(out, raw);
400
401        // SysEx
402        let raw = vec![240, 30, 127, 66, 2, 0, 0, 16, 127, 247];
403        let msg = MidiMessage::SysEx(SysExEvent::new_manufacturer(
404            sysex::ManufacturerId::Id(30),
405            &[127, 66, 2, 0, 0, 16, 127, 247],
406        ));
407        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
408        let out: Vec<u8> = msg.into();
409        assert_eq!(out, raw);
410
411        let raw = vec![240, 0, 32, 107, 127, 66, 2, 0, 0, 16, 127, 247];
412        let msg = MidiMessage::SysEx(SysExEvent::new_manufacturer(
413            sysex::ManufacturerId::ExtId(32, 107),
414            &[127, 66, 2, 0, 0, 16, 127, 247],
415        ));
416        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
417        let out: Vec<u8> = msg.into();
418        assert_eq!(out, raw);
419
420        // Universal SysEx
421        let raw = vec![240, 126, 0, 6, 2, 0, 32, 107, 2, 0, 4, 2, 67, 7, 0, 1, 247];
422        let msg = MidiMessage::SysEx(SysExEvent::new_non_realtime(
423            0,
424            [6, 2],
425            &[0, 32, 107, 2, 0, 4, 2, 67, 7, 0, 1, 247],
426        ));
427        assert_eq!(MidiMessage::from(raw.as_slice()), msg);
428        let out: Vec<u8> = msg.into();
429        assert_eq!(out, raw);
430    }
431}