## Monday, February 11, 2008

### Patch anatomy: step-sequencing

This is a tutorial on making a simple MIDI step-sequencer in Max/MSP.

The accompanying patches can be found here:

Let us begin!

Simple Patch
Let us begin with almost the simplest patch that will allow one step-sequence MIDI data. This patch can then be expanded upon.

Please note that there are many ways of doing one thing in a program such as Max/MSP, and my way is just one way. It is probably not the best way, but it works.

This simple patch allows the user to control a number of functions:
• a sixteen step MIDI pitch sequencer
• variable global velocity
• variable BPM

The interface for the sequencer is a multislider object (the item that looks like a graph in the patch). The multislider object is set up for integer use, and currently has a range of 30 - 54. This object represents the MIDI pitch data, and as such has a musical range of two octaves (24 semitones, because 54 - 20 = 24).

A multislider object outputs all of its sliders' values to the left output when one or more sliders change their value. In other words, it outputs a list with a length of sixteen integer values, where the first number in the list is the left most slider value and the last number in the list is . The aim of this simple patch is to read through these values in sequence, one after the other, with an interval in between reads and convert the values to MIDI notes.

This long list is fed into an object called listfunnel, which is a very simple yet very useful object at times. Listfunnel takes a list of numbers (such as our sixteen integers) and outputs them as a series of indexed pairs, starting with an index beginning at zero.

For example, if there is a list with the value
'57 60 72 43'

and this is put in to the listfunnel object, it will return four lists of two numbers, where the first value in each list is an index and the second is the individual value. In the example, list funnel will return:
'0 57'
'1 60'
'2 72'
'3 43'

This is very useful for sorting and dealing with lists quickly and efficiently.

Now, the MIDI pitch data is represented as a set of sixteen indexed pair. These indexed pairs are fed into the coll collection.

The coll object is a general memory area where values are stored and can be retrieved for later use. To store a value in a coll object, it must have a symbol associated with it. When this symbol (ie. number etc) is sent to the coll, then the coll retrieves the data at the location of the symbol and outputs it to its left most output.

This symbol association can be thought of as an address or an index, somewhere to look something up. So, to store a value in the coll, a list of at least two values must be sent to it. The first value in the list is the address or index (ie. the symbol that is associated with the data) and any other items in the list are then associated with that particular address. Then, when the address (by itself - ie. a single number) is sent to the coll, then the coll retrieves the data from that location.

For example, if we send four lists to the coll, like:
'0 57'
'1 60'
'2 72'
'3 43'

then we have store the value 57 at index 0, 60 at index 1, 72 at index 2 and 43 at index 3. If, after this point in time, we send the value 0 to the coll, the coll will return the value 57 because we stored that value at that index earlier.

Because our sixteen values are now already correctly formatted for the coll object, they will appear at index points 0 to 15.

A tempo object is connected to a bang button, which is in turn connected to a counter object. It is this counter that actually controls which value comes out of the coll when. The counter counts up from 0 to 15 and repeats indefinetaly. This is governed by the arguments '0 0 15'. The first zero represents the direction (o for up, 1 for down, 2 for up / down operation). The other two numbers represents the minimum and maximum values of the counter. This is simply the index range of the data in the coll object.

The counter increments up by one each time it receives a bang to its left input. This bang is sent from the bang button, which is connected to the temp object. Because the tempo object is set to a bpm of 120, with a multiplier of 1 and a beat division of 4, the next note in the sequence will be triggered every crotchet note at 120 bpm. The second-left inlet of the tempo object sets the tempo of the output in bpm by changing the number boxed marked 'bpm'. The left most inlet starts and stops the tempo object (with the toggle marked 'start', 0 for off an 1 for on).

Finally, the data from the coll object (which is the pitch data from the multislider) is sent to a makenote object as the pitch element in a MIDI note. The makenote object generates a note on and follows it up automatically with a note off after a user-defined period of time. The velocity and length of the note are initially set by the arguments '60 300' where 60 is the velocity and 300 the time in ms. The velocity is also user-controllable by the number box marked 'velocity'.

