1use super::util::*;
2
3#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
11pub struct TimeCode {
12 pub frames: u8,
14 pub seconds: u8,
16 pub minutes: u8,
18 pub hours: u8,
20 pub code_type: TimeCodeType,
21}
22
23impl TimeCode {
24 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 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 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#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum TimeCodeType {
90 FPS24 = 0,
92 FPS25 = 1,
94 DF30 = 2,
96 NDF30 = 3,
98}
99
100impl Default for TimeCodeType {
101 fn default() -> Self {
102 Self::NDF30
103 }
104}
105
106impl TimeCodeType {
107 #[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 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
153 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
154 pub struct HighResTimeCode {
155 pub fractional_frames: u8,
157 pub frames: u8,
159 pub seconds: u8,
161 pub minutes: u8,
163 pub hours: u8,
165 pub code_type: TimeCodeType,
166 }
167
168 impl HighResTimeCode {
169 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 #[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 pub frames: i8,
216 pub seconds: u8,
218 pub minutes: u8,
220 pub hours: u8,
222 pub code_type: TimeCodeType,
223 }
224
225 impl StandardTimeCode {
226 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 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 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
279 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
280 pub enum SubFrames {
281 FractionalFrames(u8),
283 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 #[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 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
336 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
337 pub struct UserBits {
338 pub bytes: (u8, u8, u8, u8),
341 pub flag1: bool,
343 pub flag2: bool,
345 }
346
347 impl UserBits {
348 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 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
370 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
371 pub struct StandardUserBits {
372 pub bytes: (u8, u8, u8, u8),
375 pub flag1: bool,
377 pub flag2: bool,
379 pub secondary_time_code: bool,
381 }
382
383 impl StandardUserBits {
384 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 #[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 #[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 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 0x01, 0x09, 0x05, 0x05, 0x07, 0x06, 0xF7
867 ]
868 );
869 }
870}