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