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, 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 , 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 messages. Like pipe
, the delay
time can be changed by sending an integer value into the right inlet.
shows the clocker
object, which is closely related to the metro
object. The primary difference between clocker
is the output: while the metro
object produces messages at regular intervals, clocker
outputs the overall elapsed time
(in milliseconds) since it was started with a . Clicking on the message will stop the clocker
, while sending a new 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 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
The fifth patch shows some of the most common boolean objects in action. The >
(greater-than) object implements a comparison
function; it outputs a if the incoming number is great than the number used as an argument, or a if it is not; the object therefore performs a test, reporting TRUE ( ) or FALSE ( ). The <
(less-than) object is the inverse, reporting true or false ( or ) 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 only when the incoming number is equal
to the argument, while the !=
(not equals) is its inverse – it reports a 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 only if both
numbers sent into the left and right inlets are non-zero
; otherwise, it reports a . In the second case, the ||
object will output a when either
the left or the right inlets are sent non-zero values. In all other cases, this object will output a .
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 is when the input value is .
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 (for a quarter-note) can be used as an alternative to millisecond timing notation.
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 and at a tempo of 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 / 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 as the timing argument. In the past, we’ve used milliseconds for the metro
object's timing, but in this case, the notation means that it will send out a 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 (via makenote
), and back to the transport
. Why does it get routed to the transport
? Because a 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 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 >
objects to determine if we are between bars and . If we are in that range, it turns on another metro
that is clocked at (16th notes). This generates a random number used to create one of four values (using the random
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 , which delays the values by a dotted-quarter note. This output has 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 message when that metrical time is reached by the transport
. In this case, when we reach the beginning of the measure (i.e. after sixteen measures), a four-note chord of numbers is sent to the makenote
, then a message is sent all the way back to a message
connected to the right inlet of the transport. This message ( ) tells the transport to immediately jump to tick 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
objects to watch for the space bar (ASCII ) being depressed. When this happens, a timer
object receives a on both
inlets. Because Max objects transmit messages in right-to-left order, hitting the space bar simultaneously stops the timer
with a to the right
(and outputs the elapsed time) and restarts it for another measurement with a to the left
. The object outputs the elapsed time in milliseconds; we divide (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 And . If we are within this range, split
will output the number out its left
outlet, and the value gets used to create a 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 , beat . This starts a clocker
that runs for seconds (10,000 milliseconds), producing a two-note chord every quarter-note ( ). The timepoint
object also sends a to a delay
object, which produces a two-note chord 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
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
, 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.
Delay numbers or lists
Delay a bang before passing it on.
Report elapsed time, at regular intervals
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
Control a master clock and report time values.
Output a bang when a transport reaches a specific time.
Look for a range of numbers