Tutorial 5: Message Order and Debugging
Introduction
This tutorial is focused on message ordering – the sequence in which messages are passed from object-to-object and how objects generate them. We will also use some of the debugging tools in Max to determine how a patch is running.
Max patches often seem to have everything happening at once; in reality, messages are produced and acted upon in a specific order. In order to make patches that operate correctly, we need to understand the order in which things occur, and how to control complex matrices of actions.
To open the tutorial patch, click on the Open Tutorial button in the upper right-hand corner of the documentation window.
Right-to-left, bottom-to-top
Take a look at the tutorial patcher. This file has a number of small patches that we will use to learn about the rules that messages follow. Click on top-most button in the top-left patch; it seems that all three of the connected button objects fire simultaneously. This is an illusion – messages are sent down the patch cables in a sequential order.
The easiest way to see this in action is to use some of the debugging tools in Max. Select Enable Debugging from the Debug menu or click the Enable Debugging (wrench) icon at the bottom of the window. Also, ensure that Auto Step is turned off in the Debug menu. Now the top row of patches in the tutorial will have small small red circles with numbers in them covering the patchcords. These are called break watchpoints or just breakpoints. When we activate our patcher, operation will stop at each breakpoint. We can examine the state of things, then continue operation to the next breakpoint.
Click on the topmost button in the left-hand patch. Instead of the immediate flash of all the button objects lighting up, the rightmost patchcord shows a message path indicator - an animated green circle. In addition, a window opens called the Debug Window. The Debug window tells us that a bang
message has been intercepted by a breakpoint; moreover it tells us which breakpoint was tripped (in this case, breakpoint #1), the name of the patcher and what class of object the sending and receiving object was (in this case, both are button objects). Select Step from the Debug menu (or click on the Step button in the Debug Window toolbar). You will see that the middle cord flashes; select Step again and you will see that the left-most cord flashes. Select Step once more to finish our patch trace. When the outlet of an object is connected to more than one inlet, the messages are sent in right-to-left order.
But what happens when the receiving objects are stacked vertically (and therefore at the same horizontal position)? With debugging still enabled, click on the topmost button in the middle patch on the top row. As the breakpoints fire, select Step from the Debug menu. When we Step through the patch, we see that the bottom-most button receives its message first, followed by the middle, then the top. So, we can see that Max has two levels of ordering: right-to-left, then bottom-to-top.
There is one more twist to message ordering, and that is the effect of messages that cause more messages to be generated. The third patch illustrates the issue. Turn Autostep on for this one. Click on the top-most button and watch how messages step from object to object. We see that messages travel all the way to the bottom of one message chain before sending a message to the next patch cord in the branch. Notice that the Debug Window shows you the entire message chain in a branch; it only clears when a branch has been completed.
To sumarize, the message-ordering rules in Max are: 1.Right-to-left, or bottom-to-top for objects that are vertically aligned. 2.All actions on a branch are completed before the next branch is activated.
Note that, for determining the right-to-left or bottom-to-top ordering, it is the location of the connected inlet, not the path of the patchcords, that determines the message route.
As an exercise, create a new matrix of button objects, with both vertical and horizontal arrays of button objects (perhaps using the Align option from the Arrange menu), and multiple depths of objects. Use comment boxes to number the boxes in the order you think they will fire then trace the patch. Do this until the message ordering becomes second nature!
Select Disable Debugging from the Debug menu, and let's look at the other patches in the tutorial patcher. (For more about use of the debugger, see the Debugging guide.)
More message ordering
Sometimes it is inconvenient to match the spatial order of the patch with our desired result. In the bottom row of patches, the left-most patch is well structured: it is cleanly laid out, and has the number message s in ascending numerical order from left to right. If we wanted the messages to happen from low to high, it is also wrong; when we click the top button (button A), we see the numbers come out in reverse order in the Max Console. This is because the message boxes are receiving bang
messages in right-to-left order.
The next patch shows a corrected version of this patch, using a new object: bangbang. This object takes an incoming message and produces bang
messages from its outlets in right-to-left order. The number of outlets is determined by the argument to bangbang. The outlets are connected to the message boxes with crossed patchcords; when we click on button B, the Max Console displays the messages in the preferred order.
Notice that the outlets of bangbang fire in right-to-left order, mimicking the message ordering of branching patchcords. Most objects with multiple outlets will follow this rule: outputs are produced in order from the right-most outlet to the left-most outlet.
Using the bangbang object makes the message order explicit; that is, it forces the messages to follow a specific path regardless of spatial orientation, and lets us place objects anywhere we want in the patch, knowing that they will be triggered in well defined order based on which outlets we trigger them from. Another object that provides explicit ordering is the trigger object. The trigger object accepts any input, and outputs messages based on its arguments. The arguments determine the number of outputs and set their type, with options of l
for list, b
for bang
, i
for integer, f
for floating-point number and s
for symbol (a text message). You can also use specific integers, floating point numbers or symbols as constant outputs.
The next patch contains two message boxes with the integer 90
. If we click on the one labeled C, which is connected directly to the print objects, the Max Console displays the typical right-to-left ordering of messages. However, if we click on the message box (D) connected to the trigger object, we see that we can route the patchcords to print in left-to-right (reversed) order. The sent messages are all printed as the integer 90, since our arguments for trigger were are of type i
.
The next patch to the right also uses the integer message 90
, and is connected to a trigger object. (Note that the name "trigger" may be abbreviated “t”). However, in this case, three different arguments are used. When we click on message box E, we see that the output of the three outlets are all different, matching the difference in arguments. The right-most outlet, with argument of type i
, produces an unchanged 90
output. The middle outlet, set to type f
casts the incoming message to its floating-point equivalent of 90.000000. Finally, the left-most outlet, of type b
turns the incoming message into a bang
. So, in addition to making the message order explicit, the trigger object can do type conversion of messages.
The right-most patch shows the use of constant values within the trigger object. When we change the floating-point box F to any number, the trigger object again converts the incoming number, but also sends a number of symbols that are used as constant values. We also see that the incoming message did not change the output of the bang
outlet – a bang is a bang, regardless of incoming message type.
Now would be a good time to build some more button matrices, combining them with trigger and bangbang objects, and labeling them with their expected message order.
The trouble with loops
The final patch in our tutorial file is a set of four button objects all interconnected in a loop. When we click on any of the button objects, the system comes to a halt with a stack overflow error. Why did this happen? This circular construction is a feedback loop. One button sends to the next, which sends to the next, which sends to the next, which sends to the first, triggering the cycle all over again. If allowed to continue, these actions would freeze Max, requiring a Force Quit and the loss of all unsaved work. When this situation occurs, Max shuts off its scheduler to prevent anything else from happening; once we've found the problem and corrected it in our patch (e.g. by disconnecting one of the button objects in the loop), we can turn the patch "on" again by clearing the stack overflow message at the top of the patch.
Summary
A deep understanding of message ordering rules is necessary to create properly functioning patches. The right-to-left, bottom-to-top, go-to-the-end order is the implicit rule for message passing, but you can use objects like bangbang and trigger to make the ordering explicit. In all cases, however, you need to watch out for feedback loops – they are literal patch-killers!