1use super::*;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct Music21ChordType {
10 pub kind: &'static str,
12 pub notation: &'static str,
14 pub abbreviation: &'static str,
17 pub abbreviations: &'static [&'static str],
19}
20
21pub fn known_chord_symbol_types() -> &'static [Music21ChordType] {
26 MUSIC21_CHORD_TYPES
27}
28
29pub(super) fn chord_type_named(kind: &str) -> Option<&'static Music21ChordType> {
30 MUSIC21_CHORD_TYPES
31 .iter()
32 .find(|chord_type| chord_type.kind == kind)
33}
34
35pub fn abbreviations_for_kind(kind: &str) -> Option<&'static [&'static str]> {
39 chord_type_named(kind).map(|chord_type| chord_type.abbreviations)
40}
41
42pub fn notation_for_kind(kind: &str) -> Option<&'static str> {
45 chord_type_named(kind).map(|chord_type| chord_type.notation)
46}
47
48pub fn current_abbreviation_for_kind(kind: &str) -> Option<&'static str> {
51 chord_type_named(kind).map(|chord_type| chord_type.abbreviation)
52}
53
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub(super) struct Music21Degree {
56 degree: u8,
57 semitone: u8,
58}
59
60pub(super) const MUSIC21_CHORD_TYPES: &[Music21ChordType] = &[
61 Music21ChordType {
62 kind: "major",
63 notation: "1,3,5",
64 abbreviation: "",
65 abbreviations: &["", "M", "maj"],
66 },
67 Music21ChordType {
68 kind: "minor",
69 notation: "1,-3,5",
70 abbreviation: "m",
71 abbreviations: &["m", "min"],
72 },
73 Music21ChordType {
74 kind: "augmented",
75 notation: "1,3,#5",
76 abbreviation: "+",
77 abbreviations: &["+", "aug"],
78 },
79 Music21ChordType {
80 kind: "diminished",
81 notation: "1,-3,-5",
82 abbreviation: "dim",
83 abbreviations: &["dim", "o"],
84 },
85 Music21ChordType {
86 kind: "dominant-seventh",
87 notation: "1,3,5,-7",
88 abbreviation: "7",
89 abbreviations: &["7", "dom7"],
90 },
91 Music21ChordType {
92 kind: "major-seventh",
93 notation: "1,3,5,7",
94 abbreviation: "maj7",
95 abbreviations: &["maj7", "M7"],
96 },
97 Music21ChordType {
98 kind: "minor-major-seventh",
99 notation: "1,-3,5,7",
100 abbreviation: "mM7",
101 abbreviations: &["mM7", "m#7", "minmaj7"],
102 },
103 Music21ChordType {
104 kind: "minor-seventh",
105 notation: "1,-3,5,-7",
106 abbreviation: "m7",
107 abbreviations: &["m7", "min7"],
108 },
109 Music21ChordType {
110 kind: "augmented-major-seventh",
111 notation: "1,3,#5,7",
112 abbreviation: "+M7",
113 abbreviations: &["+M7", "augmaj7"],
114 },
115 Music21ChordType {
116 kind: "augmented-seventh",
117 notation: "1,3,#5,-7",
118 abbreviation: "7+",
119 abbreviations: &["7+", "+7", "aug7"],
120 },
121 Music21ChordType {
122 kind: "half-diminished-seventh",
123 notation: "1,-3,-5,-7",
124 abbreviation: "ø7",
125 abbreviations: &["ø7", "m7b5"],
126 },
127 Music21ChordType {
128 kind: "diminished-seventh",
129 notation: "1,-3,-5,--7",
130 abbreviation: "o7",
131 abbreviations: &["o7", "dim7"],
132 },
133 Music21ChordType {
134 kind: "seventh-flat-five",
135 notation: "1,3,-5,-7",
136 abbreviation: "dom7dim5",
137 abbreviations: &["dom7dim5"],
138 },
139 Music21ChordType {
140 kind: "major-sixth",
141 notation: "1,3,5,6",
142 abbreviation: "6",
143 abbreviations: &["6"],
144 },
145 Music21ChordType {
146 kind: "minor-sixth",
147 notation: "1,-3,5,6",
148 abbreviation: "m6",
149 abbreviations: &["m6", "min6"],
150 },
151 Music21ChordType {
152 kind: "major-ninth",
153 notation: "1,3,5,7,9",
154 abbreviation: "M9",
155 abbreviations: &["M9", "Maj9"],
156 },
157 Music21ChordType {
158 kind: "dominant-ninth",
159 notation: "1,3,5,-7,9",
160 abbreviation: "9",
161 abbreviations: &["9", "dom9"],
162 },
163 Music21ChordType {
164 kind: "minor-major-ninth",
165 notation: "1,-3,5,7,9",
166 abbreviation: "mM9",
167 abbreviations: &["mM9", "minmaj9"],
168 },
169 Music21ChordType {
170 kind: "minor-ninth",
171 notation: "1,-3,5,-7,9",
172 abbreviation: "m9",
173 abbreviations: &["m9", "min9"],
174 },
175 Music21ChordType {
176 kind: "augmented-major-ninth",
177 notation: "1,3,#5,7,9",
178 abbreviation: "+M9",
179 abbreviations: &["+M9", "augmaj9"],
180 },
181 Music21ChordType {
182 kind: "augmented-dominant-ninth",
183 notation: "1,3,#5,-7,9",
184 abbreviation: "9#5",
185 abbreviations: &["9#5", "+9", "aug9"],
186 },
187 Music21ChordType {
188 kind: "half-diminished-ninth",
189 notation: "1,-3,-5,-7,9",
190 abbreviation: "ø9",
191 abbreviations: &["ø9"],
192 },
193 Music21ChordType {
194 kind: "half-diminished-minor-ninth",
195 notation: "1,-3,-5,-7,-9",
196 abbreviation: "øb9",
197 abbreviations: &["øb9"],
198 },
199 Music21ChordType {
200 kind: "diminished-ninth",
201 notation: "1,-3,-5,--7,9",
202 abbreviation: "o9",
203 abbreviations: &["o9", "dim9"],
204 },
205 Music21ChordType {
206 kind: "diminished-minor-ninth",
207 notation: "1,-3,-5,--7,-9",
208 abbreviation: "ob9",
209 abbreviations: &["ob9", "dimb9"],
210 },
211 Music21ChordType {
212 kind: "dominant-11th",
213 notation: "1,3,5,-7,9,11",
214 abbreviation: "11",
215 abbreviations: &["11", "dom11"],
216 },
217 Music21ChordType {
218 kind: "major-11th",
219 notation: "1,3,5,7,9,11",
220 abbreviation: "M11",
221 abbreviations: &["M11", "Maj11"],
222 },
223 Music21ChordType {
224 kind: "minor-major-11th",
225 notation: "1,-3,5,7,9,11",
226 abbreviation: "mM11",
227 abbreviations: &["mM11", "minmaj11"],
228 },
229 Music21ChordType {
230 kind: "minor-11th",
231 notation: "1,-3,5,-7,9,11",
232 abbreviation: "m11",
233 abbreviations: &["m11", "min11"],
234 },
235 Music21ChordType {
236 kind: "augmented-major-11th",
237 notation: "1,3,#5,7,9,11",
238 abbreviation: "+M11",
239 abbreviations: &["+M11", "augmaj11"],
240 },
241 Music21ChordType {
242 kind: "augmented-11th",
243 notation: "1,3,#5,-7,9,11",
244 abbreviation: "+11",
245 abbreviations: &["+11", "aug11"],
246 },
247 Music21ChordType {
248 kind: "half-diminished-11th",
249 notation: "1,-3,-5,-7,9,11",
250 abbreviation: "ø11",
251 abbreviations: &["ø11"],
252 },
253 Music21ChordType {
254 kind: "diminished-11th",
255 notation: "1,-3,-5,--7,9,11",
256 abbreviation: "o11",
257 abbreviations: &["o11", "dim11"],
258 },
259 Music21ChordType {
260 kind: "major-13th",
261 notation: "1,3,5,7,9,11,13",
262 abbreviation: "M13",
263 abbreviations: &["M13", "Maj13"],
264 },
265 Music21ChordType {
266 kind: "dominant-13th",
267 notation: "1,3,5,-7,9,11,13",
268 abbreviation: "13",
269 abbreviations: &["13", "dom13"],
270 },
271 Music21ChordType {
272 kind: "minor-major-13th",
273 notation: "1,-3,5,7,9,11,13",
274 abbreviation: "mM13",
275 abbreviations: &["mM13", "minmaj13"],
276 },
277 Music21ChordType {
278 kind: "minor-13th",
279 notation: "1,-3,5,-7,9,11,13",
280 abbreviation: "m13",
281 abbreviations: &["m13", "min13"],
282 },
283 Music21ChordType {
284 kind: "augmented-major-13th",
285 notation: "1,3,#5,7,9,11,13",
286 abbreviation: "+M13",
287 abbreviations: &["+M13", "augmaj13"],
288 },
289 Music21ChordType {
290 kind: "augmented-dominant-13th",
291 notation: "1,3,#5,-7,9,11,13",
292 abbreviation: "+13",
293 abbreviations: &["+13", "aug13"],
294 },
295 Music21ChordType {
296 kind: "half-diminished-13th",
297 notation: "1,-3,-5,-7,9,11,13",
298 abbreviation: "ø13",
299 abbreviations: &["ø13"],
300 },
301 Music21ChordType {
302 kind: "suspended-second",
303 notation: "1,2,5",
304 abbreviation: "sus2",
305 abbreviations: &["sus2"],
306 },
307 Music21ChordType {
308 kind: "suspended-fourth",
309 notation: "1,4,5",
310 abbreviation: "sus",
311 abbreviations: &["sus", "sus4"],
312 },
313 Music21ChordType {
314 kind: "suspended-fourth-seventh",
315 notation: "1,4,5,-7",
316 abbreviation: "7sus",
317 abbreviations: &["7sus", "7sus4"],
318 },
319 Music21ChordType {
320 kind: "Neapolitan",
321 notation: "1,-2,3,-5",
322 abbreviation: "N6",
323 abbreviations: &["N6"],
324 },
325 Music21ChordType {
326 kind: "Italian",
327 notation: "1,#4,-6",
328 abbreviation: "It+6",
329 abbreviations: &["It+6", "It"],
330 },
331 Music21ChordType {
332 kind: "French",
333 notation: "1,2,#4,-6",
334 abbreviation: "Fr+6",
335 abbreviations: &["Fr+6", "Fr"],
336 },
337 Music21ChordType {
338 kind: "German",
339 notation: "1,-3,#4,-6",
340 abbreviation: "Gr+6",
341 abbreviations: &["Gr+6", "Ger"],
342 },
343 Music21ChordType {
344 kind: "pedal",
345 notation: "1",
346 abbreviation: "pedal",
347 abbreviations: &["pedal"],
348 },
349 Music21ChordType {
350 kind: "power",
351 notation: "1,5",
352 abbreviation: "power",
353 abbreviations: &["power"],
354 },
355 Music21ChordType {
356 kind: "Tristan",
357 notation: "1,#4,#6,#9",
358 abbreviation: "tristan",
359 abbreviations: &["tristan"],
360 },
361];
362
363pub(super) fn chord_degrees_for_notation(notation: &str) -> Option<Vec<u8>> {
364 notation
365 .split(',')
366 .filter(|token| *token != "1")
367 .map(|token| parse_music21_degree(token).map(|degree| degree.semitone))
368 .collect()
369}
370
371pub(super) fn degree_numbers_for_notation(notation: &str) -> Option<Vec<u8>> {
372 notation
373 .split(',')
374 .map(|token| parse_music21_degree(token).map(|degree| degree.degree))
375 .collect()
376}
377
378pub(super) fn parse_music21_degree(token: &str) -> Option<Music21Degree> {
379 let alteration = token.chars().fold(0_i32, |sum, ch| match ch {
380 '#' => sum + 1,
381 '-' => sum - 1,
382 _ => sum,
383 });
384 let degree = token
385 .chars()
386 .filter(char::is_ascii_digit)
387 .collect::<String>()
388 .parse::<u8>()
389 .ok()?;
390 let semitone = (base_semitone_for_degree(degree)? + alteration).rem_euclid(12) as u8;
391
392 Some(Music21Degree { degree, semitone })
393}
394
395pub(super) fn base_semitone_for_degree(degree: u8) -> Option<IntegerType> {
396 match degree {
397 1 => Some(0),
398 2 | 9 => Some(2),
399 3 => Some(4),
400 4 | 11 => Some(5),
401 5 => Some(7),
402 6 | 13 => Some(9),
403 7 => Some(11),
404 _ => None,
405 }
406}
407
408pub(super) fn kind_pitch_names(root: &Pitch, notation: &str) -> Result<BTreeSet<String>> {
412 notation_intervals(notation)?
413 .iter()
414 .map(|(_, name)| Ok(Interval::from_name(name)?.transpose_pitch(root)?.name()))
415 .collect()
416}
417
418pub(super) fn notation_intervals(notation: &str) -> Result<Vec<(u8, String)>> {
422 notation
423 .split(',')
424 .map(|token| {
425 let degree: u8 = token
426 .trim_matches(['#', '-'])
427 .parse()
428 .map_err(|_| Error::Chord(format!("{token} is not a chord degree")))?;
429 let alter = token.matches('#').count() as IntegerType
430 - token.matches('-').count() as IntegerType;
431 let perfect = matches!(degree % 7, 1 | 4 | 5);
432 let quality = match (perfect, alter) {
433 (true, 0) => "P".to_string(),
434 (false, 0) => "M".to_string(),
435 (_, raised) if raised > 0 => "a".repeat(raised as usize),
436 (true, lowered) => "d".repeat(lowered.unsigned_abs() as usize),
437 (false, -1) => "m".to_string(),
438 (false, lowered) => "d".repeat((lowered.unsigned_abs() - 1) as usize),
439 };
440 Ok((degree, format!("{quality}{degree}")))
441 })
442 .collect()
443}