usbd_midi/message/
mod.rs

1//! Enum representing the MIDI message.
2
3pub mod channel;
4pub mod control_function;
5pub mod data;
6pub mod notes;
7pub mod raw;
8
9pub use crate::message::channel::Channel;
10pub use crate::message::control_function::ControlFunction;
11pub use crate::message::data::u14::U14;
12pub use crate::message::data::u7::U7;
13pub use crate::message::data::{FromClamped, FromOverFlow};
14pub use crate::message::notes::Note;
15
16use crate::message::raw::{Payload, Raw};
17use crate::packet::cable_number::CableNumber;
18use crate::packet::code_index_number::CodeIndexNumber;
19use crate::packet::{UsbMidiEventPacket, UsbMidiEventPacketError};
20
21/// Note velocity value.
22pub type Velocity = U7;
23
24/// Represents the MIDI messages.
25#[derive(Debug, Eq, PartialEq, Clone)]
26pub enum Message {
27    /// Note On message.
28    NoteOff(Channel, Note, Velocity),
29    /// Note Off message.
30    NoteOn(Channel, Note, Velocity),
31    /// Polyphonic aftertouch (poly-pressure) message.
32    PolyphonicAftertouch(Channel, Note, U7),
33    /// Program change message.
34    ProgramChange(Channel, U7),
35    /// Channel aftertouch message.
36    ChannelAftertouch(Channel, U7),
37    /// Pitchwheel message.
38    PitchWheelChange(Channel, U14),
39    /// Control Change (CC) message.
40    ControlChange(Channel, ControlFunction, U7),
41    /// MTC Quarter Frame message.
42    MtcQuarterFrame(U7),
43    /// Song Position Pointer message.
44    SongPositionPointer(U14),
45    /// Song Select message.
46    SongSelect(U7),
47    /// Tune Request message.
48    TuneRequest,
49    /// Timing Clock message.
50    TimingClock,
51    /// Tick message.
52    Tick,
53    /// Start message.
54    Start,
55    /// Continue message.
56    Continue,
57    /// Stop message.
58    Stop,
59    /// Active Sensing message.
60    ActiveSensing,
61    /// Reset message.
62    Reset,
63}
64
65const NOTE_OFF_MASK: u8 = 0b1000_0000;
66const NOTE_ON_MASK: u8 = 0b1001_0000;
67const POLYPHONIC_MASK: u8 = 0b1010_0000;
68const PROGRAM_MASK: u8 = 0b1100_0000;
69const CHANNEL_AFTERTOUCH_MASK: u8 = 0b1101_0000;
70const PITCH_BEND_MASK: u8 = 0b1110_0000;
71const CONTROL_CHANGE_MASK: u8 = 0b1011_0000;
72
73const MTC_QUARTER_FRAME: u8 = 0xF1;
74const SONG_POSITION_POINTER: u8 = 0xF2;
75const SONG_SELECT: u8 = 0xF3;
76const TUNE_REQUEST: u8 = 0xF6;
77const TIMING_CLOCK: u8 = 0xF8;
78const TICK: u8 = 0xF9;
79const START: u8 = 0xFA;
80const CONTINUE: u8 = 0xFB;
81const STOP: u8 = 0xFC;
82const ACTIVE_SENSING: u8 = 0xFE;
83const RESET: u8 = 0xFF;
84
85impl From<Message> for Raw {
86    fn from(value: Message) -> Raw {
87        match value {
88            Message::NoteOn(chan, note, vel) => {
89                let payload = Payload::DoubleByte(note.into(), vel);
90                let status = NOTE_ON_MASK | u8::from(chan);
91                Raw { status, payload }
92            }
93            Message::NoteOff(chan, note, vel) => {
94                let payload = Payload::DoubleByte(note.into(), vel);
95                let status = NOTE_OFF_MASK | u8::from(chan);
96                Raw { status, payload }
97            }
98            Message::PolyphonicAftertouch(chan, note, pressure) => {
99                let payload = Payload::DoubleByte(note.into(), pressure);
100                let status = POLYPHONIC_MASK | u8::from(chan);
101                Raw { status, payload }
102            }
103            Message::ProgramChange(chan, program) => {
104                let payload = Payload::SingleByte(program);
105                let status = PROGRAM_MASK | u8::from(chan);
106                Raw { status, payload }
107            }
108            Message::ChannelAftertouch(chan, pressure) => {
109                let payload = Payload::SingleByte(pressure);
110                let status = CHANNEL_AFTERTOUCH_MASK | u8::from(chan);
111                Raw { status, payload }
112            }
113            Message::PitchWheelChange(chan, value) => {
114                let value = u16::from(value);
115                let lsb = U7::try_from((value & 0x7F) as u8).unwrap();
116                let msb = U7::try_from(((value >> 7) & 0x7F) as u8).unwrap();
117                let payload = Payload::DoubleByte(lsb, msb);
118                let status = PITCH_BEND_MASK | u8::from(chan);
119                Raw { status, payload }
120            }
121            Message::ControlChange(chan, control_function, value) => {
122                let payload = Payload::DoubleByte(control_function.0, value);
123                let status = CONTROL_CHANGE_MASK | u8::from(chan);
124                Raw { status, payload }
125            }
126            Message::MtcQuarterFrame(frame) => {
127                let payload = Payload::SingleByte(frame);
128                let status = MTC_QUARTER_FRAME;
129                Raw { status, payload }
130            }
131            Message::SongPositionPointer(value) => {
132                let value = u16::from(value);
133                let lsb = U7::try_from((value & 0x7F) as u8).unwrap();
134                let msb = U7::try_from(((value >> 7) & 0x7F) as u8).unwrap();
135                let payload = Payload::DoubleByte(lsb, msb);
136                let status = SONG_POSITION_POINTER;
137                Raw { status, payload }
138            }
139            Message::SongSelect(song) => {
140                let payload = Payload::SingleByte(song);
141                let status = SONG_SELECT;
142                Raw { status, payload }
143            }
144            Message::TuneRequest => {
145                let payload = Payload::Empty;
146                let status = TUNE_REQUEST;
147                Raw { status, payload }
148            }
149            Message::TimingClock => {
150                let payload = Payload::Empty;
151                let status = TIMING_CLOCK;
152                Raw { status, payload }
153            }
154            Message::Tick => {
155                let payload = Payload::Empty;
156                let status = TICK;
157                Raw { status, payload }
158            }
159            Message::Start => {
160                let payload = Payload::Empty;
161                let status = START;
162                Raw { status, payload }
163            }
164            Message::Continue => {
165                let payload = Payload::Empty;
166                let status = CONTINUE;
167                Raw { status, payload }
168            }
169            Message::Stop => {
170                let payload = Payload::Empty;
171                let status = STOP;
172                Raw { status, payload }
173            }
174            Message::ActiveSensing => {
175                let payload = Payload::Empty;
176                let status = ACTIVE_SENSING;
177                Raw { status, payload }
178            }
179            Message::Reset => {
180                let payload = Payload::Empty;
181                let status = RESET;
182                Raw { status, payload }
183            }
184        }
185    }
186}
187
188impl TryFrom<&[u8]> for Message {
189    type Error = UsbMidiEventPacketError;
190
191    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
192        let status_byte = match data.first() {
193            Some(byte) => byte,
194            None => return Err(UsbMidiEventPacketError::MissingDataPacket),
195        };
196
197        match *status_byte {
198            MTC_QUARTER_FRAME => {
199                return Ok(Message::MtcQuarterFrame(get_u7_at(data, 1)?));
200            }
201            SONG_POSITION_POINTER => {
202                return Ok(Message::SongPositionPointer(get_u14(data)?));
203            }
204            SONG_SELECT => {
205                return Ok(Message::SongSelect(get_u7_at(data, 1)?));
206            }
207            TUNE_REQUEST => {
208                return Ok(Message::TuneRequest);
209            }
210            TIMING_CLOCK => {
211                return Ok(Message::TimingClock);
212            }
213            TICK => {
214                return Ok(Message::Tick);
215            }
216            START => {
217                return Ok(Message::Start);
218            }
219            CONTINUE => {
220                return Ok(Message::Continue);
221            }
222            STOP => {
223                return Ok(Message::Stop);
224            }
225            ACTIVE_SENSING => {
226                return Ok(Message::ActiveSensing);
227            }
228            RESET => {
229                return Ok(Message::Reset);
230            }
231            _ => {}
232        }
233
234        let event_type = status_byte & 0b1111_0000;
235        let channel_bytes = (status_byte) & 0b0000_1111;
236
237        let channel = Channel::try_from(channel_bytes).ok().unwrap();
238
239        match event_type {
240            NOTE_ON_MASK => Ok(Message::NoteOn(
241                channel,
242                get_note(data)?,
243                get_u7_at(data, 2)?,
244            )),
245            NOTE_OFF_MASK => Ok(Message::NoteOff(
246                channel,
247                get_note(data)?,
248                get_u7_at(data, 2)?,
249            )),
250            POLYPHONIC_MASK => Ok(Message::PolyphonicAftertouch(
251                channel,
252                get_note(data)?,
253                get_u7_at(data, 2)?,
254            )),
255            PROGRAM_MASK => Ok(Message::ProgramChange(channel, get_u7_at(data, 1)?)),
256            CHANNEL_AFTERTOUCH_MASK => Ok(Message::ChannelAftertouch(channel, get_u7_at(data, 1)?)),
257            PITCH_BEND_MASK => Ok(Message::PitchWheelChange(channel, get_u14(data)?)),
258            CONTROL_CHANGE_MASK => Ok(Message::ControlChange(
259                channel,
260                ControlFunction(get_u7_at(data, 1)?),
261                get_u7_at(data, 2)?,
262            )),
263            _ => Err(UsbMidiEventPacketError::InvalidEventType(event_type)),
264        }
265    }
266}
267
268impl TryFrom<&UsbMidiEventPacket> for Message {
269    type Error = UsbMidiEventPacketError;
270
271    fn try_from(value: &UsbMidiEventPacket) -> Result<Self, Self::Error> {
272        Self::try_from(&value.as_raw_bytes()[1..])
273    }
274}
275
276impl Message {
277    /// Create a packet from the message.
278    pub fn into_packet(self, cable: CableNumber) -> UsbMidiEventPacket {
279        let cin = self.code_index_number() as u8;
280
281        let mut raw = [0; 4];
282        raw[0] = (cable as u8) << 4 | cin;
283        let r = Raw::from(self);
284        raw[1] = r.status;
285
286        match r.payload {
287            Payload::Empty => {
288                raw[2] = 0;
289                raw[3] = 0;
290            }
291            Payload::SingleByte(byte) => {
292                raw[2] = byte.0;
293                raw[3] = 0;
294            }
295            Payload::DoubleByte(byte1, byte2) => {
296                raw[2] = byte1.0;
297                raw[3] = byte2.0;
298            }
299        };
300
301        UsbMidiEventPacket::try_from(raw.as_slice()).unwrap()
302    }
303
304    /// Returns the code index number for a message.
305    pub fn code_index_number(&self) -> CodeIndexNumber {
306        match self {
307            Self::NoteOn(_, _, _) => CodeIndexNumber::NoteOn,
308            Self::NoteOff(_, _, _) => CodeIndexNumber::NoteOff,
309            Self::ChannelAftertouch(_, _) => CodeIndexNumber::ChannelPressure,
310            Self::PitchWheelChange(_, _) => CodeIndexNumber::PitchBendChange,
311            Self::PolyphonicAftertouch(_, _, _) => CodeIndexNumber::PolyKeyPress,
312            Self::ProgramChange(_, _) => CodeIndexNumber::ProgramChange,
313            Self::ControlChange(_, _, _) => CodeIndexNumber::ControlChange,
314            Self::MtcQuarterFrame(_) => CodeIndexNumber::SystemCommon2Bytes,
315            Self::SongPositionPointer(_) => CodeIndexNumber::SystemCommon3Bytes,
316            Self::SongSelect(_) => CodeIndexNumber::SystemCommon2Bytes,
317            Self::TuneRequest => CodeIndexNumber::SystemCommon1Byte,
318            Self::TimingClock => CodeIndexNumber::SingleByte,
319            Self::Tick => CodeIndexNumber::SingleByte,
320            Self::Start => CodeIndexNumber::SingleByte,
321            Self::Continue => CodeIndexNumber::SingleByte,
322            Self::Stop => CodeIndexNumber::SingleByte,
323            Self::ActiveSensing => CodeIndexNumber::SingleByte,
324            Self::Reset => CodeIndexNumber::SingleByte,
325        }
326    }
327}
328
329fn get_note(data: &[u8]) -> Result<Note, UsbMidiEventPacketError> {
330    let note_byte = get_byte_at_position(data, 1)?;
331    match Note::try_from(note_byte) {
332        Ok(note) => Ok(note),
333        Err(_) => Err(UsbMidiEventPacketError::InvalidNote(note_byte)),
334    }
335}
336
337fn get_u7_at(data: &[u8], index: usize) -> Result<U7, UsbMidiEventPacketError> {
338    let data_byte = get_byte_at_position(data, index)?;
339    Ok(U7::from_clamped(data_byte))
340}
341
342fn get_u14(data: &[u8]) -> Result<U14, UsbMidiEventPacketError> {
343    let lsb = get_byte_at_position(data, 1)?;
344    let msb = get_byte_at_position(data, 2)?;
345    Ok(U14::from_clamped(((msb as u16) << 7) | (lsb as u16)))
346}
347
348fn get_byte_at_position(data: &[u8], index: usize) -> Result<u8, UsbMidiEventPacketError> {
349    match data.get(index) {
350        Some(byte) => Ok(*byte),
351        None => Err(UsbMidiEventPacketError::MissingDataPacket),
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use crate::message::channel::Channel::{Channel1, Channel2};
358    use crate::message::control_function::ControlFunction;
359    use crate::message::data::u14::U14;
360    use crate::message::data::u7::U7;
361    use crate::message::notes::Note;
362    use crate::message::Message;
363    use crate::packet::cable_number::CableNumber::{Cable0, Cable1};
364    use crate::packet::UsbMidiEventPacket;
365
366    macro_rules! decode_message_test {
367        ($($id:ident:$value:expr,)*) => {
368            $(
369                #[test]
370                fn $id() {
371                    let (usb_midi_data_packet, expected) = $value;
372                    let message = UsbMidiEventPacket::try_from(&usb_midi_data_packet[..]).unwrap();
373                    assert_eq!(expected, message);
374                }
375            )*
376        }
377    }
378
379    decode_message_test! {
380        note_on: ([9, 144, 36, 127], Message::NoteOn(Channel1, Note::C2, U7(127)).into_packet(Cable0)),
381        note_off: ([8, 128, 36, 0], Message::NoteOff(Channel1, Note::C2, U7(0)).into_packet(Cable0)),
382        polyphonic_aftertouch: ([10, 160, 36, 64], Message::PolyphonicAftertouch(Channel1, Note::C2, U7(64)).into_packet(Cable0)),
383        program_change: ([28, 192, 127, 0], Message::ProgramChange(Channel1, U7(127)).into_packet(Cable1)),
384        channel_aftertouch: ([13, 208, 127, 0], Message::ChannelAftertouch(Channel1, U7(127)).into_packet(Cable0)),
385        pitch_wheel: ([14, 224, 64, 32], Message::PitchWheelChange(Channel1, U14(4160)).into_packet(Cable0)),
386        control_change: ([11, 177, 1, 32], Message::ControlChange(Channel2, ControlFunction::MOD_WHEEL_1, U7(32)).into_packet(Cable0)),
387        mtc_quarter_frame: ([0x02, 0xF1, 12, 0], Message::MtcQuarterFrame(U7(12)).into_packet(Cable0)),
388        song_position_pointer: ([0x03, 0xF2, 38, 75], Message::SongPositionPointer(U14(9638)).into_packet(Cable0)),
389        song_select: ([0x02, 0xF3, 4, 0], Message::SongSelect(U7(4)).into_packet(Cable0)),
390        tune_request: ([0x05, 0xF6, 0, 0], Message::TuneRequest.into_packet(Cable0)),
391        timing_clock: ([0x0F, 0xF8, 0, 0], Message::TimingClock.into_packet(Cable0)),
392        tick: ([0x0F, 0xF9, 0, 0], Message::Tick.into_packet(Cable0)),
393        start: ([0x0F, 0xFA, 0, 0], Message::Start.into_packet(Cable0)),
394        continue_: ([0x0F, 0xFB, 0, 0], Message::Continue.into_packet(Cable0)),
395        stop: ([0x0F, 0xFC, 0, 0], Message::Stop.into_packet(Cable0)),
396        active_sensing: ([0x0F, 0xFE, 0, 0], Message::ActiveSensing.into_packet(Cable0)),
397        reset: ([0x0F, 0xFF, 0, 0], Message::Reset.into_packet(Cable0)),
398    }
399}