1use crate::core::{
4 base::{HasDescription, HasName, HasStaticName},
5 interval::{HasIntervals, Interval},
6 scale_kind::ScaleKind,
7};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[repr(u8)]
22pub enum ModeKind {
23 Ionian,
26 Dorian,
28 Phrygian,
30 Lydian,
32 Mixolydian,
34 Aeolian,
36 Locrian,
38
39 LocrianNatural6,
42 IonianSharp5,
44 DorianSharp4,
46 PhrygianDominant,
48 LydianSharp2,
50 Ultralocrian,
52
53 DorianFlat2,
56 LydianAugmented,
58 LydianDominant,
60 MixolydianFlat6,
62 LocrianNatural2,
64 Altered,
66}
67
68impl ModeKind {
71 pub fn parent_scale(&self) -> ScaleKind {
75 match self {
76 ModeKind::Ionian | ModeKind::Dorian | ModeKind::Phrygian | ModeKind::Lydian | ModeKind::Mixolydian | ModeKind::Aeolian | ModeKind::Locrian => ScaleKind::Major,
77
78 ModeKind::LocrianNatural6 | ModeKind::IonianSharp5 | ModeKind::DorianSharp4 | ModeKind::PhrygianDominant | ModeKind::LydianSharp2 | ModeKind::Ultralocrian => ScaleKind::HarmonicMinor,
79
80 ModeKind::DorianFlat2 | ModeKind::LydianAugmented | ModeKind::LydianDominant | ModeKind::MixolydianFlat6 | ModeKind::LocrianNatural2 | ModeKind::Altered => ScaleKind::MelodicMinor,
81 }
82 }
83
84 pub fn parent_degree(&self) -> u8 {
88 match self {
89 ModeKind::Ionian => 1,
91 ModeKind::Dorian => 2,
92 ModeKind::Phrygian => 3,
93 ModeKind::Lydian => 4,
94 ModeKind::Mixolydian => 5,
95 ModeKind::Aeolian => 6,
96 ModeKind::Locrian => 7,
97
98 ModeKind::LocrianNatural6 => 2,
100 ModeKind::IonianSharp5 => 3,
101 ModeKind::DorianSharp4 => 4,
102 ModeKind::PhrygianDominant => 5,
103 ModeKind::LydianSharp2 => 6,
104 ModeKind::Ultralocrian => 7,
105
106 ModeKind::DorianFlat2 => 2,
108 ModeKind::LydianAugmented => 3,
109 ModeKind::LydianDominant => 4,
110 ModeKind::MixolydianFlat6 => 5,
111 ModeKind::LocrianNatural2 => 6,
112 ModeKind::Altered => 7,
113 }
114 }
115}
116
117impl HasIntervals for ModeKind {
118 fn intervals(&self) -> &'static [Interval] {
119 match self {
120 ModeKind::Ionian => &[
124 Interval::PerfectUnison,
125 Interval::MajorSecond,
126 Interval::MajorThird,
127 Interval::PerfectFourth,
128 Interval::PerfectFifth,
129 Interval::MajorSixth,
130 Interval::MajorSeventh,
131 ],
132 ModeKind::Dorian => &[
134 Interval::PerfectUnison,
135 Interval::MajorSecond,
136 Interval::MinorThird,
137 Interval::PerfectFourth,
138 Interval::PerfectFifth,
139 Interval::MajorSixth,
140 Interval::MinorSeventh,
141 ],
142 ModeKind::Phrygian => &[
144 Interval::PerfectUnison,
145 Interval::MinorSecond,
146 Interval::MinorThird,
147 Interval::PerfectFourth,
148 Interval::PerfectFifth,
149 Interval::MinorSixth,
150 Interval::MinorSeventh,
151 ],
152 ModeKind::Lydian => &[
154 Interval::PerfectUnison,
155 Interval::MajorSecond,
156 Interval::MajorThird,
157 Interval::AugmentedFourth,
158 Interval::PerfectFifth,
159 Interval::MajorSixth,
160 Interval::MajorSeventh,
161 ],
162 ModeKind::Mixolydian => &[
164 Interval::PerfectUnison,
165 Interval::MajorSecond,
166 Interval::MajorThird,
167 Interval::PerfectFourth,
168 Interval::PerfectFifth,
169 Interval::MajorSixth,
170 Interval::MinorSeventh,
171 ],
172 ModeKind::Aeolian => &[
174 Interval::PerfectUnison,
175 Interval::MajorSecond,
176 Interval::MinorThird,
177 Interval::PerfectFourth,
178 Interval::PerfectFifth,
179 Interval::MinorSixth,
180 Interval::MinorSeventh,
181 ],
182 ModeKind::Locrian => &[
184 Interval::PerfectUnison,
185 Interval::MinorSecond,
186 Interval::MinorThird,
187 Interval::PerfectFourth,
188 Interval::DiminishedFifth,
189 Interval::MinorSixth,
190 Interval::MinorSeventh,
191 ],
192
193 ModeKind::LocrianNatural6 => &[
198 Interval::PerfectUnison,
199 Interval::MinorSecond,
200 Interval::MinorThird,
201 Interval::PerfectFourth,
202 Interval::DiminishedFifth,
203 Interval::MajorSixth,
204 Interval::MinorSeventh,
205 ],
206 ModeKind::IonianSharp5 => &[
209 Interval::PerfectUnison,
210 Interval::MajorSecond,
211 Interval::MajorThird,
212 Interval::PerfectFourth,
213 Interval::AugmentedFifth,
214 Interval::MajorSixth,
215 Interval::MajorSeventh,
216 ],
217 ModeKind::DorianSharp4 => &[
220 Interval::PerfectUnison,
221 Interval::MajorSecond,
222 Interval::MinorThird,
223 Interval::AugmentedFourth,
224 Interval::PerfectFifth,
225 Interval::MajorSixth,
226 Interval::MinorSeventh,
227 ],
228 ModeKind::PhrygianDominant => &[
231 Interval::PerfectUnison,
232 Interval::MinorSecond,
233 Interval::MajorThird,
234 Interval::PerfectFourth,
235 Interval::PerfectFifth,
236 Interval::MinorSixth,
237 Interval::MinorSeventh,
238 ],
239 ModeKind::LydianSharp2 => &[
242 Interval::PerfectUnison,
243 Interval::AugmentedSecond,
244 Interval::MajorThird,
245 Interval::AugmentedFourth,
246 Interval::PerfectFifth,
247 Interval::MajorSixth,
248 Interval::MajorSeventh,
249 ],
250 ModeKind::Ultralocrian => &[
253 Interval::PerfectUnison,
254 Interval::MinorSecond,
255 Interval::MinorThird,
256 Interval::DiminishedFourth,
257 Interval::DiminishedFifth,
258 Interval::MinorSixth,
259 Interval::DiminishedSeventh,
260 ],
261
262 ModeKind::DorianFlat2 => &[
267 Interval::PerfectUnison,
268 Interval::MinorSecond,
269 Interval::MinorThird,
270 Interval::PerfectFourth,
271 Interval::PerfectFifth,
272 Interval::MajorSixth,
273 Interval::MajorSeventh,
274 ],
275 ModeKind::LydianAugmented => &[
278 Interval::PerfectUnison,
279 Interval::MajorSecond,
280 Interval::MajorThird,
281 Interval::AugmentedFourth,
282 Interval::AugmentedFifth,
283 Interval::MajorSixth,
284 Interval::MajorSeventh,
285 ],
286 ModeKind::LydianDominant => &[
289 Interval::PerfectUnison,
290 Interval::MajorSecond,
291 Interval::MajorThird,
292 Interval::AugmentedFourth,
293 Interval::PerfectFifth,
294 Interval::MajorSixth,
295 Interval::MinorSeventh,
296 ],
297 ModeKind::MixolydianFlat6 => &[
300 Interval::PerfectUnison,
301 Interval::MajorSecond,
302 Interval::MajorThird,
303 Interval::PerfectFourth,
304 Interval::PerfectFifth,
305 Interval::MinorSixth,
306 Interval::MinorSeventh,
307 ],
308 ModeKind::LocrianNatural2 => &[
311 Interval::PerfectUnison,
312 Interval::MajorSecond,
313 Interval::MinorThird,
314 Interval::PerfectFourth,
315 Interval::DiminishedFifth,
316 Interval::MinorSixth,
317 Interval::MinorSeventh,
318 ],
319 ModeKind::Altered => &[
336 Interval::PerfectUnison,
337 Interval::MinorSecond,
338 Interval::MinorThird,
339 Interval::DiminishedFourth,
340 Interval::DiminishedFifth,
341 Interval::MinorSixth,
342 Interval::MinorSeventh,
343 ],
344 }
345 }
346}
347
348impl HasDescription for ModeKind {
349 fn description(&self) -> &'static str {
350 match self {
351 ModeKind::Ionian => "ionian, 1st mode of major scale, major scale",
353 ModeKind::Dorian => "dorian, 2nd mode of major scale, minor with raised 6th",
354 ModeKind::Phrygian => "phrygian, 3rd mode of major scale, minor with lowered 2nd",
355 ModeKind::Lydian => "lydian, 4th mode of major scale, major with raised 4th",
356 ModeKind::Mixolydian => "mixolydian, 5th mode of major scale, major with lowered 7th",
357 ModeKind::Aeolian => "aeolian, 6th mode of major scale, natural minor",
358 ModeKind::Locrian => "locrian, 7th mode of major scale, diminished, half-diminished chord scale",
359
360 ModeKind::LocrianNatural6 => "locrian ♮6, 2nd mode of harmonic minor, m7♭5(♮13) color",
362 ModeKind::IonianSharp5 => "ionian ♯5, 3rd mode of harmonic minor, augmented major",
363 ModeKind::DorianSharp4 => "dorian ♯4, 4th mode of harmonic minor, minor with lydian bite",
364 ModeKind::PhrygianDominant => "phrygian dominant, 5th mode of harmonic minor, spanish phrygian",
365 ModeKind::LydianSharp2 => "lydian ♯2, 6th mode of harmonic minor, bright + exotic",
366 ModeKind::Ultralocrian => "ultralocrian, 7th mode of harmonic minor, very unstable/dark",
367
368 ModeKind::DorianFlat2 => "dorian ♭2, 2nd mode of melodic minor, phrygian ♮6, minor with spicy ♭2",
370 ModeKind::LydianAugmented => "lydian augmented, 3rd mode of melodic minor, lydian ♯5",
371 ModeKind::LydianDominant => "lydian dominant, 4th mode of melodic minor, acoustic scale, dominant with ♯11",
372 ModeKind::MixolydianFlat6 => "mixolydian ♭6, 5th mode of melodic minor, aeolian dominant, dominant with ♭13",
373 ModeKind::LocrianNatural2 => "locrian ♮2, 6th mode of melodic minor, half-diminished ♮2",
374 ModeKind::Altered => "altered, 7th mode of melodic minor, super locrian, V7alt scale",
375 }
376 }
377}
378
379impl HasStaticName for ModeKind {
380 fn static_name(&self) -> &'static str {
381 match self {
382 ModeKind::Ionian => "ionian",
384 ModeKind::Dorian => "dorian",
385 ModeKind::Phrygian => "phrygian",
386 ModeKind::Lydian => "lydian",
387 ModeKind::Mixolydian => "mixolydian",
388 ModeKind::Aeolian => "aeolian",
389 ModeKind::Locrian => "locrian",
390
391 ModeKind::LocrianNatural6 => "locrian ♮6",
393 ModeKind::IonianSharp5 => "ionian ♯5",
394 ModeKind::DorianSharp4 => "dorian ♯4",
395 ModeKind::PhrygianDominant => "phrygian dominant",
396 ModeKind::LydianSharp2 => "lydian ♯2",
397 ModeKind::Ultralocrian => "ultralocrian",
398
399 ModeKind::DorianFlat2 => "dorian ♭2",
401 ModeKind::LydianAugmented => "lydian augmented",
402 ModeKind::LydianDominant => "lydian dominant",
403 ModeKind::MixolydianFlat6 => "mixolydian ♭6",
404 ModeKind::LocrianNatural2 => "locrian ♮2",
405 ModeKind::Altered => "altered",
406 }
407 }
408}
409
410impl HasName for ModeKind {
411 fn name(&self) -> String {
412 self.static_name().to_owned()
413 }
414}
415
416impl ModeKind {
417 pub fn ascii_name(&self) -> &'static str {
419 match self {
420 ModeKind::Ionian => "ionian",
422 ModeKind::Dorian => "dorian",
423 ModeKind::Phrygian => "phrygian",
424 ModeKind::Lydian => "lydian",
425 ModeKind::Mixolydian => "mixolydian",
426 ModeKind::Aeolian => "aeolian",
427 ModeKind::Locrian => "locrian",
428
429 ModeKind::LocrianNatural6 => "locrian nat6",
431 ModeKind::IonianSharp5 => "ionian #5",
432 ModeKind::DorianSharp4 => "dorian #4",
433 ModeKind::PhrygianDominant => "phrygian dominant",
434 ModeKind::LydianSharp2 => "lydian #2",
435 ModeKind::Ultralocrian => "ultralocrian",
436
437 ModeKind::DorianFlat2 => "dorian b2",
439 ModeKind::LydianAugmented => "lydian augmented",
440 ModeKind::LydianDominant => "lydian dominant",
441 ModeKind::MixolydianFlat6 => "mixolydian b6",
442 ModeKind::LocrianNatural2 => "locrian nat2",
443 ModeKind::Altered => "altered",
444 }
445 }
446}
447
448#[cfg(test)]
451mod tests {
452 use super::*;
453 use pretty_assertions::assert_eq;
454
455 #[test]
456 fn test_mode_intervals_explicit() {
457 assert_eq!(ModeKind::Ionian.intervals().len(), 7);
459 assert_eq!(ModeKind::Dorian.intervals().len(), 7);
460 assert_eq!(ModeKind::Phrygian.intervals().len(), 7);
461 assert_eq!(ModeKind::Lydian.intervals().len(), 7);
462 assert_eq!(ModeKind::Mixolydian.intervals().len(), 7);
463 assert_eq!(ModeKind::Aeolian.intervals().len(), 7);
464 assert_eq!(ModeKind::Locrian.intervals().len(), 7);
465
466 assert_eq!(
468 ModeKind::Ionian.intervals(),
469 &[
470 Interval::PerfectUnison,
471 Interval::MajorSecond,
472 Interval::MajorThird,
473 Interval::PerfectFourth,
474 Interval::PerfectFifth,
475 Interval::MajorSixth,
476 Interval::MajorSeventh,
477 ]
478 );
479
480 assert_eq!(
482 ModeKind::Dorian.intervals(),
483 &[
484 Interval::PerfectUnison,
485 Interval::MajorSecond,
486 Interval::MinorThird,
487 Interval::PerfectFourth,
488 Interval::PerfectFifth,
489 Interval::MajorSixth, Interval::MinorSeventh,
491 ]
492 );
493
494 assert_eq!(
496 ModeKind::Phrygian.intervals(),
497 &[
498 Interval::PerfectUnison,
499 Interval::MinorSecond, Interval::MinorThird,
501 Interval::PerfectFourth,
502 Interval::PerfectFifth,
503 Interval::MinorSixth,
504 Interval::MinorSeventh,
505 ]
506 );
507
508 assert_eq!(
510 ModeKind::Lydian.intervals(),
511 &[
512 Interval::PerfectUnison,
513 Interval::MajorSecond,
514 Interval::MajorThird,
515 Interval::AugmentedFourth, Interval::PerfectFifth,
517 Interval::MajorSixth,
518 Interval::MajorSeventh,
519 ]
520 );
521
522 assert_eq!(
524 ModeKind::Locrian.intervals(),
525 &[
526 Interval::PerfectUnison,
527 Interval::MinorSecond,
528 Interval::MinorThird,
529 Interval::PerfectFourth,
530 Interval::DiminishedFifth, Interval::MinorSixth,
532 Interval::MinorSeventh,
533 ]
534 );
535 }
536
537 #[test]
538 fn test_mode_names() {
539 assert_eq!(ModeKind::Ionian.static_name(), "ionian");
540 assert_eq!(ModeKind::Dorian.static_name(), "dorian");
541 assert_eq!(ModeKind::Phrygian.static_name(), "phrygian");
542 assert_eq!(ModeKind::Lydian.static_name(), "lydian");
543 assert_eq!(ModeKind::Mixolydian.static_name(), "mixolydian");
544 assert_eq!(ModeKind::Aeolian.static_name(), "aeolian");
545 assert_eq!(ModeKind::Locrian.static_name(), "locrian");
546 }
547
548 #[test]
549 fn test_mode_parent_metadata() {
550 assert_eq!(ModeKind::Ionian.parent_scale(), ScaleKind::Major);
552 assert_eq!(ModeKind::Dorian.parent_scale(), ScaleKind::Major);
553 assert_eq!(ModeKind::Locrian.parent_scale(), ScaleKind::Major);
554
555 assert_eq!(ModeKind::Ionian.parent_degree(), 1);
557 assert_eq!(ModeKind::Dorian.parent_degree(), 2);
558 assert_eq!(ModeKind::Phrygian.parent_degree(), 3);
559 assert_eq!(ModeKind::Lydian.parent_degree(), 4);
560 assert_eq!(ModeKind::Mixolydian.parent_degree(), 5);
561 assert_eq!(ModeKind::Aeolian.parent_degree(), 6);
562 assert_eq!(ModeKind::Locrian.parent_degree(), 7);
563 }
564
565 #[test]
566 fn test_mode_descriptions() {
567 assert_eq!(ModeKind::Ionian.description(), "ionian, 1st mode of major scale, major scale");
568 assert_eq!(ModeKind::Dorian.description(), "dorian, 2nd mode of major scale, minor with raised 6th");
569 assert_eq!(ModeKind::Lydian.description(), "lydian, 4th mode of major scale, major with raised 4th");
570 }
571}