@@ -20,30 +20,35 @@ impl fmt::Display for ParseChordError {
2020 }
2121}
2222
23- /// Chord quality.
24- /// https://en.wikipedia.org/wiki/Chord_names_and_symbols_(popular_music)#Chord_quality
23+ /// The type of the chord depending on the intervals it contains.
2524#[ derive( Debug , Clone , Copy , PartialEq ) ]
26- pub enum ChordQuality {
25+ pub enum ChordType {
2726 Major ,
2827 Minor ,
28+ DominantSeventh ,
29+ MinorSeventh ,
2930}
3031
31- impl ChordQuality {
32+ impl ChordType {
3233 fn get_intervals ( self ) -> Vec < Interval > {
3334 use Interval :: * ;
3435
3536 match self {
3637 Self :: Major => vec ! [ PerfectUnison , MajorThird , PerfectFifth ] ,
3738 Self :: Minor => vec ! [ PerfectUnison , MinorThird , PerfectFifth ] ,
39+ Self :: DominantSeventh => vec ! [ PerfectUnison , MajorThird , PerfectFifth , MinorSeventh ] ,
40+ Self :: MinorSeventh => vec ! [ PerfectUnison , MinorThird , PerfectFifth , MinorSeventh ] ,
3841 }
3942 }
4043}
4144
42- impl fmt:: Display for ChordQuality {
45+ impl fmt:: Display for ChordType {
4346 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
4447 let s = match self {
4548 Self :: Major => "major" ,
4649 Self :: Minor => "minor" ,
50+ Self :: DominantSeventh => "dominant 7th" ,
51+ Self :: MinorSeventh => "minor 7th" ,
4752 } ;
4853
4954 write ! ( f, "{}" , s)
@@ -53,7 +58,7 @@ impl fmt::Display for ChordQuality {
5358/// A chord such as C, Cm and so on.
5459pub struct Chord {
5560 name : String ,
56- pub quality : ChordQuality ,
61+ pub chord_type : ChordType ,
5762 pub root : Note ,
5863 notes : Vec < Note > ,
5964}
@@ -64,7 +69,7 @@ impl Chord {
6469 }
6570
6671 pub fn get_diagram ( self , min_fret : FretID ) -> ChordDiagram {
67- let chord_shapes = ChordShapeSet :: new ( self . quality ) ;
72+ let chord_shapes = ChordShapeSet :: new ( self . chord_type ) ;
6873
6974 let ( frets, intervals) = chord_shapes. get_config ( self . root , min_fret) ;
7075
@@ -80,7 +85,7 @@ impl Chord {
8085
8186impl fmt:: Display for Chord {
8287 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
83- write ! ( f, "{} - {} {}" , self . name, self . notes[ 0 ] , self . quality )
88+ write ! ( f, "{} - {} {}" , self . name, self . notes[ 0 ] , self . chord_type )
8489 }
8590}
8691
@@ -91,7 +96,7 @@ impl FromStr for Chord {
9196 let name = s. to_string ( ) ;
9297
9398 // Regular expression for chord names.
94- let re = Regex :: new ( r"(?P<root>[CDEFGAB][#b]?)(?P<quality>m ?)" ) . unwrap ( ) ;
99+ let re = Regex :: new ( r"(?P<root>[CDEFGAB][#b]?)(?P<type>m?7 ?)" ) . unwrap ( ) ;
95100
96101 // Match regex.
97102 let caps = match re. captures ( s) {
@@ -105,30 +110,33 @@ impl FromStr for Chord {
105110 Err ( _) => return Err ( ParseChordError { name } ) ,
106111 } ;
107112
108- // Get chord quality.
109- let quality = match & caps[ "quality" ] {
110- "m" => ChordQuality :: Minor ,
111- _ => ChordQuality :: Major ,
113+ // Get chord type.
114+ let chord_type = match & caps[ "type" ] {
115+ "m" => ChordType :: Minor ,
116+ "7" => ChordType :: DominantSeventh ,
117+ "m7" => ChordType :: MinorSeventh ,
118+ _ => ChordType :: Major ,
112119 } ;
113120
114121 // Collect notes of the chord.
115122 let mut notes = vec ! [ ] ;
116123
117- for interval in quality . get_intervals ( ) {
124+ for interval in chord_type . get_intervals ( ) {
118125 notes. push ( root + interval) ;
119126 }
120127
121128 Ok ( Self {
122129 name,
123130 root,
124- quality ,
131+ chord_type ,
125132 notes,
126133 } )
127134 }
128135}
129136
130137#[ cfg( test) ]
131138mod tests {
139+ #![ allow( clippy:: many_single_char_names) ]
132140 use super :: * ;
133141 use rstest:: rstest_parametrize;
134142
@@ -161,7 +169,7 @@ mod tests {
161169 let t = Note :: from_str ( third) . unwrap ( ) ;
162170 let f = Note :: from_str ( fifth) . unwrap ( ) ;
163171 assert_eq ! ( c. notes, vec![ r, t, f] ) ;
164- assert_eq ! ( c. quality , ChordQuality :: Major ) ;
172+ assert_eq ! ( c. chord_type , ChordType :: Major ) ;
165173 }
166174
167175 #[ rstest_parametrize(
@@ -193,7 +201,87 @@ mod tests {
193201 let t = Note :: from_str ( third) . unwrap ( ) ;
194202 let f = Note :: from_str ( fifth) . unwrap ( ) ;
195203 assert_eq ! ( c. notes, vec![ r, t, f] ) ;
196- assert_eq ! ( c. quality, ChordQuality :: Minor ) ;
204+ assert_eq ! ( c. chord_type, ChordType :: Minor ) ;
205+ }
206+
207+ #[ rstest_parametrize(
208+ chord,
209+ root,
210+ third,
211+ fifth,
212+ seventh,
213+ case( "C7" , "C" , "E" , "G" , "Bb" ) ,
214+ case( "C#7" , "C#" , "F" , "G#" , "B" ) ,
215+ case( "Db7" , "Db" , "F" , "Ab" , "B" ) ,
216+ case( "D7" , "D" , "F#" , "A" , "C" ) ,
217+ case( "D#7" , "D#" , "G" , "A#" , "C#" ) ,
218+ case( "Eb7" , "Eb" , "G" , "Bb" , "Db" ) ,
219+ case( "E7" , "E" , "G#" , "B" , "D" ) ,
220+ case( "F7" , "F" , "A" , "C" , "Eb" ) ,
221+ case( "F#7" , "F#" , "A#" , "C#" , "E" ) ,
222+ case( "Gb7" , "Gb" , "Bb" , "Db" , "E" ) ,
223+ case( "G7" , "G" , "B" , "D" , "F" ) ,
224+ case( "G#7" , "G#" , "C" , "D#" , "F#" ) ,
225+ case( "Ab7" , "Ab" , "C" , "Eb" , "Gb" ) ,
226+ case( "A7" , "A" , "C#" , "E" , "G" ) ,
227+ case( "A#7" , "A#" , "D" , "F" , "G#" ) ,
228+ case( "Bb7" , "Bb" , "D" , "F" , "Ab" ) ,
229+ case( "B7" , "B" , "D#" , "F#" , "A" )
230+ ) ]
231+ fn test_from_str_dominant_seventh (
232+ chord : & str ,
233+ root : & str ,
234+ third : & str ,
235+ fifth : & str ,
236+ seventh : & str ,
237+ ) {
238+ let c = Chord :: from_str ( chord) . unwrap ( ) ;
239+ let r = Note :: from_str ( root) . unwrap ( ) ;
240+ let t = Note :: from_str ( third) . unwrap ( ) ;
241+ let f = Note :: from_str ( fifth) . unwrap ( ) ;
242+ let s = Note :: from_str ( seventh) . unwrap ( ) ;
243+ assert_eq ! ( c. notes, vec![ r, t, f, s] ) ;
244+ assert_eq ! ( c. chord_type, ChordType :: DominantSeventh ) ;
245+ }
246+
247+ #[ rstest_parametrize(
248+ chord,
249+ root,
250+ third,
251+ fifth,
252+ seventh,
253+ case( "Cm7" , "C" , "Eb" , "G" , "Bb" ) ,
254+ case( "C#m7" , "C#" , "E" , "G#" , "B" ) ,
255+ case( "Dbm7" , "Db" , "E" , "Ab" , "B" ) ,
256+ case( "Dm7" , "D" , "F" , "A" , "C" ) ,
257+ case( "D#m7" , "D#" , "F#" , "A#" , "C#" ) ,
258+ case( "Ebm7" , "Eb" , "Gb" , "Bb" , "Db" ) ,
259+ case( "Em7" , "E" , "G" , "B" , "D" ) ,
260+ case( "Fm7" , "F" , "Ab" , "C" , "Eb" ) ,
261+ case( "F#m7" , "F#" , "A" , "C#" , "E" ) ,
262+ case( "Gbm7" , "Gb" , "A" , "Db" , "E" ) ,
263+ case( "Gm7" , "G" , "Bb" , "D" , "F" ) ,
264+ case( "G#m7" , "G#" , "B" , "D#" , "F#" ) ,
265+ case( "Abm7" , "Ab" , "B" , "Eb" , "Gb" ) ,
266+ case( "Am7" , "A" , "C" , "E" , "G" ) ,
267+ case( "A#m7" , "A#" , "C#" , "F" , "G#" ) ,
268+ case( "Bbm7" , "Bb" , "Db" , "F" , "Ab" ) ,
269+ case( "Bm7" , "B" , "D" , "F#" , "A" )
270+ ) ]
271+ fn test_from_str_minor_seventh (
272+ chord : & str ,
273+ root : & str ,
274+ third : & str ,
275+ fifth : & str ,
276+ seventh : & str ,
277+ ) {
278+ let c = Chord :: from_str ( chord) . unwrap ( ) ;
279+ let r = Note :: from_str ( root) . unwrap ( ) ;
280+ let t = Note :: from_str ( third) . unwrap ( ) ;
281+ let f = Note :: from_str ( fifth) . unwrap ( ) ;
282+ let s = Note :: from_str ( seventh) . unwrap ( ) ;
283+ assert_eq ! ( c. notes, vec![ r, t, f, s] ) ;
284+ assert_eq ! ( c. chord_type, ChordType :: MinorSeventh ) ;
197285 }
198286
199287 #[ rstest_parametrize(
0 commit comments