1use crate::core::{
4 base::{HasDescription, HasName, HasStaticName},
5 interval::{HasIntervals, Interval},
6};
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18#[repr(u8)]
19pub enum ScaleKind {
20 Major,
22 NaturalMinor,
24 HarmonicMinor,
26 MelodicMinor,
28 WholeTone,
30 Chromatic,
32 DiminishedWholeHalf,
34 DiminishedHalfWhole,
36 MajorPentatonic,
38 MinorPentatonic,
40 Blues,
42}
43
44impl HasIntervals for ScaleKind {
47 fn intervals(&self) -> &'static [Interval] {
48 match self {
49 ScaleKind::Major => &[
50 Interval::PerfectUnison,
51 Interval::MajorSecond,
52 Interval::MajorThird,
53 Interval::PerfectFourth,
54 Interval::PerfectFifth,
55 Interval::MajorSixth,
56 Interval::MajorSeventh,
57 ],
58 ScaleKind::NaturalMinor => &[
59 Interval::PerfectUnison,
60 Interval::MajorSecond,
61 Interval::MinorThird,
62 Interval::PerfectFourth,
63 Interval::PerfectFifth,
64 Interval::MinorSixth,
65 Interval::MinorSeventh,
66 ],
67 ScaleKind::HarmonicMinor => &[
68 Interval::PerfectUnison,
69 Interval::MajorSecond,
70 Interval::MinorThird,
71 Interval::PerfectFourth,
72 Interval::PerfectFifth,
73 Interval::MinorSixth,
74 Interval::MajorSeventh,
75 ],
76 ScaleKind::MelodicMinor => &[
77 Interval::PerfectUnison,
78 Interval::MajorSecond,
79 Interval::MinorThird,
80 Interval::PerfectFourth,
81 Interval::PerfectFifth,
82 Interval::MajorSixth,
83 Interval::MajorSeventh,
84 ],
85 ScaleKind::WholeTone => &[
86 Interval::PerfectUnison,
87 Interval::MajorSecond,
88 Interval::MajorThird,
89 Interval::AugmentedFourth,
90 Interval::AugmentedFifth,
91 Interval::AugmentedSixth,
92 ],
93 ScaleKind::Chromatic => &[
94 Interval::PerfectUnison,
95 Interval::MinorSecond,
96 Interval::MajorSecond,
97 Interval::MinorThird,
98 Interval::MajorThird,
99 Interval::PerfectFourth,
100 Interval::AugmentedFourth,
101 Interval::PerfectFifth,
102 Interval::MinorSixth,
103 Interval::MajorSixth,
104 Interval::MinorSeventh,
105 Interval::MajorSeventh,
106 ],
107 ScaleKind::DiminishedWholeHalf => &[
108 Interval::PerfectUnison,
109 Interval::MajorSecond,
110 Interval::MinorThird,
111 Interval::PerfectFourth,
112 Interval::DiminishedFifth,
113 Interval::MinorSixth,
114 Interval::DiminishedSeventh,
115 Interval::MajorSeventh,
116 ],
117 ScaleKind::DiminishedHalfWhole => &[
118 Interval::PerfectUnison,
119 Interval::MinorSecond,
120 Interval::MinorThird,
121 Interval::MajorThird,
122 Interval::AugmentedFourth,
123 Interval::PerfectFifth,
124 Interval::MajorSixth,
125 Interval::MinorSeventh,
126 ],
127 ScaleKind::MajorPentatonic => &[Interval::PerfectUnison, Interval::MajorSecond, Interval::MajorThird, Interval::PerfectFifth, Interval::MajorSixth],
129 ScaleKind::MinorPentatonic => &[Interval::PerfectUnison, Interval::MinorThird, Interval::PerfectFourth, Interval::PerfectFifth, Interval::MinorSeventh],
131 ScaleKind::Blues => &[
133 Interval::PerfectUnison,
134 Interval::MinorThird,
135 Interval::PerfectFourth,
136 Interval::AugmentedFourth, Interval::PerfectFifth,
138 Interval::MinorSeventh,
139 ],
140 }
141 }
142}
143
144impl HasDescription for ScaleKind {
145 fn description(&self) -> &'static str {
146 match self {
147 ScaleKind::Major => "major scale, ionian mode parent",
148 ScaleKind::NaturalMinor => "natural minor scale, aeolian mode parent",
149 ScaleKind::HarmonicMinor => "harmonic minor scale, raised seventh degree",
150 ScaleKind::MelodicMinor => "melodic minor scale, raised sixth and seventh degrees",
151 ScaleKind::WholeTone => "whole tone scale, all whole steps",
152 ScaleKind::Chromatic => "chromatic scale, all twelve semitones",
153 ScaleKind::DiminishedWholeHalf => "diminished scale, whole-half (W-H) pattern, fully diminished 7th chord parent",
154 ScaleKind::DiminishedHalfWhole => "diminished scale, half-whole (H-W) pattern, dominant 7♭9 (flat 9) chord parent",
155 ScaleKind::MajorPentatonic => "major pentatonic scale, five-note major scale without 4th and 7th",
156 ScaleKind::MinorPentatonic => "minor pentatonic scale, five-note minor scale without 2nd and 6th",
157 ScaleKind::Blues => "blues scale, minor pentatonic with added ♯4 (blue note)",
158 }
159 }
160}
161
162impl HasStaticName for ScaleKind {
163 fn static_name(&self) -> &'static str {
164 match self {
165 ScaleKind::Major => "major",
166 ScaleKind::NaturalMinor => "natural minor",
167 ScaleKind::HarmonicMinor => "harmonic minor",
168 ScaleKind::MelodicMinor => "melodic minor",
169 ScaleKind::WholeTone => "whole tone",
170 ScaleKind::Chromatic => "chromatic",
171 ScaleKind::DiminishedWholeHalf => "diminished (whole-half)",
172 ScaleKind::DiminishedHalfWhole => "diminished (half-whole)",
173 ScaleKind::MajorPentatonic => "major pentatonic",
174 ScaleKind::MinorPentatonic => "minor pentatonic",
175 ScaleKind::Blues => "blues",
176 }
177 }
178}
179
180impl HasName for ScaleKind {
181 fn name(&self) -> String {
182 self.static_name().to_owned()
183 }
184}
185
186impl ScaleKind {
187 pub fn ascii_name(&self) -> &'static str {
189 match self {
190 ScaleKind::Major => "major",
191 ScaleKind::NaturalMinor => "natural minor",
192 ScaleKind::HarmonicMinor => "harmonic minor",
193 ScaleKind::MelodicMinor => "melodic minor",
194 ScaleKind::WholeTone => "whole tone",
195 ScaleKind::Chromatic => "chromatic",
196 ScaleKind::DiminishedWholeHalf => "diminished whole-half",
197 ScaleKind::DiminishedHalfWhole => "diminished half-whole",
198 ScaleKind::MajorPentatonic => "major pentatonic",
199 ScaleKind::MinorPentatonic => "minor pentatonic",
200 ScaleKind::Blues => "blues",
201 }
202 }
203}
204
205#[cfg(test)]
208mod tests {
209 use super::*;
210 use pretty_assertions::assert_eq;
211
212 #[test]
213 fn test_scale_intervals() {
214 assert_eq!(ScaleKind::Major.intervals().len(), 7);
216 assert_eq!(
217 ScaleKind::Major.intervals(),
218 &[
219 Interval::PerfectUnison,
220 Interval::MajorSecond,
221 Interval::MajorThird,
222 Interval::PerfectFourth,
223 Interval::PerfectFifth,
224 Interval::MajorSixth,
225 Interval::MajorSeventh,
226 ]
227 );
228
229 assert_eq!(ScaleKind::NaturalMinor.intervals().len(), 7);
231 assert_eq!(
232 ScaleKind::NaturalMinor.intervals(),
233 &[
234 Interval::PerfectUnison,
235 Interval::MajorSecond,
236 Interval::MinorThird,
237 Interval::PerfectFourth,
238 Interval::PerfectFifth,
239 Interval::MinorSixth,
240 Interval::MinorSeventh,
241 ]
242 );
243
244 assert_eq!(ScaleKind::HarmonicMinor.intervals().len(), 7);
246
247 assert_eq!(ScaleKind::WholeTone.intervals().len(), 6);
249
250 assert_eq!(ScaleKind::Chromatic.intervals().len(), 12);
252
253 assert_eq!(ScaleKind::DiminishedWholeHalf.intervals().len(), 8);
255 assert_eq!(ScaleKind::DiminishedHalfWhole.intervals().len(), 8);
256
257 assert_eq!(ScaleKind::MajorPentatonic.intervals().len(), 5);
259 assert_eq!(
260 ScaleKind::MajorPentatonic.intervals(),
261 &[Interval::PerfectUnison, Interval::MajorSecond, Interval::MajorThird, Interval::PerfectFifth, Interval::MajorSixth,]
262 );
263
264 assert_eq!(ScaleKind::MinorPentatonic.intervals().len(), 5);
265 assert_eq!(
266 ScaleKind::MinorPentatonic.intervals(),
267 &[Interval::PerfectUnison, Interval::MinorThird, Interval::PerfectFourth, Interval::PerfectFifth, Interval::MinorSeventh,]
268 );
269
270 assert_eq!(ScaleKind::Blues.intervals().len(), 6);
272 assert_eq!(
273 ScaleKind::Blues.intervals(),
274 &[
275 Interval::PerfectUnison,
276 Interval::MinorThird,
277 Interval::PerfectFourth,
278 Interval::AugmentedFourth,
279 Interval::PerfectFifth,
280 Interval::MinorSeventh,
281 ]
282 );
283 }
284
285 #[test]
286 fn test_scale_names() {
287 assert_eq!(ScaleKind::Major.static_name(), "major");
288 assert_eq!(ScaleKind::NaturalMinor.static_name(), "natural minor");
289 assert_eq!(ScaleKind::HarmonicMinor.static_name(), "harmonic minor");
290 assert_eq!(ScaleKind::MelodicMinor.static_name(), "melodic minor");
291 assert_eq!(ScaleKind::WholeTone.static_name(), "whole tone");
292 assert_eq!(ScaleKind::MajorPentatonic.static_name(), "major pentatonic");
293 assert_eq!(ScaleKind::MinorPentatonic.static_name(), "minor pentatonic");
294 assert_eq!(ScaleKind::Blues.static_name(), "blues");
295 }
296
297 #[test]
298 fn test_scale_descriptions() {
299 assert_eq!(ScaleKind::Major.description(), "major scale, ionian mode parent");
300 assert_eq!(ScaleKind::NaturalMinor.description(), "natural minor scale, aeolian mode parent");
301 assert_eq!(ScaleKind::MajorPentatonic.description(), "major pentatonic scale, five-note major scale without 4th and 7th");
302 assert_eq!(ScaleKind::MinorPentatonic.description(), "minor pentatonic scale, five-note minor scale without 2nd and 6th");
303 assert_eq!(ScaleKind::Blues.description(), "blues scale, minor pentatonic with added ♯4 (blue note)");
304 }
305}