Tutorial 16: Remote Messaging
So far in these tutorials, all messages between objects have been sent using patch cords. Here we investigate another way to transfer messages between objects – by using the remote messaging mechanism built into Max. There are specific objects for this called, send and receive, as well as methods for sending a message box's contents to an object. Finally, we use some objects that allow the storage of variable data and can share values throughout complex patches.
As our designs get more complex, we need to take advantage of more techniques to modularize and compartmentalize our patches, making them easier to understand, maintain and extend. When our patches contain a lot of objects, long winding patch cord connections can cause unnecessary visual complexity. Using tools that allow message passing without patch cords can clean up our patches without changing how they operate.
To open the tutorial patch, click on the green Open Tutorial button in the upper right-hand corner of the documentation window.
The most common way to send messages without patch cords is through the use of the send and receive objects. If we look at the tutorial patcher, the top left part of the patch shows a pairing of send and receive objects that pass an integer value. When we change the top number box, the integer value is passed to the send object. This value is “broadcast” throughout the Max environment, and can be “received” by any receive object with the name . In this case, the receive object immediately below gets the message, and outputs it to be displayed in the attached number box.
Since a message is broadcast by name, any receive object sharing the send object's can receive the message. Hence, you can have more than one receive object for a message, and more than one send object sending messages using a single . In our test patch, the slider system connected to the send object does just that – it sends the value (set with either the slider or the number box) to all receive objects of messages. At the lower right of the patch are two different receive objects: one connected to a slider system, while another is connected directly to a number box. Changing the top slider value changes both of the receiving values simultaneously.
When working with pairs of send and receive objects, it’s important to realize that the message will be grabbed by any receive object anywhere in the Max environment – even in another patcher. To test this, open a new patcher (selecting New Patcher from the File menu). Add a new object, and enter receive . Connect the output of the receive object to a number box, then return to the tutorial patch. If you change our test slider, you will see the output received in the new patch as well as all other receive objects in the main patcher.
When working with other people’s patches, you may see objects named “s” or “r”. Since the send and receive objects are so popular, they have alias (or shorthand) names of “s” and “r” respectively. In addition to being easier to type, these shortcut names also allow you to keep the object small!
Working with forwardand
Another common use of remote messaging is to send the contents of a message box to one or more receive objects. Rather than always having to connect message boxes to send objects, there is a special format of the message box that can directly send messages to receive objects. This involves placing a semicolon ( ) and the of a receive object in front of the message. Thus, is the equivalent of sending the value to any receive object named .
The large gold message box, you will see that the two lcd objects each receive the appropriate draw commands through their respective receive objects - the top lcd ( ) draws a blue circle; the bottom lcd ( ) draws a green rectangle.box at the bottom-left of the patch is an example of direct message sending. Four messages are generated: two are sent to destinations, and two are sent to destinations. If you click on the
Another common need we might have is to send messages to one of several destinations based on some logical criteria. This can be done using switching logic (e.g. the gate object connected to different send objects). However, we can also use the forward object to route messages to selected locations. Just above our green message box example is a forward object with supporting logic.
You can select which named destination should receive the messages from forward: the message, sent to the forward object, will set the destination name for any incoming messages. Once a destination has been set, all messages will be routed appropriately. If we click on the message, changes to the number box will cause a colored rectangle to be created in the first lcd object. If we click on the message, it changes the output destination of the forward object to the receive object. Now, any changes to the number box will cause the creation of a rectangle in the second lcd object.
By a similar token, a receive object with no arguments can be used to receive messages from any named send or appropriately configured forward or message box. The message to the receive object allows it to "switch names", so to speak, so that it can listen to different parts of the patcher. Next to our forward object is one such receive object. By switching it between listening to and (using the message boxes) and then manipulating the number box and slider object sending to those names, we can see how this works. Setting the receive to a that doesn't exist (e.g. ) will disconnect it from listening to anything in our patch. Note that in order for a receive object to be "switchable", it needs to be created without an argument (otherwise it has no inlet).
Sometimes, when working with patch programming, we need a place to stash a value until we need to use it. While we could use integer and floating-point number boxes, those objects have the possibility of having the user change the value through their UI. In order to provide simple variable storage and recall, there are a set of objects specifically used for this purpose. The int and float objects can act as temporary storage of values; values coming in the left inlet are stored and sent out the outlet immediately, while values coming in the right inlet are stored without output. If you want to retrieve the value stored in an int or float object, you need to send a message (such as from a button object) into the left inlet.
One of the advantages of the int and float objects is that you can initialize the value without using any other message. The argument provided with the object gives it an initial value; there is no need to set these up with loadbang messages or other functions. If we look at the int and float objects to the right of our patch, we can see how they work. They do not send their value when the patch is loaded; sending a into them with the button objects outputs their initialized value. Once we start entering numbers into them from the number boxes, those new values are stored inside the objects until we them again.
In our patch, just below the int and float object, we find a pair of objects called value. The value object is like a combination of an int/float object and a send/receive pair – each of the value objects has a (in this case, ), and any values placed in one of the objects is shared with all of the other value objects with that . One thing to notice is that sending a message into a value object does not trigger output – you need to explicitly force output by sending a message to the object. In our test patch, change the value of one object then the other: you will see that the value is magically transferred from one to the other.
Unlike the int and float objects, the object can take messages of any type; symbols, integers and floats can all be stored by the value object, as can a list. While this is convenient, it also means that you need to be careful about the objects that you connect to a value object to display the output. Our test patch has number boxes connected to the value objects; however, it might be better to connect a message object (by the right inlet) or other display object that can accept a diverse set of values.
Being able to send messages without having to connect patch cords can give us more flexibility in patch layout, and can also allow us to produce and route messages throughout very complex patches. The send and receive objects are the most common way of passing messages around a patch, but the use of notation in message boxes and the use of the forward object for routing can help minimize the visual complexity of our work. Finally, we saw how values can be stored in the int and float objects, and how a value object can be used to both store and forward data that is sent into it. All of these objects and techniques are important for working with complex patching systems.