The output (left outlet for pitch and right outlet for velocity) is sent to a noteout object, which makes the transition into actual MIDI data.

And, Sequencing Velcocity

This simple extension adds the following features to the patch described above:
• The ability to sequence not only pitch but also velocity

This straightforward extension of the previous version adds only three objects, yet it allows the user to sequence the pitch of a note as well as velocity.

A second multislider, listfunnel and coll are added and connected to the counter identically to the first set. This multislider is also an integer-based object, but has a range of 0 to 127. The data contained within the second coll and generated by the second multislider is representative of the velocity of a note.

Since both coll objects have their data requested by the same counter object, the first note will also have the first velocity and so on.

The output of the second coll is connected to the makenote object's second inlet (which controls the velocity of the generated note). Because of this, there is no use for a global velocity control (and it has been removed).

Phase-relative step-sequencing
This simple extension adds the following features to the patch described above:
• The ability to sequence not only pitch but also velocity using loops of different lengths, creating more complex time signatures and the possibility of simple phase-relative musical structures

Once again, this patch is almost identical to the version preceding it. Only three objects have been added, but these objects offer a different type of control to that explored previously.

Imagine if it were possible to step-sequence in a loop velocity and pitch data where the loops for the velocity and the pitch data are of different lengths. This opens the door for phase-relative phrases and structures, which can produce more complex musical patterns than the source data might have one initially believe.

The idea is that although both coll objects for pitch and velocity are being clocked out by the same tempo object (and are therefore in time), different length loops of the data itself is achieved by using a counter for each coll instead of sharing a counter like the previous example. Furthermore, the maximum point for each counter (which determines the loop range) is user-definable and can be set to any number between 0 and 15. The right-most inlet of the counter objects set the maximum counts and are controlled by the length (pitch) and length (velocity) number boxes.

So, if the pitch length is set to 15, then the pitch coll will always read the index from 0 to 15. But if the velocity is set only to 2, then the velocity coll will only always read from 0 to 2 (in other words, only the first three velocity sliders) before going back to 0.

dogmeat said...

hello,
this is a great tutorial for a newby like myself.
i tried to make something similar, but replacing the multislider with toggles (i know there is no pitch with this, but all i want is to create some scaffolding for a drum-machine-like step sequencer)
so i have 16 toggles, which all go into pak object with 16 inlets, and then into the list funnel. this works flakey because the coll object doesnt reset itself after the change in sequence, and it leaves with a hum in the background. i have been breaking my brain all day trying to make it work, but nothing goes.
here is a text file of my patch:

