Building a "creative feedback loop"

Marc Mennigmann's icon

Dear Max users,

I am very new to the Max world, but I feel that all my generative processing drams will become a reality with the help of Max. I am a piano player and would love to build something that I call the „creative feedback loop“, a system that listens to my piano playing and responds to it musically.

The dream setup would listen to the audio of me playing a grand piano, analysing it in real-time, and generating something from it like melody fragments, rhythmic fragments, altered chord progressions, which is sent to a MIDI output. The system should analyse rhythm, melody and harmony.

In a first step, I would replace the audio input with a simple MIDI input, which eliminates the part of the audio analysis and should make it easier to start coding.

Has someone already built a similar system? Are there code snippets that help me learning, for example, extracting rhythm patterns from an incoming MIDI stream.

Any help and hint would be much appreciated. I’m hungry for knowledge.

Best,
Marc

Roman Thilenius's icon


for midi-only it is relatively simple to make a handful of effective algorithms such as transposition, rescaling, recording and replaying it .... so your main learning content will be the use of the max runtime itself.

for example you should start entering "midi" in the search box to find the midi tutorials, and read the helpfiles of midiin, midiout, midiparse and midiformat - makenote might also be of help to get into the whole topic.

how you exactly want to work with note event data is something you may and have to choose yourself, there is no common best practice for that afaik.

Kenneth Newby's icon

Robert Rowe has written two books that will provide you a wealth of ideas and resources:

Interactive Music Systems: Machine Listening and Composition (1992)

Machine Musicianship (2004)

Marc Mennigmann's icon

Thanks Roman and Kenneth.
I already managed to extract quarter and eighth notes from the MIDI clock signal using rtin. Now I try to understand how to record some notes, alter them (like a simple minor third shift), and play them back two bars later. Don't know how to do this, but digging is fun!
Thanks also for the book recommendation. Nothing better than something which is educating and inspirational at the same time.

Roman Thilenius's icon


before you seek for "real" record and play you should first start with a simple delay to reach the same result.

[pipe] will delay numbers.

[+ ] will transpose note numbers.

[== ] compares a number against another.

[route] and [gate] can be starting points to filter out certain stuff.

[random] should be selfexplanatory - but beware of overdoses.

when working with note events, you might want to encode the midi note number into a complex number, which consist of 2 numbers, one for they key and one for the octave:

60 -> 0, 5

if you want to change the order of two numbers within a time grid, you will write them into an array, called "list" in max - usually written as "60 61 72 60" here in the forums - and perform list operations over it, mainly with [vexpr] and the [zl.] series, then split it again into single numbers.

in the beginning, write only very small algorithms, one per job. you will later save those to disk, recall them by their name and build the bigger things from your custom small blocks.

feel free to show what you got already (copy compressed) and add what it should do, and we will try to suggest how to make it work.

Marc Mennigmann's icon

Thank you Roman, a lot of valuable information in your post.
I played with pipe, transposing notes, and understood the basic concept of [route] and [gate].
Now I try to build a simple MIDI delay, but I fail at the point to eliminate notes with a velocity below 0. I don't know how to build a condition that will catch those notes and eliminate them from feedbacking.
Please be careful with my example. I implemented a feedback from [midiformat] to [midiselect] which will run forever if you play something.

How can I avoid feedback after a note velovity is zero or less?

Best,
Marc

Max Patch
Copy patch and select New From Clipboard in Max.

nicnut's icon

I've been using 3rd order Markov Chains for analyzing midi pitch data that I play, and have a midi synth imitate me. It works pretty good for my purposes.

Marc Mennigmann's icon

Markov Chains? Sounds interesting! Do you mind sharing a basic patch, Nicnut?

Roman Thilenius's icon

as you already noted it in your patch, velocity 0 means note off.

and negative velocity doesnt exist. :)

i would first build a bunch of algorithms which only deals with the note on event.

delaying a complete note event including its length and/or the corresponding note off event is a bit more complicated and a completely different type of problem solving.

nicnut's icon

Hey Mark,

My markov chain patch is based around an object called "mchain" by Richard Dudas. If you download it and check the help file you can probably figure it out. My patch is pretty customized for my midi controllers so might not be as helpful as that help patch.

Also, what you do with that markov chain data is really the interesting part. I guess you laid out your goals in your original post. I use the data for pitched percussion and other synth melodies that imitate what I am playing. I guess what I'm saying is that mapping the data to music is a whole other creative endeavor.

Kenneth Newby's icon

The higher the order of a Markov chain—that is, the more previous events are taken into account in choosing the next—the more like the original input the output will be.

Here's an example of a first order Markov learning and playback approach made using native Max objects. First order meaning that the selection of the next event is based only on one previous event transition. Try playing a familiar melody into it to build the transitions, then use them as a generative process to hear the result. Even first order transitions capture a considerable amount of the original and make for interesting variations.

Max Patch
Copy patch and select New From Clipboard in Max.

nicnut's icon

@kenneth newby that's a nice little patch!

Kenneth Newby's icon

Thanks Nicnut. It's an old patch.

A simpler hash function would be to simply concatenate the last pitch number with the current one to generate the unique indices for each transition, e.g. 60 followed by 67, would simply be stored as index 6067 to the number times that transition has occurred. It's collision-proof, with the added benefit of being human readable.

I hope someone finds the little patch useful and extends it to do 2nd or 3rd order transitions, then shares it back here!

Marc Mennigmann's icon

@nicnut & @kenneth
Thanks for your explanations, and thanks for the Markov example patch kenneth. It's interesting what comes out depending on what you put in. Definitely something I will analyze deeper as soon as I understoof the basic Max concepts.
I will try to add the storing velociy to this patch, as this is something I miss when listening to the output. And it might be a good learning example.
As a beginner there are so many things to learn, and it's sometimes hard to find the easiest way to understand something as everything is interconnected. Analyzing patches and writing my own small scripts is currently my daily bread to make progress.
I hope to be able to give something back to this helpful community - maybe a 2nd or 3rd order Markov version.

Roman Thilenius's icon


if you want to include the velocity in a state/transition map, you would store the note number together with the velocity as one single symbol (in the coll)

"60" "127"
[pack 0 0]
[tosymbol]

inside the coll things should then look like this:

"60 127", "65 127" "65 100";

eventually it makes more sense to truncate the incoming velocity values by [/ 10]