Music Composition Tools

Introduction and Setup

This notebook extends the Miscellaneous`Music package by providing the tunePlay function for playing simple tunes (one or more notes at a time), and several functions for working out retrogrades, inversions, transpositions and other transformations likely to be used in atonal composition in the style of Schönberg. In addition , it adds a number of interval lists representing particular chord structures, suitable for input into either the Scale[] function (from the Music package), or the ShowChord/ ShowChordNames functions which just show the note values.  Also included are sets of replacement rules to show sharps and flats (black notes on the piano keyboard) with the names appropriate to a particular key.

Needs["Miscellaneous`Music`"]

Formatting Rules

\(\(allnotes = 
        ToExpression /@ \ 
          Flatten[Outer[
              StringJoin, {"A", "Asharp", "Bflat", "B", \ "Bsharp", "Cflat", "C", "Csharp", "Dflat", "D", "Dsharp", "Eflat", "E", "Esharp", "Fflat", "F", "Fsharp", "Gflat", "G", "Gsharp", "Aflat"}, 
              ToString\  /@ \ Range[0, 7]]];\)\)

\(\(noteStrings = \ 
        Flatten[Outer[
            StringJoin, {"A", "Asharp", "Bflat", "B", \ "Bsharp", "Cflat", "C", "Csharp", "Dflat", "D", "Dsharp", "Eflat", "E", "Esharp", "Fflat", "F", "Fsharp", "Gflat", "G", "Gsharp", "Aflat"}, 
            ToString\  /@ \ Range[0, 7]]];\)\)

noteRules = 
        Thread[Round[1000  allnotes]/1000 -> noteStrings];

Note that these rules round the Hertz values for various notes to three decimal places. This ensures that small numerical round-off errors do not prevent particular notes from being recognised and named.

\(\(sensibleNoteRules = 
        Join[Thread[\((\(StringJoin["Bsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["C", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Cflat", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["B", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Esharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["F", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Fflat", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["E", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

\(\(flatNoteRules = 
        Join[Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Bsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["C", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Csharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Dflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Dsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Eflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Esharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["F", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Fsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Gflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Gsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Aflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

The noteRules are set up to generate sharps not flats.  Apply these rules last to get note names compatible with particular keys with flats in them.

\(\(keyNoteRules["F"] = 
        Join[sensibleNoteRules, 
          Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

\(\(keyNoteRules["Aflat"] = 
        Join[sensibleNoteRules, 
          Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Dsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Eflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Gsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Aflat", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Csharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Dflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

\(\(keyNoteRules["Bflat"] = 
        Join[sensibleNoteRules, 
          Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Dsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Eflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

\(\(keyNoteRules["Dflat"] = 
        Join[sensibleNoteRules, 
          Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Dsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Eflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Gsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Aflat", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Csharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Dflat", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Fsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Gflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

\(\(keyNoteRules["Eflat"] = 
        Join[sensibleNoteRules, 
          Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Dsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Eflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Gsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Aflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

\(\(keyNoteRules["Gflat"] = 
        Join[sensibleNoteRules, 
          Thread[\((\(StringJoin["Asharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Bflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Dsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Eflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)], 
          Thread[\((\(StringJoin["Gsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Aflat", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Csharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Dflat", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["Fsharp", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Gflat", #] &\) /@ \ \((ToString\  /@ Range[0, 
                        7])\))\)], 
		Thread[\((\(StringJoin["B", #] &\) /@ \ \((ToString\  /@ 
                      Range[0, 
                        7])\))\) -> \((\(StringJoin["Cflat", #] &\) /@ \ \((ToString\  /@ Range[0, 7])\))\)]];\)\)

tunePlay

This function plays monophonic or polyphonic tunes, with the rhythm (note values) as given in the second column of the matrix.

tunePlay::usage = "tunePlay[{{note,length}..}] plays a tune comprised \
a sequence of notes with the pitches defined in the first column of a \
2-column matrix, and the lengths defined in the second column. The options \
bpm defines the tempo. tunePlay[{{{notes..},length}..}] plays a polyphonic \
tune."

tunePlay::notnote = "The note names `1` are not recognised note \
names.  Please check the spelling of the notes, or add the new note names and \
definitions to the allnotes and noteRules objects."

tunePlay::notpos = "The note frequencies `1` are not positive.  \
Please check the spelling of the notes, or add the new note names and \
definitions to the allnotes and noteRules objects."

bpm::usage = "bpm is an option to tunePlay that defines the tempo in \
beats per minute. The default is 120 bpm."

Options[tunePlay] = {bpm -> 120}

\(\(\(tunePlay[tune : {{{__?Positive}, _} .. }, 
        opts___Rule]\)\(:=\)\(
\)\(	\)\(With[{unit = 
            N[\(bpm /. {opts}\) /. Options[tunePlay]]/
              60. , 
			notenames = 
            Evaluate[\(Transpose[
                  tune]\)[[1]]], 
          notelengths = \(Transpose[
                tune]\)[[2]]}, 

        With[{ts = FoldList[Plus, 0. , Drop[notelengths, \(-1\)]]/unit, 
            tf = FoldList[Plus, First[notelengths], Rest[notelengths]]/
                unit}, 
			With[{sinelist = 
                Transpose[{Apply[Plus, 
                      Map[Sin[2\ π\ t\ #] &\ , \ notenames, {2}], {1}], 
                    ts, tf}]}, 
		Play[
              Fold[If[#2[[2]] < 
                      t < #2[[3]], 
                    Evaluate[#2[[1]]], #1] &, Evaluate[\(Last[
                      sinelist]\)[[1]]], 
                Rest[Reverse[sinelist]]], {t, 0, 
                Last[tf]}]
]]]\)\(\ \)\)\)

\(tunePlay[tune : {{{__?NumericQ}, _} .. }, opts___Rule] := 
	
      With[{
			notenames = 
            Evaluate[\(Transpose[
                  tune]\)[[1]]]}, 
	\ Message[tunePlay::notpos, 
          ToString[
            DeleteCases[Flatten[notenames], _?Positive\ ]]\ ]\ ]\)

\(tunePlay[tune : {{{__String}, _} .. }, opts___Rule] := 
	
      With[{unit = 
            N[\(bpm /. {opts}\) /. Options[tunePlay]]/
              60. , 
			notenames = 
            Evaluate[
              Map[ToExpression, \(Transpose[
                    tune]\)[[1]], {2}]], 
          notelengths = \(Transpose[
                tune]\)[[2]]}, 		If[
          And[And\  @@ \((NumericQ\  /@ Flatten[notenames])\), 
            And @@ \((Positive\  /@ Flatten[notenames])\)], 
          With[{ts = FoldList[Plus, 0. , Drop[notelengths, \(-1\)]]/unit, 
              tf = FoldList[Plus, First[notelengths], Rest[notelengths]]/
                  unit}, 
			With[{sinelist = 
                  Transpose[{Apply[Plus, 
                        Map[Sin[2\ π\ t\ #] &\ , \ notenames, {2}], {1}], 
                      ts, tf}]}, 
		Play[
                Fold[If[#2[[2]] < 
                        t < #2[[3]], 
                      Evaluate[#2[[1]]], #1] &, Evaluate[\(Last[
                        sinelist]\)[[1]]], Rest[Reverse[sinelist]]], {t, 0, Last[tf]}]
		]], 
          Message[tunePlay::notnote, 
            ToString[
              DeleteCases[Flatten[notenames], _?Positive]]\ ]]]\)

Monophonic Cases

\(tunePlay[tune : {{_?NumericQ, _} .. }, opts___Rule] := 
	
      With[{unit = 
            N[\(bpm /. {opts}\) /. Options[tunePlay]]/
              60. , 
			notenames = 
            Evaluate[\(Transpose[
                  tune]\)[[1]]], 
          notelengths = \(Transpose[
                tune]\)[[2]]}, 	
        If[And @@ \((Evaluate[Positive\  /@ \ Flatten[notenames]])\), 
          With[{ts = FoldList[Plus, 0. , Drop[notelengths, \(-1\)]]/unit, 
              tf = FoldList[Plus, First[notelengths], Rest[notelengths]]/
                  unit}, 
			With[{sinelist = 
                  Transpose[{\(Sin[2\ π\ t\ #] &\)\  /@ \ notenames, ts, 
                      tf}]}, 
		Play[
                Fold[If[#2[[2]] < 
                        t < #2[[3]], 
                      Evaluate[#2[[1]]], #1] &, Evaluate[\(Last[
                        sinelist]\)[[1]]], Rest[Reverse[sinelist]]], {t, 0, Last[tf]}]]], 
          Message[tunePlay::notpos, 
            ToString[
              DeleteCases[
                Flatten[notenames], _?Positive]]\ ]]
	]\)

\(tunePlay[tune : {{_String, _} .. }, opts___Rule] := 
	
      With[{unit = 
            N[\(bpm /. {opts}\) /. Options[tunePlay]]/
              60. , 
			notenames = 
            Evaluate[
              ToExpression /@ \((\(Transpose[
                      tune]\)[[1]])\)], 
          notelengths = \(Transpose[
                tune]\)[[2]]}, 
		If[
          And[And\  @@ \((NumericQ\  /@ Flatten[notenames])\), 
            And @@ \((Positive\  /@ Flatten[notenames])\)], 
          With[{ts = FoldList[Plus, 0. , Drop[notelengths, \(-1\)]]/unit, 
              tf = FoldList[Plus, First[notelengths], Rest[notelengths]]/
                  unit}, 
			With[{sinelist = 
                  Transpose[{\(Sin[2\ π\ t\ #] &\)\  /@ \ notenames, ts, 
                      tf}]}, 
		Play[
                Fold[If[#2[[2]] < 
                        t < #2[[3]], 
                      Evaluate[#2[[1]]], #1] &, Evaluate[\(Last[
                        sinelist]\)[[1]]], Rest[Reverse[sinelist]]], {t, 0, Last[tf]}]]], 
          Message[tunePlay::notnote, 
            ToString[
              DeleteCases[
                Flatten[notenames], _?Positive]]\ ]]
	]\)

tunePlay[tune : {__String}, opts___Rule] := 
      tunePlay[Transpose[{tune, Table[1, {Length[tune]}]}], opts]

tunePlay[tune : {__?Positive}, opts___Rule] := 
      tunePlay[Transpose[{tune, Table[1, {Length[tune]}]}], opts]

tunePlay[tune : {__?NumericQ}, opts___Rule] := 
      Message[tunePlay::notnote, 
        ToString[DeleteCases[Flatten[tune], _?Positive] ]]

Transformations

Error messages

moremusic::notpos = "The note frequencies `1` are not positive.  \
Please check the spelling of the notes, or add the new note names and \
definitions to the allnotes and noteRules objects."

moremusic::notnote = "The note names `1` are not recognised note \
names.  Please check the spelling of the notes, or add the new note names and \
definitions to the allnotes and noteRules objects."

HertzToNoteNames

HertzToNoteNames::usage = "HertzToNoteNames[list] converts positive \
real numbers corresponding to the frequencies of the equal-temperament notes \
defined in the Miscellanous`Audio package into the equivalent names defined \
as strings."

