Showing posts with label patch anatomy. Show all posts
Showing posts with label patch anatomy. Show all posts

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:
http://www.milkcrate.com.au/_other/downloads/max_patches/sequencer/Sequencer_1.pat
http://www.milkcrate.com.au/_other/downloads/max_patches/sequencer/Sequencer_2.pat
http://www.milkcrate.com.au/_other/downloads/max_patches/sequencer/Sequencer_3.pat

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.

Tuesday, January 29, 2008

Patch anatomy: looping

This is a tutorial on making a simple audio loop in Max/MSP.

The accompanying patches can be found here:
http://milkcrate.com.au/_other/downloads/max_patches/easy_loop/easy_loop_1.pat
http://milkcrate.com.au/_other/downloads/max_patches/easy_loop/easy_loop_2.pat

Let us begin!


Simple Patch
Let us begin with almost the simplest patch that will allow one to loop a piece of audio. This patch can then be expanded upon.


This simple patch allows the user to control four aspects of the loop:
• which file is being looped
• what the ratio of playback should be (this changes the speed and pitch accordingly)
• where the loop begins in relation to the audio in the file
• how long the loop is

The patch revolves around the groove~ object. The groove~ object controls the playback of a sample through its inlets and outlets. The groove~ object is associated with a buffer~ object by sharing the same name in their first argument. The groove~ object has the word "loop" as its first argument. The buffer~ object also has the word "loop" as its first argument. The word "loop" in this context acts as a name that binds the groove~ object to the buffer~ object.

The buffer~ is an allocated space in memory in which the sample that is played back via the groove~ is stored. The number after the name of the buffer~ (in this case the word "loop") is how long the sound file that is loaded into the buffer~ (and played back by the groove~) can be. This sets the maximum length of the file in ms.

In this patch, the maximum length of the buffer~ is 60000 ms or or one minute exactly. For example, if a wave file is loaded into the buffer~ that is two minutes long, only the first 60000 ms will be available to the groove~ object to play back. However, if a wave file is loaded into the buffer~ that is only forty seconds long, the entire 40 seconds will playback (even though 60 seconds was set as the seconds argument for the buffer~). In order to read a file into the buffer~ so that it can be played back by the groove~ object, the message "read" must be sent to the buffer~'s only inlet.

The groove~ object has three inlets and two outlets. The left-most inlet is used for control-type messages as well as changing the ratio of the sample that is being played. The middle inlet sets the start point of the loop in ms (where 0 is the beginning of the buffer~). The right inlet sets the end point of the loop in ms (where 0 is the beginning of the buffer~) .

Because the groove~ object does not loop by default, the message "loop 1, loopinterp 1" is sent upon opening the patch. This is done by first connecting a loadbang object to the message "loop 1, loopinterp 1" to the left inlet of the groove~ object. "loop 1" tells the groove~ object that it must loop. "interp 1" tells the groove~ object that it must interpolate samples when it repeated the looping section of the audio buffer~.

The three number boxes at the top of the patch control the three remaining parameters. The first box on the left controls the playback ratio of the groove~ object. The middle number box controls the loop starting point in ms. The right number box controls the loop length in ms.

The playback ratio control is connected to the left inlet of the groove~ object via a sig~object. The sig~ object transforms a number (such as what comes out of the number box) to a Max/MSP audio signal (which is what is used to control the ratio). However, we do not hear this audio signal directly - it is used to continuously change the playback ratio of the sample as the number box is changed. A ratio of 1.0 will play the looping sample at its original speed and pitch. A ratio of 0.5 will play the looping sample at half its original speed and an octave lower. A ratio of 2.0 will play the looping sample at twice its original speed and an octave higher. A negative ratio will play the sample in reverse.

Finally, it is a matter of setting the loop start and end times. The start time is simply a number box connected directly to the middle inlet of the groove~ object. The end time is a little more complex. The aim is to set the loop length in ms. However, the right inlet of the groove~ object sets the end point of the loop, not the loop length. In other words, we are dealing with a absolute value (in reference to the buffer~ length in milliseconds) instead of a delta value in comparison to our loop starting point.

The easiest way to work around this problem is to take the loop starting point and simply add a value (in ms) to it and then send the result to the right inlet of the groove~ object. This is why the loop start number box and the loop length number box go to the left and right inlets of a + object. This object performs the addition of both of these numbers.

The nature of the + means that a bang trigger is needed. This is because any changes in value to the left inlet will trigger the calculation, but the changes in the right inlet will not trigger the calculation directly. As a result, changes to the loop start point will update the groove~ start and finish loop points (because the loop start point is connected to the left inlet of the +). However, changes in the loop length will only be updated when the loop starting point has been changed.

The bang re-triggers the addition calculation every time the loop length changes by triggering the left inlet of the + object. Note the right-to-left order; the number from the loop length number box is sent to the right inlet of the + before the calculation is re-triggered. If the bang was to the right of the right inlet of the +, then the calculation would be triggered before the new value for the loop length had been sent.

Please note that in order to hear anything, the DSP Engine must be set to on (in Options > DSP Status > Audio).


Adding LFO Modulation
This simple extension adds the following features to the patch described above.
• LFO Sinewave modulation of the start point of the loop, resulting in a more complex sound and a wider variety of sounds.
• Control over modulation depth (in ms) and speed (in Hz).

The other features are retained.

The patch itself has not changed that much. The main structure from the first version is still almost completely intact. The only change in the original structure of the patch is that a + object has been added in between the loop start point number box and the rest of the patch. The loop start point number box is connected to the right inlet of the new + object.

Connected to the new + object is a sinewave generator (the cycle~ object) whose signal is routed through a signal monitor (which looks similar to a number box) and a scale object. The cycle~ object generates an audio waveform in shape of a sinewave. The frequency of this sinewave in Hz is determined by the number that is sent to its left inlet, which is connected to the mod speed number box.

The output of the sinewave is connected to the signal monitor, which is able to convert a signal into a number which is output to its left outlet. In the case of the sinewave, the numbers are in between -1.0 and 1.0. This stream of numbers is used as a starting point with which to modulation the loop start and end points with.

The numbers from the signal monitor object are sent to the scale object. The scale object takes four main arguments - low input, high input, low output and high output. The object takes a number and scales it as described by the four arguments. For example, if the scale object's four arguments are 0.0 1.0 0.0 10.0 and the value 0.5 was sent to the left inlet, then the scale object would return 5.0, because this value is to 0.0 and 10.0 what 0.5 is to 0.0 and 1.0.

Because we know that our source signal is a sinewave oscillator, we also know that the minimum data range that is generated by the sinewave is -1.0 and the maximum is 1.0. Hence, the first two arguments (the low input and the high input). The high output value is set by the mod depth number box as it is connected to the second-most-right inlet of the scale object.

This stream of numbers, as determined by the speed of the sinewave and the high output value of the scale object is then simply added to the user-determined starting point. This is how the modulation signal is integrated into the original structure of the patch.