News:

It's not Opposite Day.

Main Menu

Procedurally Generated Music

Started by Zunawe, April 28, 2016, 07:08:07 PM

Previous topic - Next topic

Zunawe

https://github.com/Zunawe/markov-chain-music-generator

So, spring semester 2016 I took a linear algebra class, and our project for the year was to find a paper on linear algebra and write a mini-paper on the applications of the ideas presented in that paper. And that was about it for guidelines. After a while of trying to come up with something interesting, I realized Markov chains could easily be applied to matrices. Turns out that's been a thing for a long time. So I started trying to think up fun things to do with Markov chains. And eventually I realized that music might be possible. I'm certainly not the first to do this, and I certainly haven't figured out the best way, but it was kinda cool to try, and it made the project fun.

The paper I used discusses higher-order multivariate chains. Basically, if a regular Markov chain uses the previous "state" to determine the next one in a single list, a higher-order uses the previous n states, and a multivariate uses s lists. In terms of music, that means you can design the program so that the next note the right hand of a piano plays depends on the previous 32 notes that both the right and left hand have played, or something along those lines.

These probabilities have to be determined somehow because a Markov chain basically predicts what's going to happen next based on what it knows has already happened. In my case, I used some of Beethoven's piano sonatas as reference. So, the program essentially tries to mimic the style of Beethoven's piano sonatas. If I had fed it Pokémon music from this site, it might have sounded vaguely more Pokémon-ish.

In order to keep things simple and reasonable, I had to limit myself a lot, and it had an impact on the results, but the point was just to prove that it might work, not to make one that does work. For one, I only considered pieces written in 4/4 and the key of C major. Each "state" was the space of an 8th note, so sometimes I had to kinda lie to the program. (I was trying to tell something that only knows what an eighth note is what notes were in a triplet or a set of sixteenth notes occasionally.) I looked 16 8th notes back, and used only the most "important" note being played at the time. I also only used the left and right hand of the piano as sequences. So basically the simplest I could get it so I wouldn't have to deal with unnecessary things.

So, now that everything's been explained, here are some literal random notes thrown on a page:
Random 1: [PDF] [MIDI]
Random 2: [PDF] [MIDI]
(Example outputs can be found in the GitHub repository)

Those are for comparison, because what my program generated certainly isn't good. But I would say it's markedly better than a random set of notes, and that's with all the limitations I put on the program. Here are four generated 8-measure pieces:
Test 1: [PDF] [MIDI]
Test 2: [PDF] [MIDI]
Test 3: [PDF] [MIDI]
Test 4: [PDF] [MIDI]
(Example outputs can be found in the GitHub repository)

By the way, ignore notation and spelling in those. The note itself was a state, not the spelling, so that's not the important part.

If I do make any major changes, I'll add more tests and explain the updates I make. For now, I'll just kinda explain some ideas I had that could build off this model:
  • Weight the first/third beat of a measure and the previous note in the same sequence more heavily. I'm not actually sure the theory behind what beats have the most important role in a measure. But that's basically what I'm going for.
  • Other musical elements could be integrated as other sequences, such as accents, chords, etc...
  • Other instruments could be integrated to the point of a full orchestra, but in a fully functional program, that might require a day or two of processing.
  • Creating an algorithm that uses the chain for generation, but also works alongside the Markov chain for broader things like repeats, time sigs/changes, key sigs/changes, etc...

For the most part, those aren't realistic for me to do without some major driving force. But it's an interesting thing to consider.

All the code has been posted on GitHub (link at the top). Feel free to ask questions, but it has been a while since I wrote this.

Thanks for reading all that. And if you didn't, thanks for reading the thank you message at the end.
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

mikey

If you really believe you're onto something here (and hey, computers writing music is a paying idea imo) then I wouldn't put the code anywhere near the internet
unmotivated

Zunawe

The thing is, I'd have to learn a lot more music theory to even consider making this into something viable. And I currently don't have the time for that.
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

mikey

you can always shelve it away until life gets a little more stable, too.  I mean this is actually something you could do, you know?
unmotivated

Zeila

#4
I agree. Although looking at an open source would be cool and all, this has potential and I think it's really cool. Nice work!

Edit: wait, so does your program just generate the notes and you write it down? Or does it recognize a musical program and write it out itself? I mean the former is more likely, but the second one is even more impressive

Zunawe

#5
No, I didn't have the time or energy to output midis or music xmls, so each note is actually just a number, and then I translated those numbers into notes by hand.

EDIT: This is what the output looks like:
Bigish
{
 {{13, 11, 6, 4, 3, 13, 9, 4, 6, 11, 8, 12, 5, 3, 13, 8, 12, 13, 1, 4,
    11, 13, 4, 6, 11, 8, 13, 13, 3, 13, 1, 4, 13, 3, 9, 8, 13, 8, 3,
   4, 3, 13, 1, 4, 13, 7, 4, 10, 9, 9, 11, 11, 12, 13, 6, 8, 9, 11,
   11, 3, 13, 2, 11, 10}},
 {{9, 8, 7, 4, 11, 13, 8, 8, 11, 7, 11, 6, 8, 1, 1, 12, 1, 13, 13, 12,
    13, 13, 13, 10, 3, 11, 9, 9, 1, 7, 11, 8, 7, 11, 13, 6, 7, 13, 11,
    12, 11, 6, 9, 11, 13, 8, 9, 13, 11, 13, 13, 11, 8, 6, 13, 11, 9,
   4, 4, 4, 13, 13, 5, 11}}
}
[close]
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

InsigTurtle

Hmm, I was thinking actually thinking of writing code to write procedurally generated music, but in a completely different manner, mostly based on the harmony instead of individual notes. So it would end up playing a 'random' harmonic sequence each time. I was planning on doing this for a four-part chorale, but then I'd have to code in stuff that'd prevent parallel octaves and fifths, ensure proper doubling, etc. etc... So I just gave up on the idea. I don't know if you want to do someone similar with your code, but there's an idea if you want to take it a bit further.

Zunawe

That's the other side to "procedural generation" where the computer is given a list of rules to follow and randomly picks sequences that follow these rules. The problem I saw with that is that either your rules aren't very strict and you end up with weird mixings of styles in music theory world, or your rules are too strict and you get an uninteresting result. By using Markov chains, the computer doesn't ever really need to care because it's only imitating another composer or music style where all those rules were already used. A really good version of my program would have few problems with straying away from the style it's fed because it's all based on something that has actually happened before.

So your idea is cool, but it kinda fundamentally strays from the basis of this project.
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

InsigTurtle

I was going to actually use Markov Chains for it, but I gave up since I honestly didn't know what I was doing at that point :P

Zunawe

That would be something like a pseudo-Markov chain. Which could work with a lot of testing.
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

Zunawe

Code posted to GitHub. Link in OP. That's all.
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

mikey

awesome!  Are you gonna keep building on it or just drop it?
unmotivated

Zunawe

Maybe if I run out of other projects, but really it was just a cool idea that I proved is doable with a lot more sophistication. So it probably will only exist as the paper I wrote (also in the repo).
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things

mikey

could you get the program to ignore certain intervals?  For example, never play note "1" and note "7" at the same time?
unmotivated

Zunawe

Yeah. At every tick, if the generation creates that interval, you just rerun that tick. It'll work as long as the probability that the interval is created isn't 1.
You know you've been playing too much Dragon Quest when you're afraid your Hershey's Kisses are going to flee.

I program things