midi_msg/
time_code.rs

1use super::util::*;
2
3/// Used to synchronize device positions, by [`SystemCommonMsg::TimeCodeQuarterFrameX`](crate::SystemCommonMsg::TimeCodeQuarterFrame1)
4/// as well as [`UniversalRealTimeMsg::TimeCodeFull`](crate::UniversalRealTimeMsg::TimeCodeFull).
5///
6/// Based on [the SMTPE time code standard](https://en.wikipedia.org/wiki/SMPTE_timecode).
7///
8/// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008)
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub struct TimeCode {
11    /// The position in frames, 0-29
12    pub frames: u8,
13    /// The position in seconds, 0-59
14    pub seconds: u8,
15    /// The position in minutes, 0-59
16    pub minutes: u8,
17    /// The position in hours, 0-23
18    pub hours: u8,
19    pub code_type: TimeCodeType,
20}
21
22impl TimeCode {
23    /// Return the four byte representation of the frame: [frame, seconds, minutes, timecode + hours]
24    pub fn to_bytes(self) -> [u8; 4] {
25        [
26            self.frames.min(29),
27            self.seconds.min(59),
28            self.minutes.min(59),
29            self.hours.min(23) + ((self.code_type as u8) << 5),
30        ]
31    }
32
33    /// Return an 8 byte, Quarter Frame representation of the Frame
34    pub fn to_nibbles(self) -> [u8; 8] {
35        let [frame, seconds, minutes, codehour] = self.to_bytes();
36
37        let [frame_msb, frame_lsb] = to_nibble(frame);
38        let [seconds_msb, seconds_lsb] = to_nibble(seconds);
39        let [minutes_msb, minutes_lsb] = to_nibble(minutes);
40        let [codehour_msb, codehour_lsb] = to_nibble(codehour);
41        [
42            (0 << 4) + frame_lsb,
43            (1 << 4) + frame_msb,
44            (2 << 4) + seconds_lsb,
45            (3 << 4) + seconds_msb,
46            (4 << 4) + minutes_lsb,
47            (5 << 4) + minutes_msb,
48            (6 << 4) + codehour_lsb,
49            (7 << 4) + codehour_msb,
50        ]
51    }
52
53    // Returns the quarter frame number
54    pub(crate) fn extend(&mut self, nibble: u8) -> u8 {
55        let frame_number = nibble >> 4;
56        let nibble = nibble & 0b00001111;
57
58        match frame_number {
59            0 => self.frames = (self.frames & 0b11110000) + nibble,
60            1 => self.frames = (self.frames & 0b00001111) + (nibble << 4),
61            2 => self.seconds = (self.seconds & 0b11110000) + nibble,
62            3 => self.seconds = (self.seconds & 0b00001111) + (nibble << 4),
63            4 => self.minutes = (self.minutes & 0b11110000) + nibble,
64            5 => self.minutes = (self.minutes & 0b00001111) + (nibble << 4),
65            6 => self.hours = (self.hours & 0b11110000) + nibble,
66            7 => {
67                self.hours = (self.hours & 0b00001111) + ((nibble & 0b0001) << 4);
68                self.code_type = match (nibble & 0b0110) >> 1 {
69                    0 => TimeCodeType::FPS24,
70                    1 => TimeCodeType::FPS25,
71                    2 => TimeCodeType::DF30,
72                    3 => TimeCodeType::NDF30,
73                    _ => panic!("Should not be reachable"),
74                }
75            }
76            _ => panic!("Should not be reachable"),
77        }
78
79        frame_number
80    }
81}
82
83/// Indicates the frame rate of the given [`TimeCode`].
84///
85/// See [the SMTPE time code standard](https://en.wikipedia.org/wiki/SMPTE_timecode).
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum TimeCodeType {
88    /// 24 Frames per second
89    FPS24 = 0,
90    /// 25 Frames per second
91    FPS25 = 1,
92    /// 30 Frames per second, Drop Frame
93    DF30 = 2,
94    /// 30 Frames per second, Non-Drop Frame
95    NDF30 = 3,
96}
97
98impl Default for TimeCodeType {
99    fn default() -> Self {
100        Self::NDF30
101    }
102}
103
104impl TimeCodeType {
105    fn from_code_hour(code_hour: u8) -> Self {
106        match (code_hour & 0b01100000) >> 5 {
107            0 => Self::FPS24,
108            1 => Self::FPS25,
109            2 => Self::DF30,
110            3 => Self::NDF30,
111            _ => panic!("Should not be reachable"),
112        }
113    }
114}
115
116#[cfg(feature = "sysex")]
117mod sysex_types {
118    use super::*;
119    use crate::MidiMsg;
120    use crate::ParseError;
121    use alloc::vec::Vec;
122    use bstr::BString;
123
124    impl TimeCode {
125        pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
126            let [frame, seconds, minutes, codehour] = self.to_bytes();
127            v.extend_from_slice(&[codehour, minutes, seconds, frame]);
128        }
129
130        pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
131            if m.len() < 4 {
132                return Err(crate::ParseError::UnexpectedEnd);
133            }
134            let code_hour = u8_from_u7(m[0])?;
135            Ok(Self {
136                frames: u8_from_u7(m[3])?,
137                seconds: u8_from_u7(m[2])?,
138                minutes: u8_from_u7(m[1])?,
139                hours: code_hour & 0b00011111,
140                code_type: TimeCodeType::from_code_hour(code_hour),
141            })
142        }
143    }
144
145    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
146    /// Like [`TimeCode`] but includes `fractional_frames`. Used in `TimeCodeCueingSetupMsg` and the SMF `Meta` event.
147    ///
148    /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008)
149    pub struct HighResTimeCode {
150        /// 0-99
151        pub fractional_frames: u8,
152        /// 0-29
153        pub frames: u8,
154        /// 0-59
155        pub seconds: u8,
156        /// 0-59
157        pub minutes: u8,
158        /// 0-23
159        pub hours: u8,
160        pub code_type: TimeCodeType,
161    }
162
163    impl HighResTimeCode {
164        /// Return the five byte representation of the frame:
165        /// [fractional_frames, frames, seconds, minutes, timecode + hours]
166        pub fn to_bytes(self) -> [u8; 5] {
167            [
168                self.fractional_frames.min(99),
169                self.frames.min(29),
170                self.seconds.min(59),
171                self.minutes.min(59),
172                self.hours.min(23) + ((self.code_type as u8) << 5),
173            ]
174        }
175
176        pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
177            let [fractional_frames, frames, seconds, minutes, codehour] = self.to_bytes();
178            v.extend_from_slice(&[codehour, minutes, seconds, frames, fractional_frames]);
179        }
180
181        pub(crate) fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> {
182            if v.len() < 5 {
183                return Err(ParseError::UnexpectedEnd);
184            }
185            let code_hour = u8_from_u7(v[0])?;
186            Ok((
187                Self {
188                    fractional_frames: u8_from_u7(v[4])?,
189                    frames: u8_from_u7(v[3])?,
190                    seconds: u8_from_u7(v[2])?,
191                    minutes: u8_from_u7(v[1])?,
192                    hours: code_hour & 0b00011111,
193                    code_type: TimeCodeType::from_code_hour(code_hour),
194                },
195                5,
196            ))
197        }
198    }
199
200    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
201    /// Like [`TimeCode`] but uses `subframes` to optionally include status flags, and fractional frames.
202    /// Also may be negative. Used in [`MachineControlCommandMsg`](crate::MachineControlCommandMsg).
203    ///
204    /// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013) and
205    /// MIDI Show Control 1.1.1 (RP002/RP014)
206    pub struct StandardTimeCode {
207        pub subframes: SubFrames,
208        /// The position in frames, where a negative value indicates a negative TimeCode, -29-29
209        pub frames: i8,
210        /// The position in seconds, 0-59
211        pub seconds: u8,
212        /// The position in minutes, 0-59
213        pub minutes: u8,
214        /// The position in hours, 0-23
215        pub hours: u8,
216        pub code_type: TimeCodeType,
217    }
218
219    impl StandardTimeCode {
220        /// Return the five byte representation of the frame:
221        /// [fractional_frames, frames, seconds, minutes, timecode + hours]
222        pub fn to_bytes(self) -> [u8; 5] {
223            let [subframes, frames] = self.to_bytes_short();
224            [
225                subframes,
226                frames,
227                self.seconds.min(59),
228                self.minutes.min(59),
229                self.hours.min(23) + ((self.code_type as u8) << 5),
230            ]
231        }
232
233        /// The two byte representation of the frame:
234        /// [fractional_frames, frames]
235        pub fn to_bytes_short(self) -> [u8; 2] {
236            let mut frames = self.frames.abs().min(29) as u8;
237            if let SubFrames::Status(_) = self.subframes {
238                frames += 1 << 5;
239            }
240            if self.frames < 0 {
241                frames += 1 << 6;
242            }
243            [self.subframes.to_byte(), frames]
244        }
245
246        pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
247            let [subframes, frames, seconds, minutes, codehour] = self.to_bytes();
248            v.extend_from_slice(&[codehour, minutes, seconds, frames, subframes]);
249        }
250
251        #[allow(dead_code)]
252        pub(crate) fn extend_midi_short(&self, v: &mut Vec<u8>) {
253            let [subframes, frames] = self.to_bytes_short();
254            v.extend_from_slice(&[frames, subframes]);
255        }
256    }
257
258    impl From<TimeCode> for StandardTimeCode {
259        fn from(t: TimeCode) -> Self {
260            Self {
261                subframes: Default::default(),
262                frames: t.frames as i8,
263                seconds: t.seconds,
264                minutes: t.minutes,
265                hours: t.hours,
266                code_type: t.code_type,
267            }
268        }
269    }
270
271    /// Used by [`StandardTimeCode`].
272    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
273    pub enum SubFrames {
274        /// The position in fractional frames, 0-99
275        FractionalFrames(u8),
276        /// Additional flags describing the status of this timecode.
277        Status(TimeCodeStatus),
278    }
279
280    impl Default for SubFrames {
281        fn default() -> Self {
282            Self::FractionalFrames(0)
283        }
284    }
285
286    impl SubFrames {
287        fn to_byte(&self) -> u8 {
288            match *self {
289                Self::FractionalFrames(ff) => ff.min(99),
290                Self::Status(s) => s.to_byte(),
291            }
292        }
293    }
294
295    /// Used by [`StandardTimeCode`].
296    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
297    pub struct TimeCodeStatus {
298        pub estimated_code: bool,
299        pub invalid_code: bool,
300        pub video_field1: bool,
301        pub no_time_code: bool,
302    }
303
304    impl TimeCodeStatus {
305        fn to_byte(&self) -> u8 {
306            let mut b: u8 = 0;
307            if self.estimated_code {
308                b += 1 << 6;
309            }
310            if self.invalid_code {
311                b += 1 << 5;
312            }
313            if self.video_field1 {
314                b += 1 << 4;
315            }
316            if self.no_time_code {
317                b += 1 << 3;
318            }
319            b
320        }
321    }
322
323    /// 32 bits defined by SMPTE for "special functions". Used in [`UniversalRealTimeMsg::TimeCodeUserBits`](crate::UniversalRealTimeMsg::TimeCodeUserBits).
324    /// See [the SMTPE time code standard](https://en.wikipedia.org/wiki/SMPTE_timecode).
325    ///
326    /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008)
327    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
328    pub struct UserBits {
329        /// Full bytes can be used here. Sent such that the first is considered
330        /// the "most significant" value
331        pub bytes: (u8, u8, u8, u8),
332        /// SMPTE time code bit 43 (EBU bit 27)
333        pub flag1: bool,
334        /// SMPTE time code bit 59 (EBU bit 43)
335        pub flag2: bool,
336    }
337
338    impl UserBits {
339        /// Turn the `UserBits` into its 9 nibble representation:
340        /// [nibble_1, nibble_2, nibble_3, nibble_4, nibble_5, nibble_6, nibble_7, nibble_8, nibble_9, nibble_flags]
341        pub fn to_nibbles(&self) -> [u8; 9] {
342            let [uh, ug] = to_nibble(self.bytes.0);
343            let [uf, ue] = to_nibble(self.bytes.1);
344            let [ud, uc] = to_nibble(self.bytes.2);
345            let [ub, ua] = to_nibble(self.bytes.3);
346            let mut flags: u8 = 0;
347            if self.flag1 {
348                flags += 1;
349            }
350            if self.flag2 {
351                flags += 2;
352            }
353            [ua, ub, uc, ud, ue, uf, ug, uh, flags]
354        }
355    }
356
357    /// Like [`UserBits`] but allows for the embedding of a "secondary time code".
358    ///
359    /// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013)
360    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
361    pub struct StandardUserBits {
362        /// Full bytes can be used here. Sent such that the first is considered
363        /// the "most significant" value
364        pub bytes: (u8, u8, u8, u8),
365        /// SMPTE time code bit 43 (EBU bit 27)
366        pub flag1: bool,
367        /// SMPTE time code bit 59 (EBU bit 43)
368        pub flag2: bool,
369        /// Contains a secondary time code
370        pub secondary_time_code: bool,
371    }
372
373    impl StandardUserBits {
374        /// Turn the `UserBits` into its 9 nibble representation:
375        /// [nibble_1, nibble_2, nibble_3, nibble_4, nibble_5, nibble_6, nibble_7, nibble_8, nibble_9, nibble_flags]
376        pub fn to_nibbles(&self) -> [u8; 9] {
377            let [uh, ug] = to_nibble(self.bytes.0);
378            let [uf, ue] = to_nibble(self.bytes.1);
379            let [ud, uc] = to_nibble(self.bytes.2);
380            let [ub, ua] = to_nibble(self.bytes.3);
381            let mut flags: u8 = 0;
382            if self.flag1 {
383                flags += 1;
384            }
385            if self.flag2 {
386                flags += 2;
387            }
388            if self.secondary_time_code {
389                flags += 4;
390            }
391            [ua, ub, uc, ud, ue, uf, ug, uh, flags]
392        }
393    }
394
395    impl From<StandardUserBits> for TimeCode {
396        fn from(t: StandardUserBits) -> Self {
397            let [ua, ub, uc, ud, ue, uf, ug, uh, _] = t.to_nibbles();
398            let frames = (ub << 4) + ua;
399            let seconds = (ud << 4) + uc;
400            let minutes = (uf << 4) + ue;
401            let hours = ((uh & 0b0001) << 4) + ug;
402            let code_type = (uh & 0b0110) >> 1;
403
404            TimeCode {
405                frames,
406                seconds,
407                minutes,
408                hours,
409                code_type: match code_type {
410                    3 => TimeCodeType::NDF30,
411                    2 => TimeCodeType::DF30,
412                    1 => TimeCodeType::FPS25,
413                    0 => TimeCodeType::FPS24,
414                    _ => panic!("Should not be reachable"),
415                },
416            }
417        }
418    }
419
420    impl From<TimeCode> for StandardUserBits {
421        fn from(t: TimeCode) -> Self {
422            let [frame, seconds, minutes, codehour] = t.to_bytes();
423            StandardUserBits {
424                bytes: (codehour, minutes, seconds, frame),
425                flag1: false,
426                flag2: false,
427                secondary_time_code: true,
428            }
429        }
430    }
431
432    impl From<UserBits> for StandardUserBits {
433        fn from(t: UserBits) -> Self {
434            Self {
435                bytes: t.bytes,
436                flag1: t.flag1,
437                flag2: t.flag2,
438                secondary_time_code: false,
439            }
440        }
441    }
442
443    /// Non-realtime Time Code Cueing. Used by [`UniversalNonRealTimeMsg::TimeCodeCueingSetup`](crate::UniversalNonRealTimeMsg::TimeCodeCueingSetup).
444    ///
445    /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008)
446    #[derive(Debug, Clone, PartialEq)]
447    pub enum TimeCodeCueingSetupMsg {
448        TimeCodeOffset {
449            time_code: HighResTimeCode,
450        },
451        EnableEventList,
452        DisableEventList,
453        ClearEventList,
454        SystemStop,
455        EventListRequest {
456            time_code: HighResTimeCode,
457        },
458        PunchIn {
459            time_code: HighResTimeCode,
460            event_number: u16,
461        },
462        PunchOut {
463            time_code: HighResTimeCode,
464            event_number: u16,
465        },
466        DeletePunchIn {
467            time_code: HighResTimeCode,
468            event_number: u16,
469        },
470        DeletePunchOut {
471            time_code: HighResTimeCode,
472            event_number: u16,
473        },
474        EventStart {
475            time_code: HighResTimeCode,
476            event_number: u16,
477            additional_information: Vec<MidiMsg>,
478        },
479        EventStop {
480            time_code: HighResTimeCode,
481            event_number: u16,
482            additional_information: Vec<MidiMsg>,
483        },
484        DeleteEventStart {
485            time_code: HighResTimeCode,
486            event_number: u16,
487        },
488        DeleteEventStop {
489            time_code: HighResTimeCode,
490            event_number: u16,
491        },
492        Cue {
493            time_code: HighResTimeCode,
494            event_number: u16,
495            additional_information: Vec<MidiMsg>,
496        },
497        DeleteCue {
498            time_code: HighResTimeCode,
499            event_number: u16,
500        },
501        EventName {
502            time_code: HighResTimeCode,
503            event_number: u16,
504            name: BString,
505        },
506    }
507
508    impl TimeCodeCueingSetupMsg {
509        pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
510            match self {
511                Self::TimeCodeOffset { time_code } => {
512                    v.push(0x00);
513                    time_code.extend_midi(v);
514                    v.push(0x00);
515                    v.push(0x00);
516                }
517                Self::EnableEventList => {
518                    v.push(0x00);
519                    HighResTimeCode::default().extend_midi(v);
520                    v.push(0x01);
521                    v.push(0x00);
522                }
523                Self::DisableEventList => {
524                    v.push(0x00);
525                    HighResTimeCode::default().extend_midi(v);
526                    v.push(0x02);
527                    v.push(0x00);
528                }
529                Self::ClearEventList => {
530                    v.push(0x00);
531                    HighResTimeCode::default().extend_midi(v);
532                    v.push(0x03);
533                    v.push(0x00);
534                }
535                Self::SystemStop => {
536                    v.push(0x00);
537                    HighResTimeCode::default().extend_midi(v);
538                    v.push(0x04);
539                    v.push(0x00);
540                }
541                Self::EventListRequest { time_code } => {
542                    v.push(0x00);
543                    time_code.extend_midi(v);
544                    v.push(0x05);
545                    v.push(0x00);
546                }
547                Self::PunchIn {
548                    time_code,
549                    event_number,
550                } => {
551                    v.push(0x01);
552                    time_code.extend_midi(v);
553                    push_u14(*event_number, v);
554                }
555                Self::PunchOut {
556                    time_code,
557                    event_number,
558                } => {
559                    v.push(0x02);
560                    time_code.extend_midi(v);
561                    push_u14(*event_number, v);
562                }
563                Self::DeletePunchIn {
564                    time_code,
565                    event_number,
566                } => {
567                    v.push(0x03);
568                    time_code.extend_midi(v);
569                    push_u14(*event_number, v);
570                }
571                Self::DeletePunchOut {
572                    time_code,
573                    event_number,
574                } => {
575                    v.push(0x04);
576                    time_code.extend_midi(v);
577                    push_u14(*event_number, v);
578                }
579                Self::EventStart {
580                    time_code,
581                    event_number,
582                    additional_information,
583                } => {
584                    if additional_information.is_empty() {
585                        v.push(0x05);
586                    } else {
587                        v.push(0x07);
588                    }
589                    time_code.extend_midi(v);
590                    push_u14(*event_number, v);
591                    push_nibblized_midi(additional_information, v);
592                }
593                Self::EventStop {
594                    time_code,
595                    event_number,
596                    additional_information,
597                } => {
598                    if additional_information.is_empty() {
599                        v.push(0x06);
600                    } else {
601                        v.push(0x08);
602                    }
603                    time_code.extend_midi(v);
604                    push_u14(*event_number, v);
605                    push_nibblized_midi(additional_information, v);
606                }
607                Self::DeleteEventStart {
608                    time_code,
609                    event_number,
610                } => {
611                    v.push(0x09);
612                    time_code.extend_midi(v);
613                    push_u14(*event_number, v);
614                }
615                Self::DeleteEventStop {
616                    time_code,
617                    event_number,
618                } => {
619                    v.push(0x0A);
620                    time_code.extend_midi(v);
621                    push_u14(*event_number, v);
622                }
623                Self::Cue {
624                    time_code,
625                    event_number,
626                    additional_information,
627                } => {
628                    if additional_information.is_empty() {
629                        v.push(0x0B);
630                    } else {
631                        v.push(0x0C);
632                    }
633                    time_code.extend_midi(v);
634                    push_u14(*event_number, v);
635                    push_nibblized_midi(additional_information, v);
636                }
637                Self::DeleteCue {
638                    time_code,
639                    event_number,
640                } => {
641                    v.push(0x0D);
642                    time_code.extend_midi(v);
643                    push_u14(*event_number, v);
644                }
645                Self::EventName {
646                    time_code,
647                    event_number,
648                    name,
649                } => {
650                    v.push(0x0E);
651                    time_code.extend_midi(v);
652                    push_u14(*event_number, v);
653                    push_nibblized_name(name, v);
654                }
655            }
656        }
657
658        #[allow(dead_code)]
659        pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
660            Err(ParseError::NotImplemented("TimeCodeCueingSetupMsg"))
661        }
662    }
663
664    /// Realtime Time Code Cueing. Used by [`UniversalRealTimeMsg::TimeCodeCueing`](crate::UniversalRealTimeMsg::TimeCodeCueing).
665    ///
666    /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008)
667    #[derive(Debug, Clone, PartialEq)]
668    pub enum TimeCodeCueingMsg {
669        SystemStop,
670        PunchIn {
671            event_number: u16,
672        },
673        PunchOut {
674            event_number: u16,
675        },
676        EventStart {
677            event_number: u16,
678            additional_information: Vec<MidiMsg>,
679        },
680        EventStop {
681            event_number: u16,
682            additional_information: Vec<MidiMsg>,
683        },
684        Cue {
685            event_number: u16,
686            additional_information: Vec<MidiMsg>,
687        },
688        EventName {
689            event_number: u16,
690            name: BString,
691        },
692    }
693
694    fn push_nibblized_midi(msgs: &[MidiMsg], v: &mut Vec<u8>) {
695        for msg in msgs.iter() {
696            for b in msg.to_midi().iter() {
697                let [msn, lsn] = to_nibble(*b);
698                v.push(lsn);
699                v.push(msn);
700            }
701        }
702    }
703
704    fn push_nibblized_name(name: &BString, v: &mut Vec<u8>) {
705        // Not sure if this actually handles newlines correctly
706        for b in name.iter() {
707            let [msn, lsn] = to_nibble(*b);
708            v.push(lsn);
709            v.push(msn);
710        }
711    }
712
713    impl TimeCodeCueingMsg {
714        pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
715            match self {
716                Self::SystemStop => {
717                    v.push(0x00);
718                    v.push(0x04);
719                    v.push(0x00);
720                }
721                Self::PunchIn { event_number } => {
722                    v.push(0x01);
723                    push_u14(*event_number, v);
724                }
725                Self::PunchOut { event_number } => {
726                    v.push(0x02);
727                    push_u14(*event_number, v);
728                }
729                Self::EventStart {
730                    event_number,
731                    additional_information,
732                } => {
733                    if additional_information.is_empty() {
734                        v.push(0x05);
735                    } else {
736                        v.push(0x07);
737                    }
738                    push_u14(*event_number, v);
739                    push_nibblized_midi(additional_information, v);
740                }
741                Self::EventStop {
742                    event_number,
743                    additional_information,
744                } => {
745                    if additional_information.is_empty() {
746                        v.push(0x06);
747                    } else {
748                        v.push(0x08);
749                    }
750                    push_u14(*event_number, v);
751                    push_nibblized_midi(additional_information, v);
752                }
753                Self::Cue {
754                    event_number,
755                    additional_information,
756                } => {
757                    if additional_information.is_empty() {
758                        v.push(0x0B);
759                    } else {
760                        v.push(0x0C);
761                    }
762                    push_u14(*event_number, v);
763                    push_nibblized_midi(additional_information, v);
764                }
765                Self::EventName { event_number, name } => {
766                    v.push(0x0E);
767                    push_u14(*event_number, v);
768                    push_nibblized_name(&name, v);
769                }
770            }
771        }
772
773        #[allow(dead_code)]
774        pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
775            Err(ParseError::NotImplemented("TimeCodeCueingMsg"))
776        }
777    }
778}
779
780#[cfg(feature = "sysex")]
781pub use sysex_types::*;
782
783#[cfg(test)]
784#[cfg(feature = "sysex")]
785mod tests {
786    use crate::*;
787    use std::vec;
788    extern crate std;
789
790    #[test]
791    fn serialize_time_code_cuing_setup_msg() {
792        assert_eq!(
793            MidiMsg::SystemExclusive {
794                msg: SystemExclusiveMsg::UniversalNonRealTime {
795                    device: DeviceID::AllCall,
796                    msg: UniversalNonRealTimeMsg::TimeCodeCueingSetup(
797                        TimeCodeCueingSetupMsg::SystemStop
798                    ),
799                },
800            }
801            .to_midi(),
802            vec![0xF0, 0x7E, 0x7f, 04, 00, 96, 00, 00, 00, 00, 04, 00, 0xF7]
803        );
804    }
805
806    #[test]
807    fn serialize_time_code_cuing_msg() {
808        assert_eq!(
809            MidiMsg::SystemExclusive {
810                msg: SystemExclusiveMsg::UniversalRealTime {
811                    device: DeviceID::AllCall,
812                    msg: UniversalRealTimeMsg::TimeCodeCueing(TimeCodeCueingMsg::SystemStop),
813                },
814            }
815            .to_midi(),
816            vec![0xF0, 0x7F, 0x7f, 05, 00, 04, 00, 0xF7]
817        );
818
819        assert_eq!(
820            MidiMsg::SystemExclusive {
821                msg: SystemExclusiveMsg::UniversalRealTime {
822                    device: DeviceID::AllCall,
823                    msg: UniversalRealTimeMsg::TimeCodeCueing(TimeCodeCueingMsg::EventStart {
824                        event_number: 511,
825                        additional_information: vec![]
826                    }),
827                },
828            }
829            .to_midi(),
830            vec![0xF0, 0x7F, 0x7f, 05, 05, 0x7f, 0x03, 0xF7]
831        );
832
833        assert_eq!(
834            MidiMsg::SystemExclusive {
835                msg: SystemExclusiveMsg::UniversalRealTime {
836                    device: DeviceID::AllCall,
837                    msg: UniversalRealTimeMsg::TimeCodeCueing(TimeCodeCueingMsg::EventStart {
838                        event_number: 511,
839                        additional_information: vec![MidiMsg::ChannelVoice {
840                            channel: Channel::Ch2,
841                            msg: ChannelVoiceMsg::NoteOn {
842                                note: 0x55,
843                                velocity: 0x67
844                            }
845                        }]
846                    }),
847                },
848            }
849            .to_midi(),
850            vec![
851                0xF0, 0x7F, 0x7f, 05, 07, 0x7f, 0x03,
852                // Note on midi msg: 0x91, 0x55, 0x67
853                0x01, 0x09, 0x05, 0x05, 0x07, 0x06, // End
854                0xF7
855            ]
856        );
857    }
858}