We’ve looked at using Markov Chains for Algorithmic Composition in PureData, Markov Chains in Keykit and Markov Chains in OpenMusic in previous posts.
In today’s Algorithmic Composition tutorial we’ll look at using Markov Chains in Max MSP to analysis existing music and generate new algorithmic music.
We’ll start off by recreating the Markov Chains in PureData example and then extend it with some new features. Creating the Markov Chain in Max is similar to composing in PureData, so if you haven’t read through the Markov Chains in PureData post yet it’s worth doing that now.
Zero Order, First Order and Second Order Markov Chains
In our last post we used the notes of ‘Happy Birthday’ as a simple musical example:
C4 C4 D4 C4 F4 E4
C4 C4 D4 C4 G4 F4
C4 C4 C5 A4 F4 E4 D4
Bb4 Bb4 A4 F4 G4 F4
A zero order Markov Chain considers only the probability of each note occurring. If we were to use Happy Birthday as source material for our algorithmic composition we can see that the notes occur with the following distributions:
- C4 8 times – 32%
- D4 3 times – 12%
- E4 2 times – 8%
- F4 5 times – 20%
- G4 2 times – 8%
- A4 2 times – 8%
- Bb4 2 times – 8%
- C5 1 time – 4%
First Order Markov Chains
In our last Algorithmic Composition post we built a first order Markov Chain analysis and generation patch. Here the next note is based on the current note and a list of probabilities for following notes. This is stored in a State Transition Matrix (STM), here’s an STM that lists the notes in our Happy Birthday example and the probabilities of subsequent notes:
Second Order Markov Chains
Second order Markov Chains choose the next note based on the two previous notes and the probability of subsequent notes following those two notes:
Markov Chains in Max MSP
Our last algorithmic composition post built a first order Markov analysis and generation patch in PureData. We’ll build the same 1st order patch in Max and then develop it to a 2nd order analysis and generation patch.
Create two patchers in your main Max patch window (press N to create a new object), and call them loadMIDIfile and markovPitchAnalysis. Your patch should look like this:
Inside your ‘p loadMIDIfile’ add two inlets and two outlets. This patcher’s job is to load MIDI files and send out the note on pitches.
Add the following objects: seq, midiparse, unpack, stripnote and a send (s finished).
Add the following messages: read, start 65536.
This subpatch should now look like this:
Inside your ‘p markovPitchAnalysis’ add an inlet and the following objects:
- a receive object (r resetMatrix) – this will be used to clear and reset our STM
- a patcher. This subpatch will chunk up incoming notes so 1, 2, 3, 4, 5 becomes 1 2, 2 3, 3 4, 4 5
- a coll – this stores our transition matrix. Each note is stored as an index followed by all of the pitches that immediately follow that pitch
Create two messages and an inlet as per this screenshot:
Setup the contents of your ‘p pair’ patcher to look like this:
We can now load up a MIDI file and perform a first order Markov analysis on the MIDI pitches. Next we need to setup the generation side of our patch to make new algorithmic music based on these Markov chains. Set your main patch window up like this:
The ‘p markovPitchGenerate’ subpatch looks up one of the pitches, chooses a random one from the possibilities and outputs this. This new note is used as the index for our next note selection.
Inside your ‘p markovPitchGenerate’ subpatch add the following objects and messages:
We can now generate new algorithmic compositions based on these Markov Chains. You can also try loading up one MIDI file, performing a Markov analysis on it, then repeating the process with a different MIDI file, this will be added to our STM – it’s best to put the files into the same key before you do this. To clear the STM click the bang in the main patch window that resets the matrix.
Second Order Markov Composition in Max
In order to modify our patch to perform a second order analysis we need to make a couple of small changes to our ‘p markovPitchAnalysis’ and ‘p markovPitchGenerate’ subpatches.
Second order markov chains base the next note on the previous two notes and the probabilities of other notes following those two notes in sequence. We therefore need to store the two notes as our index. Here’s an example 2nd order STM with MIDI notes:
We’ll modify our analysis and composition patches to store and access these two notes together e.g. 60 60 becomes 6060, 72 69 becomes 7269 etc. Inside ‘p markovPitchAnalysis‘ modify the ‘p pair’ subpatch to look like this:
Modify your ‘p markovPitchGenerate’ subpatch to look like this:
Reset the transition matrix by clicking on the bang button, we can now work with some more music composition examples by loading a MIDI file and performing a 2nd order analysis. Experiment with loading up MIDI files to the STM, performing mashups of different MIDIfiles and musical examples (it’s best to transpose them to the same key) to the same STM and developing the rhythmic aspects of the patch (these could be derived from the MIDI files too, but it’s best to make sure the MIDI files are quantized and have all tempo changes removed first).
You can hear some sample musical output from the 2nd order Markov chain here:
It’s worth building the patch from scratch, but you can also download the completed 2nd order markov chain analysis and composition Max patch here [coming soon].