use std::fmt::Debug;
pub type TimeBaseType = f64;
pub type SampleBaseType = i64;
pub type SampleRateBaseType = u32;
pub type StepBaseType = i64;
pub type BPMBaseType = f32;
#[derive(Clone, Copy)]
pub struct TimeSignature {
num: u8,
den: u8,
}
impl Debug for TimeSignature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}", self.num, self.den)
}
}
impl TimeSignature {
#[must_use]
pub const fn new(num: u8, den: u8) -> Self {
Self { num, den }
}
}
#[derive(Clone, Copy)]
pub enum TimeUnit {
Seconds(TimeBaseType),
Samples(SampleBaseType),
Steps {
steps: StepBaseType,
ts: TimeSignature,
bpm: BPMBaseType,
},
}
impl From<TimeBaseType> for TimeUnit {
fn from(value: TimeBaseType) -> Self {
Self::Seconds(value)
}
}
impl From<SampleBaseType> for TimeUnit {
fn from(value: SampleBaseType) -> Self {
Self::Samples(value)
}
}
impl Default for TimeUnit {
fn default() -> Self {
Self::Samples(0)
}
}
impl Debug for TimeUnit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Seconds(seconds) => write!(f, "{}", *seconds),
Self::Samples(samples) => write!(f, "{}", *samples),
Self::Steps { steps, ts, bpm } => write!(f, "{}[{:?}]@{:.2}bpm", *steps, ts, *bpm),
}
}
}
impl TimeUnit {
#[must_use]
pub const fn is_non_negative_finite(&self) -> bool {
match self {
Self::Seconds(seconds) => seconds.is_sign_positive(),
Self::Samples(samples) => *samples >= 0,
Self::Steps {
steps,
ts: _,
bpm: _,
} => *steps >= 0,
}
}
#[must_use]
pub fn to_seconds(self, sr: SampleRateBaseType) -> TimeBaseType {
match self {
Self::Seconds(seconds) => seconds,
Self::Samples(samples) => samples as f64 / f64::from(sr),
Self::Steps { steps, ts, bpm } => {
let steps = steps as f64;
let seconds_per_beat = (4f64 * (60f64 / f64::from(bpm))) / f64::from(ts.den); steps * seconds_per_beat
}
}
}
#[must_use]
pub fn to_samples(self, sr: SampleRateBaseType) -> SampleBaseType {
match self {
Self::Seconds(seconds) => (seconds * f64::from(sr)).round() as i64,
Self::Samples(samples) => samples,
Self::Steps {
steps: _,
ts: _,
bpm: _,
} => Self::Seconds(self.to_seconds(sr)).to_samples(sr),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum TimeFrom {
Start(TimeUnit),
End(TimeUnit),
Current(TimeUnit),
}
#[derive(Clone, Copy)]
pub struct TimeRange {
start: TimeUnit,
end: Option<TimeUnit>,
}
impl Default for TimeRange {
fn default() -> Self {
Self {
start: TimeUnit::Samples(0),
end: None,
}
}
}
impl Debug for TimeRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}..{:?}", self.start, self.end)
}
}
impl TimeRange {
#[must_use]
pub const fn new(start: TimeUnit, end: Option<TimeUnit>) -> Self {
Self { start, end }
}
#[must_use]
pub const fn start(&self) -> TimeUnit {
self.start
}
#[must_use]
pub const fn end(&self) -> Option<TimeUnit> {
self.end
}
}