Max 5 API Reference
00001 /** 00002 @page chapter_inout Inlets and Outlets 00003 00004 You are familiar with inlets and outlets when connecting two objects together in a patcher. To receive data in your object or send data to other objects, you need to create the C versions of inlets and outlets. In this section, we'll explain what inlets and outlets are, how to create them, and how to use them. We'll also discuss a more advanced type of inlet called a proxy that permits a message to be received in any of your object's inlets. Proxies are used by audio objects to permit inlets to handle both signals and normal Max messages. 00005 00006 By default, every object shows one inlet. Additional inlets appear to the right of the default inlet, with the rightmost inlet being created last. 00007 00008 Inlets are essentially message translators. For example, if you create an int inlet, your object will receive the "in1" message instead of the "int" message when a number arrives at this newly created inlet. You can use the different message name to define special behavior for numbers arriving at each inlet. For example, a basic arithmetic object in Max such as + stores the number to be added when it arrives in the right inlet, but performs the computation and outputs the result when a number arrives in the left inlet. 00009 00010 Outlets define connections between objects and are used to send messages from your object to the objects to which it is connected. What is not obvious about an outlet, however, is that when you send a number out an outlet, the outlet-sending function does not return until all computation "below" the outlet has completed. This stack-based execution model is best illustrated by observing a patch with the Max debugger window. To understand this stack-based model it may be helpful to use the breakpoint and debugging features in Max and follow the stack display as you step through the execution of a patch. Outlets, like inlets, appear in the order you create them from right-to-left. In other words, the first inlet or outlet you create will be the visually farthest to the right. 00011 00012 00013 @section chapter_inout_inlets Creating and Using Inlets 00014 00015 Proper use of an inlet involves two steps: first, add a method that will respond to the message sent via the inlet in your initialization routine, and second, create the inlet in your new instance routine. (Creating inlets at any other time is not supported.) 00016 00017 There are three types of inlets: int, float, and custom. We'll only describe int and float inlets here because proxies are generally a better way to create an inlet that can respond to any message. For int inlets, you'll bind a function to a message "in1", "in2", "in3" etc. depending on the inlet number you assign. Here's how to create a single inlet using "in1"... 00018 00019 In your initialization routine: 00020 @code 00021 class_addmethod(c, (method)myobject_in1, "in1", A_LONG, 0); 00022 @endcode 00023 00024 In your new instance routine, after calling object_alloc() to create your instance: 00025 @code 00026 intin(x, 1); 00027 @endcode 00028 00029 The method that will be called when an int is received in the right inlet: 00030 @code 00031 void myobject_in1(t_myobject *x, long n) 00032 { 00033 // do something with n 00034 } 00035 @endcode 00036 00037 Creating a single inlet in this way gives your object two inlets (remember that it always has one by default). 00038 If you want to create multiple inlets, you'll need to create them in order from right to left, as shown below: 00039 @code 00040 intin(x, 2); // creates an inlet (the right inlet) that will send your object the "in2" message 00041 intin(x, 1); // creates an inlet (the middle inlet) that will send your object the "in1" message 00042 @endcode 00043 00044 Inlets that send float messages to your object are created with floatin() and translate the float message into "ft1","ft2","ft3" etc. Example: 00045 00046 In initialization routine: 00047 @code 00048 class_addmethod(c, (method)myobject_ft1, "ft1", A_FLOAT, 0); 00049 @endcode 00050 00051 In new instance routine: 00052 @code 00053 floatin(x, 1); 00054 @endcode 00055 00056 Method: 00057 @code 00058 void myobject_ft1(t_myobject *x, double f) 00059 { 00060 post("float %.2f received in right inlet,f); 00061 } 00062 @endcode 00063 00064 Note that you can mix int and float inlets, but each inlet must have a unique number. Example: 00065 @code 00066 intin(x, 2); 00067 floatin(x, 1); 00068 @endcode 00069 00070 00071 @section chapter_inout_outlets Creating and Using Outlets 00072 00073 You create outlets in your new instance routine. Outlet creators return a pointer that you should store for later use when you want to send a message. As with inlets, outlets are created from right to left. 00074 00075 Here's a simple example. First we'll add two void pointers to our data structure to store the outlets for each instance. 00076 @code 00077 typedef struct _myobject 00078 { 00079 t_object m_ob; 00080 void *m_outlet1; 00081 void *m_outlet2; 00082 } t_myobject; 00083 @endcode 00084 00085 Then we'll create the outlets in our new instance routine. 00086 @code 00087 x = (t_myobject *)object_alloc(s_myobject_class); 00088 x->m_outlet2 = bangout((t_object *)x); 00089 x->m_outlet1 = intout((t_object *)x); 00090 return x; 00091 @endcode 00092 00093 These outlets are type-specific, meaning that we will always send the same type of message through them. If you want to create outlets that can send any message, use outlet_new(). Type-specific outlets execute faster, because they make a direct connection to the method handler that will be called at the time you send a message. When we want to send messages out these outlets, say, in our bang method, we do the following: 00094 00095 @code 00096 void myobject_bang(t_myobject *x) 00097 { 00098 outlet_bang(x->m_outlet2); 00099 outlet_int(x->m_outlet1, 74); 00100 } 00101 @endcode 00102 00103 The bang method above sends the bang message out the m_outlet2 outlet first, then sends the number 74 out the m_outlet1. This is consistent with the general design in Max to send values out outlets from right to left. However, there is nothing enforcing this design, and you could reverse the statements if you felt like it. 00104 00105 A more general message-sending routine, outlet_anything(), will be shown in the @ref chapter_atoms section. 00106 00107 00108 @section chapter_inout_proxies Creating and Using Proxies 00109 00110 A proxy is a small object that controls an inlet, but does not translate the message it receives. Instead it sets a location inside your object's data structure to a value you associate with the inlet. If the message comes "directly" to your object via the left inlet, the value will be 0. However, in order to be thread-safe, you should not read the value of this "inlet number" directly. Instead, you'll use the proxy_getinlet() routine to determine the inlet that has received the message. 00111 00112 The advantage of proxies over regular inlets is that your object can respond to any message in all of its inlets, not just the left inlet. As a Max user, you may already appreciate the proxy feature without knowing it. For example, the pack object can combine ints, floats, lists, or symbols arriving in any of its inlets. It uses proxies to make this happen. MSP audio objects that accept signals in more than one inlet use proxies as well. In fact, the proxy capability is built into the way you create audio objects, as will be discussed in the @ref chapter_msp_anatomy section. 00113 00114 If your object's non-left inlets will only respond to ints or floats, implementing proxies is usually overkill. 00115 00116 @section chapter_inout_example Example 00117 00118 First, add a place in your object to store the proxy value. You shouldn't access this directly, but the proxy needs it. Second, you'll need to store the proxy, because you need to free it when your object goes away. If you create many proxies, you'll need to store pointers to all of them, but all proxies share the same long integer value field. 00119 00120 @code 00121 typedef struct _myobject 00122 { 00123 t_object m_obj; 00124 long m_in; // space for the inlet number used by all the proxies 00125 void *m_proxy; 00126 } t_myobject; 00127 @endcode 00128 00129 In your new instance routine, create the proxy, passing your object, a non-zero code value associated with the proxy, and a pointer to your object's inlet number location. 00130 00131 @code 00132 x->m_proxy = proxy_new((t_object *)x, 1, &x->m_in); 00133 @endcode 00134 00135 If you want to create regular inlets for your object, you can do so. Proxies and regular inlets can be mixed, although such a design might confuse a user of your object. 00136 00137 Finally, here is a method that takes a different action depending on the value of x->m_in that we check using proxy_getinlet(). 00138 00139 @code 00140 void myobject_bang(t_myobject *x) 00141 { 00142 switch (proxy_getinlet((t_object *)x)) { 00143 case 0: 00144 post("bang received in left inlet"); 00145 break; 00146 case 1: 00147 post("bang received in right inlet"); 00148 break; 00149 } 00150 } 00151 @endcode 00152 00153 */
Copyright © 2008, Cycling '74