music_timer/
music_time_counter.rs

1#![allow(dead_code)]
2
3//!
4//! Data structures that handles advancing music time within a time signature.
5//!
6
7use super::{music_time::MusicTime, time_signature::TimeSignature};
8use std::time::Duration;
9
10#[derive(Debug)]
11/// Data structure that handles advancing music time within a time signature.
12pub struct MusicTimeCounter {
13    current_time: MusicTime,
14    time_signature: TimeSignature,
15}
16
17impl MusicTimeCounter {
18    /// Create a new `MusicTimeCounter` with a `TimeSignature`.
19    /// # Arguments
20    ///
21    /// * `time_signature` - The `TimeSignature` the `MusicTimeCounter` is constrained by.
22    ///
23    /// # Example
24    /// ```
25    /// use music_timer::{time_signature::TimeSignature, music_time_counter::MusicTimeCounter};
26    /// let timer = MusicTimeCounter::new(TimeSignature::new(3, 4));
27    /// ```
28    pub fn new(time_signature: TimeSignature) -> Self {
29        MusicTimeCounter {
30            current_time: MusicTime::new(1, 1, 1),
31            time_signature,
32        }
33    }
34
35    /// Gets the current time of the counter.
36    pub fn current_time(&self) -> &MusicTime {
37        &self.current_time
38    }
39
40    /// Advance this counter by 1 beat.
41    pub fn advance_beat(&mut self) {
42        self.current_time.advance_beat(&self.time_signature);
43    }
44
45    /// Advance this counter by 1 beat interval.
46    pub fn advance_beat_interval(&mut self) {
47        self.current_time
48            .advance_beat_interval(&self.time_signature);
49    }
50
51    /// Gets the time duration between beats.
52    /// # Arguments
53    ///
54    /// * `bpm` - Beats per minute
55    pub fn beat_target_frames(&self, bpm: f32) -> Duration {
56        let seconds_per_beat = 60.0 / bpm;
57        let beat_pulse_speed = seconds_per_beat * 1000000000.0;
58        Duration::from_nanos(beat_pulse_speed as u64)
59    }
60
61    /// Gets the time duration between beat intervals.
62    /// # Arguments
63    ///
64    /// * `bpm` - Beats per minute.
65    pub fn beat_interval_target_frames(&self, bpm: f32) -> Duration {
66        const INTERVAL_RESOLUTION: f32 = 16.0 / 2.0;
67        let seconds_per_beat_interval = (60.0 / bpm) / INTERVAL_RESOLUTION;
68        let beat_interval_pulse_speed = seconds_per_beat_interval * 1000000000.0;
69        Duration::from_nanos(beat_interval_pulse_speed as u64)
70    }
71
72    /// Set the current music time of the counter.
73    ///
74    /// # Arguments
75    /// * `current_time` - The new current time to set counter to.
76    pub fn set_current_time(&mut self, current_time: MusicTime) -> &mut Self {
77        self.current_time = current_time;
78        self
79    }
80}
81
82impl Default for MusicTimeCounter {
83    /// Default `MusicTimeCounter` is created with a default `TimeSignature`.
84    fn default() -> Self {
85        MusicTimeCounter::new(TimeSignature::default())
86    }
87}
88
89mod tests {
90    #[test]
91    fn test_beat_target_frames() {
92        use crate::{music_time_counter::MusicTimeCounter, time_signature::TimeSignature};
93        use std::time::Duration;
94
95        let timer = MusicTimeCounter::new(TimeSignature::new(4, 4));
96        let duration = timer.beat_target_frames(80.0);
97        let expected = Duration::from_millis(750);
98        assert_eq!(duration, expected);
99
100        let timer = MusicTimeCounter::new(TimeSignature::new(4, 4));
101        let duration = timer.beat_target_frames(60.0);
102        let expected = Duration::from_secs(1);
103        assert_eq!(duration, expected);
104    }
105
106    #[test]
107    fn test_beat_interval_target_frames() {
108        use crate::{music_time_counter::MusicTimeCounter, time_signature::TimeSignature};
109        use std::time::Duration;
110
111        let timer = MusicTimeCounter::new(TimeSignature::new(4, 4));
112        let duration = timer.beat_interval_target_frames(60.0);
113        let expected = Duration::from_millis(125);
114        assert_eq!(duration, expected);
115    }
116
117    #[test]
118    fn test_set_current_time() {
119        use crate::{music_time::MusicTime, music_time_counter::MusicTimeCounter};
120
121        let mut timer = MusicTimeCounter::default();
122        assert_eq!(timer.current_time(), &MusicTime::new(1, 1, 1));
123        timer.advance_beat();
124        assert_eq!(timer.current_time(), &MusicTime::new(1, 2, 1));
125        timer.set_current_time(MusicTime::new(3, 2, 1));
126        assert_eq!(timer.current_time(), &MusicTime::new(3, 2, 1));
127    }
128}