chasp
Composing Harmonies with ASP

Overview

chasp creates simple accompanying pieces of different genres. To accomplish this ASP is used to solve the problem of chord progressions, based on the rules proposed by the theory of harmony. This results into a harmonic sequence that eventually provides the basis for the creation of simple musical pieces by applying genre-specific templates, through an additional imperative control framework.

Contents

Download

The source package is available here and can be used with Linux and Windows systems (tested) as well as OS X (not tested) as long as the requirements listed below (see Installation) are met. There are currently no binaries available.

The source package contains:

./
|- music.py : main script
|- asp/ : ASP music model
|- lilystyles/ : LilyPond code generators for multiple genres
|- out/ : generated (example) output (LilyPond, PDF and MIDI)

Installation

Requirements

Make sure to install the pyclingo target (i.e. Python "gringo" module).

To install chasp simply make sure that all requirements are met and download the chasp directory.

Usage

To generate chord progressions in the form given below use:

$ python music.py

command line output sample:


	    key: c major
	    cadenza length: 4 chords
	    found 4 chord progression(s)

	    models: 
	    [cadenza(4,(1,0)), cadenza(3,(5,0)), cadenza(2,(6,0)), cadenza(1,(1,0)),
	    thiskey(c,maj), cadenza_chord(3,g,maj,0), cadenza_chord(1,c,maj,0),
	    cadenza_chord(4,c,maj,0), cadenza_chord(2,a,min,0), cadenza_notes(3,g,b,d,g),
	    cadenza_notes(1,c,e,g,c), cadenza_notes(4,c,e,g,c), cadenza_notes(2,a,c,e,a)]

	    ...
	
To create PDF an MIDI output of a chord sequence in a specific style use
$ python music.py -i tango

Instead of tango you may also choose between chords, bach, pop, foxtrot, raggae, rumba and voices.
chord creates a simple score containing the chords of the given chord progression.
voices creates a 4-voices homophonic choral. (This feature is still in the early stages of development.)
All other options create simple accompanying pieces in the corresponding genre. The output files can be found in ./out

To change the length of the generated progression you can use this command line flag:

$ python music.py -l 8

To change the key of the output, use:

$ python music.py -k g -m min

-k changes the root of the key (default c)
-m changes the mode (min or maj - default maj) of the key

To see a list of all available options, use:

$ python music.py --help

Examples

With chasp it is possible to create musical pieces of different styles and genres while using one and the same underlying harmonic structure. This harmonic structure is a chord progression created with ASP. Basic musical knowledge (such as notes, scales and chords) and rules concerning harmony theory are modeled with ASP so that valid chord progressions can be created. For more detailed infromation concerning the ASP modeling have a look at the literature.

ASP output

The ASP output contains the four predicates thiskey/2, cadenza/2, cadenza_chord/4 and cadenza_notes/5. For a chord progression containing four chords in the key E-Minor ASP may generate the following output:


	    thiskey(e,min),
	    cadenza(1,(1,0)), cadenza(2,(4,7)),
	    cadenza(3,(5,7)), cadenza(4,(1,0)),
	    cadenza_chord(1,e,min,0), cadenza_chord(2,a,min,7),
	    cadenza_chord(3,b,min,7), cadenza_chord(4,e,min,0),
	    cadenza_notes(1,e,g,b,e), cadenza_notes(2,a,c,e,g),
	    cadenza_notes(3,b,d,f,a), cadenza_notes(4,e,g,b,e)
	

thiskey/2 contains information about the key (root note and mode) in which the chord progression was created.

