euphony_units/time/
tempo.rs

1use crate::time::{beat::Beat, duration::Duration, unquantized_beat::UnquantizedBeat};
2use core::ops::{Div, Mul};
3
4new_ratio!(Tempo, u64);
5
6const MINUTE: Duration = Duration::from_secs(60);
7
8impl Tempo {
9    pub const DEFAULT: Self = Self(120, 1);
10
11    pub fn as_beat_duration(self) -> Duration {
12        MINUTE / self.as_ratio()
13    }
14
15    pub fn from_beat_duration(duration: Duration) -> Self {
16        Tempo(MINUTE.as_nanos() as u64, duration.as_nanos() as u64)
17            .reduce()
18            .simplify(64)
19    }
20}
21
22impl Mul<Beat> for Tempo {
23    type Output = Duration;
24
25    fn mul(self, beat: Beat) -> Self::Output {
26        self.as_beat_duration() * beat.as_ratio()
27    }
28}
29
30impl Div<Tempo> for Duration {
31    type Output = UnquantizedBeat;
32
33    fn div(self, tempo: Tempo) -> Self::Output {
34        let a = self.as_nanos();
35        let b = tempo.as_beat_duration().as_nanos();
36
37        let whole = (a / b) as u64;
38        let fract = (a % b) as u64;
39
40        UnquantizedBeat(whole, Beat(fract, b as u64).reduce())
41    }
42}
43
44#[test]
45fn beat_round_trip_test() {
46    let tempos = 60..320;
47    let beats = [
48        Beat(1, 64),
49        Beat(1, 48),
50        Beat(1, 32),
51        Beat(1, 24),
52        Beat(1, 16),
53        Beat(1, 12),
54        Beat(1, 8),
55        Beat(1, 6),
56        Beat(1, 4),
57        Beat(1, 3),
58        Beat(1, 2),
59        Beat(1, 1),
60    ];
61    let counts = [
62        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 10_000, 100_000, 1_000_000,
63    ];
64    for tempo in tempos {
65        let tempo = Tempo(tempo, 1);
66        assert_eq!(tempo, Tempo::from_beat_duration(tempo.as_beat_duration()));
67
68        for count in counts.iter() {
69            for beat in beats.iter() {
70                let expected = *beat * *count;
71                let actual: UnquantizedBeat = (tempo * expected) / tempo;
72                assert_eq!(
73                    actual.quantize(Beat(1, 192)),
74                    expected,
75                    "{:?} {:?}",
76                    tempo,
77                    beat
78                );
79            }
80        }
81    }
82}