Excursus of microscopic timing and message ordering
We have just mentioned the timing on a macroscopic level; let's quickly introduce one of the most important things we'll learn about Max: microscopic timing, as I call it, or message ordering. Consider this situation: we'd like to use a MIDI [notein]
object to trigger a custom sequence or function. So, we know that we need to construct something that takes pitch and velocity and produces a bang (always a good design strategy; think what's coming in and out, and then what needs to be in there). Simple enough. However, [notein]
also sends note-off messages (a velocity of zero), so we need to filter those out. On an incoming velocity, we first need to test it (is it zero?) and then decide whether to pass it or not. That's what you can see in the following patcher; it's the contents of the ourStripnote
subpatcher contained in the Micro_Timing_and_Message_Order.maxpat
patcher:
The first do this then do that process is expressed by [t i i]
. It's an abbreviation for [trigger i i]
, and [trigger]
is an object that lets us manage timing. It takes its input and sends it out as the given data type (i stands for integer) as many times as the argument is provided, in this case twice, and it sends everything in the right-to-left order, because Max is evaluating things in a right-to-left, depth-first order. So what happens here is that velocity comes in first (since [notein]
, as all the objects, sends out its right outlets first), then it's tested for unequality to zero, [!= 0]
, the result being either one or zero, which is sent to the gates and causes them to pass (or not) what's coming in their right inlets. Then, the velocity is sent to the right [gate]
object and out (or not), and then the pitch comes through (or not). What if we don't care about this? Max needs to order things at least somehow if we don't state the order of things explicitly, so it does it in the right-to-left order, as shown in the following screenshot:
That's not what we want. We don't want to cause our patches to function differently if we were to move stuff around, say, for example, just to tidy up a bit. Study the mentioned patcher and the help-patcher of [trigger]
carefully; there is a reason why there is an abbreviation, [t]
, for it; we'll need it all the time.
Note
Exercise
Take another example of ordering; we want to detect the change of a running float. For example, we have somehow tracked 3D or 2D data of a person moving, and we just want to use the "how much is she or he moving" data to control the amplitude of a track or something similar. Essentially, we want to take the derivative of the data. Therefore, we'd like to always subtract the last value received from the one at the input of our imagined derivative patch and output the result. Sounds simple, right? Beware; the simple things are the most important and hard ones. Give it a try though! There are multiple solutions but here is a possible hint: you'll need these objects: [- 0.]
, [f ]
, and [trigger f b]
, or just the [- 0.]
and [t f f]
objects.
The answer is also in the Micro_Timing_and_Message_Order.maxpat
patcher. Have you been able to make it? If yes, congratulations, you have constructed a simple high-pass filter and you are far ahead in the MSP/gen~ section of the book!
But wait a moment; so all this is right to left? How unintuitive can this be for a person used to left-to-right writing systems? What's the logic here? The logic simply is that if we want the leftmost inlet to be the most important and hot one (following a left-to-right idea), the global right-to-left idea rises as a consequence. Therefore, we typically (take [f]
or [*]
as an example) first store a value at the right inlet and then trigger the output at the left inlet.