use crate::error::{Context, Result};
use crate::scribe::Scribe;
use crate::Error;
use std::convert::TryFrom;
use std::io::Write;
clamp!(
QuarterNoteDivision,
u16,
1,
32767,
1024,
pub
);
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub enum Division {
QuarterNote(QuarterNoteDivision),
Smpte(SmpteRate),
}
impl Default for Division {
fn default() -> Self {
Division::QuarterNote(QuarterNoteDivision::default())
}
}
const DIVISION_TYPE_BIT: u16 = 0b1000000000000000;
impl Division {
pub(crate) fn from_u16(value: u16) -> Result<Self> {
if value & DIVISION_TYPE_BIT == DIVISION_TYPE_BIT {
let frame_rate = FrameRate::from_i8((value >> 8) as u8 as i8)?;
let resolution = (value & 0x00FF) as u8;
Ok(Division::Smpte(SmpteRate::new(frame_rate, resolution)))
} else {
ensure!(value != 0, ctx!(crate::error::ErrorType::Other));
Ok(Division::QuarterNote(QuarterNoteDivision::new(value)))
}
}
pub(crate) fn write<W: Write>(&self, w: &mut Scribe<W>) -> Result<()> {
match self {
Division::QuarterNote(q) => Ok(w.write_all(&q.get().to_be_bytes()).context(wr!())?),
Division::Smpte(s) => {
let hi = s.frame_rate().as_i8() as u8;
Ok(w.write_all(&[hi, s.resolution()]).context(wr!())?)
}
}
}
}
impl TryFrom<u16> for Division {
type Error = Error;
fn try_from(value: u16) -> crate::Result<Self> {
Division::from_u16(value)
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, Default)]
pub enum FrameRate {
#[default]
N24,
N25,
N29,
N30,
}
impl FrameRate {
pub(crate) fn as_i8(self) -> i8 {
match self {
FrameRate::N24 => -24,
FrameRate::N25 => -25,
FrameRate::N29 => -29,
FrameRate::N30 => -30,
}
}
pub(crate) fn from_i8(value: i8) -> Result<Self> {
match value {
-24 => Ok(FrameRate::N24),
-25 => Ok(FrameRate::N25),
-29 => Ok(FrameRate::N29),
-30 => Ok(FrameRate::N30),
_ => invalid_file!("invalid SMPTE frame rate {}", value),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct SmpteRate {
frame_rate: FrameRate,
resolution: u8,
}
impl Default for SmpteRate {
fn default() -> Self {
SmpteRate {
frame_rate: FrameRate::N25,
resolution: 40,
}
}
}
impl SmpteRate {
pub fn new(frame_rate: FrameRate, resolution: u8) -> Self {
Self {
frame_rate,
resolution,
}
}
pub fn frame_rate(&self) -> FrameRate {
self.frame_rate
}
pub fn resolution(&self) -> u8 {
self.resolution
}
}