1use super::util::*;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub struct TimeCode {
11 pub frames: u8,
13 pub seconds: u8,
15 pub minutes: u8,
17 pub hours: u8,
19 pub code_type: TimeCodeType,
20}
21
22impl TimeCode {
23 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum TimeCodeType {
88 FPS24 = 0,
90 FPS25 = 1,
92 DF30 = 2,
94 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 pub struct HighResTimeCode {
150 pub fractional_frames: u8,
152 pub frames: u8,
154 pub seconds: u8,
156 pub minutes: u8,
158 pub hours: u8,
160 pub code_type: TimeCodeType,
161 }
162
163 impl HighResTimeCode {
164 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 pub struct StandardTimeCode {
207 pub subframes: SubFrames,
208 pub frames: i8,
210 pub seconds: u8,
212 pub minutes: u8,
214 pub hours: u8,
216 pub code_type: TimeCodeType,
217 }
218
219 impl StandardTimeCode {
220 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 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 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
273 pub enum SubFrames {
274 FractionalFrames(u8),
276 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 #[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 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
328 pub struct UserBits {
329 pub bytes: (u8, u8, u8, u8),
332 pub flag1: bool,
334 pub flag2: bool,
336 }
337
338 impl UserBits {
339 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 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
361 pub struct StandardUserBits {
362 pub bytes: (u8, u8, u8, u8),
365 pub flag1: bool,
367 pub flag2: bool,
369 pub secondary_time_code: bool,
371 }
372
373 impl StandardUserBits {
374 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 #[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 #[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 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 0x01, 0x09, 0x05, 0x05, 0x07, 0x06, 0xF7
855 ]
856 );
857 }
858}