1use serde::{Deserialize, Serialize};
2use std::fmt::Display;
3
4use crate::prelude::{Chord, Key, Note, Octave, Pitch, Semitones, Syllable};
5use crate::tone::Tone;
6
7#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
9pub enum Scale {
10 Ionian,
11 Dorian,
12 Phrygian,
13 Lydian,
14 Mixolydian,
15 Aeolian,
16 Locrian,
17}
18impl Display for Scale {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 write!(f, "{:?}", self)
21 }
22}
23impl Default for Scale {
24 fn default() -> Self {
25 Self::Ionian
26 }
27}
28impl Scale {
29 #[allow(non_upper_case_globals)]
30 pub const Major: Scale = Scale::Ionian;
31 #[allow(non_upper_case_globals)]
32 pub const Minor: Scale = Scale::Aeolian;
33 pub const ALL: [ Scale; 7 ] = [
34 Scale::Ionian, Scale::Dorian, Scale::Phrygian, Scale::Lydian, Scale::Mixolydian, Scale::Aeolian, Scale::Locrian,
35 ];
36
37 pub fn to_ident(&self) -> String {
38 format!("{}", self)
39 }
40 pub fn from_ident(ident: &str) -> Self {
41 match ident {
42 "Major" => Self::Major,
43 "Minor" => Self::Minor,
44 "Ionian" => Self::Ionian,
45 "Dorian" => Self::Dorian,
46 "Phrygian" => Self::Phrygian,
47 "Lydian" => Self::Lydian,
48 "Mixolydian" => Self::Mixolydian,
49 "Aeolian" => Self::Aeolian,
50 "Locrian" => Self::Locrian,
51 _ => Self::default(),
52 }
53 }
54 pub fn get_syllables(&self) -> Vec<Syllable> {
55 match self {
56 Scale::Ionian => vec![
57 Syllable::Do,
58 Syllable::Re,
59 Syllable::Mi,
60 Syllable::Fa,
61 Syllable::So,
62 Syllable::La,
63 Syllable::Ti,
64 ],
65 Scale::Dorian => vec![
66 Syllable::Re,
67 Syllable::Mi,
68 Syllable::Fa,
69 Syllable::So,
70 Syllable::La,
71 Syllable::Ti,
72 Syllable::Do,
73 ],
74 Scale::Phrygian => vec![
75 Syllable::Mi,
76 Syllable::Fa,
77 Syllable::So,
78 Syllable::La,
79 Syllable::Ti,
80 Syllable::Do,
81 Syllable::Re,
82 ],
83 Scale::Lydian => vec![
84 Syllable::Fa,
85 Syllable::So,
86 Syllable::La,
87 Syllable::Ti,
88 Syllable::Do,
89 Syllable::Re,
90 Syllable::Mi,
91 ],
92 Scale::Mixolydian => vec![
93 Syllable::So,
94 Syllable::La,
95 Syllable::Ti,
96 Syllable::Do,
97 Syllable::Re,
98 Syllable::Mi,
99 Syllable::Fa,
100 ],
101 Scale::Aeolian => vec![
102 Syllable::La,
103 Syllable::Ti,
104 Syllable::Do,
105 Syllable::Re,
106 Syllable::Mi,
107 Syllable::Fa,
108 Syllable::So,
109 ],
110 Scale::Locrian => vec![
111 Syllable::Ti,
112 Syllable::Do,
113 Syllable::Re,
114 Syllable::Mi,
115 Syllable::Fa,
116 Syllable::So,
117 Syllable::La,
118 ],
119 }
120 }
121 pub fn calc_key_index(&self, key: Key) -> usize {
122 let offset = Semitones::from(key) - Semitones::from(self.get_keys()[0]);
123 let offset_val = if offset.0 >= 0 { offset.0 % 12 } else { offset.0 % 12 + 12 };
124 match offset_val {
125 0 => 0,
126 1 => 7,
127 2 => 2,
128 3 => 9,
129 4 => 4,
130 5 => 11,
131 6 => 6,
132 7 => 1,
133 8 => 8,
134 9 => 3,
135 10 => 10,
136 11 => 5,
137 _ => 0,
138 }
139 }
140 pub fn get_keys(&self) -> [Key; 12] {
142 match self {
143 Scale::Ionian => [
144 Key::C,
145 Key::G,
146 Key::D,
147 Key::A,
148 Key::E,
149 Key::B,
150 Key::F_SHARP,
151 Key::D_FLAT,
152 Key::A_FLAT,
153 Key::E_FLAT,
154 Key::B_FLAT,
155 Key::F,
156 ],
157 Scale::Dorian => [
158 Key::D,
159 Key::A,
160 Key::E,
161 Key::B,
162 Key::F_SHARP,
163 Key::C_SHARP,
164 Key::G_SHARP,
165 Key::E_FLAT,
166 Key::B_FLAT,
167 Key::F,
168 Key::C,
169 Key::G,
170 ],
171 Scale::Phrygian => [
172 Key::E,
173 Key::B,
174 Key::F_SHARP,
175 Key::C_SHARP,
176 Key::G_SHARP,
177 Key::D_SHARP,
178 Key::A_SHARP,
179 Key::F,
180 Key::C,
181 Key::G,
182 Key::D,
183 Key::A,
184 ],
185 Scale::Lydian => [
186 Key::F,
187 Key::C,
188 Key::G,
189 Key::D,
190 Key::A,
191 Key::E,
192 Key::B,
193 Key::G_FLAT,
194 Key::D_FLAT,
195 Key::A_FLAT,
196 Key::E_FLAT,
197 Key::B_FLAT,
198 ],
199 Scale::Mixolydian => [
200 Key::G,
201 Key::D,
202 Key::A,
203 Key::E,
204 Key::B,
205 Key::F_SHARP,
206 Key::C_SHARP,
207 Key::A_FLAT,
208 Key::E_FLAT,
209 Key::B_FLAT,
210 Key::F,
211 Key::C,
212 ],
213 Scale::Aeolian => [
214 Key::A,
215 Key::E,
216 Key::B,
217 Key::F_SHARP,
218 Key::C_SHARP,
219 Key::G_SHARP,
220 Key::D_SHARP,
221 Key::B_FLAT,
222 Key::F,
223 Key::C,
224 Key::G,
225 Key::D,
226 ],
227 Scale::Locrian => [
228 Key::B,
229 Key::F_SHARP,
230 Key::C_SHARP,
231 Key::G_SHARP,
232 Key::D_SHARP,
233 Key::A_SHARP,
234 Key::E_SHARP,
235 Key::C,
236 Key::G,
237 Key::D,
238 Key::A,
239 Key::E,
240 ],
241 }
242 }
243}
244
245impl Scale {
246 pub fn calc_do_offset(&self) -> i8 {
247 match self {
248 Scale::Ionian => 0,
249 Scale::Dorian => -2,
250 Scale::Phrygian => -4,
251 Scale::Lydian => -5,
252 Scale::Mixolydian => 5,
253 Scale::Aeolian => 3,
254 Scale::Locrian => 1,
255 }
256 }
257 pub fn calc_do_semitones(&self, key: &Key) -> Semitones {
258 let semitones = Semitones::from(*key).0 + self.calc_do_offset();
259 Semitones(semitones)
260 }
261 pub fn calc_root_syllable(&self) -> Syllable {
262 Semitones(0 - self.calc_do_offset()).into()
263 }
264 pub fn calc_syllable_for_sort(&self, syllable: &Syllable) -> Syllable {
265 let semitones = Semitones::from(*syllable).0 + self.calc_do_offset();
266 Semitones(semitones).into()
267 }
268 pub fn calc_chord_for_sort(&self, chord: &Chord) -> Chord {
269 if *self == Scale::Ionian {
270 return chord.clone();
271 }
272 let root = self.calc_syllable_for_sort(&chord.root);
273 Chord {
274 root,
275 intervals: chord.intervals.clone(),
276 bass: chord.bass.clone(),
277 }
278 }
279 pub fn calc_syllable(&self, key: &Key, pitch: &Pitch) -> Syllable {
280 (Semitones::from(*pitch) - self.calc_do_semitones(key)).into()
281 }
282 pub fn calc_pitch(&self, key: &Key, syllable: &Syllable) -> Pitch {
283 let key_index = self.calc_key_index(key.clone());
284 if let Some(keys) = match syllable {
285 Syllable::Do => Some(Scale::Ionian.get_keys()),
286 Syllable::Re => Some(Scale::Dorian.get_keys()),
287 Syllable::Mi => Some(Scale::Phrygian.get_keys()),
288 Syllable::Fa => Some(Scale::Lydian.get_keys()),
289 Syllable::So => Some(Scale::Mixolydian.get_keys()),
290 Syllable::La => Some(Scale::Aeolian.get_keys()),
291 Syllable::Ti => Some(Scale::Locrian.get_keys()),
292 _ => None,
293 } {
294 keys[key_index].into()
295 } else {
296 (Semitones::from(*syllable) + self.calc_do_semitones(key)).into()
297 }
298 }
299 pub fn calc_note_from_pitch(&self, key: &Key, pitch: &Pitch, octave: &Octave) -> Note {
300 let syllable = self.calc_syllable(key, pitch);
301 Note::new(*octave, *pitch, syllable)
302 }
303 pub fn calc_note_from_syllable(&self, key: &Key, syllable: &Syllable, octave: &Octave) -> Note {
304 let semitones = Semitones::from(*octave) + self.calc_do_semitones(key) + Semitones::from(*syllable);
305 let pitch = self.calc_pitch(key, syllable);
306 Note::new(Octave::from(semitones), pitch, *syllable)
307 }
308 pub fn calc_note_from_semitones(&self, key: &Key, semitones: Semitones) -> Note {
309 let (pitch, octave) = semitones.as_pitch_octave();
310 self.calc_note_from_pitch(key, &pitch, &octave)
311 }
312 pub fn calc_click_note(&self, key: &Key, octave: &Octave, syllable: &Syllable) -> Note {
313 let pitch = self.calc_pitch(key, syllable);
314 Note::new(*octave, pitch, *syllable)
315 }
316 pub fn calc_click_tone(&self, key: &Key, octave: &Octave, syllable: &Syllable) -> Tone {
317 Tone::Single(self.calc_click_note(key, octave, syllable))
318 }
319}