acorde_core/model/
duration.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub enum Duration {
6 Whole,
7 Half,
8 Quarter,
9 Eighth,
10 Sixteenth,
11 ThirtySecond,
12 SixtyFourth,
13}
14
15impl Duration {
16 pub fn as_fraction(&self) -> (u32, u32) {
18 match self {
19 Duration::Whole => (1, 1),
20 Duration::Half => (1, 2),
21 Duration::Quarter => (1, 4),
22 Duration::Eighth => (1, 8),
23 Duration::Sixteenth => (1, 16),
24 Duration::ThirtySecond => (1, 32),
25 Duration::SixtyFourth => (1, 64),
26 }
27 }
28
29 pub fn to_ticks(&self, dot_count: u8) -> u32 {
31 let (num, den) = self.as_fraction();
32 let base_ticks = 4 * 480 * num / den;
33 let mut ticks = base_ticks;
34 let mut dot_value = base_ticks / 2;
35 for _ in 0..dot_count {
36 ticks += dot_value;
37 dot_value /= 2;
38 }
39 ticks
40 }
41
42 pub fn to_musicxml_type(&self) -> &'static str {
44 match self {
45 Duration::Whole => "whole",
46 Duration::Half => "half",
47 Duration::Quarter => "quarter",
48 Duration::Eighth => "eighth",
49 Duration::Sixteenth => "16th",
50 Duration::ThirtySecond => "32nd",
51 Duration::SixtyFourth => "64th",
52 }
53 }
54
55 pub fn beats(&self, dot_count: u8) -> f64 {
57 let (num, den) = self.as_fraction();
58 let base = 4.0 * num as f64 / den as f64;
59 let mut total = base;
60 let mut dot = base / 2.0;
61 for _ in 0..dot_count {
62 total += dot;
63 dot /= 2.0;
64 }
65 total
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn quarter_ticks() {
75 assert_eq!(Duration::Quarter.to_ticks(0), 480);
76 }
77
78 #[test]
79 fn dotted_quarter_ticks() {
80 assert_eq!(Duration::Quarter.to_ticks(1), 720);
81 }
82
83 #[test]
84 fn whole_beats() {
85 assert!((Duration::Whole.beats(0) - 4.0).abs() < 1e-9);
86 }
87
88 #[test]
89 fn dotted_half_beats() {
90 assert!((Duration::Half.beats(1) - 3.0).abs() < 1e-9);
91 }
92
93 #[test]
94 fn musicxml_type_strings() {
95 assert_eq!(Duration::Sixteenth.to_musicxml_type(), "16th");
96 assert_eq!(Duration::ThirtySecond.to_musicxml_type(), "32nd");
97 }
98}