HertzToNoteNames[
        hertzlist : {__Real?Positive}] :=  (Round[1000  hertzlist]/
              1000)  /. noteRules  /. sensiblenoterules

\(HertzToNoteNames[hertzlist : {__?NumericQ}] := \ 

      Message[moremusic::notpos, 
        ToString[DeleteCases[Flatten[hertzlist], _?Positive]]\ ]\)

noteTranspose

noteTranspose::usage  =  "noteTranspose[notes,n] transposes a list \
of notes up or down by n semitones (down if n is negative)."

\(noteTranspose[notes : {__?Positive}, 
        semitones_Integer] := 
	\(\((Round[
                1000  CentsToHertz[FoldList[Plus, \ 0, HertzToCents[notes]], 
                    Last[CentsToHertz[{0, 100. \ \ semitones}, 
                        First[notes]]\ ]\ ]]/1000)\) /. 
		noteRules\) /. 
        sensibleNoteRules\)

noteTranspose[notes : {__?NumericQ}, semitones_Integer] := 
      Message[moremusic::notpos, 
        ToString[DeleteCases[Flatten[notes], _?Positive]] ]

Retrograde

Retrograde::usage = "Retrograde[{notes}] reverses a sequence of hertz \
or string-valued note names, that is, it plays them backwards. This is useful \
for atonal composition in the style of Schoenberg."

