Mathematica function SoundNote
in conjunction with Sound
is capable of playing a wide range of notes in different musical instruments. But unfortunately, you can't specify chords in SoundNote
.
In this post, I'll share a simple code of mine to show the possibilities of implementing this.
Before jumping into chords, first, we'll create functions that operate on notes. I find it useful to delimit the operational behavior of the code before implementing it. Bellow is shown what the function Note
will accomplish.
NoteQ[note:_Integer | _String] (* test if "note" is valid *)
Note[note:_String | {__String}, "Number"] (* return the "number"-code of the note. C4 = 0, D5=14, etc. *)
Note[numb:_Integer | {__Integer}, "Name"] (* return the name of the note with number code "numb" *)
And now for the real code:
Note$RegExp := "^([A-G])(#|b)?(-1|[0-9])?$" (* Regular Expression to match notes *)
NoteQ[note_String] := StringMatchQ[note, RegularExpression@Note$RegExp]
NoteQ[note_Integer] := -60 <= note <= 67 (* This range is taken from SoundNote *)
Note[note_String?NoteQ, "Number"] := Block[{r, sf, n},
(* Divide the note into its "components" given by the RegExp *)
{r, sf, n} = First@StringCases[note, RegularExpression@Note$RegExp :> {"$1", "$2", "$3"}];
<|"C"->0,"D"->2,"E"->4,"F"->5,"G"->7,"A"->9,"B"->11|>[r] + (* map the base not A-G *)
Which[sf=="#",1, sf=="b", -1, True, 0] + (* Map sharp and flats *)
If[n!="", (ToExpression@n - 4)*12, 0]] (* Map octave *)
(* The opposite of what was previously defined *)
Note[number_Integer?NoteQ, "Name"] := <|0->"C",1->"C#",2->"D",3->"D#",4->"E",5->"F",
6->"F#",7->"G",8->"G#",9->"A",10->"A#",11->"B"|>[Mod[number, 12]] <> ToString[4 + Floor[number/12]]
(* To create chords we'll need to transpose the root note *)
Note[number:_Integer?NoteQ|{__Integer?NoteQ}, transp_Integer, "Transposition"] /; NoteQ@Max[transp+transp] \[And] NoteQ@Min[transp+transp] := number + transp
Note[note:_String?NoteQ|{__String?NoteQ}, transp_Integer, "Transposition"] := Note[Note[Note[note, "Number"], transp, "Transposition"], "Name"]
(* "Vectorize" the inputs *)
Note[notes:{__String?NoteQ}, "Number"] := Note[#, "Number"] & /@ notes
Note[numbers:{__Integer?NoteQ}, "Name"] := Note[#, "Name"] & /@ numbers
Note[note:_Integer?NoteQ | _String?NoteQ, transp:{__Integer}, "Transposition"] := Note[note, #, "Transposition"] & /@ transp
Now we can define a Chord
function.
Chord[chord_String, "Root"] (* Give the root note for the chord. Caug13 -> C *)
Chord[chord_String, "Type"] (* return the quality/type of chord, Major, Minor, etc. *)
Chord[chord_String, "Number"] (* Return a list with the number-form of the notes in the chord *)
Chord[chord_String, "Notes"] (* The actual notes of that chord *)
Now for the real code:
Chord[chord_String /; StringLength@chord >= 2, "Root"] := If[MemberQ[{"#","b"}, StringTake[chord, {2}]], StringTake[chord, 2], StringTake[chord, 1]]
Chord[chord_String, "Notes"] := Note[Chord[chord, "Root"], Chord[Chord[chord, "Type"], "Number"], "Transposition"]
Chord[notes : {__String}, "Notes"] := Chord[#, "Notes"] & /@ notes
Chord[chord_String /; StringLength@chord >= 2, "Type"] :=
Block[{rest = StringReplace[chord, Chord[chord, "Root"] -> ""]},
Which[
StringMatchQ[rest, RegularExpression@"^(M|maj)?$"], "Major",
StringMatchQ[rest, RegularExpression@"^(m|min)$"], "Minor",
StringMatchQ[rest, RegularExpression@"^(aug|[+])$"], "Augmented",
StringMatchQ[rest, RegularExpression@"^(dim|o)$"], "Diminished",
StringMatchQ[rest, RegularExpression@"^(M|maj)6$"], "Major Sixth",
StringMatchQ[rest, RegularExpression@"^(m|min)6$"], "Minor Sixth",
StringMatchQ[rest, RegularExpression@"^(7|dom7)$"], "Dominant Seventh",
StringMatchQ[rest, RegularExpression@"^(M|maj)7$"], "Major Seventh",
StringMatchQ[rest, RegularExpression@"^(m|min)7$"], "Minor Seventh",
StringMatchQ[rest, RegularExpression@"^(aug|[+])7$"], "Augmented Seventh",
StringMatchQ[rest, RegularExpression@"^(dim|o)7$"], "Diminished Seventh",
StringMatchQ[rest, RegularExpression@"^(0)7$"], "Half Diminished Seventh",
StringMatchQ[rest, RegularExpression@"^(m/M7)$"], "Minor/Major Seventh",
StringMatchQ[rest, RegularExpression@"^(M|maj)7[+]5$"], "Augmented Major Seventh",
StringMatchQ[rest, RegularExpression@"^(maj7[-]5)$"], "Dominant Seventh Flat Five Chord",
StringMatchQ[rest, RegularExpression@"^(9|dom9)$"], "Dominant Ninth",
StringMatchQ[rest, RegularExpression@"^(M|maj)9$"], "Major Ninth",
StringMatchQ[rest, RegularExpression@"^(m|min)9$"], "Minor Dominant Ninth",
StringMatchQ[rest, RegularExpression@"^[+](M|maj)9$"], "Augmented Major Ninth",
StringMatchQ[rest, RegularExpression@"^(aug|[+])9$"], "Augmented Dominant Ninth",
StringMatchQ[rest, RegularExpression@"^(m/M9)$"], "Minor/Major Ninth",
StringMatchQ[rest, RegularExpression@"^(0)9$"], "Half Diminished Ninth",
StringMatchQ[rest, RegularExpression@"^(0b)9$"], "Half Diminished Minor Ninth",
StringMatchQ[rest, RegularExpression@"^(o|dim)9$"], "Diminished Ninth",
StringMatchQ[rest, RegularExpression@"^(ob)9$"], "Diminished Minor Ninth",
StringMatchQ[rest, RegularExpression@"^(11|dom11)$"], "Dominant Eleventh",
StringMatchQ[rest, RegularExpression@"^(M|maj)11$"], "Major Eleventh",
StringMatchQ[rest, RegularExpression@"^(m/M11)$"], "Minor/Major Eleventh",
StringMatchQ[rest, RegularExpression@"^(m|min)11$"], "Minor Eleventh",
StringMatchQ[rest, RegularExpression@"^[+](M|maj)11$"], "Augmented Major Eleventh",
StringMatchQ[rest, RegularExpression@"^(aug|[+])11$"], "Augmented Eleventh",
StringMatchQ[rest, RegularExpression@"^(0)11$"], "Half Diminished Eleventh",
StringMatchQ[rest, RegularExpression@"^(dim|o)11$"], "Diminished Eleventh",
StringMatchQ[rest, RegularExpression@"^(M|maj)13$"], "Major Thirteenth",
StringMatchQ[rest, RegularExpression@"^(13|dom13)$"], "Dominant Thirteenth",
StringMatchQ[rest, RegularExpression@"^(m/M13)$"], "Minor/Major Thirteenth",
StringMatchQ[rest, RegularExpression@"^(m|min)13$"], "Minor Dominant Thirteenth",
StringMatchQ[rest, RegularExpression@"^[+](M|maj)13$"], "Augmented Major Thirteenth",
StringMatchQ[rest, RegularExpression@"^(aug|[+])13$"], "Augmented Dominant Thirteenth",
StringMatchQ[rest, RegularExpression@"^(0)13$"], "Half Diminished Thirteenth",
True, $Failed
]
]
(* Chords *)
Chord["Major", "Number"] := {0, 4, 7}
Chord["Minor", "Number"] := {0, 3, 7}
Chord["Augmented", "Number"] := {0, 4, 8}
Chord["Diminished", "Number"] := {0, 3, 6}
Chord["Major Sixth", "Number"] := {0, 4, 7, 9}
Chord["Minor Sixth", "Number"] := {0, 3, 7, 9}
Chord["Dominant Seventh", "Number"] := {0, 4, 7, 10}
Chord["Major Seventh", "Number"] := {0, 4, 7, 11}
Chord["Minor Seventh", "Number"] := {0, 3, 7, 10}
Chord["Augmented Seventh", "Number"] := {0, 4, 8, 10}
Chord["Diminished Seventh", "Number"] := {0, 3, 6, 9}
Chord["Half Diminished Seventh", "Number"] := {0, 3, 6, 10}
Chord["Minor/Major Seventh", "Number"] := {0, 3, 7, 11}
Chord["Augmented Major Seventh", "Number"] := {0, 4, 8, 11}
Chord["Dominant Seventh Flat Five", "Number"] := {0, 4, 6, 10}
Chord["Major Ninth", "Number"] := {0, 4, 7, 11, 14}
Chord["Dominant Ninth", "Number"] := {0, 4, 7, 10, 14}
Chord["Minor/Major Ninth", "Number"] := {0, 3, 7, 11, 14}
Chord["Minor Dominant Ninth", "Number"] := {0, 3, 7, 10, 14}
Chord["Augmented Major Ninth", "Number"] := {0, 4, 8, 11, 14}
Chord["Augmented Dominant Ninth", "Number"] := {0, 4, 8, 10, 14}
Chord["Half Diminished Ninth", "Number"] := {0, 3, 6, 10, 14}
Chord["Half Diminished Minor Ninth", "Number"] := {0, 3, 6, 10, 13}
Chord["Diminished Ninth", "Number"] := {0, 3, 6, 10, 14}
Chord["Diminished Minor Ninth", "Number"] := {0, 3, 6, 9, 13}
Chord["Dominant Eleventh", "Number"] := {0, 4, 7, 10, 14, 17}
Chord["Major Eleventh", "Number"] := {0, 4, 7, 11, 14, 17}
Chord["Minor/Major Eleventh", "Number"] := {0, 3, 7, 11, 14, 17}
Chord["Minor Eleventh", "Number"] := {0, 3, 7, 10, 14, 17}
Chord["Augmented Major Eleventh", "Number"] := {0, 4, 8, 11, 14, 17}
Chord["Augmented Eleventh", "Number"] := {0, 4, 8, 10, 14, 17}
Chord["Half Diminished Eleventh", "Number"] :={0, 3, 6, 10, 13, 17}
Chord["Diminished Eleventh", "Number"] := {0, 3, 6, 2, 13, 16}
Chord["Major Thirteenth", "Number"] := {0, 4, 7, 11, 2, 17, 21}
Chord["Dominant Thirteenth", "Number"] := {0, 4, 7, 10, 14, 17, 21}
Chord["Minor/Major Thirteenth", "Number"] := {0, 3, 7, 11, 14, 17, 21}
Chord["Minor Dominant Thirteenth", "Number"] := {0, 3, 7, 10, 14, 17, 21}
Chord["Augmented Major Thirteenth", "Number"] := {0, 4, 8, 11, 14, 17, 21}
Chord["Augmented Dominant Thirteenth", "Number"] := {0, 4, 8, 10, 14, 17, 21}
Chord["Half Diminished Thirteenth", "Number"] := {0, 3, 6, 10, 14, 17, 21}
And real-life "application":
SoundNote[#, 0.5] & /@ Chord[StringSplit["C Dm7 Fmaj7 Dm F C Gm Dm Gm F C", " "],"Notes"] // Sound
You can listen it:
Note: Full-disclaimer, I'm not a musician, the only instrument I play is the flute and I'm self-taught, hence I know next to nothing about chords. All my knowledge came from wikipedia: https://en.wikipedia.org/wiki/Chord_names_and_symbols_(popular_music). There are possibily many wrong things or bugs, and the code could be more efficient and less cumbersome.. The main goal of this post is to share the idea.