cadenza/2 describes a chord at a time step. The notation used here is based on scale steps and the figured bass. More precisely, a chord is represented by two facts. First of all, the root note of the chord is determined by the scale step over which the chord is built. This scale step also represents the chord's bass (lowest) note. Secondly, The other notes of the chord are determined by a number derived from the figured bass that indicates the chord's inversion. For example in the key C Major (6,6) represents a chord with the lowest note a (which is the sixth scale step in C Major) in its first inversion (where the bass note is the chord's third), which results in F Major.

cadenza_chord/4 is a simplified representation of cadenza/2 giving the chord symbol for each chord. This representation is needed for the creation of additional chord symbols in LilyPond files to ensure a faster recognition of a final musical piece's chord structure. Therefore the information given by cadenza is simplyfied in such a way that the actual inversion of the chord is ignored and reduced to the distinction between simple triads and seventh chords.

cadenza_notes/5 is needed to provide the actual notes of each chord and is thus derived from cadenza_chord/4.

Creating musical pieces

The output given by ASP is passed to a Python class (which one depends on the user input concerning the desired genre ouf the final musical piece) which contains a LilyPond template. This template is filled in with the given information to create a complete LilyPond file which is then comiled with LilyPond to create the final PDF an MIDI output.

Creating an accompanying piece in the style of a Rumba

To create an accompanying piece of a certain genre corresponding characteristics were extracted from samples of the genre and put into a template. This is the vizualisation of a template for a Rumba accompaniment:

Rumba template

The measure above shows the rules for rhythm and note distribution to create a Rumba accompaniment. The notes are not absolute but rather described by a number which represents the scale step of each note in the key of the corresponding chord for each measure. When translating these rules to a LilyPond notation we get the following template:


		  upper stave: r8 < 3 5 1 > r < 3 5 1 > r < 3 5 1 > r < 3 5 1 >
			  lower stave: 14. 5’8~ 54 5,
		      

Some genres yield more complex templates, e.g different chord types might have to be treated individually. Such considerations result in conditional statements that have to be wrapped around a LilyPond template as the one shown above. Additionally, a basic LilyPond file has to be created around the part containing the actual notes. Furthermore, chord symbols are added to each LilyPond template to provide an easier understanding of the piece’s harmonic structure. Therefor a combination of a given answer set with the complete Rumba template creates the final output as shown below:


			  thiskey(c,maj),
			  cadenza(1,(1,0)), cadenza(2,(6,0)),
			  cadenza(3,(5,0)), cadenza(4,(1,0)),
			  cadenza chord(1,c,maj,0), cadenza chord(2,a,min,0),
			  cadenza chord(3,g,maj,0), cadenza chord(4,c,maj,0),
			  cadenza notes(1,c,e,g,c), cadenza notes(2,a,c,e,a),
			  cadenza notes(3,g,b,d,g), cadenza notes(4,c,e,g,c)
		      
Rumba accompaniment PDF output

MIDI file of the sheet music above (also available as mp3)

Creating a Prelude for solo piano in the Style of Bach's Prelude No. 1 in C Major, BWV 846

With a process very similar to the one described in the previous section a musical piece in the style of Prelude No. 1 in C major, BWV 846, from Johann Sebastian Bach’s Well-tempered Clavier can be created. A detailed analysis of the whole piece would lead to many different and complex rules which would allow the creation of a very similar piece. But as the purpose here is only to achieve a slightly similar result only the first four measures of the piece have been analyzed and used as a base for a LilyPond template. The analysis is limited to these four measures because they describe a complete harmonic cycle as the first and the fourth measure of the piece are identical. When analyzing the first four measures of the original score several rules for the utilization of a chord’s notes can be extracted.

First of all, the piece consists of three different voices that are played at the same time. The first and highest voice shows a repetitive pattern of eighth notes that can be formalized as the LilyPond template r8 N16 N N N, N N with N being any note. r16 N8.( N4) is a formalization of the second voice’s rhythm while N2 describes the third voice’s rhythm. Each voice’s rhythmic pattern is repeated twice per measure.

Bach does not utilize a repetitive pattern concerning the use of each chord’s actual notes but sticks to the harmonic pattern of one chord per measure. Therefore, to be able to create a piece of a similar style the use of each chords notes in the original can be analyzed to observe the patterns used there. The first and fourth measure are identical and as only these two measures do not contain a seventh chord the contribution of any triad’s each contain a seventh chord with the difference of a minor chord in the second measure and a major chord in the third measure. The note contribution between the three voices for a minor seventh chord would be low(7), middle(1), high(5,1,3) while the note contribution pattern for a major seventh chord is low(3), middle(5), high(1,5,7). With this information a (already rather complex) LilyPond template can be created. The template for a measure containing a simple triad would thus look like this:


			    bachfile.write("\\relative c’’ { r8 "
			    + str(n[2]) + "16 " + str(n[0]) + " " + str(n[1]) + " "
			    + str(n[2]) + ", " + str(n[0]) + " " + str(n[1]) + " r8 "
			    + str(n[2]) + ",16 " + str(n[0]) + " " + str(n[1]) + " "
			    + str(n[2]) + ", " + str(n[0]) + " " + str(n[1]) + " }\n")
			    bachfile.write("\\relative c’ { r16 "
			    + str(n[1]) + "8.~ " + str(n[1]) + "4 r16 "
			    + str(n[1]) + "8.~ " + str(n[1]) + "4} |\n")
			    bachfile.write("\\relative c’ { "
			    + str(n[0]) + "2 " + str(n[0]) + " } |\n")
			

Now, the following output may be generated with ASP:


			    thiskey(c,maj),
			    cadenza(1,(1,0)), cadenza(2,(2,7)),
			    cadenza(3,(5,7)), cadenza(4,(1,0)),
			    cadenza_chord(1,c,maj,0), cadenza_chord(2,d,min,7),
			    cadenza_chord(3,g,maj,7), cadenza_chord(4,c,maj,0),
			    cadenza_notes(1,c,e,g,c), cadenza_notes(2,d,f,a,c),
			    cadenza_notes(3,g,b,d,f), cadenza_notes(4,c,e,g,c)
			

When passing the output from ASP to the LilyPond template a complete LilyPond file is created which produces the sheet music and MIDI file as shown below.

PDF output of a Prelude in the style of Bach

MIDI file of the sheet music above (also available as mp3)

Creating a four-voices homophonic choral

For the creation of a four-voices homophonic choral additional ASP code generates the notes for each voice. To achieve this the different voices were modeled with ASP as well as the required rules concerning voice leading which were then applied to the basic chord progression. The notes for each voice at each time step of the chord progression are generated by taking into account a chord’s inversion and distributing the notes accordingly among the four different voices. Additional rules ensure that each voice is only assigned such notes as it is able to reach. Furthermore, more rules are added to make sure that basic restrictions of voice leading such as forbidden parallels or the avoidance of voice crossing are satisfied. Thus, additional ASP output containing the valid note distributions in form of the voice/3<7tt> (containing information about which voice at which time step is singing which note) predicate is created.

To generate the final sheet music the Python framework transfers the output given by ASP and additionally including voice/3 to a Python class containing a LilyPond template for a choral. For now no additional rhythm is applied to the choral, therefore each measure contains exactly four chords resulting in each voice changing its note at each quarter note of every measure. The distribution of the chord’s notes has already been taken care of by ASP. No additional thoughts are required before filling the LilyPond template with the given notes. The template contains a choir framework and loops that fill in each voice’s notes. As an example the loop for the soprano voice from the corresponding LilyPond template is shown below:


				  for s in soprano:
				  for ss in s:
				  if str(ss[1]) == "1":
				  voicefile.write("	" + str(ss[0]) + "'4 ")
				  else:
				  voicefile.write("	" + str(ss[0]) + "''4 ")
				  voicefile.write("\n")
				  voicefile.write("}\n")
			      

The sheet music output presented below is created when executing a LilyPond file that has been completed with the following information given by ASP:


				  thiskey(d,min),
				  cadenza(1,(1,0)), cadenza(2,(7,46)),
				  cadenza(3,(5,0)), cadenza(4,(1,0)),
				  cadenza_chord(1,d,min,0), cadenza_chord(2,g,min,0),
				  cadenza_chord(3,a,min,0), cadenza_chord(4,d,min,0),
				  cadenza_notes(1,d,f,a,d), cadenza_notes(2,g,bes,d,g),
				  cadenza_notes(3,a,c,e,a), cadenza_notes(4,d,f,a,d),
				  voice(soprano,1,(a,1)), voice(soprano,2,(bes,1)),
				  voice(soprano,3,(e,2)), voice(soprano,4,(a,2)),
				  voice(alto,1,(f,1)), voice(alto,2,(g,1)),
				  voice(alto,3,(c,1)), voice(alto,4,(f,1)),
				  voice(tenor,1,(d,0)), voice(tenor,2,(d,0)),
				  voice(tenor,3,(a,0)), voice(tenor,4,(d,0)),
				  voice(basso,1,(d,0)), voice(basso,2,(g,-1)),
				  voice(basso,3,(a,-1)), voice(basso,4,(a,-1))
			      
PDF output of a four-voices homophonic choral

MIDI file of the sheet music above (also available as mp3)

As the creation of chorals is only in the early stages of development there is still much work to be done, including the consideration of rhythm and the optimization of each voice's melody (e.g. avoiding large intevals).

Literature

  • Sarah Opolka. Theoretische Grundlagen der automatischen Komposition, Bachelor Thesis, 2012.
  • Sarah Opolka. chasp - An approach to harmonic composition using Answer Set Programming, Master Thesis, 2015.
  • Sarah Opolka, Philipp Obermeier and Torsten Schaub. Automatic Genre-Dependent Composition using Answer Set Programming (ISEA'15), To appear, 2015. PDF Preliminary Version
  • Sarah Opolka, Philipp Obermeier and Torsten Schaub. Automatic Genre-Dependent Composition using Answer Set Programming. (slides, to be published)