AMI: Agent for Music Inscrtiption AMI (pronounced "Amy"), is an agent that writes music. This software composes short musical pieces, in a simple but interesting style. The rest of this document will cover the following aspects of AMI: Domain By definition, an agent is an AI program that acts in a domain. AMI's domain is the world of music. For my purposes this is a two-dimensional world. The set of dimensions of AMI's domain can be thought of as a tuple: Domain = {pitch, rhythm} This domain is represented in Prolog in the following structure:
staff(Note,Rhythm)Such that Note represents a note and Rhythm represents a rhythm. These variables can take on the following values: Note can be {a,a#,b,c,c#,d,d#,e,f,f#,g,g#} Rhythm can be {4,5,8,12,16}
(meaning: quarter note, dotted quarter note, eighth note, dotted eighth note, sixteenth note) A list of these structures can be seen as a piece of music. For example: Could be seen as:
[staff(a,12), staff(a,16), staff(a,16), staff(c,16), staff(b,16), staff(a,16)]With these structures AMI can write music, and her output can be in the above form.
Illustration A typical session with AMI from the user's point of view is the following:
- Start Prolog and load AMI.
- Type a query of the following form:
go(c-sharp,16).
This asks AMI to write a piece of music in the key of c-sharp minor that is sixteen bars long. (All of AMI's music is in a minor key because that is the type of music that I like best). - AMI will then write a piece of music on the screen in the form of a list.
- The user will then read the output and manually convert it to a musical staff, as described in the Domain above.
- At this point the user should play what is on the staff. If the user wants more music, he can ask AMI another query, in the same form of the above, and she will write a different piece of music.
Methods There are four main problems to solve to make an agent like AMI:
- Come up with a chord progression
- Come up with a motif that implies any chord
- Transform any motif so that it implies any chord in any progression
- Apply music theory to do the above three.
minor_third(g-sharp, X)to which AMI would reply that X = b, since b is the minor third of g sharp. I then formalized the chord-scales in a similar way. Chord scales are how musicians look at a chord progression of a piece in a general way. They are scales, which in this case are harmonized to make up a three note chord. Solving Problem Three We can use AMI's music theory predicates and the representations described in the domain to write a new predicate that will transform a motif that implies one chord from a progression, to make it imply another chord in the same progression. The approximate algorithm to do this is the following:
- Remove the head of the list (i.e.take the first note of the motif). The head will be a staff structure, which has a harmonic and rhythmic aspect.
- Make a new staff structure N to be the head for the new motif list.
- Set N to have the same rhythm as the note of the head of the original list.
- To set the harmonic aspect of N use the chord that the original motif was trying to imply, and compute the interval I1 between that chord and the note in the head. Then use the same I1 with respect to the chord that N is trying to imply, to set N.
- Recurse until the original list is empty.
- AMI is going to write a motif that will be the musical heart and soul of the piece of music she will compose.
- AMI is going to generate some random numbers, to be used with a few simple rules that I came up with, to generate a list of staff structures.
m(chord(X,minor), List):- rm(L), rn(chord(X,minor), L, List),rm is true if L is a rhythmic motif, i.e. L is a list of numbers, such that those numbers represent rhytmic values, as mentioned in the domain discussion above. rn is true if List is a motif, i.e. List is a list of staff structures. The rhythmic values of these staff structures must have the same values as L, and the harmonic values can only be notes from the X-minor scale. rm is defined in terms of a set of random numbers n, whose range is from one to four. Another set of random numbers are used to determine which rhythmic value (quarter note, dotted quarter note, eighth note, sixteenth note) will be selected, and this rhythmic value will be repeated n times. This entire procedure will occur recursively, with a randomly picked recursion depth of r, such that 0 < r < 5. The procedure will generate a motif of ultimately random length and random time signature, that will be maintained for the entire piece. The rhythmic motif is generated this way because it imitates a common paradigm of music; repetition. Most motifs repeat a rhythmic value between one and four times, before switching to a new rhythmic value. The other important feature of music is that these rhythms seem to be chosen in no particular order, so again I used random numbers. The predicate rn gives a harmonic aspect to the rhythmic motif, that implies a particular chord. The general rule in this case is the following. A three note chord (the type AMI works with) has a seven note scale, and a motif will imply that chord, just in case its notes are contained within that scale. This rule is satisfied by rn because rn will randomly select only the notes that are within the scale of the chord that it is trying to imply. Solving Problem One If this final problem is solved, AMI will be able come up with a chord progression. This problem also requires AMI to be somewhat autonomous, so random numbers will again play an important role. In this case the challenge is met by using the metaphor of a search problem. The predicate cruel_world defines the next point in the search space relative to the key of the piece. AMI must search through the cruel world to find what chord she will play next. The reasoning behind the title of this predicate will be discussed later. In the common seven note scale found in almost all of western music, there are seven chords, based on the scale of the key, also known as the root. These seven chords are usually written in roman numerals, such that upper case numerals indicate major chords, and lower case numerals indicate minor chords. Each of these chords generate feelings of tension and resolution in the average listener. The degree of tension or resolution that these chords can make the average listener feel have traditionally been separated into three categories, which I have listed below, in order of increasing dissonance:
Tonic: I Dominant: V, vii Pre-Dominant: ii, IV, viAMI searches the cruel world for the Tonic, so that the piece of music can resolve, but the odds of her finding it are not in her favor. Hence the name of the predicate that represents the search space is "cruel world". She sometimes finds a Dominant chord so that the listener feels a partial resolution, but most of the time she finds Pre-Dominant chords. This keeps the piece of music interesting and gives the listener a sense of adventure. A good analogy to this idea is to imagine a story in which the main character lives a happy life and is never challenged. The average person would find this story boring in comparison to one in which the main character starts out being happy (all of AMI's pieces start on the Tonic), faces some challenges (Pre-Dominant chords), achieves partial success (Dominant chords), and then in the end achieves total success and resolution (the Tonic). This search problem was implemented with the predicate cruel_world, which uses random numbers ranging from 1 to 6. This predicate will only be true 1/6 of the time if the next chord is the Tonic. It will also only be true 1/3 of the time if the next chord is the Dominant, and the other half of the time it will be true if the next chord is the Pre-Dominant. This scheme was implemented with a random number generator and some formalizations of these sets of chords. The cruel world is not such a bad place for AMI, when she uses a little bit of planning. This planning becomes relevant to coming up with a good chord progression, because such a chord progression should use a cadence to end on the tonic. A cadence is a set of chords that end a piece so that it resolves. You can read more about them, as well as some of the relations that I formalized at: http://dspace.dial.pipex.com/andymilne/Cadentialprog.shtml Any effective cadence must contain at least three chords, if the progression has only triads (the kinds of progressions AMI will be writing). This means that AMI will have to plan on how to end the progression using the following idea. If a piece of music is n bars long, then at bar k, where k = n - 3, AMI will leave the cruel word and finish the progression by randomly choosing, with equal probability, one of the following six cadences.
(b = flat) 1. v - iv - i 2. bVII - iv - i 3. bVI - bVII - i 4. iv - bVII - i 5. bVI - v - i 6. iv - v - iFor this reason the cruel world search space always has a consonant ending. Download the source code in Prolog here.
Conclusions Creating AMI taught me more about Artificial Intelligence, as well as Music. I will now discuss this project's impact in both areas. This project makes one question what it means to compose a piece of music. One of my main criticisms of my program is that it is lacking an important element that goes into the composition of a piece. The average human composer might go through a process like the following:
- Improvise in a certain key (this could be a structured but random process that is similar to what AMI does).
- Decide if he likes what he hears, and then go back to step one, or write down what he has improvised, for further variation.
Future Plans AMI's interface is somewhat limiting with regard to how a user asks AMI a question and what the user can do with her answer. Input I would like to configure a CGI so that users can then ask AMI to generate music over the web. The main predicate for AMI is "go":
go(Key, Bars).I would like to create a simple online form so that both Key and Bars can be input through a GUI over the web. Output AMI's staff based lists which come from Prolog are not difficult to read but are non-standard:
[staff(a,12), staff(a,16), staff(a,16), staff(c,16), staff(b,16), staff(a,16)]I would like to create a script which can parse the output to convert it into notation. I am also interested in a script that might be able to convert the above Prolog list into a format which would allow it to be heard immediately (perhaps MIDI).