1mod controller_destination;
2pub use controller_destination::*;
3mod file_dump;
4pub use file_dump::*;
5mod file_reference;
6pub use file_reference::*;
7mod global_parameter;
8pub use global_parameter::*;
9mod key_based_instrument_control;
10pub use key_based_instrument_control::*;
11mod machine_control;
12pub use machine_control::*;
13mod notation;
14pub use notation::*;
15mod sample_dump;
16pub use sample_dump::*;
17mod show_control;
18pub use show_control::*;
19mod tuning;
20pub use tuning::*;
21
22use alloc::vec::Vec;
23
24use super::ReceiverContext;
25use super::general_midi::GeneralMidi;
26use super::parse_error::*;
27use super::time_code::*;
28use super::util::*;
29
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[derive(Debug, Clone, PartialEq)]
35pub enum SystemExclusiveMsg {
36 Commercial { id: ManufacturerID, data: Vec<u8> },
39 NonCommercial { data: Vec<u8> },
41 UniversalRealTime {
44 device: DeviceID,
45 msg: UniversalRealTimeMsg,
46 },
47 UniversalNonRealTime {
50 device: DeviceID,
51 msg: UniversalNonRealTimeMsg,
52 },
53}
54
55impl SystemExclusiveMsg {
56 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>, first_byte_is_f0: bool) {
57 if first_byte_is_f0 {
58 v.push(0xF0);
59 }
60 match self {
61 SystemExclusiveMsg::Commercial { id, data } => {
62 id.extend_midi(v);
63 data.iter().for_each(|d| v.push(to_u7(*d)));
64 }
65 SystemExclusiveMsg::NonCommercial { data } => {
66 v.push(0x7D);
67 data.iter().for_each(|d| v.push(to_u7(*d)));
68 }
69 SystemExclusiveMsg::UniversalRealTime { device, msg } => {
70 v.push(0x7F);
71 v.push(device.to_u8());
72 msg.extend_midi(v);
73 }
74 SystemExclusiveMsg::UniversalNonRealTime { device, msg } => {
75 let p = v.len();
76 v.push(0x7E);
77 v.push(device.to_u8());
78 msg.extend_midi(v);
79 if let UniversalNonRealTimeMsg::SampleDump(SampleDumpMsg::Packet { .. }) = msg {
80 let q = v.len();
81 v[q - 1] = checksum(&v[p..q - 1]);
82 }
83 if let UniversalNonRealTimeMsg::KeyBasedTuningDump(_) = msg {
84 let q = v.len();
85 v[q - 1] = checksum(&v[p..q - 1]);
86 }
87 if let UniversalNonRealTimeMsg::ScaleTuning1Byte(_) = msg {
88 let q = v.len();
89 v[q - 1] = checksum(&v[p..q - 1]);
90 }
91 if let UniversalNonRealTimeMsg::ScaleTuning2Byte(_) = msg {
92 let q = v.len();
93 v[q - 1] = checksum(&v[p..q - 1]);
94 }
95 if let UniversalNonRealTimeMsg::FileDump(FileDumpMsg::Packet { .. }) = msg {
96 let q = v.len();
97 v[q - 1] = checksum(&v[p..q - 1]);
98 }
99 }
100 }
101 v.push(0xF7);
102 }
103
104 fn sysex_bytes_from_midi(m: &[u8], first_byte_is_f0: bool) -> Result<&[u8], ParseError> {
105 if first_byte_is_f0 && m.first() != Some(&0xF0) {
106 return Err(ParseError::UndefinedSystemExclusiveMessage(
107 m.first().copied(),
108 ));
109 }
110 let offset = if first_byte_is_f0 { 1 } else { 0 };
111 for (i, b) in m[offset..].iter().enumerate() {
112 if b == &0xF7 {
113 return Ok(&m[offset..i + offset]);
114 }
115 if b > &127 {
116 return Err(ParseError::ByteOverflow);
117 }
118 }
119 Err(ParseError::NoEndOfSystemExclusiveFlag)
120 }
121
122 pub(crate) fn from_midi(
123 m: &[u8],
124 ctx: &mut ReceiverContext,
125 ) -> Result<(Self, usize), ParseError> {
126 let m = Self::sysex_bytes_from_midi(m, !ctx.is_smf_sysex)?;
127 match m.first() {
128 Some(0x7D) => Ok((
129 Self::NonCommercial {
130 data: m[1..].to_vec(),
131 },
132 m.len() + 2,
133 )),
134 Some(0x7E) => Ok((
135 Self::UniversalNonRealTime {
136 device: DeviceID::from_midi(&m[1..])?,
137 msg: UniversalNonRealTimeMsg::from_midi(&m[2..])?,
138 },
139 m.len() + 2,
140 )),
141 Some(0x7F) => Ok((
142 Self::UniversalRealTime {
143 device: DeviceID::from_midi(&m[1..])?,
144 msg: UniversalRealTimeMsg::from_midi(&m[2..], ctx)?,
145 },
146 m.len() + 2,
147 )),
148 Some(_) => {
149 let (id, len) = ManufacturerID::from_midi(m)?;
150 Ok((
151 Self::Commercial {
152 id,
153 data: m[len..].to_vec(),
154 },
155 m.len() + 2,
156 ))
157 }
158 None => Err(crate::ParseError::UnexpectedEnd),
159 }
160 }
161}
162
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub struct ManufacturerID(pub u8, pub Option<u8>);
171
172impl ManufacturerID {
173 fn extend_midi(&self, v: &mut Vec<u8>) {
174 if let Some(second) = self.1 {
175 v.push(0x00);
176 v.push(to_u7(self.0));
177 v.push(to_u7(second));
178 } else {
179 v.push(self.0.min(0x7C))
180 }
181 }
182
183 fn from_midi(m: &[u8]) -> Result<(Self, usize), ParseError> {
184 let b1 = u7_from_midi(m)?;
185 if b1 == 0x00 {
186 if m.len() < 3 {
187 return Err(crate::ParseError::UnexpectedEnd);
188 }
189 let b2 = u7_from_midi(&m[1..])?;
190 let b3 = u7_from_midi(&m[2..])?;
191 Ok((Self(b2, Some(b3)), 3))
192 } else {
193 Ok((Self(b1, None), 1))
194 }
195 }
196}
197
198impl From<u8> for ManufacturerID {
199 fn from(a: u8) -> Self {
200 Self(a, None)
201 }
202}
203
204impl From<(u8, u8)> for ManufacturerID {
205 fn from((a, b): (u8, u8)) -> Self {
206 Self(a, Some(b))
207 }
208}
209
210#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214pub enum DeviceID {
215 Device(u8),
216 AllCall,
217}
218
219impl DeviceID {
220 fn to_u8(self) -> u8 {
221 match self {
222 Self::AllCall => 0x7F,
223 Self::Device(x) => to_u7(x),
224 }
225 }
226
227 fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
228 let b = u7_from_midi(m)?;
229 if b == 0x7F {
230 Ok(Self::AllCall)
231 } else {
232 Ok(Self::Device(b))
233 }
234 }
235}
236
237#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
239#[derive(Debug, Clone, PartialEq)]
240pub enum UniversalRealTimeMsg {
241 TimeCodeFull(TimeCode),
245 TimeCodeUserBits(UserBits),
247 ShowControl(ShowControlMsg),
249 BarMarker(BarMarker),
251 TimeSignature(TimeSignature),
253 TimeSignatureDelayed(TimeSignature),
255 MasterVolume(u16),
257 MasterBalance(u16),
259 MasterFineTuning(i16),
263 MasterCoarseTuning(i8),
267 GlobalParameterControl(GlobalParameterControl),
269 TimeCodeCueing(TimeCodeCueingMsg),
271 MachineControlCommand(MachineControlCommandMsg),
273 MachineControlResponse(MachineControlResponseMsg),
275 TuningNoteChange(TuningNoteChange),
277 ScaleTuning1Byte(ScaleTuning1Byte),
279 ScaleTuning2Byte(ScaleTuning2Byte),
281 ChannelPressureControllerDestination(ControllerDestination),
283 PolyphonicKeyPressureControllerDestination(ControllerDestination),
285 ControlChangeControllerDestination(ControlChangeControllerDestination),
287 KeyBasedInstrumentControl(KeyBasedInstrumentControl),
289}
290
291impl UniversalRealTimeMsg {
292 fn extend_midi(&self, v: &mut Vec<u8>) {
293 match self {
294 UniversalRealTimeMsg::TimeCodeFull(code) => {
295 v.push(0x1);
296 v.push(0x1);
297 code.extend_midi(v);
298 }
299 UniversalRealTimeMsg::TimeCodeUserBits(user_bits) => {
300 v.push(0x1);
301 v.push(0x2);
302 let [ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9] = user_bits.to_nibbles();
303 v.extend_from_slice(&[ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9]);
304 }
305 UniversalRealTimeMsg::ShowControl(msg) => {
306 v.push(0x2);
307 msg.extend_midi(v);
308 }
309 UniversalRealTimeMsg::BarMarker(marker) => {
310 v.push(0x3);
311 v.push(0x1);
312 marker.extend_midi(v);
313 }
314 UniversalRealTimeMsg::TimeSignature(signature) => {
315 v.push(0x3);
316 v.push(0x2);
317 signature.extend_midi(v);
318 }
319 UniversalRealTimeMsg::TimeSignatureDelayed(signature) => {
320 v.push(0x3);
321 v.push(0x42);
322 signature.extend_midi(v);
323 }
324 UniversalRealTimeMsg::MasterVolume(vol) => {
325 v.push(0x4);
326 v.push(0x1);
327 push_u14(*vol, v);
328 }
329 UniversalRealTimeMsg::MasterBalance(bal) => {
330 v.push(0x4);
331 v.push(0x2);
332 push_u14(*bal, v);
333 }
334 UniversalRealTimeMsg::MasterFineTuning(t) => {
335 v.push(0x4);
336 v.push(0x3);
337 let [msb, lsb] = i_to_u14(*t);
338 v.push(lsb);
339 v.push(msb);
340 }
341 UniversalRealTimeMsg::MasterCoarseTuning(t) => {
342 v.push(0x4);
343 v.push(0x4);
344 v.push(i_to_u7(*t));
345 }
346 UniversalRealTimeMsg::GlobalParameterControl(gp) => {
347 v.push(0x4);
348 v.push(0x5);
349 gp.extend_midi(v);
350 }
351 UniversalRealTimeMsg::TimeCodeCueing(msg) => {
352 v.push(0x5);
353 msg.extend_midi(v);
354 }
355 UniversalRealTimeMsg::MachineControlCommand(msg) => {
356 v.push(0x6);
357 msg.extend_midi(v);
358 }
359 UniversalRealTimeMsg::MachineControlResponse(msg) => {
360 v.push(0x7);
361 msg.extend_midi(v);
362 }
363 UniversalRealTimeMsg::TuningNoteChange(note_change) => {
364 v.push(0x8);
365 v.push(if note_change.tuning_bank_num.is_some() {
366 0x7
367 } else {
368 0x2
369 });
370 if let Some(bank_num) = note_change.tuning_bank_num {
371 v.push(to_u7(bank_num))
372 }
373 note_change.extend_midi(v);
374 }
375 UniversalRealTimeMsg::ScaleTuning1Byte(tuning) => {
376 v.push(0x8);
377 v.push(0x8);
378 tuning.extend_midi(v);
379 }
380 UniversalRealTimeMsg::ScaleTuning2Byte(tuning) => {
381 v.push(0x8);
382 v.push(0x9);
383 tuning.extend_midi(v);
384 }
385 UniversalRealTimeMsg::ChannelPressureControllerDestination(d) => {
386 v.push(0x9);
387 v.push(0x1);
388 d.extend_midi(v);
389 }
390 UniversalRealTimeMsg::PolyphonicKeyPressureControllerDestination(d) => {
391 v.push(0x9);
392 v.push(0x2);
393 d.extend_midi(v);
394 }
395 UniversalRealTimeMsg::ControlChangeControllerDestination(d) => {
396 v.push(0x9);
397 v.push(0x3);
398 d.extend_midi(v);
399 }
400 UniversalRealTimeMsg::KeyBasedInstrumentControl(control) => {
401 v.push(0xA);
402 v.push(0x1);
403 control.extend_midi(v);
404 }
405 }
406 }
407
408 fn from_midi(m: &[u8], ctx: &mut ReceiverContext) -> Result<Self, ParseError> {
409 if m.len() < 2 {
410 return Err(crate::ParseError::UnexpectedEnd);
411 }
412
413 match (m[0], m[1]) {
414 (0x1, 0x1) => {
415 if m.len() > 6 {
416 Err(ParseError::Invalid(
417 "Extra bytes after a UniversalRealTimeMsg::TimeCodeFull",
418 ))
419 } else {
420 let time_code = TimeCode::from_midi(&m[2..])?;
421 ctx.time_code = time_code;
422 Ok(Self::TimeCodeFull(time_code))
423 }
424 }
425 _ => Err(ParseError::NotImplemented("UniversalRealTimeMsg")),
426 }
427 }
428}
429
430#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
432#[derive(Debug, Clone, PartialEq)]
433pub enum UniversalNonRealTimeMsg {
434 SampleDump(SampleDumpMsg),
436 ExtendedSampleDump(ExtendedSampleDumpMsg),
438 TimeCodeCueingSetup(TimeCodeCueingSetupMsg),
440 IdentityRequest,
442 IdentityReply(IdentityReply),
444 FileDump(FileDumpMsg),
446 TuningBulkDumpRequest(u8, Option<u8>),
449 KeyBasedTuningDump(KeyBasedTuningDump),
451 ScaleTuningDump1Byte(ScaleTuningDump1Byte),
453 ScaleTuningDump2Byte(ScaleTuningDump2Byte),
456 TuningNoteChange(TuningNoteChange),
458 ScaleTuning1Byte(ScaleTuning1Byte),
460 ScaleTuning2Byte(ScaleTuning2Byte),
462 GeneralMidi(GeneralMidi),
464 FileReference(FileReferenceMsg),
466 EOF,
468 Wait,
471 Cancel,
473 NAK(u8),
476 ACK(u8),
479}
480
481impl UniversalNonRealTimeMsg {
482 fn extend_midi(&self, v: &mut Vec<u8>) {
483 match self {
484 UniversalNonRealTimeMsg::SampleDump(msg) => {
485 match msg {
486 SampleDumpMsg::Header { .. } => v.push(0x1),
487 SampleDumpMsg::Packet { .. } => v.push(0x2),
488 SampleDumpMsg::Request { .. } => v.push(0x3),
489 SampleDumpMsg::LoopPointTransmission { .. } => {
490 v.push(0x5);
491 v.push(0x1);
492 }
493 SampleDumpMsg::LoopPointsRequest { .. } => {
494 v.push(0x5);
495 v.push(0x2);
496 }
497 }
498 msg.extend_midi(v);
499 }
500 UniversalNonRealTimeMsg::ExtendedSampleDump(msg) => {
501 v.push(0x5);
502 match msg {
503 ExtendedSampleDumpMsg::SampleName { .. } => v.push(0x3),
504 ExtendedSampleDumpMsg::SampleNameRequest { .. } => v.push(0x4),
505 ExtendedSampleDumpMsg::Header { .. } => v.push(0x5),
506 ExtendedSampleDumpMsg::LoopPointTransmission { .. } => v.push(0x6),
507 ExtendedSampleDumpMsg::LoopPointsRequest { .. } => v.push(0x7),
508 }
509 msg.extend_midi(v);
510 }
511 UniversalNonRealTimeMsg::TimeCodeCueingSetup(msg) => {
512 v.push(0x4);
513 msg.extend_midi(v);
514 }
515 UniversalNonRealTimeMsg::IdentityRequest => {
516 v.push(0x6);
517 v.push(0x1);
518 }
519 UniversalNonRealTimeMsg::IdentityReply(identity) => {
520 v.push(0x6);
521 v.push(0x2);
522 identity.extend_midi(v);
523 }
524 UniversalNonRealTimeMsg::FileDump(msg) => {
525 v.push(0x7);
526 msg.extend_midi(v);
527 }
528 UniversalNonRealTimeMsg::TuningBulkDumpRequest(program_num, bank_num) => {
529 v.push(0x8);
530 v.push(if bank_num.is_some() { 0x3 } else { 0x0 });
531 if let Some(bank_num) = bank_num {
532 v.push(to_u7(*bank_num))
533 }
534 v.push(to_u7(*program_num));
535 }
536 UniversalNonRealTimeMsg::KeyBasedTuningDump(tuning) => {
537 v.push(0x8);
538 v.push(if tuning.tuning_bank_num.is_some() {
539 0x4
540 } else {
541 0x1
542 });
543 tuning.extend_midi(v);
544 }
545 UniversalNonRealTimeMsg::ScaleTuningDump1Byte(tuning) => {
546 v.push(0x8);
547 v.push(0x5);
548 tuning.extend_midi(v);
549 }
550 UniversalNonRealTimeMsg::ScaleTuningDump2Byte(tuning) => {
551 v.push(0x8);
552 v.push(0x6);
553 tuning.extend_midi(v);
554 }
555 UniversalNonRealTimeMsg::TuningNoteChange(tuning) => {
556 v.push(0x8);
557 v.push(0x7);
558 if let Some(bank_num) = tuning.tuning_bank_num {
559 v.push(to_u7(bank_num))
560 } else {
561 v.push(0x0); }
563 tuning.extend_midi(v);
564 }
565 UniversalNonRealTimeMsg::ScaleTuning1Byte(tuning) => {
566 v.push(0x8);
567 v.push(0x8);
568 tuning.extend_midi(v);
569 }
570 UniversalNonRealTimeMsg::ScaleTuning2Byte(tuning) => {
571 v.push(0x8);
572 v.push(0x9);
573 tuning.extend_midi(v);
574 }
575 UniversalNonRealTimeMsg::GeneralMidi(gm) => {
576 v.push(0x9);
577 v.push(*gm as u8);
578 }
579 UniversalNonRealTimeMsg::FileReference(msg) => {
580 v.push(0xB);
581 match msg {
582 FileReferenceMsg::Open { .. } => v.push(0x1),
583 FileReferenceMsg::SelectContents { .. } => v.push(0x2),
584 FileReferenceMsg::OpenSelectContents { .. } => v.push(0x3),
585 FileReferenceMsg::Close { .. } => v.push(0x4),
586 }
587 msg.extend_midi(v);
588 }
589
590 UniversalNonRealTimeMsg::EOF => {
591 v.push(0x7B);
592 v.push(0x0);
593 }
594 UniversalNonRealTimeMsg::Wait => {
595 v.push(0x7C);
596 v.push(0x0);
597 }
598 UniversalNonRealTimeMsg::Cancel => {
599 v.push(0x7D);
600 v.push(0x0);
601 }
602 UniversalNonRealTimeMsg::NAK(packet_num) => {
603 v.push(0x7E);
604 v.push(to_u7(*packet_num));
605 }
606 UniversalNonRealTimeMsg::ACK(packet_num) => {
607 v.push(0x7F);
608 v.push(to_u7(*packet_num));
609 }
610 }
611 }
612
613 fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
614 if m.len() < 2 {
615 return Err(crate::ParseError::UnexpectedEnd);
616 }
617
618 match (m[0], m[1]) {
619 (0x6, 0x2) => {
620 if m.len() < 3 {
621 return Err(crate::ParseError::UnexpectedEnd);
622 }
623 Ok(Self::IdentityReply(IdentityReply::from_midi(&m[2..])?))
624 }
625 _ => Err(ParseError::NotImplemented("UniversalNonRealTimeMsg")),
626 }
627 }
628}
629
630#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
634#[derive(Debug, Copy, Clone, PartialEq, Eq)]
635pub struct IdentityReply {
636 pub id: ManufacturerID,
637 pub family: u16,
638 pub family_member: u16,
639 pub software_revision: (u8, u8, u8, u8),
641}
642
643impl IdentityReply {
644 fn extend_midi(&self, v: &mut Vec<u8>) {
645 self.id.extend_midi(v);
646 push_u14(self.family, v);
647 push_u14(self.family_member, v);
648 v.push(to_u7(self.software_revision.0));
649 v.push(to_u7(self.software_revision.1));
650 v.push(to_u7(self.software_revision.2));
651 v.push(to_u7(self.software_revision.3));
652 }
653
654 fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
655 let (manufacturer_id, shift) = ManufacturerID::from_midi(m)?;
656 if m.len() < shift + 8 {
657 return Err(crate::ParseError::UnexpectedEnd);
658 }
659 Ok(IdentityReply {
660 id: manufacturer_id,
661 family: u14_from_midi(&m[shift..])?,
662 family_member: u14_from_midi(&m[(shift + 2)..])?,
663 software_revision: (m[shift + 4], m[shift + 5], m[shift + 6], m[shift + 7]),
664 })
665 }
666
667 pub fn family_as_bytes(&self) -> [u8; 4] {
669 let [family_msb, family_lsb] = to_u14(self.family);
670 let [family_member_msb, family_member_lsb] = to_u14(self.family_member);
671 [family_lsb, family_msb, family_member_lsb, family_member_msb]
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 use super::super::*;
678 use alloc::vec;
679
680 #[test]
681 fn serialize_system_exclusive_msg() {
682 assert_eq!(
683 MidiMsg::SystemExclusive {
684 msg: SystemExclusiveMsg::Commercial {
685 id: 1.into(),
686 data: vec![0xff, 0x77, 0x00]
687 }
688 }
689 .to_midi(),
690 vec![0xF0, 0x01, 0x7F, 0x77, 0x00, 0xF7]
691 );
692
693 assert_eq!(
694 MidiMsg::SystemExclusive {
695 msg: SystemExclusiveMsg::Commercial {
696 id: (1, 3).into(),
697 data: vec![0xff, 0x77, 0x00]
698 }
699 }
700 .to_midi(),
701 vec![0xF0, 0x00, 0x01, 0x03, 0x7F, 0x77, 0x00, 0xF7]
702 );
703
704 assert_eq!(
705 MidiMsg::SystemExclusive {
706 msg: SystemExclusiveMsg::NonCommercial {
707 data: vec![0xff, 0x77, 0x00]
708 }
709 }
710 .to_midi(),
711 vec![0xF0, 0x7D, 0x7F, 0x77, 0x00, 0xF7]
712 );
713
714 assert_eq!(
715 MidiMsg::SystemExclusive {
716 msg: SystemExclusiveMsg::UniversalNonRealTime {
717 device: DeviceID::AllCall,
718 msg: UniversalNonRealTimeMsg::EOF
719 }
720 }
721 .to_midi(),
722 vec![0xF0, 0x7E, 0x7F, 0x7B, 0x00, 0xF7]
723 );
724
725 assert_eq!(
726 MidiMsg::SystemExclusive {
727 msg: SystemExclusiveMsg::UniversalRealTime {
728 device: DeviceID::Device(3),
729 msg: UniversalRealTimeMsg::MasterVolume(1000)
730 }
731 }
732 .to_midi(),
733 vec![0xF0, 0x7F, 0x03, 0x04, 0x01, 0x68, 0x07, 0xF7]
734 );
735 }
736
737 #[test]
738 fn deserialize_system_exclusive_msg() {
739 let mut ctx = ReceiverContext::new();
740
741 test_serialization(
742 MidiMsg::SystemExclusive {
743 msg: SystemExclusiveMsg::Commercial {
744 id: 1.into(),
745 data: vec![0x7f, 0x77, 0x00],
746 },
747 },
748 &mut ctx,
749 );
750
751 test_serialization(
752 MidiMsg::SystemExclusive {
753 msg: SystemExclusiveMsg::Commercial {
754 id: (1, 3).into(),
755 data: vec![0x7f, 0x77, 0x00],
756 },
757 },
758 &mut ctx,
759 );
760
761 test_serialization(
762 MidiMsg::SystemExclusive {
763 msg: SystemExclusiveMsg::NonCommercial {
764 data: vec![0x7f, 0x77, 0x00],
765 },
766 },
767 &mut ctx,
768 );
769
770 test_serialization(
771 MidiMsg::SystemExclusive {
772 msg: SystemExclusiveMsg::UniversalRealTime {
773 device: DeviceID::AllCall,
774 msg: UniversalRealTimeMsg::TimeCodeFull(TimeCode {
775 frames: 29,
776 seconds: 58,
777 minutes: 20,
778 hours: 23,
779 code_type: TimeCodeType::DF30,
780 }),
781 },
782 },
783 &mut ctx,
784 );
785
786 assert_eq!(
787 ctx.time_code,
788 TimeCode {
789 frames: 29,
790 seconds: 58,
791 minutes: 20,
792 hours: 23,
793 code_type: TimeCodeType::DF30,
794 }
795 );
796 }
797}