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