Michael Pickard
|
IntroductionThe foundation for this project was originally established when I elected to use the topic of "Fractal Music" for my seminar presentation. Many hours of research later, and my interest in the topic was such that I decided then and there, that it would be expanded in to a final project as well. The concept of Fractals has been around for a long time - although it has typically been studied in visual medium. Fractals themselves though, can be found in virtually anything. Essentially, they are some form such that the structure of the whole can be detected in sub-sections as well. In music, this can be done through any number of ways. The first would simply be to modify the sound wave itself through some recursive formula. While this would create a new fractal sound, it would not be generally considered "musical". This is where Midi comes in to play. Being a collection of note events, Midi allows for music creation through mathematical manipulation. This was the basis for the entire project. | |||||||||||||||||||||||||||||
The First AttemptIn time for the seminar presentation, I wanted to be able to construct my own fractal Midi score. The first step to this, was to decipher the Midi file format. This turned out to be significantly more challenging that I had been expecting. The combination of some portions of data needing to be converted to hexadecimal, and other data having to be in a special seven-bit left aligned binary format, made things a headache to say the least! After many hours of working through the system, I was able to put together a simple recursive fractal generator. This fractal generator was coded in C++, and could handle a recursive depth of five. All scores began with every note presumed to be C4 (Note C in the 4th octave), and the exact same length. Every note (except for sharps and flats – which were not included in the generator) of the final score was recursively calculated based off of a hard coded integer array. This array contained precisely 8 numbers (between -4 and +4), which were the "change" to make to the notes in the score. For example:
Each of these notes would then be turned in to eight identical notes, and the pattern would be recursively applied to each new segment of eight notes. The program accomplished what was intended, however it had to do so in a very messy way (one that was not conducive to expansion!). All values were hard coded to avoid having to do decimal to hex and decimal to 7-bit left-justified binary conversions on the fly. Unfortunately, it would not be sufficient for the type of project I wanted to create. | |||||||||||||||||||||||||||||
Welcome to JMusicAfter exploring numerous Midi options available in C/C++, I found that none enabled me to work with individual notes in a way significantly less complex than what I already had done. Branching out in to other languages I am familiar with, I finally found what I was searching for (and then some!) in Java. I discovered a fantastic library called JMusic. The major advantage this library had, was that it did not require a super deep knowledge of music theory or the inner workings of Midi. It allowed for easy creation of single notes, more complex chords, entire phrases, and a complete score. What this allowed, was that I could focus on the fractal creation process instead of the very fine details of Midi itself. At this point I sat down and decided on two major styles of fractal music I wanted to implement. I also went through the JMusic documentation to figure out how complex I would be able to make the music. Eventually I had a solid idea in mind of what I wanted to allow:
| |||||||||||||||||||||||||||||
Basic Recursive FractalThis form of fractal began with the same simple concept introduced in the original program I wrote for my seminar. It was going to be able to modify notes based on some user provided pattern, and continue to do so recursively. I knew it wasn't going to be feasible to go beyond the depth five from the initial program (due to the extreme number of notes created), however I wanted to expand beyond the simple set from before. Instead of beginning with 8 C4 notes of the same length, I wanted to begin with a variety of different patterns and recurse upon those themselves. I wanted the notes to be of a potential different length, and to have up to sixteen of them (instead of only eight). Since final length was going to be determined in a different way, it made the most sense to start with one bar of music and have no note shorter than 1/16th. Further, instead of only having an integer array of "changes", I would instead have to create a change array, as well as a ratio array (due to the notes being different lengths). For the change array, I elected to calculate the average note value in the original bar, and determine how far each note is from that average; this would become the change array. After a few trials, I elected to remove rests from the equation. The reason being, that a rest in the original bar of music would become an extremely long rest after levels of recursion. I also decided to save chords for the second form of fractal as they messed up some of the calculations. Despite these removals, the options were still significantly larger than the original program I created for the seminar. | |||||||||||||||||||||||||||||
Advanced Construction FractalFrom my research for my seminar, I found that there was one form of fractal music in particular that really intrigued me. Essentially it involved 9 phrases of music, each with 9 bars, which would be reformed in to one whole based on some fractal pattern. For this, I wanted the user to be able to provide all 9 phrases of music in one file, and then give a fractal pattern to follow. The pattern would involve the numbers 1 through 3, which would then be recursed upon to generate a pattern containing all 9 phrases. As no particular bar was going to be stretched as in the basic fractal, rests were now a reasonable option for the composition. In addition, I wanted to incorporate chords in to the music, so set it up to accept those as well. At that point, it was a matter of calculating how the pattern would work through the recursion, and then bringing it to life in code. | |||||||||||||||||||||||||||||
Data EntryMy initial thought, was to have the user manually type in the notes one at a time. Realizing that this would make it exceptionally slow to work with, and not allow easy changes, I decided to use text file input instead. I figured this would be much quicker for testing, as well as allow a user to very quickly try one set of data, change it if they were not happy with the result, and then test again. The first option, would be to have to user list all notes they wanted as integer values for the note (0 to 127), and double values for the duration. While this would have been the easiest option by far, it wouldn’t allow for chords, and would also rely on a user knowing all the note values. In addition, it would not allow for multiple phrases to be saved in one file. To make the most user friendly text entry possible, I designed my own simple language, and built a parser for it. There would still be some run time I/O with the user - but all music entry would be done via text file and this new language. The parser would be responsible for reading in a standard text file, tokenizing the result, detecting commands from the new language I created, and parse each of those commands individually. | |||||||||||||||||||||||||||||
The New LanguageUsing concepts learned in Compiler Design, I put together a very basic language for music entry. It involves a handful of commands based on simple music concept. These commands are as follows: INST(instrumentNumber_Int)
PHRASE(series_of_NOTE_REST_CHORD_events)
NOTE(noteLetter_Char noteOctave_Int noteDuration_Double optional+/-_Char)
CHORD(noteLetter_Char noteOctave_Int optional+/-_Char ... chordDuration_Double)
REST(restDuration_Double)
Example of one bar of music
| |||||||||||||||||||||||||||||
Input/OutputAs mentioned previously, text file input is the standard means for the program. There is also some direct user I/O during the program execution; such as menu item selection, choosing a recursive depth for the basic fractal, etc. The use of direct user interaction, was to allow for simple things (such as the minimum note length after recursion) to be changed whenever the user ran the program (without having to modify external text files each time). For the output side of things, all final compositions are exported as .mid files after completion. These files are given specific names, so if you wish to execute a given fractal generation more than once (and still retain the previous result), it is recommended that you rename the file on your computer so that it is not overwritten. | |||||||||||||||||||||||||||||
Random MusicAfter creating a nine phrase, 81 bar text file for use in the advanced construction fractal, I decided that a great feature would be to have the program randomly generate starting music. Since I still had some time before the project was due, I decided that this feature would be added to the project. I started by writing a method to generate one single bar of music (with no chords, no rests, no notes less than 1/16th, and at least 3 notes in the bar minimum. In order to keep the sound a little less extreme, I used a Gaussian distribution to keep the notes close to the middle of the octave scale. It was a little trickier than I was expecting to create this method, but the result was interesting to say the least! With that method written, I wanted to create a more complex one for the advanced construction fractal. Not only did it need nine phrases (each with nine bars), but rests and chords should both be permitted. I also wanted there to be the possibility of bars containing only one or two notes. Initially I had it generate 1/32nd and 1/64th notes, however after hearing a number of randomly generated compositions, I removed those from the system. I also found with these longer compositions, the randomness factor (even with the Gaussian distribution) was too extreme. So for this method, I modified the distribution to be based note off the centre of the octave scale, but centred on the previous note instead. This would allow notes to have the chance to be a fair distance away, but would tend to stay reasonably close to its predecessor. Rests were generated the same way as the notes, however there was only a 5% chance for a rest to be randomly generated. Chords had a higher chance, but that chance dropped dramatically for the chance to have more than 2 notes in a chord. Initially, I allowed up to 4 notes in a chord, but far too often the chords sounded terrible. Thus, I dropped it to a maximum of 3 notes in a chord, and even that had a very low chance of occurring. In the end, the compositions sounded interesting, and made for some very unusual fractals! | |||||||||||||||||||||||||||||
ConclusionThe final results were even better than I was expecting; and I am very glad I had time to had extra functionality such as the random music generation. If I was to continue developing the system, I would add in the ability to have multiple phrases playing at the same time (to allow for multiple instruments to be in the composition). I also think a very interesting progression from here, would be to add an artificial intelligence component to the program. It would use genetic algorithms (with fitness scores based on music theory) to help create scores that are more appealing to listen too. They wouldn’t need to rely on the fractal component per se, but could generate a longer score (say 4-8 bars of music), and perform recursive fractal generation on that. There is no doubt, it is a field with enormous room to explore! |