/* Copyright (c) 2002-2004 krzYszcz and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include "m_pd.h" #include "common/loud.h" #include "common/vefl.h" #include "hammer/tree.h" #include "hammer/file.h" typedef struct _funbuff { t_object x_ob; t_canvas *x_canvas; t_symbol *x_defname; t_float x_value; int x_valueset; /* CHECKED filling with a large set, then sending 'goto', 'read', 'next'... outputs the previous, replaced contents (same with deletion) -- apparently a node pointer is stored, corrupt in these cases */ t_hammernode *x_pointer; int x_pointerset; /* set-with-goto flag */ int x_lastdelta; int x_embedflag; t_hammerfile *x_filehandle; t_hammertree x_tree; t_outlet *x_deltaout; t_outlet *x_bangout; } t_funbuff; static t_class *funbuff_class; static void funbuff_dooutput(t_funbuff *x, float value, float delta) { /* CHECKED lastdelta sent for 'next', 'float', 'min', 'max', 'interp', 'find' */ outlet_float(x->x_deltaout, delta); outlet_float(((t_object *)x)->ob_outlet, value); } static void funbuff_bang(t_funbuff *x) { t_hammernode *np; int count = 0; int xmin = 0, xmax = 0; t_float ymin = 0, ymax = 0; if (np = x->x_tree.t_first) { /* LATER consider using extra fields, updated on the fly */ count = 1; xmin = np->n_key; xmax = x->x_tree.t_last->n_key; ymin = ymax = HAMMERNODE_GETFLOAT(np); while (np = np->n_next) { t_float f = HAMMERNODE_GETFLOAT(np); if (f < ymin) ymin = f; else if (f > ymax) ymax = f; count++; } } /* format CHECKED */ post("funbuff info: %d elements long", count); /* CHECKED 0 and 1 */ if (count) { post(" -> minX= %d maxX= %d", xmin, xmax); post(" -> minY= %g maxY= %g", ymin, ymax); post(" -> domain= %d range= %g", xmax - xmin, ymax - ymin); } } static void funbuff_float(t_funbuff *x, t_float f) { int ndx = (int)f; /* CHECKED float is silently truncated */ t_hammernode *np; if (x->x_valueset) { np = hammertree_insertfloat(&x->x_tree, ndx, x->x_value, 1); x->x_valueset = 0; } else if (np = hammertree_closest(&x->x_tree, ndx, 0)) funbuff_dooutput(x, HAMMERNODE_GETFLOAT(np), x->x_lastdelta); /* CHECKED pointer is updated -- 'next' outputs np also in a !valueset case (it is sent twice) */ x->x_pointer = np; /* FIXME */ x->x_pointerset = 0; } static void funbuff_ft1(t_funbuff *x, t_floatarg f) { /* this is incompatible -- CHECKED float is silently truncated */ x->x_value = f; x->x_valueset = 1; } static void funbuff_clear(t_funbuff *x) { hammertree_clear(&x->x_tree, 0); /* CHECKED valueset is not cleared */ x->x_pointer = 0; } /* LATER dirty flag handling */ static void funbuff_embed(t_funbuff *x, t_floatarg f) { x->x_embedflag = (f != 0); } static void funbuff_goto(t_funbuff *x, t_floatarg f) { /* CHECKED truncation */ x->x_pointer = hammertree_closest(&x->x_tree, (int)f, 1); x->x_pointerset = 1; /* CHECKED delta output by 'next' will be zero */ } /* LATER consider using an extra field, updated on the fly */ static void funbuff_min(t_funbuff *x) { t_hammernode *np; if (np = x->x_tree.t_first) /* CHECKED nop if empty */ { t_float result = HAMMERNODE_GETFLOAT(np); while (np = np->n_next) if (HAMMERNODE_GETFLOAT(np) < result) result = HAMMERNODE_GETFLOAT(np); funbuff_dooutput(x, result, x->x_lastdelta); /* CHECKED pointer not updated */ } } /* LATER consider using an extra field, updated on the fly */ static void funbuff_max(t_funbuff *x) { t_hammernode *np; if (np = x->x_tree.t_first) /* CHECKED nop if empty */ { t_float result = HAMMERNODE_GETFLOAT(np); while (np = np->n_next) if (HAMMERNODE_GETFLOAT(np) > result) result = HAMMERNODE_GETFLOAT(np); funbuff_dooutput(x, result, x->x_lastdelta); /* CHECKED pointer not updated */ } } static void funbuff_next(t_funbuff *x) { t_hammernode *np; if (!x->x_tree.t_root) return; if (!(np = x->x_pointer)) { outlet_bang(x->x_bangout); /* CHECKED banging until reset */ return; } if (x->x_pointerset) x->x_lastdelta = 0; else if (np->n_prev) x->x_lastdelta = np->n_key - np->n_prev->n_key; else x->x_lastdelta = 0; /* CHECKED corrupt delta sent here... */ funbuff_dooutput(x, HAMMERNODE_GETFLOAT(np), x->x_lastdelta); x->x_pointer = np->n_next; x->x_pointerset = 0; } static void funbuff_set(t_funbuff *x, t_symbol *s, int ac, t_atom *av) { /* CHECKED symbols somehow bashed to zeros, decreasing x coords corrupt the funbuff -- not emulated here... */ int i = ac; t_atom *ap = av; while (i--) if (ap++->a_type != A_FLOAT) { loud_error((t_pd *)x, "bad input (not a number) -- no data to set"); return; } if (!ac || (ac % 2)) { /* CHECKED odd/null ac loudly rejected, current contents preserved */ loud_error((t_pd *)x, "bad input (%s) -- no data to set", (ac ? "odd arg count" : "no input")); return; } funbuff_clear(x); /* CHECKED the contents is replaced */ while (ac--) { int ndx = (int)av++->a_w.w_float; if (!hammertree_insertfloat(&x->x_tree, ndx, av++->a_w.w_float, 1)) return; ac--; } } static void funbuff_doread(t_funbuff *x, t_symbol *fn) { t_binbuf *bb = binbuf_new(); int ac; t_atom *av; char buf[MAXPDSTRING]; /* FIXME use open_via_path() */ canvas_makefilename(x->x_canvas, fn->s_name, buf, MAXPDSTRING); binbuf_read(bb, buf, "", 0); if ((ac = binbuf_getnatom(bb)) && (av = binbuf_getvec(bb)) && av->a_type == A_SYMBOL && av->a_w.w_symbol == gensym("funbuff")) { post("funbuff_read: %s read successful", fn->s_name); /* CHECKED */ funbuff_set(x, 0, ac-1, av+1); } else /* CHECKED no complaints... */ loud_error((t_pd *)x, "invalid file %s", fn->s_name); binbuf_free(bb); } static void funbuff_readhook(t_pd *z, t_symbol *fn, int ac, t_atom *av) { funbuff_doread((t_funbuff *)z, fn); } static void funbuff_dowrite(t_funbuff *x, t_symbol *fn) { t_binbuf *bb = binbuf_new(); char buf[MAXPDSTRING]; t_hammernode *np; /* specifying the object as cyclone/funbuff breaks the file writing/ * reading as the it doesn't start with 'funbuff'. A call to * libgen/basename fixes this. fjk, 2015-01-24 */ t_symbol *objName = atom_getsymbol(binbuf_getvec(x->x_ob.te_binbuf)); objName->s_name = basename(objName->s_name); binbuf_addv(bb, "s", objName); for (np = x->x_tree.t_first; np; np = np->n_next) binbuf_addv(bb, "if", np->n_key, HAMMERNODE_GETFLOAT(np)); canvas_makefilename(x->x_canvas, fn->s_name, buf, MAXPDSTRING); binbuf_write(bb, buf, "", 0); binbuf_free(bb); } static void funbuff_writehook(t_pd *z, t_symbol *fn, int ac, t_atom *av) { funbuff_dowrite((t_funbuff *)z, fn); } static void funbuff_embedhook(t_pd *z, t_binbuf *bb, t_symbol *bindsym) { t_funbuff *x = (t_funbuff *)z; if (x->x_embedflag) { t_hammernode *np; binbuf_addv(bb, "ssi;", bindsym, gensym("embed"), 1); if (np = x->x_tree.t_first) { binbuf_addv(bb, "ss", bindsym, gensym("set")); for (; np; np = np->n_next) binbuf_addv(bb, "if", np->n_key, HAMMERNODE_GETFLOAT(np)); binbuf_addsemi(bb); } } } /* CHECKED symbol arg ok */ static void funbuff_read(t_funbuff *x, t_symbol *s) { if (s && s != &s_) funbuff_doread(x, s); else hammerpanel_open(x->x_filehandle, 0); } /* CHECKED symbol arg not allowed -- a bug? but CHECKME other classes (cf seq's filetype dilemma) */ static void funbuff_write(t_funbuff *x, t_symbol *s) { if (s && s != &s_) funbuff_dowrite(x, s); else /* CHECKME default name */ hammerpanel_save(x->x_filehandle, canvas_getdir(x->x_canvas), x->x_defname); } static void funbuff_delete(t_funbuff *x, t_symbol *s, int ac, t_atom *av) { if (ac && av->a_type == A_FLOAT && (ac == 1 || (ac == 2 && av[1].a_type == A_FLOAT))) { /* CHECKED float is silently truncated */ int ndx = (int)av->a_w.w_float; t_hammernode *np; if ((np = hammertree_search(&x->x_tree, ndx)) && (ac == 1 || HAMMERNODE_GETFLOAT(np) == av[1].a_w.w_float)) { if (np == x->x_pointer) x->x_pointer = 0; /* CHECKED corrupt pointer left here... */ hammertree_delete(&x->x_tree, np); /* FIXME */ } /* CHECKED mismatch silently ignored */ } else loud_messarg((t_pd *)x, s); /* CHECKED */ } static void funbuff_find(t_funbuff *x, t_floatarg f) { t_hammernode *np; if (np = x->x_tree.t_first) { do { /* CHECKED lastdelta preserved */ if (HAMMERNODE_GETFLOAT(np) == f) funbuff_dooutput(x, np->n_key, x->x_lastdelta); } while (np = np->n_next); /* CHECKED no bangout, no complaint if nothing found */ } else loud_error((t_pd *)x, "nothing to find"); /* CHECKED */ } static void funbuff_dump(t_funbuff *x) { t_hammernode *np; if (np = x->x_tree.t_first) { do { x->x_lastdelta = HAMMERNODE_GETFLOAT(np); /* CHECKED */ /* float value preserved (this is incompatible) */ funbuff_dooutput(x, np->n_key, x->x_lastdelta); } while (np = np->n_next); /* CHECKED no bangout */ } else loud_error((t_pd *)x, "nothing to dump"); /* CHECKED */ } /* CHECKME if pointer is updated */ static void funbuff_dointerp(t_funbuff *x, t_floatarg f, int vsz, t_word *vec) { t_hammernode *np1; int trunc = (int)f; if (trunc > f) trunc--; /* CHECKME negative floats */ if (np1 = hammertree_closest(&x->x_tree, trunc, 0)) { float value = HAMMERNODE_GETFLOAT(np1); t_hammernode *np2 = np1->n_next; if (np2) { float delta = (float)(np2->n_key - np1->n_key); /* this is incompatible -- CHECKED float argument is silently truncated (which does not make much sense here), CHECKME again */ float frac = f - np1->n_key; if (frac < 0 || frac >= delta) { loudbug_bug("funbuff_dointerp"); return; } frac /= delta; if (vec) { /* CHECKME */ float vpos = (vsz - 1) * frac; int vndx = (int)vpos; float vfrac = vpos - vndx; if (vndx < 0 || vndx >= vsz - 1) { loudbug_bug("funbuff_dointerp redundant test..."); return; } vec += vndx; frac = vec[0].w_float + (vec[1].w_float - vec[0].w_float) * vfrac; } value += (HAMMERNODE_GETFLOAT(np2) - HAMMERNODE_GETFLOAT(np1)) * frac; } funbuff_dooutput(x, value, x->x_lastdelta); /* CHECKME !np2 */ } else if (np1 = hammertree_closest(&x->x_tree, trunc, 1)) /* CHECKME */ funbuff_dooutput(x, HAMMERNODE_GETFLOAT(np1), x->x_lastdelta); } static void funbuff_interp(t_funbuff *x, t_floatarg f) { funbuff_dointerp(x, f, 0, 0); } static void funbuff_interptab(t_funbuff *x, t_symbol *s, t_floatarg f) { int vsz; t_word *vec; if (vec = vefl_get(s, &vsz, 0, (t_pd *)x)) { if (vsz > 2) funbuff_dointerp(x, f, vsz, vec); else funbuff_dointerp(x, f, 0, 0); } } static void funbuff_reduce(t_funbuff *x, t_floatarg f) { loud_notimplemented((t_pd *)x, "reduce"); } static void funbuff_select(t_funbuff *x, t_floatarg f1, t_floatarg f2) { loud_notimplemented((t_pd *)x, "select"); } /* CHECKED (sub)buffer's copy is stored, as expected -- 'delete' does not modify the clipboard */ /* CHECKED cut entire contents if no selection */ static void funbuff_cut(t_funbuff *x) { loud_notimplemented((t_pd *)x, "cut"); } /* CHECKED copy entire contents if no selection */ static void funbuff_copy(t_funbuff *x) { loud_notimplemented((t_pd *)x, "copy"); } static void funbuff_paste(t_funbuff *x) { loud_notimplemented((t_pd *)x, "paste"); } static void funbuff_undo(t_funbuff *x) { /* CHECKED apparently not working in 4.07 */ loud_notimplemented((t_pd *)x, "undo"); } #ifdef HAMMERTREE_DEBUG static void funbuff_debug(t_funbuff *x, t_floatarg f) { hammertree_debug(&x->x_tree, (int)f, 0); } #endif static void funbuff_free(t_funbuff *x) { hammerfile_free(x->x_filehandle); hammertree_clear(&x->x_tree, 0); } static void *funbuff_new(t_symbol *s) { t_funbuff *x = (t_funbuff *)pd_new(funbuff_class); x->x_canvas = canvas_getcurrent(); x->x_valueset = 0; x->x_pointer = 0; x->x_pointerset = 0; /* CHECKME, rename to intraversal? */ x->x_lastdelta = 0; x->x_embedflag = 0; hammertree_inittyped(&x->x_tree, HAMMERTYPE_FLOAT, 0); inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1")); outlet_new((t_object *)x, &s_float); x->x_deltaout = outlet_new((t_object *)x, &s_float); x->x_bangout = outlet_new((t_object *)x, &s_bang); if (s && s != &s_) { x->x_defname = s; /* CHECKME if 'read' changes this */ funbuff_doread(x, s); } else x->x_defname = &s_; x->x_filehandle = hammerfile_new((t_pd *)x, funbuff_embedhook, funbuff_readhook, funbuff_writehook, 0); return (x); } void funbuff_setup(void) { funbuff_class = class_new(gensym("funbuff"), (t_newmethod)funbuff_new, (t_method)funbuff_free, sizeof(t_funbuff), 0, A_DEFSYM, 0); class_addbang(funbuff_class, funbuff_bang); class_addfloat(funbuff_class, funbuff_float); class_addmethod(funbuff_class, (t_method)funbuff_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_clear, gensym("clear"), 0); class_addmethod(funbuff_class, (t_method)funbuff_goto, gensym("goto"), A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_min, gensym("min"), 0); class_addmethod(funbuff_class, (t_method)funbuff_max, gensym("max"), 0); class_addmethod(funbuff_class, (t_method)funbuff_next, gensym("next"), 0); class_addmethod(funbuff_class, (t_method)funbuff_embed, gensym("embed"), A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_read, gensym("read"), A_DEFSYM, 0); class_addmethod(funbuff_class, (t_method)funbuff_write, gensym("write"), A_DEFSYM, 0); class_addmethod(funbuff_class, (t_method)funbuff_set, gensym("set"), A_GIMME, 0); class_addmethod(funbuff_class, (t_method)funbuff_delete, gensym("delete"), A_GIMME, 0); class_addmethod(funbuff_class, (t_method)funbuff_find, gensym("find"), A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_dump, gensym("dump"), 0); class_addmethod(funbuff_class, (t_method)funbuff_interp, gensym("interp"), A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_interptab, gensym("interptab"), A_FLOAT, A_SYMBOL, 0); class_addmethod(funbuff_class, (t_method)funbuff_reduce, gensym("reduce"), A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_select, gensym("select"), A_FLOAT, A_FLOAT, 0); class_addmethod(funbuff_class, (t_method)funbuff_cut, gensym("cut"), 0); class_addmethod(funbuff_class, (t_method)funbuff_copy, gensym("copy"), 0); class_addmethod(funbuff_class, (t_method)funbuff_paste, gensym("paste"), 0); class_addmethod(funbuff_class, (t_method)funbuff_undo, gensym("undo"), 0); #ifdef HAMMERTREE_DEBUG class_addmethod(funbuff_class, (t_method)funbuff_debug, gensym("debug"), A_DEFFLOAT, 0); #endif hammerfile_setup(funbuff_class, 1); logpost(NULL, 4, "this is cyclone/funbuff %s, %dth %s build", CYCLONE_VERSION, CYCLONE_BUILD, CYCLONE_RELEASE); }