euphony-units 0.1.1

Core types and traits for music composition
Documentation
use crate::{
    ratio::Ratio,
    time::{measure::Measure, time_signature::TimeSignature},
};
use core::ops::{Div, Mul, Rem};

new_ratio!(Beat, u64);
new_ratio!(Instant, u64);

new_extern_ratio_arithmetic!(Instant, Add, add, AddAssign, add_assign, Beat);
new_extern_ratio_arithmetic!(Instant, Sub, sub, SubAssign, sub_assign, Beat);

impl Div<Beat> for Instant {
    type Output = Ratio<u64>;

    fn div(self, rhs: Beat) -> Self::Output {
        self.as_ratio().div(rhs.as_ratio())
    }
}

impl Rem<Beat> for Instant {
    type Output = Ratio<u64>;

    fn rem(self, rhs: Beat) -> Self::Output {
        self.as_ratio().rem(rhs.as_ratio())
    }
}

impl Beat {
    pub const DEFAULT_RESOLUTION: Self = Beat(1, 4096);

    pub const EIGHTH: Beat = Beat(1, 8);
    pub const EIGHTH_TRIPLET: Beat = Beat(1, 6);
    pub const HALF: Beat = Beat(1, 2);
    pub const QUARTER: Beat = Beat(1, 4);
    pub const QUARTER_TRIPLET: Beat = Beat(1, 3);
    pub const SIXTEENTH: Beat = Beat(1, 16);
    pub const SIXTY_FOURTH: Beat = Beat(1, 64);
    pub const THIRTY_SECOND: Beat = Beat(1, 32);
    pub const WHOLE: Beat = Beat(1, 1);

    pub fn vec(denominators: impl IntoIterator<Item = u64>) -> alloc::vec::Vec<Self> {
        denominators.into_iter().map(|d| Beat(1, d)).collect()
    }
}

impl Mul<TimeSignature> for Beat {
    type Output = Beat;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, time_signature: TimeSignature) -> Self::Output {
        (self / time_signature.beat()).into()
    }
}

impl core::ops::Div<TimeSignature> for Beat {
    type Output = Measure;

    fn div(self, time_signature: TimeSignature) -> Self::Output {
        let beat_count = self / time_signature.beat();
        (beat_count / time_signature.count()).into()
    }
}

impl Instant {
    pub fn arc_after(self, duration: Beat) -> Arc {
        let start = self;
        let end = self + duration;
        Arc { start, end }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Arc {
    pub start: Instant,
    pub end: Instant,
}

impl Arc {
    pub fn contains(&self, instant: Instant) -> bool {
        self.start <= instant && instant < self.end
    }
}

#[test]
fn div_time_signature_test() {
    assert_eq!(Beat(1, 4) / TimeSignature(4, 4), Measure(1, 4));
    assert_eq!(Beat(2, 4) / TimeSignature(4, 4), Measure(1, 4) * 2);
    assert_eq!(Beat(1, 4) / TimeSignature(6, 8), Measure(1, 3));
    assert_eq!(Beat(5, 4) / TimeSignature(4, 4), Measure(5, 4));
}