rstmt_core/intervals/
kinds.rs1use crate::{IntoPitch, Pitch};
6
7#[derive(
11 Clone,
12 Copy,
13 Debug,
14 Default,
15 Eq,
16 Hash,
17 Ord,
18 PartialEq,
19 PartialOrd,
20 strum::AsRefStr,
21 strum::Display,
22 strum::EnumIs,
23)]
24#[cfg_attr(
25 feature = "serde",
26 derive(serde::Deserialize, serde::Serialize),
27 serde(rename_all = "lowercase")
28)]
29#[repr(u8)]
30#[strum(serialize_all = "lowercase")]
31pub enum Intervals {
32 #[default]
33 Semitone = 1,
34 Tone = 2,
35 Thirds(Third),
36 Fourths(Fourth),
37 Fifths(Fifth),
38 Sevenths(Seventh),
39 Octave = 12,
40}
41
42impl Intervals {
43 pub fn dist(a: impl IntoPitch, b: impl IntoPitch) -> Self {
44 Self::new(a.into_pitch().absmod(), b.into_pitch().absmod())
45 }
46 pub fn new<A, B, C>(lhs: A, rhs: B) -> Self
47 where
48 A: core::ops::Sub<B, Output = C>,
49 C: Into<Intervals>,
50 {
51 (lhs - rhs).into()
52 }
53 pub fn from_value(value: impl IntoPitch) -> Self {
55 use Intervals::*;
56 let Pitch(pitch) = value.into_pitch();
57 match pitch.abs() % 12 {
58 0 => Octave,
59 1 => Semitone,
60 2 => Tone,
61 3 => Thirds(Third::Minor),
62 4 => Thirds(Third::Major),
63 5 => Fourths(Fourth::Perfect),
64 6 => Fifths(Fifth::Diminished),
65 7 => Fifths(Fifth::Perfect),
66 8 => Fifths(Fifth::Augmented),
67 9 => Sevenths(Seventh::Diminished),
68 10 => Sevenths(Seventh::Minor),
69 11 => Sevenths(Seventh::Major),
70 _ => panic!("Invalid interval value: {}", pitch),
71 }
72 }
73 pub fn octave() -> Self {
75 Intervals::Octave
76 }
77 pub fn semitone() -> Self {
79 Intervals::Semitone
80 }
81 pub fn tone() -> Self {
83 Intervals::Tone
84 }
85 pub fn third(third: Third) -> Self {
87 Intervals::Thirds(third)
88 }
89
90 pub fn major_third() -> Self {
91 Intervals::Thirds(Third::Major)
92 }
93
94 pub fn minor_third() -> Self {
95 Intervals::Thirds(Third::Minor)
96 }
97
98 pub fn fourth(fourth: Fourth) -> Self {
100 Intervals::Fourths(fourth)
101 }
102
103 pub fn perfect_fourth() -> Self {
104 Intervals::Fourths(Fourth::Perfect)
105 }
106 pub fn fifth(fifth: Fifth) -> Self {
108 Intervals::Fifths(fifth)
109 }
110 pub fn augmented_fifth() -> Self {
111 Intervals::Fifths(Fifth::Augmented)
112 }
113
114 pub fn diminished_fifth() -> Self {
115 Intervals::Fifths(Fifth::Diminished)
116 }
117
118 pub fn perfect_fifth() -> Self {
119 Intervals::Fifths(Fifth::Perfect)
120 }
121 pub fn seventh(seventh: Seventh) -> Self {
123 Intervals::Sevenths(seventh)
124 }
125
126 pub fn augmented_seventh() -> Self {
127 Intervals::Sevenths(Seventh::Augmented)
128 }
129
130 pub fn diminished_seventh() -> Self {
131 Intervals::Sevenths(Seventh::Diminished)
132 }
133
134 pub fn major_seventh() -> Self {
135 Intervals::Sevenths(Seventh::Major)
136 }
137
138 pub fn minor_seventh() -> Self {
139 Intervals::Sevenths(Seventh::Minor)
140 }
141 pub fn as_pitch(&self) -> Pitch {
143 Pitch::from(self.value())
144 }
145 pub fn name(&self) -> &str {
147 self.as_ref()
148 }
149 pub fn value(&self) -> i8 {
151 match *self {
152 Intervals::Semitone => 1,
153 Intervals::Tone => 2,
154 Intervals::Thirds(third) => third as i8,
155 Intervals::Fourths(fourth) => fourth as i8,
156 Intervals::Fifths(fifth) => fifth as i8,
157 Intervals::Sevenths(seventh) => seventh as i8,
158 Intervals::Octave => 12,
159 }
160 }
161}
162
163macro_rules! impl_from_value {
164 (@impl $name:ident::$variant:ident($T:ty)) => {
165 impl From<$T> for $name {
166 fn from(value: $T) -> Self {
167 $name::$variant(value)
168 }
169 }
170 };
171 ($($name:ident::$variant:ident($T:ty)),* $(,)?) => {
172 $(
173 impl_from_value!(@impl $name::$variant($T));
174 )*
175 };
176}
177
178impl<P> From<P> for Intervals
179where
180 P: IntoPitch,
181{
182 fn from(value: P) -> Self {
183 Intervals::from_value(value)
184 }
185}
186
187impl_from_value! {
188 Intervals::Thirds(Third),
189 Intervals::Fourths(Fourth),
190 Intervals::Fifths(Fifth),
191 Intervals::Sevenths(Seventh),
192}
193
194interval! {
195 default: Major;
196 pub enum Third {
197 Minor = 3,
198 Major = 4,
199 }
200}
201
202interval! {
203 default: Perfect;
204 pub enum Fourth {
205 Perfect = 5,
206 }
207}
208
209interval! {
210 default: Perfect;
211 pub enum Fifth {
212 Diminished = 6,
213 Perfect = 7,
214 Augmented = 8,
215 }
216}
217
218interval! {
219 default: Diminished;
220 pub enum Seventh {
221 Diminished = 9,
222 Minor = 10,
223 Major = 11,
224 Augmented = 12,
225 }
226}
227
228impl Fifth {
229 pub fn from_thirds(lhs: Third, rhs: Third) -> Self {
230 let value = lhs as i8 + rhs as i8;
231 match value {
232 6 => Fifth::Diminished,
233 7 => Fifth::Perfect,
234 8 => Fifth::Augmented,
235 _ => panic!("Invalid fifth value: {}", value),
236 }
237 }
238}