/** @file rb.randomize.c - modify incoming values randomly 32 bits version (for Max 6 - not for 6.1's 64 bits mode and later updates). version 4 - 2018/05/23 - uses the RANDOM standard library version 3 - uses random() function for floats and rand_r() for integers this object receives either int or floats lists should be possible in a future version random seed should be possible in a future version too first inlet is for incoming data to randomize second inlet is for random range an optional mode always produces the same value else if the range changes or if a "reset" message is sent a bang in left outpu sends the last output the objets work in the integer domain else if a float argument is provided @Roald Baudoux 2013-2018 */ // The standard random() function is not standard on Windows. // We need to do this to setup the rand_s() function. #include "ext.h" // you must include this - it contains the external object's link to available Max functions #include "ext_obex.h" // this is required for all Max objects using the newer style for writing objects. #include "stdlib.h" // for rand and srand functions #include "time.h" // to seed srand with time typedef struct _rb_randomize { // defines our object's internal variables for each instance in a patch t_object ob; // object header - ALL objects MUST begin with this... t_atom l; // stored value from left inlet (data to randomize) t_atom range; // stored value from right inlet (range) void *outlet; // outlet creation - inlets are automatic, but objects must "own" their own outlets void *proxy; // proxy inlet long proxy_inletnum; // # of inlet currently in use int rangetriggers; // flag to let right inlet trigger output } t_rb_randomize; // these are prototypes for the methods that are defined below void *rb_randomize_new(t_symbol *s, long argc, t_atom *argv); void rb_randomize_free(t_rb_randomize *x); void rb_randomize_assist(t_rb_randomize *x, void *b, long m, long a, char *s); void rb_randomize_bang(t_rb_randomize *x); void rb_randomize_int(t_rb_randomize *x, long n); void rb_randomize_float(t_rb_randomize *x, double f); t_class *rb_randomize_class; // global pointer to the object class - so max can reference the object unsigned int globalSeed; //-------------------------------------------------------------------------- void ext_main(void *r) { t_class *c; globalSeed = time(NULL); c = class_new("rb.randomize", (method)rb_randomize_new, (method)rb_randomize_free, sizeof(t_rb_randomize), 0L, A_GIMME, 0); class_addmethod(c, (method)rb_randomize_bang, "bang", 0); // the method it uses when it gets a bang in the left inlet class_addmethod(c, (method)rb_randomize_int, "int", A_LONG, 0); // the method for ints in any inlet class_addmethod(c, (method)rb_randomize_float, "float", A_FLOAT, 0); // the method for floats in any inlet class_addmethod(c, (method)rb_randomize_assist, "assist", A_CANT, 0); // (optional) assistance method needs to be declared like this class_register(CLASS_BOX, c); //useful to allow the user to benefit from auto-completion when typing name in box rb_randomize_class = c; post("rb.randomize v 4 - © Roald Baudoux 2018/05/23",0); // post any important info to the max window when our class is loaded return 0; } //-------------------------------------------------------------------------- void *rb_randomize_new(t_symbol *s, long argc, t_atom *argv) { // local variable (pointer to a t_rb_randomize data structure) t_rb_randomize *x = NULL ; if (x = (t_rb_randomize *)object_alloc(rb_randomize_class)) { srand (time(NULL)) ; // seed rand with time //srandom (time(NULL)) ; // seed rand with time x->proxy = proxy_new(x, 1, &x->proxy_inletnum); // fully-flexible inlet for any type (remember one default inlet is always provided) x->outlet = outlet_new(x, NULL); // fully-flexible outlet for any type // initialize L and R inlet atoms to (int) 0 atom_setlong(&x->l, 0); atom_setlong(&x->range, 0); long i; // object instantiation, NEW STYLE switch (argc) { case 1: switch (argv->a_type) { case A_LONG: atom_setlong(&x->range, atom_getlong(argv)); break; case A_FLOAT: atom_setfloat(&x->range, atom_getfloat(argv)); break; default: object_error((t_object *)x, "forbidden argument type"); x = NULL; break; } break; case 2: for (i = 0; i < 2; i++) { if (i == 0) { // first argument sets range switch ((argv + i)->a_type) { case A_LONG: atom_setlong(&x->range, atom_getlong(argv+i)); break; case A_FLOAT: atom_setfloat(&x->range, atom_getfloat(argv+i)); break; default: object_error((t_object *)x, "forbidden argument type (int or float only)"); x = NULL; break; } } else { // second argument makes second inlet triggerable if ((argv + i)->a_type == A_LONG) { if (atom_getlong(argv+i) == 0 || atom_getlong(argv+i) == 1) { x->rangetriggers = atom_getlong(argv+i) ; } else { object_error((t_object *)x, "second argument's value should be either 1 or 0"); x = NULL; } } else { object_error((t_object *)x, "forbidden argument type (int only)"); x = NULL; } } } break; case 0: object_error((t_object *)x, "at least one argument is required"); x = NULL; break; default: // more than two arguments object_error((t_object *)x, "only two first arguments will be used"); x = NULL; break; } } return x; } void rb_randomize_free(t_rb_randomize *x) { ; //object_free(x); // frees all resources associated with the /* t_object ob; // object header - ALL objects MUST begin with this... t_atom l; // stored value from left inlet (data to randomize) t_atom range; // stored value from right inlet (range) void *outlet; // outlet creation - inlets are automatic, but objects must "own" their own outlets void *proxy; // proxy inlet long proxy_inletnum; // # of inlet currently in use int rangetriggers; // flag to let right inlet trigger output */ } //-------------------------------------------------------------------------- void rb_randomize_assist(t_rb_randomize *x, void *b, long m, long a, char *s) // 4 final arguments are always the same for the assistance method { if (m == ASSIST_INLET) { switch (a) { case 0: sprintf(s,"Inlet %ld: incoming number to randomize (float / int)", a); break; case 1: sprintf(s,"Inlet %ld: random range (float / int)", a); break; } } else sprintf(s,"Randomized number"); } void rb_randomize_bang(t_rb_randomize *x) { long irand; double dfrand; // if range is a FLOAT, output a FLOAT if (x->range.a_type == A_FLOAT) { //dfrand = (double)(random()%2000000001) / 1000000000 ; //dfrand = dfrand - 1.0; dfrand = rand() / (double)RAND_MAX * 2 - 1; dfrand = (float)(dfrand * atom_getfloat(&x->range)); dfrand = atom_getfloat(&x->l) + dfrand; outlet_float(x->outlet, (float)dfrand); } else if (x->range.a_type == A_LONG) { // OUTPUT AN INT irand = (rand() %((atom_getlong(&x->range) * 2) + 1)) - atom_getlong(&x->range); irand = atom_getlong(&x->l) + irand; outlet_int(x->outlet, irand); // rand() / (double)RAND_MAX * 2 - 1; /* irand = ((rand_r(&globalSeed)) %((atom_getlong(&x->range) * 2) + 1)) - atom_getlong(&x->range); irand = atom_getlong(&x->l) + irand; */ } } void rb_randomize_int(t_rb_randomize *x, long n) { long inlet = proxy_getinlet((t_object *)x); // what inlet did this message come in through? //post("int came in via inlet %ld", inlet); if (inlet == 1) { // RIGHT INLET if (x->range.a_type == A_FLOAT){ // ARGUMENT IS A FLOAT atom_setfloat(&x->range, (double)n); // SET FLOAT VAL } else if (x->range.a_type == A_LONG) { atom_setlong(&x->range, n); // SET INT VAL } if (x->rangetriggers == 1) { rb_randomize_bang(x); } } else { // LEFT INLET atom_setlong(&x->l, n); rb_randomize_bang(x); // bang for left inlet, trigger calculation } } void rb_randomize_float(t_rb_randomize *x, double f) { long inlet = proxy_getinlet((t_object *)x); // what inlet did this message come in through? //post("float came in via inlet %ld", inlet); if (inlet == 1) { // RIGHT INLET if (x->range.a_type == A_FLOAT){ // ARGUMENT IS A FLOAT atom_setfloat(&x->range, f); // SET FLOAT VAL } else if (x->range.a_type == A_LONG) { atom_setlong(&x->range, (long)f); // SET INT VAL } if (x->rangetriggers == 1) { rb_randomize_bang(x); } } else { // LEFT INLET atom_setfloat(&x->l, f); rb_randomize_bang(x); // bang for left inlet, trigger calculation } }