This page will document progress on “MusicMaker” by Drew Jex, which aims to create new music written in the style of other music. My program is written in PHP so that it can eventually be hooked up with a front-end interface and be used as an easily-accessible web application.
A Web Client to the current version of the Music Maker is available HERE!
A Web Client to the old version of the Music Maker (Feb 2017) is available HERE!
So far, the Music Maker can:
You can listen to examples of what the MusicMaker has created so far here:
Examples of songs that implement this idea of repeating patterns can be heard below (for some reason, the server is slow sometimes. Be patient or refresh):
Things I want to analyze:
Questions:
So - do I pick notes based on score or based purely on my stats? I like the idea of the genetic algorithm because it introduces some randomness and potentially crossover (because it can somehow combine two good songs). Basically, it uses stats to get a score, and then makes mutations based on stats OR randomness. But will random guesses ever produce a higher score? It depends on how I write it.
Songs are built of pieces of sub-songs that simply sound good together. They also repeat sections, but introduce variety in order to increase interest. Repeating the same things too often becomes boring. That is important to realize.
So, build sub-pieces, then combine them.
Our genetic algorithm could potentially focus on creating these sub-pieces, and then we simply combine them.
So, we give the algorithm several songs and say we want to create a new song that implements styles we find in those songs. There is also the option of defining song structure, or the song will do it itself (ie. ABABCAvarBvar)…?
These parts are independent to an extent, but they still need to sound good with the other parts…. So they are dependent, at least to an extent.
So, look at each track and find the structure of each track, starting at the measure level, and then getting bigger.
Combine the stats from one or more songs in same database. Know structure of each track Know order of notes Know order of chord progressions
I need to start small, and then grow from there. That’s how the song expands and grows, and the problem becomes more manageable.
So, start out with a bunch of random songs. What will these consist of? How will these combine elements of all the songs? How are mutations chosen?
Start with just a couple notes. Then add other tracks (and consequently, chords).
A song is a bunch of measures - they are built of pieces, not just one giant interconnected DNA strand. It must be built piece by piece, but the pieces have to work well together. What is an elegant way to do that assuming I have 10 songs I feed the program?
So, it’s like I need to build it piece by piece, but perhaps in order. That way, the different pieces can fit well together. In order to do that - that can be another statistic I get - how are transitions handled in other songs? Imitate them!
Big question - is chord progression and structure decided at beginning or is it built as I build the song? Hmmm…
Right now, my Song object is set up so it’s really easy to repeat something if I want to. The idea is that each track is the same length for each section of the song. So how is the structure determined? How do I know I want to repeat a section? To some extent, it needs to be decided in advance. I can build the song, piece by piece, but also make sure the transitions work. Then I can just throw them together.
A major element of the transition is simply the chord progression. What major feeling with the next section have?
In order to keep track of “shape” of phrase, we could just look at the difference between each note, and use something like that again. (ex. 1 1 -1 -1 2 -2 -1 1 for a measure, or something like that)
Things I accomplished this week:
keeping track of difference between notes in melody
Next week:
analyzer that looks at difference between notes.
Would doing the chord progression first severely limit me? Perhaps… I wish I could implement the GA to do this part. The chords need to flow well. Imitating repeating patterns is a similar thing - i can copy different parts completely. Again, I don’t want to focus too much on specific restraints, like for example, if a part is 16 measures but I only want 8, etc. (unless i want to copy the chord progression, structure of song exactly)
Using stats from analyzed songs to know which chords/notes come next. that is important.
So maybe I just need to break the piece down into tiny structures, and do it piece by piece that way? (instead of bigger structural pieces?)
A big question is: how do i combine elements of several songs? And how do i introduce randomness?
Obviously, the big structural idea can be represented across songs, but what about at a smaller level? We can’t be too specific or controlling, can we?
Different tracks: A A A A A - always the same A B B B B - variety was introduced at 2nd iteration A A B B B - variety was introduced at 3rd iteration Uses perhaps the structure of previous songs, or combines them in interesting ways. Uses chord progression of other songs, and combines them in interesting ways. Now for actual notes being played - here’s where the GA comes in, perhaps.
After reviewing some of my ideas and what I was trying to accomplish, I've moved farther away from doing the genetic algorithm. Instead, I am focusing more on the idea of repeating patterns in music. My original fitness function was fundamentally built on this idea - a song is a song because it isn't random - it's musical, meaning it consists of recognizable patterns that our minds can remember.
So this week, instead of having a structural analyzer that just looks at structures by measure, I implemented what my fitness function accomplished by keeping track of patterns across the entire song that are even smaller than one measure. There is still an element of randomness because the rhythms and notes are assigned randomly from notes being played at the same time in the original song - I want to improve this element this week by looking at how different patterns align with other patterns as well as note-pattern shapes, etc. The really cool part of this week, however, is the fact that I can repeat and imitate patterns that are found throughout the song, whether they are two-note patterns or eight-note patterns or whatever. The new songs that were produced, therefore, contain more patterns at a smaller level and less randomness, even though the notes and rhythms are still being assigned randomly for each pattern. I think this shows the idea I'm trying to explore about music - repeating patterns (and a little bit of variety) are the essence of music. These patterns, combined with notes that sound good together, are fundamentally what I'm trying to recreate with MusicMaker.
Examples of songs that implement this idea of repeating patterns can be heard below (for some reason, the server is slow sometimes. Be patient or refresh):
There are still some bugs that need to be worked out, but you can hear more repeating patterns, especially at smaller levels (some of which you probably don't even notice). As they become smaller, you are more likely to get the original song. Theoretically, at the smallest level, you should end up with the original song (some bugs are preventing me from testing this currently).
Keep in mind also that two patterns are considered the same if their similarity is less than 4. This can be adjusted, especially at smaller levels.
Here is the structural output of the original song at a quarter-note level so you can see visually:
Array (
[0] => N|N|N|0 [1] => 1|2|2|0 [2] => 1|2|3|4 [3] => 5|6|7|8 [4] => 2|2|9|N [5] => 10|2|2|N [6] => 10|2|3|4 [7] => 5|6|7|8 [8] => 2|2|9|0 [9] => N|N|N|0 [10] => 1|2|2|0 [11] => 1|2|3|4 [12] => 5|11|7|12 [13] => 2|2|9|N [14] => 13|2|2|N [15] => 13|2|3|4 [16] => 5|11|7|12 [17] => 2|2|9|0 [18] => 2|14|N|0 [19] => 1|15|N|0 [20] => 1|2|3|16 [21] => 17|18|19|20 [22] => 21|22|7|2 [23] => 21|2|3|8 [24] => 23|N|24|25 [25] => 26|N|3|27 [26] => N|N|N|2 [27] => N|N|3|28 [28] => 29|2|30|31 [29] => 23|2|32|0 [30] => 29|2|30|31 [31] => 23|2|32|0 [32] => 29|2|30|31 [33] => 29|2|33|2 [34] => 29|2|30|31 [35] => 1|2|3|28 [36] => 29|2|30|31 [37] => 34|2|3|4 [38] => 35|2|3|4 [39] => 2|36|37|0 [40] => 1|2|2|0 [41] => 1|2|3|4 [42] => 5|6|7|8 [43] => 2|2|9|N [44] => 10|2|2|N [45] => 10|2|3|4 [46] => 5|6|7|8 [47] => 2|2|9|28 [48] => 2|14|N|28 [49] => 1|15|N|28 [50] => 1|2|3|38 [51] => 17|18|19|20 [52] => 21|22|7|2 [53] => 21|2|3|8 [54] => 23|N|24|25 [55] => 26|N|3|27 [56] => N|N|N|2 [57] => N|N|3|28 [58] => 29|2|30|31 [59] => 23|2|32|0 [60] => 29|2|30|31 [61] => 23|2|32|0 [62] => 29|2|30|31 [63] => 29|2|33|2 [64] => 29|2|30|31 [65] => 1|2|3|28 [66] => 29|2|30|31 [67] => 34|2|3|4 [68] => 35|2|3|4)
From here, I think it would be cool to combine songs by combining patterns from different songs at various levels. For example, I could take huge structural pattern of ABAB from one song, then in section A implement a smaller pattern from another song, then within each measure, use pattern structure from yet another song, etc.
I would also like to improve the quality of each pattern and specifically how patterns can sound well together. My analyzers can focus on transitions between patterns at different levels (perhaps starting at the smallest and slowly getting bigger), implementing note-shapes and other data it has gathered from the original song.
Fixed the bugs that were in the code last week! Now we can generate songs given three parameters: a midi file, the level to which we want to analyze the structure of the midi file, and the maximum number of differences two structural pieces can have to be considered the same.
Check out the basic web-client I am making so people can hear/compare songs that the MusicMaker creates: http://music-djex.rhcloud.com/makerFeb21/client/
Just some background on what the different parameters mean:
Here are the differences between the creation styles:
I have found that a 4 for structural-level and 1 for similarity-level will create songs that sound like a remix of the original. 8 and 2 will create a good mix of variety and newness. 1 and 1 will sound almost exactly as the original.
There is still much work to be done with making the patterns sound better together, and across tracks. But the original song gives us a great structural template of chords, patterns, and notes. It wouldn't be too difficult to take structural elements from several songs and combine them. I like the idea of trying to create patterns and repeat them throughout the song - I think it is the fundamental characteristic of music.
I would love to align the notes in such a way that they emphasize “the beat.” In other words, the song sounds in such a way that you don't need a metronome to know where the beat of the song is. This idea of “keeping the beat” is essential. This is accomplished by making each of these patterns have similar characteristics so they sound good together and are repetitive and consistent.
I improved my MusicMaker in simple ways this week that dramatically affected the outcome of the song. I haven't yet updated the web client version, but on my machine, I've implemented the following new creation style:
Due to outside circumstances, this week was rough in terms of making actual progress in the code. I did make some progress on getting some ORCA business done, however, such as attending the IRB workshop meeting. I also took some time to establish the major ideas I want to implement before the semester is over. These include:
Right now, my MusicMaker can successfully “remix” a song if you provide strict parameters that limits how creative the MusicMaker can be. This provides an unintended use-case in which you could easily create new renditions of popular music or give ideas for a new song-cover. Often, you end up with a “jazzed-up” version of the original song, which sounds quite pleasant because you can identify the original music.
I have made some good progress in combining multiple songs into one generated piece. Here is the basic idea of how I am accomplishing this:
function combineSongs(Array songs) { rhythm_patterns = []; note_patterns = []; structure = []; foreach (songs as song) { rhythm_patterns.push(Analyzer::getRhythmPattern(song)); note_patterns.push(Analyzer::getNotePattern(song)); structure.push(Analyzer::getStructure(song)); } num_tracks = structure[0].length; //num_tracks will be number of tracks in first song //we can mix/match across tracks - we don't care so long as we are repeating patterns in a way that returns a higher score. //create structure (almost) from scratch from patterns and finding places where they can match up - we can create "chains" new_song_structure = []; n = 3; //get structure for each track of first song foreach (track in structure[0]) { track_structure = []; //look for chains of at least length n, starting at the beginning. //initialize our chain with first part of first song for that track. for (i=0; i<n; i++) { track_structure.push(track[i]); } //now for each pattern of track[i], if we find a match from a pattern from another song, add a new part to the chain i = n; while (i < track.length) foreach (patterns as key => song_pattern) { if (key == 0) continue; //skip the first pattern foreach (song_pattern as another_key => pattern) { if (patterns[0][track[i]]->notes.isSimilar(pattern->notes)) { //when we find another pattern from another song with same chord for that pattern, add that part of song to the chain //but find the place that will return the highest score, based on repeating patterns max_score = 0; best_chain = null; foreach (place in structure that has pattern) { new_chain = []; for (j=0; j<n; j++) { new_chain.push(structure[key][place that has pattern+j]); } if (Analyzer::getScore(track_structure.concat(new_chain)) > max_score) { best_chain = new_chain; max_score = Analyzer::getScore(track_structure); } } //add in the best chain track_structure.concat(best_chain); i += n; //we don't want to break - we want to continue traversing through all the tracks of all the songs } } } } new_song_structure.push(track_structure); } return new Song(new_song_structure, rhythm_patterns, note_patterns); }
The main idea here is that we are creating a new structure from the provided songs by “chaining” structural elements together based on similar chord progressions/notes across songs. We want to choose those elements that contain more patterns because part of my idea is that “songs generally sound better that contain repeating patterns, plus some variety.” This is an rough idea for how this can be accomplished. I think there are better ways that I can experiment with, such as mixing/matching elements at different places using a genetic algorithm that implements Analyzer::getScore() as the fitness function. Essentially, I want to find the best “mix” of songs that produces the highest number of repeating patterns at some level for each track.
Another idea at a very high-level using a genetic algorithm, which I think would be sweet, could be:
function findBestCombination(Array songs) { max_iterations = 10000; //whatever we want i = 0; population = generatePopulationOfSize(songs, 10); //perhaps randomly adds/modifies patterns in song with patterns of another song while (i < max_iterations) { i++; selected = getIndividualsWithBestFitness(population); //selects 2 best in population. See http://geneticalgorithms.ai-depot.com/Tutorial/Overview.html performCrossOver(selected); //cross over is based on "chaining" idea presented in previous function - chains based on similar notes/chords from other members of population. performMutation(selected); //mutation randomly changes some structural elements to introduce variation population = selected; //the next generation is the result crossover and mutation } return getIndividualWithBestFitness(population); }
I would want to connect the chain at points that are consistent with the original music, not just randomly. Also, after determining a structure, I could change specific numbers to try and increase the repeating pattern score? I would like to do something like that, otherwise at the very end I could end up with a bunch of different structural elements from different songs but associated with different patterns, so it wouldn't be very repetitive. Idea: Use this chaining method for each major sub-part (i.e. A B A B C). Then there would be some repetition. At a smaller level, however, I would want to make sure there are more patterns that show up: so keep the notes the same, but change the rhythmic structure! Also, I start off looking for exact matches on connections to the chain, but if there are none, I just look for ones that are close until I find one. There can also be repeating parts if it fits well, but I don't want to do that too much. Also take the number of notes into account, and perhaps other variables as well.
I worked hard to build a genetic algorithm that would build songs with repeating patterns from multiple songs. I ran into a few problems that halted my progress, however, and made me reconsider what I can accomplish within this semester. The main problem I ran into, which I didn't realize initially was such a big problem, was the fact I couldn't consistently and accurately put all songs in the same key. This step is essential, because combining songs in different keys will obviously result in something that doesn't sound very pleasant. I have a few ideas that could potentially solve this problem, such as:
I was able to combine songs by simple taking the notes from one song and combining that with the structure from another song. It had a pleasant-sounding result, because obviously we weren't attempting to combine notes from songs in different keys, but it didn't allow for combining more than 2 songs, and I'm not sure how well it would work if we tried to combine more than just 2.
I want to start putting together an online survey that would allow people to choose music they like and use the MusicMaker to generate a song written using the same style/internal structure. I am wondering if I should do this by having the user only choose one song at a time rather than attempting to combine multiple songs. It would still potentially be cool, but ultimately I would want to be able to combine styles from multiple songs. Hmmm…
I created a new client website that allows people to now upload their own MIDI files for analysis rather than a provided set of songs. This new client-side is located here: http://music-djex.rhcloud.com/musicmaker/client/. I intend for this client to gather responses from surveys about the MusicMaker. The idea is that people can upload songs that they enjoy and then fill out a brief survey on how the MusicMaker did at making something that sounded similar yet unique to the original set of music they provided.
I also am working on creating a slight alteration to my pattern finder that will find patterns across entire songs rather than just each track. This will help tracks sound better when played together, especially for songs such as Coldplay's “Viva La Vida” where the tracks play notes on the very same rhythms.
I also started working on my ICCC-style report. I've written an introduction and I am part way through filling in my approach. Not sure it's going to be quite as high-quality as Paul or Ben's report, but it's fun to write either way. Right now it's being written in Google docs but I am transferring it to Latex once that's all set up.
I created my survey and I am currently receiving responses! Look HERE!
Also I created my Powerpoint presentation to give next week and I am working on the paper.