\(Retrograde[
        notes : {__?Positive}] := \(Reverse[
            Round[1000  notes]/1000] /. 
		noteRules\) /. 
        sensibleNoteRules\)

Retrograde[notes : {__?NumericQ}] := 
      Message[moremusic::notpos, 
        ToString[DeleteCases[Flatten[notes], _?Positive]] ]

\(Retrograde[notes : {__String}] := \ 
      With[{newnotes = ToExpression\  /@ \ notes}, 
		If[
          And[And\  @@ \((NumericQ\  /@ Flatten[newnotes])\), 
            And @@ \((Positive\  /@ Flatten[newnotes])\)], 
          Retrograde[newnotes], 
          Message[moremusic::notnote, 
            ToString[
              DeleteCases[Flatten[newnotes], _?Positive]]\ ]\ ]\ ]\)

Inversion

Inversion::usage = " Inversion[{notes}] inverts a sequence of hertz \
or string-valued note names, that is, it reverses the direction of the \
corresponding intervals between the notes, and then outputs the resulting \
sequence of notes. This is useful for atonal composition in the style of \
Schoenberg."

\(Inversion[
        notes : {__?
              Positive}] := \(\((Round[
                1000  CentsToHertz[\(-FoldList[Plus, \ 0, 
                        HertzToCents[notes]]\), 
                    Last[CentsToHertz[{0, 0}, First[notes]]\ ]\ ]]/
              1000)\) /. 
		noteRules\) /. sensibleNoteRules\)

