Max 5 API Reference
00001 /** 00002 @file 00003 times~ - the *~ signal operator 00004 SDK example to illustrate platform-safe Altivec optimization 00005 00006 updated 3/22/09 ajm: new API 00007 00008 @ingroup examples 00009 */ 00010 00011 #include "ext.h" 00012 #include "ext_obex.h" 00013 #include "z_dsp.h" 00014 00015 /* 00016 00017 The idea here is that we can create source code that works on multiple 00018 platforms at compile time, and works on both Altivec- and non-Altivec 00019 PowerPC machines at runtime. We do this by isolating Altivec-specific 00020 code inside #ifdefs and using the routines sys_optimize and sys_altivec 00021 that return whether the user can and wants to optimize 00022 to protect non-Altivec machines from crashing. 00023 00024 The conditions for sys_optimize() to return true: 00025 00026 1. Altivec-capable machine 00027 2. User has checked Optimize in the DSP Status window 00028 3. Signal Vector size is a multiple of 4 00029 00030 Another thing is that assuming you are going to want to decide whether to 00031 use optimized code or not, you should only call sys_optimize() in your DSP 00032 method. Then you can assume that the value will not change until your DSP 00033 method is called again. In other words, the change of the user optimization 00034 state is a "triggering event" for a DSP chain recompilation. sys_optimize() 00035 CANNOT be called in your perform routine because it may allocate memory, 00036 and in any case it is SLOW, much slower than storing its current value 00037 in your object or, as is done in the example below, simply putting different 00038 functions (optimized vs. non-optimized) on the DSP call chain depending 00039 on what its value is in your DSP method. 00040 00041 When you want to use something that is specific to the Altivec compiler, 00042 you need to put it within two things: 00043 00044 #ifdef __ALTIVEC__ 00045 00046 and 00047 00048 #pragma altivec_model on 00049 00050 The first one is true is the compiler is capable of generating altivec 00051 code. This could be true WHETHER OR NOT the actual machine running the 00052 compiler has Altivec instructions! The second tells the compiler to start 00053 generating these instructions (and therefore, understanding the use of 00054 the word vector etc.). By leaving the 00055 altivec code generation OFF by default in the project settings, we eliminate 00056 the risk that in the future, Altivec optimization might be automatically 00057 performed by the compiler that would crash non-Altivec machines. 00058 00059 ALSO note that the only include file you need to write altivec code etc. is 00060 z_dsp.h, which includes z_altivec.h. See z_altivec.h for more obscurity... 00061 00062 */ 00063 00064 void *times_class; 00065 00066 00067 typedef struct _times 00068 { 00069 t_pxobject x_obj; 00070 t_float x_val; 00071 #ifdef __ALTIVEC__ 00072 #pragma altivec_model on 00073 vector float x_vecVal; 00074 #pragma altivec_model off 00075 #endif // __ALTIVEC__ 00076 } t_times; 00077 00078 t_int *times_perform(t_int *w); 00079 t_int *scale_perform(t_int *w); 00080 void times_float(t_times *x, double f); 00081 void times_int(t_times *x, long n); 00082 #ifdef __ALTIVEC__ // these are our altivec-specific routines 00083 void times_floatAV(t_times *x, double f); 00084 t_int *times_performAV(t_int *w); 00085 t_int *scale_performAV(t_int *w); 00086 #endif 00087 void times_dsp(t_times *x, t_signal **sp, short *count); 00088 void times_assist(t_times *x, void *b, long m, long a, char *s); 00089 void *times_new(double val); 00090 00091 int main(void) 00092 { 00093 t_class *c; 00094 00095 c = class_new("times~", (method)times_new, (method)dsp_free, (short)sizeof(t_times), 0L, A_DEFFLOAT, 0); 00096 class_dspinit(c); 00097 class_addmethod(c, (method)times_dsp, "dsp", A_CANT, 0); 00098 class_addmethod(c, (method)times_float, "float", A_FLOAT, 0); 00099 class_addmethod(c, (method)times_int, "int", A_LONG, 0); 00100 class_addmethod(c, (method)times_assist, "assist", A_CANT, 0); 00101 class_setname("*~","times~"); // because the filename on disk is different from the object name in Max 00102 class_register(CLASS_BOX, c); 00103 times_class = c; 00104 00105 return 0; 00106 } 00107 00108 // this routine covers both inlets. It doesn't matter which one is involved 00109 void times_float(t_times *x, double f) 00110 { 00111 x->x_val = f; 00112 if (DSP_SIMPLE_OPTIMIZE_TEST_PARAM) 00113 #ifdef __ALTIVEC__ 00114 times_floatAV(x,f); 00115 #else 00116 ; 00117 #endif 00118 } 00119 00120 void times_int(t_times *x, long n) 00121 { 00122 times_float(x,(double)n); 00123 } 00124 00125 // here are the two non-vector-optimized perform routines 00126 00127 t_int *times_perform(t_int *w) 00128 { 00129 t_float *in1,*in2,*out; 00130 int n; 00131 #ifdef DENORM_WANT_FIX // if we want to fix denormalized floating-point numbers (Windows XP) 00132 float ftmp; 00133 #endif 00134 00135 if (*(long *)(w[1])) 00136 goto out; 00137 in1 = (t_float *)(w[2]); 00138 in2 = (t_float *)(w[3]); 00139 out = (t_float *)(w[4]); 00140 n = (int)(w[5]); 00141 00142 #ifdef DENORM_WANT_FIX // if we want to fix denormalized floating-point numbers (Windows XP) 00143 while (n--) { 00144 ftmp = *in1++ * *in2++; 00145 FIX_DENORM_NAN_FLOAT(ftmp); 00146 *out++ = ftmp; 00147 } 00148 #else 00149 while (n--) *out++ = *in1++ * *in2++; 00150 #endif 00151 00152 out: 00153 return (w + 6); 00154 } 00155 00156 t_int *scale_perform(t_int *w) 00157 { 00158 t_float *in, *out; 00159 float val; 00160 int n; 00161 #ifdef DENORM_WANT_FIX // if we want to fix denormalized floating-point numbers (Windows XP) 00162 float ftmp; 00163 #endif 00164 00165 t_times *x = (t_times *)(w[3]); 00166 if (x->x_obj.z_disabled) 00167 goto out; 00168 in = (t_float *)(w[1]); 00169 out = (t_float *)(w[2]); 00170 val = x->x_val; 00171 n = (int)(w[4]); 00172 00173 #ifdef DENORM_WANT_FIX // if we want to fix denormalized floating-point numbers (Windows XP) 00174 while (n--) { 00175 ftmp = val * *in++; 00176 FIX_DENORM_NAN_FLOAT(ftmp); 00177 *out++ = ftmp; 00178 } 00179 #else 00180 while (n--) *out++ = val * *in++; 00181 #endif 00182 out: 00183 return (w + 5); 00184 } 00185 00186 #ifdef __ALTIVEC__ 00187 #pragma altivec_model on // turn AltiVec code generation on 00188 00189 void times_floatAV(t_times *x, double f) 00190 { 00191 vec_splat_float(x->x_vecVal, f); // copy the new scalar to the vector 00192 } 00193 00194 // here is an Altivec-optimized routine that multiples two signals together, producing a third signal 00195 // vec_madd(a,b,c) does result = a * (b + c), so we need a zero vector that we initialize locally 00196 00197 t_int *times_performAV(t_int *w) 00198 { 00199 vector float *v_in1, *v_in2, *v_out, zero; 00200 unsigned int n = (int)(w[5]); 00201 00202 if (*(long *)(w[1])) 00203 goto out; 00204 zero = (vector float) (0, 0, 0, 0); 00205 v_in1 = (vector float *)(w[2]); 00206 v_in2 = (vector float *)(w[3]); 00207 v_out = (vector float *)(w[4]); 00208 while (n--) { 00209 *v_out++ = vec_madd(*v_in1++, *v_in2++, zero); 00210 } 00211 out: 00212 return (w + 6); 00213 } 00214 00215 // here is an Altivec-optimized routine that multiples a signal by a scalar 00216 // vec_madd is used as above, but the val argument refers to the vector 00217 // we stored in our object 00218 00219 t_int *scale_performAV(t_int *w) 00220 { 00221 t_times *x = (t_times *)(w[3]); 00222 int n = (int)(w[4]); 00223 vector float *v_in, *v_out, val, zero; 00224 floatToVector foo; 00225 00226 if (x->x_obj.z_disabled) 00227 goto out; 00228 00229 zero = (vector float) (0, 0, 0, 0); 00230 v_in = (vector float *)(w[1]); 00231 v_out = (vector float *)(w[2]); 00232 val = x->x_vecVal; 00233 while (n--) { 00234 *v_out++ = vec_madd(*v_in++, val, zero); 00235 } 00236 00237 out: 00238 return (w + 5); 00239 } 00240 00241 #pragma altivec_model off // turn AltiVec code generation off 00242 #endif // __ALTIVEC__ 00243 00244 00245 void times_dsp(t_times *x, t_signal **sp, short *count) 00246 { 00247 if (DSP_SIMPLE_OPTIMIZE_TEST(sp[0])) { 00248 #ifdef __ALTIVEC__ 00249 #pragma altivec_model on 00250 // we need to do this in case the scalar was stored with optimization off 00251 // and we are just now turning optimization on. See I told you using this 00252 // vector stuff was tricky... 00253 times_floatAV(x,x->x_val); // splat the vector 00254 // if optimization is on, we are going to use one of our two optimized 00255 // perform routines. We pick the proper one based on whether signals 00256 // are connected to both inlets. If they are, we use times_performAV, 00257 // otherwise we will be multiplying by a scalar 00258 if (!count[1]) 00259 dsp_add(scale_performAV, 4, sp[0]->s_vec, sp[2]->s_vec, x, (sp[0]->s_n / 4)); 00260 else if (!count[0]) 00261 dsp_add(scale_performAV, 4, sp[1]->s_vec, sp[2]->s_vec, x, (sp[0]->s_n / 4)); 00262 else { 00263 dsp_add(times_performAV, 5, &x->x_obj.z_disabled, sp[0]->s_vec, sp[1]->s_vec, 00264 sp[2]->s_vec, (sp[0]->s_n / 4)); 00265 } 00266 #pragma altivec_model off 00267 #else 00268 // this error would be generated at runtime if the code were to be compiled on 00269 // a compiler or machine without ALTIVEC and for some reason sys_optimize returned true 00270 object_error((t_object *)x, "no optimizations available"); // in theory this should never happen... 00271 #endif // __ALTIVEC__ 00272 } else { 00273 if (!count[1]) 00274 dsp_add(scale_perform, 4, sp[0]->s_vec, sp[2]->s_vec, x, sp[0]->s_n); 00275 else if (!count[0]) 00276 dsp_add(scale_perform, 4, sp[1]->s_vec, sp[2]->s_vec, x, sp[0]->s_n); 00277 else 00278 dsp_add(times_perform, 5, &x->x_obj.z_disabled, sp[0]->s_vec, sp[1]->s_vec, 00279 sp[2]->s_vec, sp[0]->s_n); 00280 } 00281 } 00282 00283 void times_assist(t_times *x, void *b, long m, long a, char *s) 00284 { 00285 if (m == ASSIST_OUTLET) 00286 sprintf(s,"(Signal) Multiplication Result"); 00287 else { 00288 switch (a) { 00289 case 0: 00290 sprintf(s,"(Signal/Float) This * Right Inlet"); 00291 break; 00292 case 1: 00293 sprintf(s,"(Signal/Float) Left Inlet * This"); 00294 break; 00295 } 00296 } 00297 } 00298 00299 void *times_new(double val) 00300 { 00301 t_times *x = object_alloc(times_class); 00302 dsp_setup((t_pxobject *)x,2); 00303 outlet_new((t_pxobject *)x, "signal"); 00304 x->x_val = val; // splatted in _dsp method if optimizations are on 00305 00306 return (x); 00307 } 00308
Copyright © 2008, Cycling '74