1pub 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
21pub type Velocity = U7;
23
24#[derive(Debug, Eq, PartialEq, Clone)]
26pub enum Message {
27 NoteOff(Channel, Note, Velocity),
29 NoteOn(Channel, Note, Velocity),
31 PolyphonicAftertouch(Channel, Note, U7),
33 ProgramChange(Channel, U7),
35 ChannelAftertouch(Channel, U7),
37 PitchWheelChange(Channel, U14),
39 ControlChange(Channel, ControlFunction, U7),
41 MtcQuarterFrame(U7),
43 SongPositionPointer(U14),
45 SongSelect(U7),
47 TuneRequest,
49 TimingClock,
51 Tick,
53 Start,
55 Continue,
57 Stop,
59 ActiveSensing,
61 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 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 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}