/* June 2004 Kurt Ralske, in collaboration with Andrea Polli */ #include "jit.common.h" #include "ext_path.h" /* #include "ext.h" #include "ext_strings.h" #include "ext_support.h" #include "SysHeaders.h" */ #define MAX_NUM_OF_OUTLETS 64 enum { LOOP_OFF, LOOP_ON, LOOP_PALINDROME, LOOP_REVERSE, LOOP_ENUM_SIZE }; ///////////////////////////////////////////// structure definition typedef struct datareader { Object e_ob; // required header void *bin, *tmpbin; void *out[MAX_NUM_OF_OUTLETS]; long numOutlets; double index; double increment; long incrementSign; double smooth; long normalize; double normRangeMin, normRangeMax; double normMin[MAX_NUM_OF_OUTLETS], normMax[MAX_NUM_OF_OUTLETS], smoothData[MAX_NUM_OF_OUTLETS]; long numAtoms; long rows; long loopType; } DataReader; void *datareader_class; char filename[256]; short path; t_atom holder; ///////////////////////////////////////////// prototypes void datareader_bang(DataReader *x); void datareader_read(DataReader *x, t_symbol *s); void *datareader_new(long n); void datareader_doread(DataReader *x, t_symbol *s, short argc, t_atom *argv); long datareader_parse(DataReader *x); void datareader_free(DataReader *x); void scopy(char *s1, char *s2); void datareader_float(DataReader *x, float val); void datareader_density(DataReader *x, float val); void datareader_smooth(DataReader *x, float val); void datareader_reset(DataReader *x, t_symbol *s); void datareader_loop(DataReader *x, t_symbol *s, long argc, t_atom *argv); void datareader_normalize(DataReader *x, long val); void datareader_normalize_range(DataReader *x, t_symbol *s, long argc, t_atom *argv); void datareader_assist(DataReader *x, void *b, long m, long a, char *s); ///////////////////////////////////////////// initialization void main(fptr *f) { setup((Messlist **)&datareader_class, (method)datareader_new, (method)datareader_free, (short)sizeof(DataReader), 0L, A_DEFLONG, 0); addbang((method)datareader_bang); addfloat((method)datareader_float); addmess((method)datareader_read, "read", A_DEFSYM, 0); addmess((method)datareader_reset, "reset", A_DEFSYM, 0); addmess((method)datareader_density, "density", A_FLOAT, 0); addmess((method)datareader_smooth, "smooth", A_FLOAT, 0); addmess((method)datareader_loop, "loop", A_GIMME, 0); addmess((method)datareader_normalize, "normalize", A_LONG, 0); addmess((method)datareader_normalize_range, "normalize_range", A_GIMME, 0); addmess((method)datareader_assist, "assist", A_CANT, 0); /* list object in the new object list */ finder_addclass("Data","datareader"); } ///////////////////////////////////////////// methods void datareader_assist(DataReader *x, void *b, long m, long a, char *s) { if (m == 1) // i.e. if message passed is inlet { scopy(s, "accepts read, reset, bang, float, smooth, loop, normalize"); } else if (m == 2) //i.e. if message passed is outlet { switch(a) // identifies outlet { case 0: scopy (s, "float: index # of data"); break; case 1: scopy (s, "float: column 1 data"); break; case 2: scopy (s, "float: column 2 data"); break; case 3: scopy (s, "float: column 3 data"); break; case 4: scopy (s, "float: column 4 data"); break; case 5: scopy (s, "float: column 5 data"); break; case 6: scopy (s, "float: column 6 data"); break; case 7: scopy (s, "float: column 7 data"); break; case 8: scopy (s, "float: column 8 data"); break; default: scopy (s, "float: data"); // you get the idea } } } void datareader_float(DataReader *x, float val) { if(val < 0.0d) val = val * -1; if(val >= x->rows) val = 0; x->index = val; datareader_bang(x); } void datareader_density(DataReader *x, float val) { x->increment = 1.0d / (val * 0.01d); if(x->increment >= (double)x->rows && x->rows) x->increment = x->rows; } void datareader_smooth(DataReader *x, float val) { if(val < 0.0d) val = val * -1.0d; if(val > 100.0d) val = 100.0d; x->smooth = val * 0.01; } void datareader_reset(DataReader *x, t_symbol *s) { x->index = 0.0d; } void datareader_read(DataReader *x, t_symbol *s) { defer(x, (method)datareader_doread, s, 0, 0); // always defer this message } void datareader_loop(DataReader *x, t_symbol *s, long argc, t_atom *argv) { long val; if(argv[0].a_type == A_LONG) { val = argv[0].a_w.w_long % LOOP_ENUM_SIZE; switch(val) { case 0 : x->loopType = LOOP_OFF; break; case 1 : x->loopType = LOOP_ON; break; case 2 : x->loopType = LOOP_PALINDROME; break; case 3 : x->loopType = LOOP_REVERSE; break; default : x->loopType = LOOP_ON; break; } } else if(argv[0].a_type == A_SYM) { if(argv[0].a_w.w_sym == gensym("off")) x->loopType = LOOP_OFF; else if(argv[0].a_w.w_sym == gensym("on")) x->loopType = LOOP_ON; else if(argv[0].a_w.w_sym == gensym("palindrome")) x->loopType = LOOP_PALINDROME; else if(argv[0].a_w.w_sym == gensym("reverse")) x->loopType = LOOP_REVERSE; } if(x->loopType == LOOP_REVERSE) x->incrementSign = -1.0d; else x->incrementSign = 1.0d; } ///////////////////////////////////////////// internally set range of normalization, for each column of data void datareader_normalize(DataReader *x, long val) { long i, j, k, to, so, numOutlets, rows; double current, lowest, highest; numOutlets = x->numOutlets; rows = x->rows; x->normalize = val & 1; if(x->normalize) { for(i = 0; i < numOutlets; i++) { to = i; so = to * 8; for(j = 0; j <= rows; j++) { binbuf_getatom(x->bin, &to, &so, &holder); current = holder.a_w.w_float; if(j == 0) lowest = highest = current; if(current <= lowest) lowest = current; if(current >= highest) highest = current; to = i + j * numOutlets; so = to * 8; } x->normMin[i] = lowest; x->normMax[i] = highest; } } } ///////////////////////////////////////////// range of normalization, programatically set by user void datareader_normalize_range(DataReader *x, t_symbol *s, long argc, t_atom *argv) { double val, val2, tmp; if(argc == 1 && argv[0].a_type == A_FLOAT) { x->normRangeMin = 0.0d; val = argv[0].a_w.w_float; if(val <= 0.0d) val = val * -1.0d; x->normRangeMax = val; } else if(argc == 2 && argv[0].a_type == A_FLOAT && argv[1].a_type == A_FLOAT) { val = argv[0].a_w.w_float; val2 = argv[1].a_w.w_float; if(val > val2) // swap { tmp = val; val = val2; val2 = tmp; } x->normRangeMin = val; x->normRangeMax = val2; } } void datareader_bang(DataReader *x) { long numOutlets = x->numOutlets; long k, to, so; double oldData[MAX_NUM_OF_OUTLETS], newData[MAX_NUM_OF_OUTLETS]; double lerp, revlerp, result, normRange, denom; ///////////////////////////////////// byte-addressing crap ~= in-place addressing for binbuf to = (int)x->index * x->numOutlets; so = to * 8; ///////////////////////////////////// index out of 1st outlet outlet_float(x->out[0], x->index); ///////////////////////////////////// the other data values -- interpolate / normalize / smooth normRange = x->normRangeMax - x->normRangeMin; lerp = x->index - (int)x->index; revlerp = 1.0d - lerp; for(k = 0; k < numOutlets; k++) { binbuf_getatom(x->bin, &to, &so, &holder); oldData[k] = holder.a_w.w_float; } if(x->index > x->rows - 1) to = 0, so = 0; for(k = 0; k < numOutlets; k++) { binbuf_getatom(x->bin, &to, &so, &holder); newData[k] = holder.a_w.w_float; } for(k = 0; k < numOutlets; k++) { result = oldData[k] * revlerp + newData[k] * lerp; result = x->smoothData[k] * x->smooth + result * (1.0 - x->smooth); x->smoothData[k] = result; if(x->normalize) { result = (result - x->normMin[k])/ (x->normMax[k] - x->normMin[k]) * normRange + x->normRangeMin; // if column's value never changes, there is no range, so avoid NAN if(x->normMax[k] == x->normMin[k]) result = x->normRangeMin; } outlet_float(x->out[k + 1], result); } ///////////////////////////////////// sort out new index, loop appropriately x->index += (x->increment * x->incrementSign); switch(x->loopType) { case LOOP_OFF : if(x->index > x->rows - 1) x->index = x->rows - 1; break; case LOOP_ON : if(x->index >= x->rows) x->index = x->index - (double)(x->rows); break; case LOOP_PALINDROME : if(x->index >= x->rows - 1) { x->incrementSign = -1.0d; x->index += x->increment * -1.0d; } if(x->index <= 0.0d) { x->incrementSign = 1.0d; x->index += x->increment; } break; case LOOP_REVERSE : if(x->index < 0.0d) x->index = (double)x->rows + x->index; break; default : if(x->index > x->rows - 1) x->index = x->rows - 1; } } void datareader_doread(DataReader *x, t_symbol *s, short argc, t_atom *argv) { // this method opens the file and reads it into a binbuf short err; OSType type = 'TEXT'; // some file type you're looking for OSType outtype = 0L; long longtype; long longouttype, to = 0, so = 0, i; FILE_REF fd; char* cp; char ch; long atype; if (!s->s_name[0]) // empty symbol { if (open_dialog(filename, &path, &outtype, &type, 1)) { return; // user cancelled } } else { longtype = type; longouttype = outtype; scopy(filename,s->s_name); // important: copy symbol arg to local string if (locatefile_extended(filename, &path, &longouttype, &longtype, 1)) { post("datareader: error - could not locate file \"%s\"", filename); return; } } ///////////////////////////////////// make a fresh binbuf, clean up stored data ///////////////////////////////////// (set to normMax to 1. to avoid divide-by-zero bug) freeobject(x->bin); freeobject(x->tmpbin); x->bin = binbuf_new(); x->tmpbin = binbuf_new(); for(i = 0; i < MAX_NUM_OF_OUTLETS; i++) { x->normMin[i] = x->smoothData[i] = 0.0d; x->normMax[i] = 1.0d; } ///////////////////////////////////// at this point, a valid name is in filename and a valid path is in path: ///////////////////////////////////// open file for reading err = path_openfile(filename,path,&fd,READ_PERM); if (err) { error("datareader: error - could not open file \"%s\"", filename); return; } // vol is the volume or working directory reference num of file err = (binbuf_read(x->tmpbin, filename, path, 0)); if (err) { error("datareader: error - could not read file \"%s\"", filename); } else { post("datareader: opened and read file \"%s\"", filename); } ///////////////////////////////////// parse/format data if(datareader_parse(x)) post("datareader: error - irregularly formatted data file"); ///////////////////////////////////// retain normalized state, if loading subsequent files datareader_normalize(x, x->normalize); } long datareader_parse(DataReader *x) { long err = 0, to = 0, so = 0, i, numTmpAtoms = 0, numWriteAtoms = 0, atomsOffset = 0; float tmpfloat; char* cp; char ch; long atype; ///////////////////////////////////// count "raw" atoms in x->tmpbin while(!binbuf_getatom(x->tmpbin, &to, &so, &holder)) { numTmpAtoms++; } to = so = 0; ///////////////////////////////////// find first atom that is int or float for(i = 0; i < numTmpAtoms; i++) { binbuf_getatom(x->tmpbin, &to, &so, &holder); if(holder.a_type != A_FLOAT && holder.a_type != A_LONG) atomsOffset++; if(holder.a_type == A_FLOAT || holder.a_type == A_LONG) break; } if(i == numTmpAtoms) { post("datareader: error - this file does not contain any numeric data"); err = 1; return err; } ///////////////////////////////////// scan past the header to = so = 0; for(i = 0; i < atomsOffset; i++) { binbuf_getatom(x->tmpbin, &to, &so, &holder); } ///////////////////////////////////// now passed header, so copy remaining numeric atoms into x->bin. ///////////////////////////////////// after this, we are 100% positive that x->bin contains only floats i = atomsOffset; while(i < numTmpAtoms) { binbuf_getatom(x->tmpbin, &to, &so, &holder); if(holder.a_type == A_LONG) { tmpfloat = holder.a_w.w_long; SETFLOAT(&holder, tmpfloat); } if(holder.a_type != A_FLOAT) { i++; continue; } binbuf_append(x->bin, 0, 1, &holder); i++; numWriteAtoms++; } ///////////////////////////////////// # of rows = (# of atoms) / (# of columns) x->numAtoms = numWriteAtoms; x->rows = x->numAtoms / x->numOutlets; return err; } void scopy(char *s1, char *s2) { short i; i = 0; while ((s1[i] = s2[i]) != '\0') i++; } void datareader_free(DataReader *x) { freeobject(x->bin); freeobject(x->tmpbin); } void *datareader_new(long argNumOutlets) { DataReader *x; int i; if(argNumOutlets == 0) argNumOutlets = 4; if(argNumOutlets >= MAX_NUM_OF_OUTLETS - 1) argNumOutlets = MAX_NUM_OF_OUTLETS; x = newobject(datareader_class); x->numOutlets = argNumOutlets; ///////////////////////////////////// create an outlet for each column of data, plus one for the index for(i = argNumOutlets; i >= 0; i--) { x->out[i] = floatout(x); } x->bin = binbuf_new(); x->tmpbin = binbuf_new(); x->index = 0.0d; x->increment = 1.0d; x->incrementSign = 1; x->smooth = 0.0d; x->normalize = 0; x->normRangeMin = 0.0d; x->normRangeMax = 1.0d; x->loopType = LOOP_OFF; x->numAtoms = 0; x->rows = 0; return (x); } /* i = to = so = 0; while(i < x->numAtoms) { if(!binbuf_getatom(x->bin, &to, &so, &holder)) { atype = holder.a_type; switch(atype) { case A_LONG: post("argument %ld is a long: %ld", i, holder.a_w.w_long); break; case A_SYM: post("argument %ld is a symbol: name %s", i, holder.a_w.w_sym->s_name); break; case A_FLOAT: post("argument %ld is a float: %lf", i, holder.a_w.w_float); break; default: post("...didn't quite understand the input..."); } } i++; } */