klavier_core/
rhythm.rs

1use std::{fmt::{self, Display}, str::FromStr};
2
3use super::duration::Duration;
4
5/// Minimum numerator value for time signatures.
6pub const MIN_NUMERATOR: u8 = 1;
7/// Maximum numerator value for time signatures.
8pub const MAX_NUMERATOR: u8 = 99;
9
10/// Time signature numerator (beats per measure).
11///
12/// Represents the top number in a time signature (e.g., the 3 in 3/4).
13#[derive(serde::Deserialize, serde::Serialize)]
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub struct Numerator(u8);
16
17impl Display for Numerator {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        write!(f, "{}", self.0)
20    }
21}
22
23/// Error type for numerator operations.
24#[derive(Debug)]
25pub enum NumeratorError {
26    /// The numerator value is out of valid range (1-99).
27    InvalidValue(u8),
28    /// Failed to parse the numerator from a string.
29    CannotParse(String),
30}
31
32impl Numerator {
33    pub fn from_value(value: u8) -> Result<Numerator, NumeratorError> {
34        if !(MIN_NUMERATOR..=MAX_NUMERATOR).contains(&value) {
35            Err(NumeratorError::InvalidValue(value))
36        } else {
37            Ok(Numerator(value))
38        }
39    }
40
41    pub fn value(self) -> u8 {
42        self.0
43    }
44}
45
46impl FromStr for Numerator {
47    type Err = NumeratorError;
48
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        match u8::from_str(s) {
51            Ok(value) => Self::from_value(value),
52            Err(_) => Err(NumeratorError::CannotParse(s.to_owned())),
53        }
54    }
55}
56
57/// Time signature denominator (note value per beat).
58///
59/// Represents the bottom number in a time signature (e.g., the 4 in 3/4).
60/// Only standard note values are supported: 2, 4, 8, 16, 32, 64.
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62#[derive(serde::Deserialize, serde::Serialize)]
63pub enum Denominator {
64    /// Half note (2)
65    D2,
66    /// Quarter note (4)
67    D4,
68    /// Eighth note (8)
69    D8,
70    /// Sixteenth note (16)
71    D16,
72    /// Thirty-second note (32)
73    D32,
74    /// Sixty-fourth note (64)
75    D64,
76}
77
78/// Array of all valid denominators.
79pub const DENOMINATORS: [Denominator; 6] = [
80    Denominator::D2, Denominator::D4, Denominator::D8,
81    Denominator::D16, Denominator::D32, Denominator::D64,
82];
83
84/// Error type for denominator operations.
85pub enum DenominatorError {
86    /// The denominator value is not a valid note value.
87    InvalidValue(u8),
88}
89
90impl Denominator {
91    pub fn from_value(value: u8) -> Result<Denominator, DenominatorError> {
92        match value {
93            2 => Ok(Denominator::D2),
94            4 => Ok(Denominator::D4),
95            8 => Ok(Denominator::D8),
96            16 => Ok(Denominator::D16),
97            32 => Ok(Denominator::D32),
98            64 => Ok(Denominator::D64),
99            _ => Err(DenominatorError::InvalidValue(value)),
100        }
101    }
102
103    pub fn value(self) -> u8 {
104        match self {
105            Denominator::D2 => 2,
106            Denominator::D4 => 4,
107            Denominator::D8 => 8,
108            Denominator::D16 => 16,
109            Denominator::D32 => 32,
110            Denominator::D64 => 64,
111        }
112    }
113}
114
115impl fmt::Display for Denominator {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        write!(f, "{}", self.value())
118    }
119}
120
121/// Error type for rhythm (time signature) operations.
122#[derive(serde::Deserialize, serde::Serialize)]
123#[derive(Debug, Copy, Clone, PartialEq)]
124pub enum RhythmError {
125    /// Invalid numerator value.
126    NumeratorError(u8),
127    /// Invalid denominator value.
128    DenominatorError(u8)
129}
130
131/// Time signature (rhythm) combining numerator and denominator.
132///
133/// Represents a time signature like 4/4, 3/4, 6/8, etc.
134///
135/// # Examples
136///
137/// ```
138/// # use klavier_core::rhythm::Rhythm;
139/// let common_time = Rhythm::new(4, 4);
140/// let waltz = Rhythm::new(3, 4);
141/// let compound = Rhythm::new(6, 8);
142/// ```
143#[derive(serde::Deserialize, serde::Serialize)]
144#[derive(Debug, Copy, Clone, PartialEq, Eq)]
145pub struct Rhythm {
146    /// The numerator (beats per measure).
147    pub numerator: Numerator,
148    /// The denominator (note value per beat).
149    pub denominator: Denominator,
150}
151
152impl Rhythm {
153    /// Creates a new time signature.
154    ///
155    /// # Arguments
156    ///
157    /// * `numerator` - Beats per measure (1-99).
158    /// * `denominator` - Note value per beat (2, 4, 8, 16, 32, or 64).
159    ///
160    /// # Panics
161    ///
162    /// Panics if the values are invalid.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// # use klavier_core::rhythm::Rhythm;
168    /// let four_four = Rhythm::new(4, 4);
169    /// let six_eight = Rhythm::new(6, 8);
170    /// ```
171    pub fn new(numerator: u8, denominator: u8) -> Rhythm {
172        match Self::value_of(numerator, denominator) {
173            Err(_pe) => panic!("Logic error."),
174            Ok(r) => r
175        }
176    }
177
178    /// Returns the numerator of this time signature.
179    pub fn numerator(self) -> Numerator {
180        self.numerator
181    }
182
183    /// Returns the denominator of this time signature.
184    pub fn denominator(self) -> Denominator {
185        self.denominator
186    }
187
188    /// Attempts to create a time signature from raw values.
189    ///
190    /// # Arguments
191    ///
192    /// * `numerator` - Beats per measure (1-99).
193    /// * `denominator` - Note value per beat (2, 4, 8, 16, 32, or 64).
194    ///
195    /// # Returns
196    ///
197    /// - `Ok(Rhythm)` - The time signature.
198    /// - `Err(RhythmError)` - If either value is invalid.
199    pub fn value_of(numerator: u8, denominator: u8) -> Result<Rhythm, RhythmError> {
200        let numerator = Numerator::from_value(numerator);
201        let numerator = match numerator {
202            Err(NumeratorError::InvalidValue(v)) => return Err(RhythmError::NumeratorError(v)),
203            Err(_) => panic!("Logic error."),
204            Ok(n) => n,
205        };
206
207        let denominator = Denominator::from_value(denominator);
208        let denominator = match denominator {
209            Err(DenominatorError::InvalidValue(v)) => return Err(RhythmError::DenominatorError(v)),
210            Ok(d) => d,
211        };
212
213        Ok(Self {numerator, denominator})
214    }
215
216    /// Calculates the length of one measure in ticks.
217    ///
218    /// # Returns
219    ///
220    /// The number of ticks in one measure of this time signature.
221    pub fn tick_len(self) -> u32 {
222        ((self.numerator.0 as i32) * Duration::TICK_RESOLUTION * 4 / (self.denominator.value() as i32)) as u32
223    }
224}
225
226impl Default for Rhythm {
227    fn default() -> Self {
228        Self::new(4, 4)
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use serde_json::Value;
235    use serde_json::json;
236
237    use super::Rhythm;
238    use super::RhythmError;
239
240    #[test]
241    #[should_panic]
242    fn new_with_invalid_denominator() {
243        Rhythm::new(1, 3);
244    }
245
246    #[test]
247    #[should_panic]
248    fn new_with_invalid_numerator() {
249        Rhythm::new(100, 4);
250    }
251
252    #[test]
253    fn value_of_with_invalid_denominator() {
254        assert_eq!(Rhythm::value_of(1, 3).err().unwrap(), RhythmError::DenominatorError(3))
255    }
256
257    #[test]
258    fn value_of_with_invalid_numerator() {
259        assert_eq!(Rhythm::value_of(100, 2).err().unwrap(), RhythmError::NumeratorError(100))
260    }
261
262    #[test]
263    fn value_of() {
264        assert_eq!(Rhythm::value_of(2, 4).ok().unwrap(), Rhythm::new(2, 4))
265    }
266
267    #[test]
268    fn tick_len() {
269        assert_eq!(Rhythm::value_of(4, 4).ok().unwrap().tick_len(), 240 * 4);
270        assert_eq!(Rhythm::value_of(2, 4).ok().unwrap().tick_len(), 240 * 2);
271        assert_eq!(Rhythm::value_of(2, 2).ok().unwrap().tick_len(), 480 * 2);
272        assert_eq!(Rhythm::value_of(6, 8).ok().unwrap().tick_len(), 120 * 6);
273    }
274
275    #[test]
276    fn can_serialize_to_json() {
277        let json_str = serde_json::to_string(&Rhythm::new(3, 4)).unwrap();
278        let json: Value = serde_json::from_str(&json_str).unwrap();
279        assert_eq!(
280            json,
281            json!({
282                "numerator": 3,
283                "denominator": "D4"
284            })
285        );
286    }
287
288    #[test]
289    fn can_deserialize_from_json() {
290        let rhythm: Rhythm = serde_json::from_str(r#"
291            {
292                "numerator": 3,
293                "denominator": "D4"
294            }
295        "#).unwrap();
296        assert_eq!(rhythm, Rhythm::new(3, 4));
297    }
298}