Max 5 API Reference
00001 /** 00002 @page chapter_msp_advanced Advanced Signal Object Topics 00003 00004 Here are some techniques for implementing additional features found in most signal objects. 00005 00006 @section chapter_msp_advanced_internal_state Saving Internal State 00007 00008 To implement unit generators such as filters and ramp generators, you need to save internal state between calls to your object's perform routine. Here is a very simple low-pass filter (it just averages successive samples) that saves the value of the last sample in a vector to be averaged with the first sample of the next vector. First we add a field to our data structure to hold the value: 00009 00010 @code 00011 typedef struct _myfilter 00012 { 00013 t_pxobject f_obj; 00014 t_float f_sample; 00015 } t_myfilter; 00016 @endcode 00017 00018 Then, in our dsp method (which has one input and one output), we pass a pointer to the object as one of the DSP chain arguments. The dsp method also initializes the value of the internal state, to avoid any noise when the audio starts. 00019 00020 @code 00021 void myfilter_dsp(t_myfilter *x, t_signal **sp, short *count) 00022 { 00023 dsp_add(myfilter_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); 00024 00025 x->f_sample = 0; 00026 } 00027 @endcode 00028 00029 Here is the perform routine, which obtains the internal state before entering the processing loop, then stores the most recent value after the loop is finished. 00030 00031 @code 00032 t_int *myfilter_perform(t_int *w) 00033 { 00034 t_myfilter *x = (t_myfilter *)w[1]; 00035 t_float *in = (t_float *)w[2]; 00036 t_float *out = (t_float *)w[3]; 00037 int n = (int)w[4]; 00038 t_float samp = x->f_sample; // read from internal state 00039 t_float val; 00040 00041 while (n--) { 00042 val = *in++; 00043 *out++ = (val + samp) * 0.5; 00044 samp = val; 00045 } 00046 x->f_sample = samp; // save to internal state 00047 00048 return w + 5; 00049 } 00050 @endcode 00051 00052 00053 @section chapter_msp_advanced_muting Observing Patcher Muting 00054 00055 The enable message to the thispatcher object, as well as the MSP mute object~ can be used to disable a subpatcher. If your object is at all computationally expensive in its perform routine, it should check to see whether it has been disabled. To do this, you'll need to pass a pointer to your object as one of the DSP chain arguments when calling dsp_add(). Here is a simple modification of our filter object's perform routine that checks to see if the object has been disabled. 00056 00057 @code 00058 t_int *myfilter_perform(t_int *w) 00059 { 00060 t_myfilter *x = (t_myfilter *)w[1]; 00061 t_float *in = (t_float *)w[2]; 00062 t_float *out = (t_float *)w[3]; 00063 int n = (int)w[4]; 00064 t_float samp = x->f_sample; // read from internal state 00065 t_float val; 00066 00067 if (x->f_obj.z_disabled) // check for object being disabled 00068 return w + 5; 00069 00070 while (n--) { 00071 val = *in++; 00072 *out++ = (val + samp) * 0.5; 00073 samp = val; 00074 } 00075 x->f_sample = samp; // save to internal state 00076 00077 return w + 5; 00078 } 00079 @endcode 00080 00081 00082 @section chapter_msp_advanced_connections Using Connection Information 00083 00084 The third argument to the dsp method is an array of numbers that enumerate the number of objects connected to each of your objects inputs and outputs. More advanced dsp methods can use this information for optimization purposes. For example, if you find that your object has no inputs or outputs, you could avoid calling dsp_add() altogether. The MSP signal operator objects (such as +~ and *~) to implement a basic polymorphism: they look at the connections count to determine whether the perform routine should use scalar or signal inputs. For example, if the right input has no connected signals, the user can add a scalar value sent to the right inlet. 00085 00086 To implement this behavior, you have a few different options. The first option is to write two different perform methods, one which handles the two-signal case, and one which handles the scalar case. The dsp method looks at the count array and passes a different function to dsp_add(). The example below assumes that the second element in the signal and count arrays (sp[1]) is the right input: 00087 00088 @code 00089 if (count[1]) // signal connected 00090 dsp_add(mydspobject_twosigperform, 5, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); 00091 else 00092 dsp_add(mydspobject_scalarperform, 4, x, sp[0]->s_vec, sp[2]->s_vec, sp[0]->s_n); 00093 @endcode 00094 00095 00096 The second option is to pass the value of the count array for a particular signal to the perform method, which can make the decision whether to use the signal value or a scalar value that has been stored inside the object. In this case, many objects use a single sample value from the signal as a substitute for the scalar. Using the first sample (i.e., the value at index 0) is a technique that works for any vector size, since vector sizes could be as small as a single sample. Here is an example of this technique for an object that has two inputs and one output. The connection count for the right input signal is passed as the second argument on the DSP chain, and the right input signal vector is passed even if it not connected: 00097 00098 @code 00099 dsp_add(mydspobject_perform, 6, x, count[1], sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); 00100 @endcode 00101 00102 Here is a perform routine that uses the connection count information as passed in format shown above: 00103 00104 @code 00105 t_int mydspobject_perform(t_int *w) 00106 { 00107 t_mydspobject *x = (t_mydspobject *)w[1]; 00108 int connected = (int)w[2]; 00109 t_float *in = (t_float *)w[3]; 00110 t_float *in2 = (t_float *)w[4]; 00111 t_float *out = (t_float *)w[5]; 00112 int n = (int)w[6]; 00113 00114 double in2value; 00115 00116 // get scalar sample or use signal depending on whether signal is connected 00117 00118 in2value = connected? *in2 : x->m_scalarvalue; 00119 00120 // do calculation here 00121 00122 return w + 7; 00123 } 00124 @endcode 00125 00126 */
Copyright © 2008, Cycling '74