use core::fmt::{Debug, Display, Formatter};
use intbits::Bits;
use crate::TimecodeFrame;
#[derive(Clone)]
pub(crate) struct LtcFrameData {
data: u64,
}
struct BitIndex {
index: u8,
weight: u8,
}
impl BitIndex {
const fn new(index: u8, weight: u8) -> Self {
Self {
index: 63 - index,
weight,
}
}
}
impl LtcFrameData {
const BIT_INDEX_FRAMES: [BitIndex; 6] =
[BitIndex::new(0, 1),
BitIndex::new(1, 2),
BitIndex::new(2, 4),
BitIndex::new(3, 8),
BitIndex::new(8, 10),
BitIndex::new(9, 20)];
const BIT_INDEX_SECONDS: [BitIndex; 7] =
[BitIndex::new(16, 1),
BitIndex::new(17, 2),
BitIndex::new(18, 4),
BitIndex::new(19, 8),
BitIndex::new(24, 10),
BitIndex::new(25, 20),
BitIndex::new(26, 40)];
const BIT_INDEX_MINUTES: [BitIndex; 7] =
[BitIndex::new(32, 1),
BitIndex::new(33, 2),
BitIndex::new(34, 4),
BitIndex::new(35, 8),
BitIndex::new(40, 10),
BitIndex::new(41, 20),
BitIndex::new(42, 40)];
const BIT_INDEX_HOURS: [BitIndex; 6] =
[BitIndex::new(48, 1),
BitIndex::new(49, 2),
BitIndex::new(50, 4),
BitIndex::new(51, 8),
BitIndex::new(56, 10),
BitIndex::new(57, 20)];
const BIT_INDEX_SYNCWORD_START_FIRST_HALF: [BitIndex; 8] =
[BitIndex::new(63, 1),
BitIndex::new(62, 2),
BitIndex::new(61, 4),
BitIndex::new(60, 8),
BitIndex::new(59, 16),
BitIndex::new(58, 32),
BitIndex::new(57, 64),
BitIndex::new(56, 128)];
const BIT_INDEX_SYNCWORD_START_SECOND_HALF: [BitIndex; 8] =
[BitIndex::new(55, 1),
BitIndex::new(54, 2),
BitIndex::new(53, 4),
BitIndex::new(52, 8),
BitIndex::new(51, 16),
BitIndex::new(50, 32),
BitIndex::new(49, 64),
BitIndex::new(48, 128)];
const SYNC_WORD_SECOND_HALF: u8 = 0b0011_1111;
const SYNC_WORD_FIRST_HALF: u8 = 0b1111_1101;
pub(crate) fn invalidate(&mut self) {
self.data = 0;
}
}
#[cfg(feature = "decode_ltc")]
impl LtcFrameData {
pub(crate) fn new_empty() -> Self {
Self {
data: 0
}
}
fn get_bits(&self, index: &[BitIndex]) -> u8 {
let mut val = 0;
for i in index {
if self.data.bit(i.index) {
val += i.weight
}
}
val
}
pub(crate) fn next_bit_is_start_of_frame(&self) -> bool {
Self::SYNC_WORD_FIRST_HALF == self.get_bits(&Self::BIT_INDEX_SYNCWORD_START_FIRST_HALF) &&
Self::SYNC_WORD_SECOND_HALF == self.get_bits(&Self::BIT_INDEX_SYNCWORD_START_SECOND_HALF)
}
pub(crate) fn get_frames(&self) -> u8 {
self.get_bits(&Self::BIT_INDEX_FRAMES)
}
pub(crate) fn get_seconds(&self) -> u8 {
self.get_bits(&Self::BIT_INDEX_SECONDS)
}
pub(crate) fn get_minutes(&self) -> u8 {
self.get_bits(&Self::BIT_INDEX_MINUTES)
}
pub(crate) fn get_hours(&self) -> u8 {
self.get_bits(&Self::BIT_INDEX_HOURS)
}
pub(crate) fn shift_bit_with_overflow(&mut self, bit: bool) -> bool {
let highest_bit = self.data.bit(63);
self.data <<= 1;
self.data.set_bit(0, bit);
highest_bit
}
}
#[cfg(feature = "decode_ltc")]
impl LtcFrameData {
pub(crate) fn make_ltc_frame(&self, duration_for_frame_without_syncword_in_s: f32) -> TimecodeFrame {
TimecodeFrame::new_from_duration(self.get_hours(), self.get_minutes(), self.get_seconds(), self.get_frames(), duration_for_frame_without_syncword_in_s)
}
}
#[cfg(test)]
impl PartialEq<Self> for LtcFrameData {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
#[cfg(feature = "debug")]
impl Debug for LtcFrameData {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:0>2}:{:0>2}:{:0>2}:{:0>2} 0b_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}_{:04b}",
self.get_hours(),
self.get_minutes(),
self.get_seconds(),
self.get_frames(),
self.data.bits(60..64),
self.data.bits(56..60),
self.data.bits(52..56),
self.data.bits(48..52),
self.data.bits(44..48),
self.data.bits(40..44),
self.data.bits(36..40),
self.data.bits(32..36),
self.data.bits(28..32),
self.data.bits(24..28),
self.data.bits(20..24),
self.data.bits(16..20),
self.data.bits(12..16),
self.data.bits(8..12),
self.data.bits(4..8),
self.data.bits(0..4))
}
}
#[cfg(feature = "debug")]
impl Display for LtcFrameData {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:0>2}:{:0>2}:{:0>2}:{:0>2}",
self.get_hours(),
self.get_minutes(),
self.get_seconds(),
self.get_frames())
}
}