- Multimedia Programming Using Max-MSP and TouchDesigner
- Patrik Lechner
- 1538字
- 2021-08-05 17:21:22
Basic Max patching and GUI
Now, we'll finally go ahead and start patching. In this section, we'll go through the basic functionality of Max and we will talk a bit about the craft of patching. It's an easy thing, but we will try to explore a lot about shortcuts and ways to go about things faster. Additionally, we will of course get to know the GUI in more detail. Nearly all patches you see here can of course be used readily as they are from your download. On the other hand, you will learn more if you just replicate them quickly, or even better, understand them and rebuild the principle without looking at them more than once. Often, there are many ways to achieve a goal and the number of ways to achieve the goal increases with the complexity of the goal.
Objects in Max
Before we dive into patching, let's just very briefly think about the Max objects that we have been talking about. We can imagine them as small virtual machines (Cipriani and Giri 2010, 51) that fulfill different tasks for us. I think you have a basic understanding of what they are by now, but there is something left to say about how they work. Look at the following screenshot. We see an [int]
object. The bubbles appear when you hover over the corresponding inlets and outlets but of course, usually not all at the same time, as shown here. As you can see, there is a red ring on the left hot inlet and a blue one on the right cold inlet.
These are called hot and cold and also illustrated that way (as shown in the following screenshot) because if we send a message, for example, an integer into the right or cold inlet, there won't be any output. It will be stored there as it says in the bubble, whereas if we send an integer or bang message into the left or hot inlet, it will output the integer or the last stored integer in the case of the bang message:
The concept of hot and cold inlets is everywhere in Max, and we will encounter it at the beginning or maybe later when we discuss message ordering. The important thing is to keep this idea in mind.
Note
Technical detail
Max objects are written in C. When we create a Max object, we create an instance of the compiled C code that represents that object. If we send a message into it, we are simply calling a function within that code.
Arguments
You have already seen arguments, for example, in an previous illustration. There, we saw a [* 0.]
object. It is, in fact, a [*]
object with the argument 0
. In general, arguments can fulfill two purposes:
- To initialize a variable
- To set a constant regarding the object's behavior
In the case of our [* 0.]
object, we do both at the same time. When we start up our patcher and just send something into the left inlet, we do a multiplication by 0. As soon as we send another number, for example, 5 into the right inlet, the zero is overridden and we multiply by 5. So here, we initialized one of the factors. However, what constant did we set at the same time?
We didn't just initialize the multiplication with 0. Guess what's the default initial value; 0 of course. So why did we do it? Because we didn't put 0 in there but 0. Many math (and other) objects operate with integers by default. If we initialize them with a float argument, we tell the object to do floating point calculations, thereby defining its internal behavior constantly, shown as follows:
In the preceding screenshot, you can see the different behavior of two differently initialized [*]
objects. The integer one simply performs a floor operation, cutting way everything behind the decimal point. Remember that this is one of the most common mistakes both for beginners and advanced users. It is recommended that you initialize everything explicitly (instead of relying on the default value because we know or believe it will suit our needs) because it makes patches more readable. Also, it's a psychological trick; we at least think about the value very shortly and state it explicitly instead of hiding it, setting the value implicitly, and maybe a little more unconsciously.
We can find what arguments are obligatory or optional for any given object in its help patch, in the object's reference, or in the descriptions that pop up after you typed a space after the object name of the object you are creating, as shown in the following screenshot:
The syntax is important here; we always get the object name, a single space, the arguments separated by spaces, and the attributes afterwards, whereas each attribute is preceded by @, and all of them are also separated by spaces:
In the previous screenshot, we can see a [metro]
object that has been given one argument, its interval in ms, and with one attribute set, active 1
, which means it is switched on. Before we go looking at attributes, we will consider another example of arguments, that is, the ones of the [counter]
object:
In this case, we are provided with three arguments: 1, 0, 5. These are as follows:
- The mode of the counter where 0 means count forwards, 1 means backwards, and 2 means back and forth
- The minimum of the count
- The maximum of the count
So this counter counts from 5 down to 0. Most of the time, we need something that counts from 0 to some number, so up, not down. In this case, we can provide only one argument and it will be taken as the maximum. If we provide two arguments, it will be taken as minimum and maximum. Pretty confusing, right? That's something we will get used to, but if there are more parameters, nobody wants to count how many arguments are provided to identify their function. That's where attributes come into play.
Attributes
As we have seen, attributes are like arguments, but they have the advantage of being able to be addressed by writing their name instead of knowing (or looking up often) that the property we are searching for can be set via, say, the third argument.
For example, the [dosomething 1000 0.5 0.9]
object won't mean a lot to us in a couple of weeks, whereas [dosomethin @cutoff 1000 @resonance 0.5 @gain 0.9]
is a lot more descriptive.
Most of the time, we cannot choose whether we address something as an attribute or an argument (for our own abstractions we can!); some properties can be set via one while some by the other. The [metro]
object's interval can be set via the first argument or the argument @interval n
, but that's an exception. For the sake of completeness, the attribute overrides the argument in the rare case in which we will provide both [metro]
and @interval n
at the same time. It might seem that arguments and attributes are doing more or less the same work, but internally, they are substantially different. Their actual difference doesn't matter for us too much, but we just want to get a feeling of whether a property is an attribute or an argument. Arguments initialize and attributes configure an object.
All attributes are collected in the inspector. Remembering the shortcut for the inspector, the shortcut is command/Ctrl + I (this only works if we have an object selected), will help us a lot since we can then also select an attribute by typing the name of the attribute, then hit Enter, and configure everything as we like in a very fast manner. The following screenshot shows the inspector window:
The inspector has a couple of built-in gadgets that help us a lot. By pressing the little @ sign at the bottom of the inspector's screen. We can see the attribute names, so we can remember it and type it into the object box next time we need it. Also, we can just drag-and-drop attributes out of the inspector into our patches. This will cause a little pop up to open, asking us what should be done. Try out these options; they are all made to interact with the chosen attribute in a manual or programmatic manner or to monitor it. This saves us a lot of typing.
Note
You can set all attributes with the inspector but you can't access all of them with the @
notation or dynamically using messages. There are ways around this limitation, which we will cover in the section on scripting using the [thispatcher]
object.
If we configure a certain attribute in the inspector, it can happen that our newly set value is not saved. Some attributes are simply not saved by default, but we can change this by freezing the attribute. We can find out whether an attribute is being saved if we look at their names. Those that are written in italics won't be saved; those that are not will be. Those that are written in italics will also be available through the @
notation and therefore can be set permanently this way as well.