talw-timecode 0.1.0

SMPTE timecode arithmetic — parse, format, convert, drop-frame
Documentation
#![cfg_attr(not(feature = "std"), no_std)]

use core::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rational {
    pub num: u32,
    pub den: u32,
}

impl Rational {
    pub const fn new(num: u32, den: u32) -> Self {
        Self { num, den }
    }

    pub fn to_f64(self) -> f64 {
        self.num as f64 / self.den as f64
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FrameRate {
    Fps23_976,
    Fps24,
    Fps25,
    Fps29_97Df,
    Fps29_97Ndf,
    Fps30,
    Fps48,
    Fps50,
    Fps59_94Df,
    Fps59_94Ndf,
    Fps60,
}

impl FrameRate {
    pub const fn rational(self) -> Rational {
        match self {
            Self::Fps23_976 => Rational::new(24000, 1001),
            Self::Fps24 => Rational::new(24, 1),
            Self::Fps25 => Rational::new(25, 1),
            Self::Fps29_97Df | Self::Fps29_97Ndf => Rational::new(30000, 1001),
            Self::Fps30 => Rational::new(30, 1),
            Self::Fps48 => Rational::new(48, 1),
            Self::Fps50 => Rational::new(50, 1),
            Self::Fps59_94Df | Self::Fps59_94Ndf => Rational::new(60000, 1001),
            Self::Fps60 => Rational::new(60, 1),
        }
    }

    pub const fn nominal(self) -> u32 {
        match self {
            Self::Fps23_976 => 24,
            Self::Fps24 => 24,
            Self::Fps25 => 25,
            Self::Fps29_97Df | Self::Fps29_97Ndf => 30,
            Self::Fps30 => 30,
            Self::Fps48 => 48,
            Self::Fps50 => 50,
            Self::Fps59_94Df | Self::Fps59_94Ndf => 60,
            Self::Fps60 => 60,
        }
    }

    pub const fn is_drop_frame(self) -> bool {
        matches!(self, Self::Fps29_97Df | Self::Fps59_94Df)
    }

    pub const fn drop_count(self) -> u32 {
        match self {
            Self::Fps29_97Df => 2,
            Self::Fps59_94Df => 4,
            _ => 0,
        }
    }

    pub fn to_f64(self) -> f64 {
        self.rational().to_f64()
    }

    pub fn from_float(fps: f64, drop_frame: bool) -> Option<Self> {
        let rounded = (fps * 100.0).round() as u32;
        match (rounded, drop_frame) {
            (2397, _) => Some(Self::Fps23_976),
            (2400, _) => Some(Self::Fps24),
            (2500, _) => Some(Self::Fps25),
            (2997, true) => Some(Self::Fps29_97Df),
            (2997, false) => Some(Self::Fps29_97Ndf),
            (3000, _) => Some(Self::Fps30),
            (4800, _) => Some(Self::Fps48),
            (5000, _) => Some(Self::Fps50),
            (5994, true) => Some(Self::Fps59_94Df),
            (5994, false) => Some(Self::Fps59_94Ndf),
            (6000, _) => Some(Self::Fps60),
            _ => None,
        }
    }

    pub const fn discriminant(self) -> u8 {
        match self {
            Self::Fps23_976 => 0,
            Self::Fps24 => 1,
            Self::Fps25 => 2,
            Self::Fps29_97Df => 3,
            Self::Fps29_97Ndf => 4,
            Self::Fps30 => 5,
            Self::Fps48 => 6,
            Self::Fps50 => 7,
            Self::Fps59_94Df => 8,
            Self::Fps59_94Ndf => 9,
            Self::Fps60 => 10,
        }
    }

    pub fn from_discriminant(d: u8) -> Option<Self> {
        match d {
            0 => Some(Self::Fps23_976),
            1 => Some(Self::Fps24),
            2 => Some(Self::Fps25),
            3 => Some(Self::Fps29_97Df),
            4 => Some(Self::Fps29_97Ndf),
            5 => Some(Self::Fps30),
            6 => Some(Self::Fps48),
            7 => Some(Self::Fps50),
            8 => Some(Self::Fps59_94Df),
            9 => Some(Self::Fps59_94Ndf),
            10 => Some(Self::Fps60),
            _ => None,
        }
    }
}

impl fmt::Display for FrameRate {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Fps23_976 => write!(f, "23.976"),
            Self::Fps24 => write!(f, "24"),
            Self::Fps25 => write!(f, "25"),
            Self::Fps29_97Df => write!(f, "29.97df"),
            Self::Fps29_97Ndf => write!(f, "29.97"),
            Self::Fps30 => write!(f, "30"),
            Self::Fps48 => write!(f, "48"),
            Self::Fps50 => write!(f, "50"),
            Self::Fps59_94Df => write!(f, "59.94df"),
            Self::Fps59_94Ndf => write!(f, "59.94"),
            Self::Fps60 => write!(f, "60"),
        }
    }
}