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 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 #[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 pub struct HighResTimeCode {
152 pub fractional_frames: u8,
154 pub frames: u8,
156 pub seconds: u8,
158 pub minutes: u8,
160 pub hours: u8,
162 pub code_type: TimeCodeType,
163 }
164
165 impl HighResTimeCode {
166 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 pub struct StandardTimeCode {
209 pub subframes: SubFrames,
210 pub frames: i8,
212 pub seconds: u8,
214 pub minutes: u8,
216 pub hours: u8,
218 pub code_type: TimeCodeType,
219 }
220
221 impl StandardTimeCode {
222 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 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 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
275 pub enum SubFrames {
276 FractionalFrames(u8),
278 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 #[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 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
330 pub struct UserBits {
331 pub bytes: (u8, u8, u8, u8),
334 pub flag1: bool,
336 pub flag2: bool,
338 }
339
340 impl UserBits {
341 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 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
363 pub struct StandardUserBits {
364 pub bytes: (u8, u8, u8, u8),
367 pub flag1: bool,
369 pub flag2: bool,
371 pub secondary_time_code: bool,
373 }
374
375 impl StandardUserBits {
376 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 #[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 #[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 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 0x01, 0x09, 0x05, 0x05, 0x07, 0x06, 0xF7
857 ]
858 );
859 }
860}