Skip to content

Commit

Permalink
Merge pull request #33 from ReeceMcMillin/master
Browse files Browse the repository at this point in the history
Chords from string of notes, fixed sus4 intervals.
  • Loading branch information
ozankasikci committed Mar 25, 2021
2 parents e18533d + df87c12 commit 2e09fa3
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 1 deletion.
50 changes: 49 additions & 1 deletion src/chord/chord.rs
Expand Up @@ -46,14 +46,62 @@ impl Chord {
}
}

pub fn from_string(string: &str) -> Self {
let notes: Vec<Pitch> = string.to_string()
.replace(",", "")
.split_whitespace()
.into_iter()
.map(|x| Pitch::from_str(x).expect(&format!("Invalid note {:?}.", x)))
.collect();

let intervals: Vec<u8> = notes.iter()
.map(|&x| Pitch::into_u8(x) % 12)
.zip(notes[1..].iter().map(|&x| Pitch::into_u8(x)))
.map(|(x, y)| if x < y {y - x} else {y + 12 - x})
.collect();

Chord::from_interval(notes[0], &intervals)
}

pub fn from_interval(root: Pitch, interval: &[u8]) -> Self {
use Number::*;
use Quality::*;
let (quality, number) = match interval {
&[4, 3] => (Major, Triad),
&[3, 4] => (Minor, Triad),
&[2, 5] => (Suspended2, Triad),
&[5, 2] => (Suspended4, Triad),
&[4, 4] => (Augmented, Triad),
&[3, 3] => (Diminished, Triad),
&[4, 3, 4] => (Major, Seventh),
&[3, 4, 3] => (Minor, Seventh),
&[4, 4, 2] => (Augmented, Seventh),
&[4, 4, 3] => (Augmented, MajorSeventh),
&[3, 3, 3] => (Diminished, Seventh),
&[3, 3, 4] => (HalfDiminished, Seventh),
&[3, 4, 4] => (Minor, MajorSeventh),
&[4, 3, 3] => (Dominant, Seventh),
&[4, 3, 3, 4] => (Dominant, Ninth),
&[4, 3, 4, 3] => (Major, Ninth),
&[4, 3, 3, 4, 4] => (Dominant, Eleventh),
&[4, 3, 4, 3, 3] => (Major, Eleventh),
&[3, 4, 3, 4, 3] => (Minor, Eleventh),
&[4, 3, 3, 4, 3, 4] => (Dominant, Thirteenth),
&[4, 3, 4, 3, 3, 4] => (Major, Thirteenth),
&[3, 4, 3, 4, 3, 4] => (Minor, Thirteenth),
_ => panic!(format!("Couldn't create chord! {:?}", interval))
};
Self::new(root, quality, number)
}

pub fn chord_intervals(quality: Quality, number: Number) -> Vec<Interval> {
use Number::*;
use Quality::*;
match (&quality, &number) {
(Major, Triad) => Interval::from_semitones(&[4, 3]),
(Minor, Triad) => Interval::from_semitones(&[3, 4]),
(Suspended2, Triad) => Interval::from_semitones(&[2, 5]),
(Suspended4, Triad) => Interval::from_semitones(&[5, 7]),
(Suspended4, Triad) => Interval::from_semitones(&[5, 2]),
(Augmented, Triad) => Interval::from_semitones(&[4, 4]),
(Diminished, Triad) => Interval::from_semitones(&[3, 3]),
(Major, Seventh) => Interval::from_semitones(&[4, 3, 4]),
Expand Down
29 changes: 29 additions & 0 deletions tests/chord/test_chord.rs
Expand Up @@ -19,6 +19,8 @@ mod chord_tests {
((C, Minor, Triad), vec![C, Ds, G]),
((C, Augmented, Triad), vec![C, E, Gs]),
((C, Diminished, Triad), vec![C, Ds, Fs]),
((C, Suspended2, Triad), vec![C, D, G]),
((C, Suspended4, Triad), vec![C, F, G]),
((C, Major, Seventh), vec![C, E, G, B]),
((C, Minor, Seventh), vec![C, Ds, G, As]),
((C, Augmented, Seventh), vec![C, E, Gs, As]),
Expand Down Expand Up @@ -87,4 +89,31 @@ mod chord_tests {
assert_eq!(chord.inversion, 2);
assert_eq!(chord_num.inversion, 2);
}

#[test]
fn test_chord_from_string() {
let c = Pitch::from_str("C").unwrap();
let chord_tuples = [
((c, Major, Triad), "C E G"),
((c, Minor, Triad), "C Ds G"),
((c, Augmented, Triad), "C E Gs"),
((c, Diminished, Triad), "C Ds Fs"),
((c, Suspended2, Triad), "C D G"),
((c, Suspended4, Triad), "C F G"),
((c, Major, Seventh), "C E G B"),
((c, Minor, Seventh), "C Ds G As"),
((c, Augmented, Seventh), "C E Gs As"),
((c, Augmented, MajorSeventh), "C, E, Gs, B"),
((c, Diminished, Seventh), "C, Ds, Fs, A"),
((c, HalfDiminished, Seventh), "C, Ds, Fs, As"),
((c, Minor, MajorSeventh), "C, Ds, G, B"),
((c, Dominant, Seventh), "C, E, G, As"),
];

for chord_pair in chord_tuples.iter() {
let chord = Chord::from_string(chord_pair.1);
let (root, quality, number) = (chord.root, chord.quality, chord.number);
assert_eq!((root, quality, number), (chord_pair.0));
}
}
}

0 comments on commit 2e09fa3

Please sign in to comment.