note_pen/time_signature.rs
1use crate::duration::PrimitiveDuration;
2
3#[derive(Clone, Debug)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub enum TimeSignatureSymbol {
6 CommonTime,
7 CutTime,
8 Custom(String)
9}
10
11/// Represents a time signature.
12///
13/// Internally, it stores the number of notes in a measure and the beat value.
14#[derive(Clone, Debug)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct TimeSignature {
17 pub(crate) notes: u64,
18 pub(crate) beat_value: u64,
19 pub(crate) symbol: Option<TimeSignatureSymbol>
20}
21
22impl TimeSignature {
23 /// 4/4 time signature
24 pub const COMMON_TIME: Self = Self {
25 notes: 4,
26 beat_value: 4,
27 symbol: Some(TimeSignatureSymbol::CommonTime)
28 };
29 /// 2/2 time signature
30 pub const CUT_TIME: Self = Self {
31 notes: 2,
32 beat_value: 2,
33 symbol: Some(TimeSignatureSymbol::CutTime)
34 };
35
36 /// Create a new time signature.
37 /// It takes the number of notes in a measure and the beat value.
38 /// # Examples
39 /// ```rust
40 /// use note_pen::prelude::*;
41 /// let time_signature = TimeSignature::new(4, 4);
42 /// assert_eq!(time_signature, TimeSignature::COMMON_TIME);
43 /// let time_signature = TimeSignature::new(2, 2);
44 /// assert_eq!(time_signature, TimeSignature::CUT_TIME);
45 /// ```
46 #[inline]
47 pub const fn new(notes: u64, beat_value: u64) -> Self {
48 Self {
49 notes,
50 beat_value,
51 symbol: None
52 }
53 }
54
55 /// Create a simple time signature.
56 #[inline]
57 pub const fn simple(beats: u64, value: PrimitiveDuration) -> Self {
58 Self::new(beats, value.value())
59 }
60
61 /// Create a compound time signature.
62 ///
63 /// # Examples
64 /// ```rust
65 /// use note_pen::prelude::*;
66 /// // 6/8 time signature
67 /// let time_signature = TimeSignature::compound(2, PrimitiveDuration::EIGHTH);
68 /// let expected = TimeSignature::new(6, 8);
69 /// assert_eq!(time_signature, expected);
70 /// ```
71 #[inline]
72 pub const fn compound(beats: u64, value: PrimitiveDuration) -> Self {
73 Self::new(beats * 3, value.value())
74 }
75
76 /// Check if the time signature is compound.
77 /// # Examples
78 /// ```rust
79 /// use note_pen::prelude::*;
80 /// let time_signature = TimeSignature::new(6, 8);
81 /// assert!(time_signature.is_compound());
82 /// ```
83 #[inline]
84 pub const fn is_compound(&self) -> bool {
85 self.notes % 3 == 0
86 }
87
88 /// Check if the time signature is simple.
89 /// # Examples
90 /// ```rust
91 /// use note_pen::prelude::*;
92 /// let time_signature = TimeSignature::new(4, 4);
93 /// assert!(time_signature.is_simple());
94 /// let time_signature = TimeSignature::new(5, 4);
95 /// assert!(time_signature.is_simple());
96 #[inline]
97 pub const fn is_simple(&self) -> bool {
98 !self.is_compound()
99 }
100
101 /// Get the number of beats in a measure.
102 /// For compound time signatures, it returns the number of dotted notes
103 /// -- the number of notes in a measure divided by 3.
104 /// # Examples
105 /// ```rust
106 /// use note_pen::prelude::*;
107 /// let time_signature = TimeSignature::new(6, 8);
108 /// assert_eq!(time_signature.beats(), 2);
109 /// let time_signature = TimeSignature::new(4, 4);
110 /// assert_eq!(time_signature.beats(), 4);
111 /// ```
112 #[inline]
113 pub const fn beats(&self) -> u64 {
114 if self.is_compound() {
115 self.notes / 3
116 } else {
117 self.notes
118 }
119 }
120
121 /// Get the beat value.
122 /// # Examples
123 /// ```rust
124 /// use note_pen::prelude::*;
125 /// let time_signature = TimeSignature::new(6, 8);
126 /// assert_eq!(time_signature.value(), PrimitiveDuration::EIGHTH);
127 /// let time_signature = TimeSignature::new(4, 4);
128 /// assert_eq!(time_signature.value(), PrimitiveDuration::QUARTER);
129 /// ```
130 /// # Panics
131 /// It panics
132 /// if the beat value is invalid and [`PrimitiveDuration::try_from`] returns an error.
133 #[inline]
134 pub fn value(&self) -> PrimitiveDuration {
135 PrimitiveDuration::try_from(self.beat_value).expect("Invalid time signature value")
136 }
137}
138
139impl PartialEq for TimeSignature {
140 fn eq(&self, other: &Self) -> bool {
141 self.notes == other.notes && self.beat_value == other.beat_value
142 }
143}
144
145#[cfg(feature = "midi")]
146mod midi {
147 use crate::duration::PrimitiveDuration;
148 use crate::TimeSignature;
149 use midi_file::core::{Clocks, DurationName};
150
151 impl TimeSignature {
152 /// Convert the time signature to MIDI time signature.
153 pub fn denominator_to_midi(&self) -> DurationName {
154 match self.value() {
155 PrimitiveDuration::WHOLE => DurationName::Whole,
156 PrimitiveDuration::HALF => DurationName::Half,
157 PrimitiveDuration::QUARTER => DurationName::Quarter,
158 PrimitiveDuration::EIGHTH => DurationName::Eighth,
159 PrimitiveDuration::SIXTEENTH => DurationName::Sixteenth,
160 _ => unimplemented!("Unsupported time signature: {:?}", self),
161 }
162 }
163
164 pub fn midi_clicks(&self) -> Clocks {
165 if self.is_compound() {
166 match self.value() {
167 PrimitiveDuration::HALF => Clocks::DottedWhole,
168 PrimitiveDuration::QUARTER => Clocks::DottedHalf,
169 PrimitiveDuration::EIGHTH => Clocks::DottedQuarter,
170 PrimitiveDuration::SIXTEENTH => Clocks::DottedEighth,
171 PrimitiveDuration::THIRTY_SECOND => Clocks::DottedSixteenth,
172 _ => unimplemented!("Unsupported time signature: {:?}", self),
173 }
174 } else {
175 match self.value() {
176 PrimitiveDuration::WHOLE => Clocks::Whole,
177 PrimitiveDuration::HALF => Clocks::Half,
178 PrimitiveDuration::QUARTER => Clocks::Quarter,
179 PrimitiveDuration::EIGHTH => Clocks::Eighth,
180 PrimitiveDuration::SIXTEENTH => Clocks::Sixteenth,
181 _ => unimplemented!("Unsupported time signature: {:?}", self),
182 }
183 }
184 }
185 }
186}