Inversion[notes : {__?NumericQ}] := 
      Message[moremusic::notpos, 
        ToString[DeleteCases[Flatten[notes], _?Positive]] ]

Inversion[notes : {__String}] := 
      With[{newnotes = ToExpression  /@  notes}, 
        If[And[And  @@ (NumericQ  /@ Flatten[newnotes]), 
            And @@ (Positive  /@ Flatten[newnotes])], 
          Inversion[newnotes], 
          Message[moremusic::notnote, 
            ToString[
              DeleteCases[Flatten[newnotes], _?Positive]] ] ] ]

RetrogradeInversion

RetrogradeInversion::usage = "RetrogradeInversion[{notes}] produces \
the retrograde of the inversion of the sequence of notes.  This is useful to \
atonal composition in the style of Schoenberg."

\(RetrogradeInversion[
        notes : {__?Positive}] := \(Reverse[
            Inversion[notes]] /. 
		noteRules\) /. 
        sensibleNoteRules\)

RetrogradeInversion[notes : {__?NumericQ}] := 
      Message[moremusic::notpos, 
        ToString[DeleteCases[Flatten[notes], _?Positive]] ]

RetrogradeInversion[notes : {__String}] := 
      With[{newnotes = ToExpression  /@  notes}, 
        If[And[And  @@ (NumericQ  /@ Flatten[newnotes]), 
            And @@ (Positive  /@ Flatten[newnotes])], 
          RetrogradeInversion[newnotes], 
          Message[moremusic::notnote, 
            ToString[
              DeleteCases[Flatten[newnotes], _?Positive]] ] ] ]

Chords

chord interval specifications

MajorChordRoot = {0, 400, 700, 1200};
MajorChordFirstInversion = {400, 700, 1200, 1600};
MajorChordSecondInversion = {700, 1200, 1600, 1900};
MajorChord7Root = {0, 400, 700, 1000};
MajorChord7FirstInversion = {400, 700, 1000, 1200};
MajorChord7SecondInversion = {700, 1000, 1200, 1600};
MajorChordmaj7Root = {0, 400, 700, 1100};
MajorChordmaj7FirstInversion = {400, 700, 1100, 1200};
MajorChordmaj7SecondInversion = {700, 1100, 1200, 1600};
MajorChord6Root = {0, 400, 700, 900, 1200};
MajorChord6FirstInversion = {400, 700, 900, 1200, 1600};
MajorChord6SecondInversion = {700, 900, 1200, 1600, 
          1900};
MajorChord9Root = {0, 400, 700, 1000, 1400};
MajorChord9FirstInversion = {400, 700, 1000, 1400, 1600};
MajorChord9SecondInversion = {700, 1000, 1400, 1600, 
          1900};
MajorChordsus4Root = {0, 500, 700, 1200};
MajorChordsus4FirstInversion = {500, 700, 1200, 1700};
MajorChordsus4SecondInversion = {700, 1200, 1700, 1900};
MajorChord7sus4Root = {0, 500, 700, 1000, 1200};
MajorChord7sus4FirstInversion = {500, 700, 1000, 1200, 
          1700};
MajorChord7sus4SecondInversion = {700, 1000, 1200, 1900};
MajorChordAug7Root = {0, 400, 800, 1000, 1200};
MajorChordAug7FirstInversion = {400, 800, 1000, 1200, 
          1600};
MajorChordAug7SecondInversion = {800, 1000, 1200, 1600, 
          2000};

DiminishedChord = {0, 300, 600, 900, 1200};

