Max 5 API Reference
00001 /** 00002 @page chapter_msp_anatomy Anatomy of a MSP Object 00003 00004 An MSP object that handles audio signals is a regular Max object with a few extras. Refer to the <a href="simplemsp~_8c-source.html">simplemsp~</a> example project source as we detail these additions. simplemsp~ is simply an object that adds a number to a signal, identical in function to the regular MSP +~ object if you were to give it an argument of 1. 00005 00006 Here is an enumeration of the basic tasks: 00007 00008 1) additional header files 00009 00010 After including ext.h and ext_obex.h, include z_dsp.h 00011 @code 00012 #include "z_dsp.h" 00013 @endcode 00014 00015 2) C structure declaration 00016 00017 The C structure declaration must begin with a #t_pxobject, not a #t_object: 00018 @code 00019 typedef struct _mydspobject 00020 { 00021 t_pxobject m_obj; 00022 // rest of the structure's fields 00023 } t_mydspobject; 00024 @endcode 00025 00026 3) initialization routine 00027 00028 When creating the class with class_new(), you must have a free function. If you have nothing special to do, use dsp_free(), which is defined for this purpose. If you write your own free function, the first thing it should do is call dsp_free(). This is essential to avoid crashes when freeing your object when audio processing is turned on. 00029 @code 00030 c = class_new("mydspobject", (method)mydspobject_new, (method)dsp_free, sizeof(t_mydspobject), NULL, 0); 00031 @endcode 00032 00033 After creating your class with class_new(), you must call class_dspinit(), which will add some standard method handlers for internal messages used by all signal objects. 00034 @code 00035 class_dspinit(c); 00036 @endcode 00037 00038 Your signal object needs a method that is bound to the symbol "dsp" -- we'll detail what this method does below, but the following line needs to be added while initializing the class: 00039 @code 00040 class_addmethod(c, (method)mydspobject_dsp, "dsp", A_CANT, 0); 00041 @endcode 00042 00043 4) new instance routine 00044 00045 The new instance routine must call dsp_setup(), passing a pointer to the newly allocated object pointer plus a number of signal inlets the object will have. If the object has no signal inlets, you may pass 0. The <a href="simplemsp~_8c-source.html">simplemsp~</a> object (as an example) has a single signal inlet: 00046 @code 00047 dsp_setup((t_pxobject *)x, 1); 00048 @endcode 00049 00050 dsp_setup() will make the signal inlets (as proxies) so you need not make them yourself. 00051 00052 If your object will have audio signal outputs, they need to be created in the new instance routine with outlet_new(). However, you will never access them directly, so you don't need to store pointers to them as you do with regular outlets. Here is an example of creating two signal outlets: 00053 @code 00054 outlet_new((t_object *)x, "signal"); 00055 outlet_new((t_object *)x, "signal"); 00056 @endcode 00057 00058 5) The dsp method and perform routine 00059 00060 The dsp method specifies the signal processing function your object defines along with its arguments. Your object's dsp method will be called when the MSP signal compiler is building a sequence of operations (known as the DSP Chain) that will be performed on each set of audio samples. The operation sequence consists of a pointers to functions (called perform routines) followed by arguments to those functions. 00061 00062 The dsp method is declared as follows: 00063 @code 00064 void mydspobject_dsp(t_mydspobject *x, t_signal **sp, short *count); 00065 @endcode 00066 00067 To add an entry to the DSP chain, your dsp method uses dsp_add(). The dsp method is passed an array of signals (#t_signal pointers), which contain pointers to the actual sample memory your object's perform routine will be using for input and output. The array of signals starts with the inputs (from left to right), followed by the outputs. For example, if your object has two inputs (because your new instance routine called dsp_setup(x, 2)) and three outputs (because your new instance created three signal outlets), the signal array sp would contain five items as follows: 00068 @code 00069 sp[0] // left input 00070 sp[1] // right input 00071 sp[2] // left output 00072 sp[3] // middle output 00073 sp[4] // right output 00074 @endcode 00075 00076 The #t_signal data structure (defined in z_dsp.h), contains two important elements: the s_n field, which is the size of the signal vector, and s_vec, which is a pointer to an array of 32-bit floats containing the signal data. All t_signals your object will receive have the same size. This size is not necessarily the same as the global MSP signal vector size, because your object might be inside a patcher within a poly~ object that defines its own size. Therefore it is important to use the s_n field of a signal passed to your object's dsp method. 00077 00078 You can use a variety of strategies to pass arguments to your perform routine via dsp_add(). For simple unit generators that don't store any internal state between computing vectors, it is sufficient to pass the inputs, outputs, and vector size. For objects that need to store internal state between computing vectors such as filters or ramp generators, you will pass a pointer to your object, whose data structure should contain space to store this state. The plus1~ object does not need to store internal state. It passes the input, output, and vector size to its perform routine. The plus1~ dsp method is shown below: 00079 00080 @code 00081 void plus1_dsp(t_plus1 *x, t_signal **sp, short *count) 00082 { 00083 dsp_add(plus1_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); 00084 } 00085 @endcode 00086 00087 The first argument to dsp_add() is your perform routine, followed by the number of additional arguments you wish to copy to the DSP chain, and then the arguments. 00088 00089 The perform routine is not a "method" in the traditional sense. It will be called within the callback of an audio driver, which, unless the user is employing the Non-Real Time audio driver, will typically be in a high-priority thread. Thread protection inside the perform routine is minimal. You can use a clock, but you cannot use qelems or outlets. The design of the perform routine is somewhat unlike other Max methods. It receives a pointer to a piece of the DSP chain and it is expected to return the location of the next perform routine on the chain. The next location is determined by the number of arguments you specified for your perform routine with your call to dsp_add(). For example, if you will pass three arguments, you need to return w + 4. 00090 00091 Here is the plus1 perform routine: 00092 00093 @code 00094 t_int *plus1_perform(t_int *w) 00095 { 00096 t_float *in, *out; 00097 int n; 00098 00099 in = (t_float *)w[1]; // get input signal vector 00100 out = (t_float *)w[2]; // get output signal vector 00101 n = (int)w[3]; // vector size 00102 00103 00104 while (n--) // perform calculation on all samples 00105 *out++ = *in++ + 1.; 00106 00107 return w + 4; // must return next DSP chain location 00108 } 00109 @endcode 00110 00111 6) Free function 00112 00113 The free function for the class must either be dsp_free() or it must be written to call dsp_free() as shown in the example below: 00114 00115 @code 00116 void mydspobject_free(t_mydspobject *x) 00117 { 00118 dsp_free((t_pxobject *)x); 00119 00120 // can do other stuff here 00121 } 00122 @endcode 00123 00124 */
Copyright © 2008, Cycling '74