midi_control/
sysex.rs

1//
2// (c) 2020-2022 Hubert Figuière
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5
6//! System Exclusive specific
7
8use crate::consts::system_event;
9use crate::message::{SysExEvent, SysExType};
10use crate::MidiMessage;
11
12/// System Exclusive Manufacturer Id
13///
14/// Originally a single u8.
15/// If it is zero, two more u8.
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub enum ManufacturerId {
18    Id(u8),
19    ExtId(u8, u8),
20}
21
22impl ManufacturerId {
23    /// Creates a ManufacturerId from raw data.
24    /// ## Error
25    /// Return None if the raw data is too short.
26    pub fn from_raw(raw: &[u8]) -> Option<ManufacturerId> {
27        if raw[0] != 0 {
28            Some(ManufacturerId::Id(raw[0]))
29        } else if raw.len() < 3 {
30            None
31        } else {
32            Some(ManufacturerId::ExtId(raw[1], raw[2]))
33        }
34    }
35
36    /// The byte length this take in raw data.
37    pub fn raw_len(self) -> usize {
38        match self {
39            ManufacturerId::Id(_) => 1,
40            ManufacturerId::ExtId(_, _) => 3,
41        }
42    }
43
44    /// Push manufacturer id to raw data vector
45    /// Will push exactly raw_len() bytes.
46    pub fn push_to(self, vec: &mut Vec<u8>) {
47        match self {
48            ManufacturerId::Id(m) => vec.push(m),
49            ManufacturerId::ExtId(m1, m2) => {
50                vec.push(system_event::sysex::ID_EXTENSION);
51                vec.push(m1);
52                vec.push(m2);
53            }
54        }
55    }
56}
57
58/// Decoder to parse the a SysEx message
59/// The decoder is not meant to live longer than the message.
60pub struct SysExDecoder<'a>(&'a SysExEvent);
61
62impl SysExDecoder<'_> {
63    /// Return a decoder for the message.
64    ///
65    /// ## Error
66    /// Return `None` if the message isn't a SysEx messsage.
67    pub fn decode(msg: &MidiMessage) -> Option<SysExDecoder> {
68        if let MidiMessage::SysEx(ref e) = msg {
69            Some(SysExDecoder(e))
70        } else {
71            None
72        }
73    }
74
75    /// Message is Universal SysEx
76    pub fn is_universal_sysex(&self) -> bool {
77        matches!(
78            self.0.get_type(),
79            SysExType::NonRealTime(_, _) | SysExType::RealTime(_, _)
80        )
81    }
82}
83
84/// Universal SysEx decoder.
85pub struct USysExDecoder<'a>(&'a SysExEvent);
86
87impl USysExDecoder<'_> {
88    /// Creates a new decoder for the message.
89    /// ## Error
90    /// Returns None if the message isn't a Universal System Exclusive
91    pub fn decode(msg: &MidiMessage) -> Option<USysExDecoder> {
92        // TODO check the size of the data (at least 6 bytes)
93        if let MidiMessage::SysEx(ref e) = msg {
94            match e.get_type() {
95                SysExType::NonRealTime(_, _) | SysExType::RealTime(_, _) => {
96                    return Some(USysExDecoder(e))
97                }
98                _ => {}
99            }
100        }
101        None
102    }
103
104    /// Tell if message is non realtime
105    pub fn is_non_realtime(&self) -> bool {
106        matches!(self.0.get_type(), SysExType::NonRealTime(_, _))
107    }
108
109    /// Returns the target device
110    /// ## Panic
111    /// If the message isn't a Universal System Exclusive it will panic.
112    /// The decode() function is supposed to check for it.
113    pub fn target_device(&self) -> u8 {
114        match self.0.get_type() {
115            SysExType::NonRealTime(d, _) | SysExType::RealTime(d, _) => *d,
116            _ => unreachable!(),
117        }
118    }
119
120    /// Returns the subid for the Universel System Exclusive message.
121    /// ## Panic
122    /// If the message isn't a Universal System Exclusive it will panic.
123    /// The decode() function is supposed to check for it.
124    pub fn subid(&self) -> [u8; 2] {
125        match self.0.get_type() {
126            SysExType::NonRealTime(_, subid) | SysExType::RealTime(_, subid) => *subid,
127            _ => unreachable!(),
128        }
129    }
130
131    /// Return the manufacturer ID from the General Info reply
132    /// Check that the SysEx message is the right one.
133    /// ## Error
134    /// Returns None if the message isn't the right one.
135    pub fn general_info_reply_manufacturer_id(&self) -> Option<ManufacturerId> {
136        if self.is_non_realtime() && self.subid() == [6, 2] {
137            let data = self.0.get_data();
138            if data.len() >= 8 {
139                return ManufacturerId::from_raw(data);
140            }
141        }
142        None
143    }
144
145    /// Return the device family from the General Info reply
146    /// Check that the SysEx message is the right one.
147    /// ## Error
148    /// Returns None if the message isn't the right one.
149    pub fn general_info_reply_family(&self) -> Option<([u8; 2], [u8; 2])> {
150        if self.is_non_realtime() && self.subid() == [6, 2] {
151            let data = self.0.get_data();
152            if data.len() >= 8 {
153                return Some(([data[3], data[4]], [data[5], data[6]]));
154            }
155        }
156        None
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    pub fn test_sysex_decoder() {
166        // SysEx
167        let msg = MidiMessage::SysEx(SysExEvent::new_non_realtime(
168            0,
169            [6, 2],
170            &[0, 32, 107, 2, 0, 4, 2, 67, 7, 0, 1, 247],
171        ));
172        let decoder = SysExDecoder::decode(&msg);
173        assert!(decoder.is_some());
174        let decoder = decoder.unwrap();
175        assert!(decoder.is_universal_sysex());
176
177        // Universal SysEx
178        let decoder = USysExDecoder::decode(&msg);
179        assert!(decoder.is_some());
180        let decoder = decoder.unwrap();
181        assert!(decoder.is_non_realtime());
182        assert_eq!(decoder.target_device(), 0);
183        assert_eq!(decoder.subid(), [6, 2]);
184        assert_eq!(
185            decoder.general_info_reply_manufacturer_id(),
186            Some(ManufacturerId::ExtId(32, 107))
187        );
188        assert_eq!(decoder.general_info_reply_family(), Some(([2, 0], [4, 2])));
189    }
190}