Skip to main content

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