max v2;
#N vpatcher 278 58 1045 642;
#P origin 0 13;
#P window setfont "Sans Serif" 9.;
#P newex 194 335 26 9109513 print;
#P slider 442 397 15 24 30 1;
#P newex 119 305 209 9109513 pak 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
#P newex 119 328 46 9109513 listfunnel;
#N coll ;
#P newobj 100 409 53 9109513 coll;
#P toggle 371 235 15 0;
#P toggle 351 235 15 0;
#P toggle 331 235 15 0;
#P toggle 311 235 15 0;
#P toggle 292 235 15 0;
#P toggle 272 235 15 0;
#P toggle 253 235 15 0;
#P toggle 233 235 15 0;
#P toggle 214 235 15 0;
#P toggle 193 235 15 0;
#P toggle 173 235 15 0;
#P toggle 154 235 15 0;
#P toggle 135 235 15 0;
#P toggle 115 235 15 0;
#P toggle 95 235 15 0;
#P toggle 74 235 15 0;
#P hidden newex 230 473 55 9109513 noteout a 1;
#P hidden newex 230 431 80 9109513 makenote 60 300;
#P toggle 206 45 15 0;
#P message 322 45 18 9109513 16;
#P message 304 45 14 9109513 8;
#P message 288 45 14 9109513 4;
#P number 245 46 35 9 1 16 3 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P number 127 151 35 9 0 0 0 139 0 0 0 221 221 221 222 222 222 0 0 0;
#N coll ;
#P hidden newobj 402 334 52 9109513 coll;
#P hidden newex 422 277 45 9109513 listfunnel;
#P user multiSlider 422 238 243 32 0. 127. 16 2665 15 0 0 2 0 0 0;
#M frgb 0 0 0;
#M brgb 255 255 255;
#M rgb2 127 127 127;
#M rgb3 0 0 0;
#M rgb4 37 52 91;
#M rgb5 74 105 182;
#M rgb6 112 158 18;
#M rgb7 149 211 110;
#M rgb8 187 9 201;
#M rgb9 224 62 37;
#M rgb10 7 114 128;
#P button 371 214 15 0;
#P button 351 214 15 0;
#P button 331 214 15 0;
#P button 311 214 15 0;
#P button 292 214 15 0;
#P button 272 214 15 0;
#P button 252 214 15 0;
#P button 232 214 15 0;
#P button 213 214 15 0;
#P button 193 214 15 0;
#P button 173 214 15 0;
#P button 153 214 15 0;
#P button 134 214 15 0;
#P button 114 214 15 0;
#P button 94 214 15 0;
#P button 74 214 15 0;
#P newex 83 178 222 9109513 select 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15;
#N counter 0 0 15;
#X flags 0 0;
#P newobj 78 108 68 9109513 counter 0 0 15;
#P newex 78 73 73 9109513 tempo 120 1 16;
#P number 99 42 35 9 40 300 3 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P toggle 78 42 15 0;
#P comment 423 221 100 9109513 Velocity;
#P comment 98 21 35 9109513 BPM;
#P comment 206 21 35 9109513 Steps;
#P hidden connect 7 0 8 0;
#P connect 3 0 5 0;
#P connect 5 0 6 0;
#P connect 6 0 7 0;
#P fasten 32 0 6 1 211 93 97 93;
#P hidden connect 7 1 9 0;
#P connect 4 0 5 1;
#P fasten 6 0 51 0 83 387 105 387;
#P fasten 52 0 51 0 124 386 105 386;
#P hidden connect 7 2 10 0;
#P connect 35 0 53 0;
#P connect 53 0 52 0;
#P connect 6 0 27 0;
#P connect 36 0 53 1;
#P fasten 28 0 6 4 250 98 139 98;
#P hidden fasten 29 0 6 4 293 107 139 107;
#P hidden fasten 30 0 6 4 309 107 139 107;
#P hidden fasten 31 0 6 4 327 104 139 104;
#P hidden connect 7 3 11 0;
#P connect 37 0 53 2;
#P hidden connect 7 4 12 0;
#P connect 38 0 53 3;
#P connect 39 0 53 4;
#P hidden connect 7 5 13 0;
#P connect 40 0 53 5;
#P hidden connect 7 6 14 0;
#P connect 53 0 55 0;
#P connect 41 0 53 6;
#P connect 42 0 53 7;
#P hidden connect 7 7 15 0;
#P connect 43 0 53 8;
#P connect 51 0 33 0;
#P hidden connect 33 0 34 0;
#P hidden connect 7 8 16 0;
#P connect 44 0 53 9;
#P connect 45 0 53 10;
#P hidden connect 7 9 17 0;
#P hidden fasten 33 1 34 1 305 460 257 460;
#P connect 46 0 53 11;
#P fasten 26 0 33 1 407 392 270 392;
#P hidden connect 7 10 18 0;
#P connect 47 0 53 12;
#P connect 48 0 53 13;
#P hidden connect 7 11 19 0;
#P connect 49 0 53 14;
#P hidden connect 7 12 20 0;
#P connect 50 0 53 15;
#P hidden connect 7 13 21 0;
#P hidden connect 7 14 22 0;
#P hidden connect 7 15 23 0;
#P hidden fasten 6 0 26 0 83 171 407 171;
#P hidden fasten 25 0 26 0 427 316 407 316;
#P hidden fasten 24 0 25 0 427 275 427 275;
#P pop;