MinorChordRoot = {0, 300, 700, 1200};
MinorChordFirstInversion = {300, 700, 1200, 1300};
MinorChordSecondInversion = {700, 900, 1200, 1500};
MinorChord9Root = {0, 300, 700, 1000, 1400};
MinorChord9FirstInversion = {300, 700, 1000, 1400, 1500};
MinorChord9SecondInversion = {700, 1000, 1400, 1500, 
          1900};
MinorChord11Root = {0, 300, 700, 1000, 1400, 1700};
MinorChord11FirstInversion = {300, 700, 1000, 1400, 1700, 
          1900};
MinorChord11SecondInversion = {700, 1000, 1400, 1700, 1900, 
          2200};
MinorChordHalfDimRoot = {0, 300, 600, 1000, 1200};
MinorChordHalfDimFirstInversion = {300, 600, 1000, 1200, 
          1500};
MinorChordHalfDimSecondInversion = {600, 1000, 1200, 1500, 
          1800};
MinorChordAug7Root = {0, 300, 700, 1100};
MinorChordAug7FirstInversion = {300, 700, 1100, 1200};
MinorChordAug7SecondInversion = {700, 1100, 1200, 
          1500};

Alternative short names for chord configurations

MajorChord[0] = MajorChordRoot;
MajorChord[1] = MajorChordFirstInversion;
MajorChord[2] = MajorChordSecondInversion;
MajorChord7[0] = MajorChord7Root;
MajorChord7[1] = MajorChord7FirstInversion;
MajorChord7[2] = MajorChord7SecondInversion;
MajorChordmaj7[0] = MajorChordmaj7Root;
MajorChordmaj7[1] = MajorChordmaj7FirstInversion;
MajorChordmaj7[2] = MajorChordmaj7SecondInversion;
MajorChord6[0] = MajorChord6Root;
MajorChord6[1] = MajorChord6FirstInversion;
MajorChord6[2] = MajorChord6SecondInversion;
MajorChord9[0] = MajorChord9Root;
MajorChord9[1] = MajorChord9FirstInversion;
MajorChord9[2] = MajorChord9SecondInversion;
MajorChordsus4[0] = MajorChordsus4Root;
MajorChordsus4[1] = MajorChordsus4FirstInversion;
MajorChordsus4[2] = MajorChordsus4SecondInversion;
MajorChord7sus4[0] = MajorChord7sus4Root;
MajorChord7sus4[1] = MajorChord7sus4FirstInversion;
MajorChord7sus4[2] = MajorChord7sus4SecondInversion;
MajorChordAug7[0] = MajorChordAug7Root;
MajorChordAug7[1] = MajorChordAug7FirstInversion;
MajorChordAug7[2] = MajorChordAug7SecondInversion;

MinorChord[0] = MinorChordRoot;
MinorChord[1] = MinorChordFirstInversion;
MinorChord[2] = MinorChordSecondInversion;
MinorChord7[0] = MinorChord7Root;
MinorChord7[1] = MinorChord7FirstInversion;
MinorChord7[2] = MinorChord7SecondInversion;
MinorChordmaj7[0] = MinorChordmaj7Root;
MinorChordmaj7[1] = MinorChordmaj7FirstInversion;
MinorChordmaj7[2] = MinorChordmaj7SecondInversion;
MinorChord6[0] = MinorChord6Root;
MinorChord6[1] = MinorChord6FirstInversion;
MinorChord6[2] = MinorChord6SecondInversion;
MinorChord9[0] = MinorChord9Root;
MinorChord9[1] = MinorChord9FirstInversion;
MinorChord9[2] = MinorChord9SecondInversion;
MinorChord11[0] = MinorChord11Root;
MinorChord11[1] = MinorChord11FirstInversion;
MinorChord11[2] = MinorChord11SecondInversion;
MinorChordHalfDim[0] = MinorChordHalfDimRoot;
MinorChordHalfDim[1] = MinorChordHalfDimFirstInversion;
MinorChordHalfDim[2] = MinorChordHalfDimSecondInversion;
MinorChordAug7[0] = MinorChordAug7Root;
MinorChordAug7[1] = MinorChordAug7FirstInversion;
MinorChordAug7[2] = MinorChordAug7SecondInversion;

ShowChord and ShowChordNames

ShowChordNames::usage = "ShowChordNames[rootnote,chordform] shows the \
names of the notes in a particular cord with root rootnote and form \
chordform, with the letter names of the notes displayed as strings."

