In this tutorial, we will be working with time – objects that deal with time, and a set of objects that work with a metrical timing system built into Max. Additionally, we will look at some of the decision-making object that use logic operations to determine program flow.
As we’ve seen in earlier tutorials, the use of time is an important part of working with Max programs; time-based objects can help create generative music and drawing actions, and can be used in conjunction with user actions to create playable programs. The metrical timing system introduced in Max 5 provides a centralized 'musical' timing system that can provide transport controls, bar/beat feedback and can even cause scheduled events to fire at specific points in time. Part of this is the ability to define specific times and time durations in metrically-significant ways.
In addition to the time functions, we also cover some of the boolean comparison objects – objects that create logical results based on comparing and testing input values. These are especially useful when working with time-based objects, since you will often want to make operational decisions based on comparisons between the current time and some desired time or sub-time function.
The left-hand side of the tutorial has five small patches that show the operation of a handful of new objects that deal with time in Max. Patch
1 provides an example of the
pipe object, which can be used to delay the throughput of any message or list for a certain amount of time. The argument provides the default delay time – in this case,
1000 milliseconds (1 second). If you change the value of the
left number box, you will see your changes reflected in the output 1 second later. The delay time can be changed with a value sent to the
right inlet – an integer value is used to set the number of milliseconds of delay time. Change the right-hand number box to
2000, then change the left input – you will now see the changes occur after a 2 second delay.
The next small patch, labeled
2, is an example of the
delay object. This is very similar to the
pipe object, except that it is specifically tailored to delay
bang messages. Like
pipe, the
delay time can be changed by sending an integer value into the right inlet.
Patch
3 shows the
clocker object, which is closely related to the
metro object. The primary difference between
clocker and
metro is the output: while the
metro object produces
bang messages at regular intervals,
clocker outputs the overall
elapsed time (in milliseconds) since it was started with a
bang. Clicking on the
stop message will stop the
clocker, while sending a new
bang message to clocker will restart the timer and the elapsed time measurement.
The patch labeled
4 demonstrates another object that counts elapsed time: the
timer object. This object takes
bang messages in both inlets; the
left inlet starts the interval to be timed, and the
right inlet stops the interval. The
timer object is therefore equivalent to a
stopwatch; click the left-hand
button to start the clock, wait a few seconds, and then click the right-hand
button to see how much time has elapsed. Note that
timer is a slightly unusual Max object in that its
right inlet is
hot (produces output), not its
left inlet.
The fifth patch shows some of the most common boolean objects in action. The
> (greater-than) object implements a
comparison function; it outputs a
1 if the incoming number is great than the number used as an argument, or a
0 if it is not; the object therefore performs a test, reporting TRUE (
1) or FALSE (
0). The
< (less-than) object is the inverse, reporting true or false (
1 or
0) depending on whether the input is lower than the object's argument. Both of these symbols are in common use across mathematics. The remaining logical objects will probably be more familiar to programmers. The
== (equals) object reports a
1 only when the incoming number is
equal to the argument, while the
!= (not equals) is its inverse – it reports a
1 when the incoming number is not equal to the argument. As with many Max objects, a new comparison operator can be supplied by a number sent into the
right inlet of the object.
The final two logical operators are
&& (logical AND) and
|| (logical OR). In the first case, the
&& object will output a
1 only if
both numbers sent into the left and right inlets are
non-zero; otherwise, it reports a
0. In the second case, the
|| object will output a
1 when
either the left or the right inlets are sent non-zero values. In all other cases, this object will output a
0.
Change the
number box at the top of the patch containing these objects and you'll see how they behave. Notice how the only time all six operators yield a
1 is when the input value is
10.
The large patch on the right (labeled
6) is a performance patch that uses all of these new objects (and some old ones) within the context of the
metrical timing system. Start the patch (using the large yellow
toggle at the top); you will hear a steady beat out of your default MIDI synthesizer, with variations that occur at different times throughout the performance. The performance is looped, rerunning itself every 16 measures. In order to understand this rather complex patch, we first need to understand the metrical time system.
Metrical timing is based on a central time-keeping object – the
transport – and its ability to broadcast time information to other objects that are expecting timing information. Many of these “listening” objects are already familiar to us (
metro) or have been introduced in this tutorial (
clocker). We have used these objects in self-clocking mode, where they use a timebase expressed in milliseconds to produce output at regular times. When used in conjunction with the
transport object, however, time is expressed using
metrical notation: values such as
4n (for a quarter-note) can be used as an alternative to millisecond timing notation.
The
transport object is the central time keeper, and provides time in
bar/beat/tick format – all driven by a
tempo and a
time signature. Hence, if a transport is set to run with a time signature of
4/4 and at a tempo of
120 beats-per-minute, each beat will take 500 milliseconds, and a measure will take 2 seconds (2000 milliseconds). In order to see all the ways the
transport controls this patch, we need to break it down into sections.
Our “big checkbox” starts the process – mainly because it starts the
transport. The
transport accepts a
1/
0 message for start and stop, which is perfect for control by a
toggle. The
toggle also starts the
metro to the left (part of the “red” section of the patch), which uses
4n as the timing argument. In the past, we’ve used milliseconds for the
metro object's timing, but in this case, the
4n notation means that it will send out a
bang every
quarter note, based on the timing broadcast by the transport. The output of this
metro goes to two places: to a
message box that creates a MIDI note
70 (via
makenote), and back to the
transport. Why does it get routed to the
transport? Because a
bang message received by the
transport will cause it to output the current time, which we display in three
number boxes connected to the first three outlets of the object. This time is displayed in bars, beats and ticks, respectively. There are
480 ticks per beat (480 pulses-per-quarter-note, or
ppq, by default).
Next, let’s look at the “green” section of the patch. This uses the bar count (as output by the
transport), and uses the
> and
< objects to determine if we are between bars
4 and
8. If we are in that range, it turns on another
metro that is clocked at
16n (16th notes). This generates a random number used to create one of four values (using the
random and
select objects); these numbers are sent to the
makenote object to generate MIDI notes. That value is also sent to a
pipe object with an argument of
4nd, which delays the values by a dotted-quarter note. This output has
7 added to it, causing output that, when sent to the MIDI synthesizer, sounds a perfect fifth above the original. The result is a rapid-fire set of notes for a four-bar segment of the loop.
Let’s now dig into the “blue” section, which is the part of the patch that forces the sequence to
loop. The head of this section of the patch is a new object:
timepoint. The
timepoint object takes a metrical time as an argument; its sole purpose is to generate a
bang message when that metrical time is reached by the
transport. In this case, when we reach the beginning of the measure
17 (i.e. after sixteen measures), a four-note chord of numbers is sent to the
makenote, then a
bang message is sent all the way back to a
message connected to the right inlet of the transport. This message (
0.) tells the transport to immediately jump to tick
0. of the sequence, i.e. bar 1, beat 1 and tick 0. This is how we force the sequence to loop: every time we hit the 17th bar, we are immediately sent back to the beginning.
Next, we should decode the “brown” section of the patch. It uses
key and
select objects to watch for the space bar (ASCII
32) being depressed. When this happens, a
timer object receives a
bang on
both inlets. Because Max objects transmit messages in right-to-left order, hitting the space bar simultaneously stops the
timer with a
bang to the
right (and outputs the elapsed time) and restarts it for another measurement with a
bang to the
left. The object outputs the elapsed time in milliseconds; we divide
60000 (the number of milliseconds in a minute) by the time to get a bpm (beats-per-minute) value.
However, we want this bpm value to fit in a sensible range, so we use the
split object, and only use values that are between
30. And
200.. If we are within this range,
split will output the number out its
left outlet, and the value gets used to create a
tempo message that is sent to the
transport. This will immediately change the playback tempo of the sequence, and will change the relative speeds of many (but not all) of the objects used to create our sequence. If the
timer outputs a value outside of the
split object's range (for example, if we haven't hit the space bar in a while), it will be sent out the right outlet of the object and discarded.
One of the reasons that not all of the sequence is changed is because some of the patch functions use standard (millisecond) timing rather than metrical time. The right-most (“blue-and-purple”) section of the patch is a good example. A
timepoint object is fired when the transport time is at bar
6, beat
3. This starts a
clocker that runs for
10 seconds (10,000 milliseconds), producing a two-note chord every quarter-note (
4n). The
timepoint object also sends a
bang to a
delay 3000 object, which produces a two-note chord
3000 milliseconds after it receives the message. At a very slow tempo, this may occur only a few beats after the
timepoint is fired, while at rapid tempo, if may take many bars. This is a good example of how metrical and standard time can be mixed and matched for interesting generative results. Note that the
clocker object's running time is compared against a
> object to switch itself off after 10 seconds... a
timer object tracking the interval between the start and stop of the
clocker object confirms this.
Another thing that can cause variant behavior in this patch is the Overdrive setting, found in the Options menu. If Overdrive is checked (on), event processing and calculations are given priority over screen-drawing and graphical processes. When Overdrive is unchecked (off), drawing processes get the same priority as event handling. Depending on the nature of your patch, Overdrive may provide better performance for time-critical event handling tasks.
Since our tutorial patch is, in fact, time-critical, it will perform best with Overdrive turned on. If Overdrive is off, you may see that the transport output display doesn’t always occur on the 0 tick, and you may find that playback gets jerky when the density of notes is high or you perform other tasks on your computer (such as opening another patcher in Max). Modern computers are incredibly fast, but our ears are very sensitive to timing discontinuities. Therefore, when working on patches that sequence events in a time-critical way, it is normally advisable to turn Overdrive on.
In this tutorial, we covered an extensive new system within Max: the
metrical timing system. This provides a centralized timing control for your Max patch, coordinating the generation of events and easing the creation of musical sequences. We also learned about several new time-based object, including
pipe,
delay,
clocker, and
timer, which can be used (either with or without metrical timing) to create sequenced actions. Finally, we saw the use of boolean comparison and logical operator objects to aid in decision making within a complex patch.
See Also
Name |
Description |
pipe |
Delay numbers or lists
|
delay |
Delay a bang before passing it on.
|
clocker |
Report elapsed time, at regular intervals
|
timer |
Report elapsed time between two events
|
> |
Is greater than, comparison of two numbers
|
< |
Is less than, comparison of two numbers
|
== |
Compare two numbers, output 1 if they are equal
|
!= |
Compare two numbers, output 1 if they are not equal
|
&& |
If both numbers are non-zero, output a 1
|
|| |
If either of two numbers is non-zero, output a 1
|
transport |
Control a master clock and report time values.
|
timepoint |
Output a bang when a transport reaches a specific time.
|
split |
Look for a range of numbers
|