redact_composer_musical/
interval.rs1use std::iter::Sum;
2use std::ops::{Add, AddAssign};
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7#[cfg(feature = "redact-composer")]
8use redact_composer_core::derive::Element;
9
10#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[cfg_attr(feature = "redact-composer", derive(Element))]
14pub struct Interval(pub u8);
15
16#[allow(non_upper_case_globals)]
17impl Interval {
18 pub const P1: Interval = Interval(0);
20 pub const m2: Interval = Interval(1);
22 pub const M2: Interval = Interval(2);
24 pub const m3: Interval = Interval(3);
26 pub const M3: Interval = Interval(4);
28 pub const P4: Interval = Interval(5);
30 pub const TT: Interval = Interval(6);
32 pub const A4: Interval = Interval(6);
34 pub const d5: Interval = Interval(6);
36 pub const P5: Interval = Interval(7);
38 pub const m6: Interval = Interval(8);
40 pub const A5: Interval = Interval(8);
42 pub const M6: Interval = Interval(9);
44 pub const d7: Interval = Interval(9);
46 pub const m7: Interval = Interval(10);
48 pub const M7: Interval = Interval(11);
50 pub const P8: Interval = Interval(12);
52 pub const Octave: Interval = Self::P8;
54 pub const m9: Interval = Interval(13);
56 pub const M9: Interval = Interval(14);
58 pub const m10: Interval = Interval(15);
60 pub const M10: Interval = Interval(16);
62 pub const P11: Interval = Interval(17);
64 pub const P12: Interval = Interval(19);
66 pub const m13: Interval = Interval(20);
68 pub const M13: Interval = Interval(21);
70
71 pub fn is_simple(&self) -> bool {
77 self.0 <= 12
78 }
79
80 pub fn to_simple(self) -> Interval {
87 Interval(self.0 % 12)
88 }
89
90 pub fn is_compound(&self) -> bool {
96 !self.is_simple()
97 }
98
99 pub fn to_compound(self) -> Interval {
105 if self.is_simple() {
106 Interval(self.0 + 12)
107 } else {
108 self
109 }
110 }
111
112 pub fn inversion(&self) -> Interval {
118 if self.is_simple() {
119 Interval(12 - self.0)
120 } else {
121 let octaves = self.0 / 12 + 1;
122
123 Interval(12 * octaves - self.0)
124 }
125 }
126}
127
128impl Add for Interval {
129 type Output = Interval;
130
131 fn add(self, rhs: Self) -> Self::Output {
132 Interval(self.0 + rhs.0)
133 }
134}
135
136impl AddAssign for Interval {
137 fn add_assign(&mut self, rhs: Self) {
138 self.0 += rhs.0;
139 }
140}
141
142impl Sum for Interval {
143 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
144 iter.fold(Interval::default(), |i1, i2| i1 + i2)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use crate::Interval as I;
151
152 #[test]
153 fn check_simple_interval() {
154 assert!(I(0).is_simple());
155 assert!(I(12).is_simple());
156 assert!(!I(13).is_simple());
157 }
158
159 #[test]
160 fn check_compound_interval() {
161 assert!(I(13).is_compound());
162 assert!(I(24).is_compound());
163 assert!(!I(12).is_compound());
164 }
165
166 #[test]
167 fn check_simple_inversions() {
168 assert_eq!(I::P1.inversion(), I::P8);
169 assert_eq!(I::P8.inversion(), I::P1);
170
171 assert_eq!(I::m2.inversion(), I::M7);
172 assert_eq!(I::M7.inversion(), I::m2);
173
174 assert_eq!(I::M2.inversion(), I::m7);
175 assert_eq!(I::m7.inversion(), I::M2);
176
177 assert_eq!(I::m3.inversion(), I::M6);
178 assert_eq!(I::M6.inversion(), I::m3);
179
180 assert_eq!(I::M3.inversion(), I::m6);
181 assert_eq!(I::m6.inversion(), I::M3);
182
183 assert_eq!(I::P4.inversion(), I::P5);
184 assert_eq!(I::P5.inversion(), I::P4);
185
186 assert_eq!(I::A4.inversion(), I::A4);
187 }
188
189 #[test]
190 fn check_compound_inversions() {
191 assert_eq!(I::m9.inversion(), I::M7);
192 assert_eq!(I::M9.inversion(), I::m7);
193 assert_eq!(I::m10.inversion(), I::M6);
194 assert_eq!(I::M10.inversion(), I::m6);
195 assert_eq!(I::P11.inversion(), I::P5);
196 assert_eq!(I::P12.inversion(), I::P4);
197 assert_eq!(I::m13.inversion(), I::M3);
198 assert_eq!(I::M13.inversion(), I::m3);
199 }
200}