ShowChordNames[theroot_, 
        form_] := (Round[1000 ShowChord[theroot, form]]/1000) /. 
          noteRules /. sensibleNoteRules

ShowChord::usage = "ShowChord[rootnote, chordform] shows the hertz \
(frequency) values of the notes in the chord with root rootnote and form \
chord form. The output is suitable for putting into tunePlay. In essence this \
function is just a simpler interface to CentzToHertz."

ShowChord[rootnote_, form_] := CentsToHertz[form, rootnote]

Examples

The definitions of chords can be used in the standard Scale[] command defined in the standard package "Miscellaneous`Music`"

tunePlay[ShowChordNames[C3, MajorChord9[1]]]

tunePlay can accomodate both polyphonic and monophonic tunes.

tunePlay[{{{"C4", "G5", "E5"}, 
            1}, {{"A4", "D5", "Fsharp5"}, 
            1}, {{"G3", "B5", "D5", "G5"}, 2}}];

Here is an example at a different tempo

tunePlay[{{{"C4", "G5", "E5"}, 
            1}, {{"A4", "D5", "Fsharp5"}, 1}}, 
        bpm -> 60];

The note names can be passed as either strings or expressions, for example if you are using the equal-temperament notes defined in the "Miscellaneous`Music`" package.  You could also define, for example, names for quarter-tones or other note systems.

You don't even have to have the same number of notes in each chord.

tunePlay[{{{"C4", "G5", "E5"}, 
            1}, {{"A4", "D5", "Fsharp5"}, 
            1}, {{"G3", "B5", "D5", "G5"}, 2}}];

Nor does the "tune" need to be in recognisable notes at all.

tunePlay[
        Evaluate[Table[{1000 Random[], Random[]}, {10}]]];

Here is one of the chord naming functions.

ShowChordNames[C3, MajorChord9[1]]

These can be input into the tunePlay function.

tunePlay[{{ShowChordNames[C3, MajorChord9[1]], 
          1}, {ShowChordNames[G3, MajorChord9[1]], 1}}]

And here is one of the prime-sequence manipulation functions. Again, this

RetrogradeInversion[{"E5", "F5", "B5"}]

Error Cases

tunePlay[{{C4, 1}, {-C4, 1}}]

tunePlay[{{"C4", 1}, {"H4", 1}}]

tunePlay[{{{"C4", "E5", "C5"}, 
          1}, {{"B4", "D5", "H5"}, 1}}]

RetrogradeInversion[{"E5", "F5", "H5"}]

RetrogradeInversion[{E5, F5, -C5}]

tunePlay[{{{C4, E5, C5}, 1}, {{B4, D5, -C4}, 1}}]

A more substantial example – Frère Jacques

Here is an example of the use of Mathematica's expression manipulation capabilities. Note that each of the crochets (quarter-notes) is represented as two notes. This is because they will be played at the same time as quavers (eighth-notes) at times in the round, and tunePlay[] can't do counterpoint.

\(\(frerejacques
       = {C5, C5, D5, D5, E5, E5, C5, C5, C5, C5, D5, D5, E5, E5, C5, C5, 

        		E5\ , E5, F5, F5, G5, G5, G5, G5, E5\ , E5, F5, F5, G5, G5, G5, 
        G5, 
		G5, A6, G5, F5, E5, E5, C5, C5, G5, A6, G5, F5, E5, E5, C5, 
        C5, 
		C5, C5, G4, G4, C5, C5, C5, C5, C5, C5, G4, G4, C5, C5, C5, 
        C5}; \)\)

Try this -- it will take FOREVER to load, though.

tunePlay[frerejacques];

Now, let's put the round together.  Just to make it interesting, some of the parts will be transposed up or down an octave.

\(frerejacquesbig = 
      \({#, 1}&\)/@\ 
        \((\(DeleteCases[#, 0]&\)\ /@\ 
            Transpose[{Join[frerejacques, Table[0, {48}]], 
                RotateRight[
                  Join[ToExpression\ /@\ noteTranspose[frerejacques, 12], 
                    Table[0, {48}]], 16], 
                RotateRight[
                  Join[ToExpression\ /@\ 
                      noteTranspose[frerejacques, \(-12\)], Table[0, {48}]], 
                  32], 
				
                RotateRight[Join[frerejacques, Table[0, {48}]], 48]}])\)\)

This will take a LONG time to render, if ever.

tunePlay[frerejacquesbig, bpm -> 160]