This week I decided to work on a little script for the session I'll be holding at Portland Code Camp called "Artistic Expression Through Code". The first night I developed the meat of the script: A markov chain based algorithm that you could recite a song to which it would then use to create its own song. This first version didn't have support for timings so everything was quarter beats. It didn't sound as bad as random computer noise, but it didn't sound like a song. It was very... computer-like.
Tonight I decided to implement a second markov chain to track the timings of the recited song and use those along with the notes to create real songs. The results this time weren't too bad at all. (I've attached a sample of the song to this post)
Give it a listen. I think you'll be somewhat surprised that that's a completely computer generated song.
There's a fairly simple computer science technique known as a "Markov Chain". Don't let the whole reference to computer science fool you, it's really not tough to grasp. Basically I created a table of numbers that answers the following question: When note X was played what percentage of the time were the other notes played next? So just imagine a table with all of the notes from a to g# laid out on top (these are the notes that we last played) and vertical axis is all of the same notes but this axis represents the probability that that particular note was played next.
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 6, 0, 1, 0, 0, 0, 0, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 2, 0, 3, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
You'll have to just imagine that same list of notes listed along the left hand edge of that table since it's too hard for me to put it there myself in this editor. :)
Here's the timings table (1, 1/2, 1/4, 1/8, 1/16 notes):
1 2 4 8 16
[0, 0, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 3, 5, 0],
[0, 2, 4, 11, 0],
[0, 0, 0, 0, 0]
But that's it, that's the magic that drives the whole process. As you can see, I'm actually not storing percentages but instead just the count of the number of times a note led to a different note. It works out to be the same thing in the end.
Grok'd
So to summarize, my program was able to generate the music because I fed it a sample musical score and it figured out what percentage of the time a c note led to another note. Here's a step by step run through:
- Give program a note and a timing.
- When I give it a second note/timing it notes in it's table that the first note led to the 2nd note one time. It also notes that the first timing led to this 2nd timing one time. (note I don't attempt to relate notes/timings, it's not important surprisingly).
- Enter a third note.
- The program then notes in its table that the 2nd note led to the 3rd note one time.
- Continue ad inifinitum
So that's how we set the system up. Next this is how we get the program to come up with its own song: