diff options
author | N.N. <krzyszcz@users.sourceforge.net> | 2003-08-14 14:32:00 +0000 |
---|---|---|
committer | N.N. <krzyszcz@users.sourceforge.net> | 2003-08-14 14:32:00 +0000 |
commit | a0f8c026117d1bbe094894d614136efea4b65c97 (patch) | |
tree | 26fe6a8efad5dddc42a5986084606fddbf1e057a /cyclone/sickle/curve.c | |
parent | 053845e60bc1f6143e7c50aa10069a18405c2161 (diff) |
10 new sickle classes
svn path=/trunk/externals/miXed/; revision=846
Diffstat (limited to 'cyclone/sickle/curve.c')
-rw-r--r-- | cyclone/sickle/curve.c | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/cyclone/sickle/curve.c b/cyclone/sickle/curve.c new file mode 100644 index 0000000..e6bade1 --- /dev/null +++ b/cyclone/sickle/curve.c @@ -0,0 +1,462 @@ +/* Copyright (c) 2003 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 <math.h> +#include "m_pd.h" +#include "shared.h" +#include "common/grow.h" +#include "common/loud.h" +#include "sickle/sic.h" + +//#define CURVE_DEBUG + +/* CHECKED apparently c74's formula was not very carefully tuned. It has 5% + deviation from the straight line for ccinput=0 (ccinput is user's curve + control parameter, <0..1>) at half-domain, range=1. It generates nans for + ccinput > .995. + + The formula below generates curves with < .000004% deviation and no nans. + + Problem: find a function f : ccinput -> cc, such that the curves will bend + in a semi-linear way over the ccinput's range of 0..1. The curve function + is then g(x, p) = (exp(f(p) * x) - 1) / (exp(f(p)) - 1), where x is curve's + domain, and p is ccinput. If, for example, the points g(0.5, p) are to make + a semi-linear pattern, then the solution is a function f that minimizes + the integral of the error function e(p) = sqr(((1-p)/2)-g(.5, p)) over 0..1. + Until someone does this analytically, we are left with a lame formula, which + has been tweaked and tested in gnuplot: f(p) = h(p) / (1 - h(p)), where + h(p) = (((p + 1e-20) * 1.2) ** .41) * .91. The file curve.gp, in the + sickle's source directory, may come handy, in case there is anyone, who + fancy tweaking it even further. + + To implement this, start from these equations: + bb * mm ^ npoints = bb + 1 + (bb ^ 2) * (mm ^ npoints) = ((exp(ff/2) - 1) / (exp(ff) - 1)) ^ 2 + + and calculate: + hh = pow(((ccinput + c1) * c2), c3) * c4 + ff = hh / (1 - hh) + eff = exp(ff) - 1 + gh = (exp(ff * .5) - 1) / eff + bb = gh * (gh / (1 - (gh + gh))) + mm = ((exp(ff * (1/npoints)) - 1) / (eff * bb)) + 1 + + The loop is: + for (vv = bb, i = 0; i < n; vv *= mm, i++) + result = (vv - bb) * (y1 - y0) + y0 + where y0, y1 are start and destination values +*/ + +#define CURVE_C1 1e-20 +#define CURVE_C2 1.2 +#define CURVE_C3 0.41 +#define CURVE_C4 0.91 + +#define CURVE_MINCCINPUT -1. +#define CURVE_MAXCCINPUT 1. + +#define CURVE_INISIZE 64 /* LATER rethink */ +#define CURVE_MAXSIZE 64 + +typedef struct _curveseg +{ + float s_target; + float s_delta; + int s_npoints; + float s_ccinput; + double s_bb; + double s_mm; +} t_curveseg; + +typedef struct _curve +{ + t_sic x_sic; + float x_value; + float x_ccinput; + float x_target; + float x_delta; + int x_deltaset; + double x_vv; + double x_bb; + double x_mm; + float x_y0; + float x_dy; + float x_ksr; + int x_nleft; + int x_retarget; + int x_size; /* as allocated */ + int x_nsegs; /* as used */ + t_curveseg *x_curseg; + t_curveseg *x_segs; + t_curveseg x_segini[CURVE_INISIZE]; + t_clock *x_clock; + t_outlet *x_bangout; +#ifdef CURVE_DEBUG + int dbg_nretargets; + int dbg_exitpoint; + int dbg_npoints; +#endif +} t_curve; + +static t_class *curve_class; +static double curve_coef; + +static void curve_cc(t_curve *x, t_curveseg *segp, float f) +{ + int npoints = segp->s_delta * x->x_ksr + 0.5; /* LATER rethink */ + segp->s_ccinput = f; + if (npoints > 0) + { + double hh, ff, eff, gh; + segp->s_npoints = npoints; + if (f < 0) + { + if (f < CURVE_MINCCINPUT) + f = CURVE_MINCCINPUT; + hh = pow(((CURVE_C1 - f) * CURVE_C2), CURVE_C3) * CURVE_C4; + ff = hh / (1. - hh); + eff = exp(ff) - 1.; + gh = (exp(ff * .5) - 1.) / eff; + segp->s_bb = gh * (gh / (1. - (gh + gh))); + segp->s_mm = 1. / (((exp(ff * (1. / (double)npoints)) - 1.) / + (eff * segp->s_bb)) + 1.); + } + else + { + if (f > CURVE_MAXCCINPUT) + f = CURVE_MAXCCINPUT; + hh = pow(((f + CURVE_C1) * CURVE_C2), CURVE_C3) * CURVE_C4; + ff = hh / (1. - hh); + eff = exp(ff) - 1.; + gh = (exp(ff * .5) - 1.) / eff; + segp->s_bb = gh * (gh / (1. - (gh + gh))); + segp->s_mm = ((exp(ff * (1. / (double)npoints)) - 1.) / + (eff * segp->s_bb)) + 1.; + } + } + else + { + segp->s_npoints = 0; + segp->s_bb = segp->s_mm = 1.; + } +#ifdef CURVE_DEBUG + post("%g %g %g %g", + segp->s_target, segp->s_delta, segp->s_bb, segp->s_mm); +#endif +} + +static void curve_tick(t_curve *x) +{ + outlet_bang(x->x_bangout); +#ifdef CURVE_DEBUG + post("exit point %d, after %d retarget calls", + x->dbg_exitpoint, x->dbg_nretargets); + post("at value %g, after last %d npoints, with bb %g, mm %g", + x->x_value, x->dbg_npoints, x->x_bb, x->x_mm); + x->dbg_nretargets = x->dbg_exitpoint = x->dbg_npoints = 0; +#endif +} + +static t_int *curve_perform(t_int *w) +{ + t_curve *x = (t_curve *)(w[1]); + t_float *out = (t_float *)(w[2]); + int nblock = (int)(w[3]); + int nxfer = x->x_nleft; + float curval = x->x_value; + double vv = x->x_vv; + double bb = x->x_bb; + double mm = x->x_mm; + float dy = x->x_dy; + float y0 = x->x_y0; + if (PD_BADFLOAT(curval)) /* LATER rethink */ + curval = x->x_value = 0; +retarget: + if (x->x_retarget) + { + float target = x->x_curseg->s_target; + float delta = x->x_curseg->s_delta; + int npoints = x->x_curseg->s_npoints; + mm = x->x_curseg->s_mm; + if (x->x_curseg->s_ccinput < 0) + { + bb = x->x_curseg->s_bb + 1.; + dy = x->x_value - target; + } + else + { + bb = x->x_curseg->s_bb; + dy = target - x->x_value; + } +#ifdef CURVE_DEBUG + x->dbg_nretargets++; +#endif + x->x_nsegs--; + x->x_curseg++; + while (npoints <= 0) + { + curval = x->x_value = target; + if (x->x_nsegs) + { + target = x->x_curseg->s_target; + delta = x->x_curseg->s_delta; + npoints = x->x_curseg->s_npoints; + mm = x->x_curseg->s_mm; + if (x->x_curseg->s_ccinput < 0) + { + bb = x->x_curseg->s_bb + 1.; + dy = x->x_value - target; + } + else + { + bb = x->x_curseg->s_bb; + dy = target - x->x_value; + } + x->x_nsegs--; + x->x_curseg++; + } + else + { + while (nblock--) *out++ = curval; + x->x_nleft = 0; +#ifdef CURVE_DEBUG + x->dbg_exitpoint = 1; +#endif + clock_delay(x->x_clock, 0); + x->x_retarget = 0; + return (w + 4); + } + } + nxfer = x->x_nleft = npoints; + x->x_vv = vv = bb; + x->x_bb = bb; + x->x_mm = mm; + x->x_dy = dy; + x->x_y0 = y0 = x->x_value; + x->x_target = target; + x->x_retarget = 0; +#ifdef CURVE_DEBUG + x->dbg_npoints = npoints; +#endif + } + if (nxfer >= nblock) + { + int silly = ((x->x_nleft -= nblock) == 0); /* LATER rethink */ + while (nblock--) + { + *out++ = curval = (vv - bb) * dy + y0; + vv *= mm; + } + if (silly) + { + if (x->x_nsegs) x->x_retarget = 1; + else + { +#ifdef CURVE_DEBUG + x->dbg_exitpoint = 2; +#endif + clock_delay(x->x_clock, 0); + } + x->x_value = x->x_target; + } + else + { + x->x_value = curval; + x->x_vv = vv; + } + } + else if (nxfer > 0) + { + nblock -= nxfer; + do + *out++ = (vv - bb) * dy + y0, vv *= mm; + while (--nxfer); + curval = x->x_value = x->x_target; + if (x->x_nsegs) + { + x->x_retarget = 1; + goto retarget; + } + else + { + while (nblock--) *out++ = curval; + x->x_nleft = 0; +#ifdef CURVE_DEBUG + x->dbg_exitpoint = 3; +#endif + clock_delay(x->x_clock, 0); + } + } + else while (nblock--) *out++ = curval; + return (w + 4); +} + +static void curve_float(t_curve *x, t_float f) +{ + if (x->x_deltaset) + { + x->x_deltaset = 0; + x->x_target = f; + x->x_nsegs = 1; + x->x_curseg = x->x_segs; + x->x_curseg->s_target = f; + x->x_curseg->s_delta = x->x_delta; +#ifdef CURVE_DEBUG + startpost("single segment: "); +#endif + curve_cc(x, x->x_curseg, x->x_ccinput); + x->x_retarget = 1; + } + else + { + x->x_value = x->x_target = f; + x->x_nsegs = 0; + x->x_curseg = 0; + x->x_nleft = 0; + x->x_retarget = 0; + } +} + +/* CHECKED delta is not persistent, but ccinput is */ +static void curve_ft1(t_curve *x, t_floatarg f) +{ + x->x_delta = f; + x->x_deltaset = (f > 0); +} + +static void curve_list(t_curve *x, t_symbol *s, int ac, t_atom *av) +{ + int natoms, nsegs, odd; + t_atom *ap; + t_curveseg *segp; + for (natoms = 0, ap = av; natoms < ac; natoms++, ap++) + { + if (ap->a_type != A_FLOAT) + { + loud_messarg((t_pd *)x, &s_list); /* CHECKED */ + return; /* CHECKED */ + } + } + if (!natoms) + return; /* CHECKED */ + odd = natoms % 3; + nsegs = natoms / 3; + if (odd) nsegs++; + if (nsegs > x->x_size) + { + int ns = nsegs; + x->x_segs = grow_nodata(&ns, &x->x_size, x->x_segs, + CURVE_INISIZE, x->x_segini, + sizeof(*x->x_segs)); + if (ns < nsegs) + { + natoms = ns * 3; + nsegs = ns; + odd = 0; + } + } + x->x_nsegs = nsegs; +#ifdef CURVE_DEBUG + post("%d segments:", x->x_nsegs); +#endif + segp = x->x_segs; + if (odd) nsegs--; + while (nsegs--) + { + segp->s_target = av++->a_w.w_float; + segp->s_delta = av++->a_w.w_float; + curve_cc(x, segp, av++->a_w.w_float); + segp++; + } + if (odd) + { + segp->s_target = av->a_w.w_float; + if (odd > 1) + segp->s_delta = av[1].a_w.w_float; + else + segp->s_delta = 0; + curve_cc(x, segp, 0.); + } + x->x_deltaset = 0; + x->x_target = x->x_segs->s_target; + x->x_curseg = x->x_segs; + x->x_retarget = 1; +} + +/* CHECKED no stop, pity... */ +#if 0 +static void curve_stop(t_curve *x) +{ + x->x_target = x->x_value; + x->x_nleft = 0; + x->x_retarget = 0; + x->x_nsegs = 0; + x->x_curseg = 0; +} +#endif + +static void curve_dsp(t_curve *x, t_signal **sp) +{ + float ksr = sp[0]->s_sr * 0.001; + if (ksr != x->x_ksr) + { + int nsegs = x->x_nsegs; + t_curveseg *segp = x->x_segs; + x->x_ksr = ksr; + while (nsegs--) + { + curve_cc(x, segp, segp->s_ccinput); + segp++; + } + } + dsp_add(curve_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void curve_free(t_curve *x) +{ + if (x->x_segs != x->x_segini) + freebytes(x->x_segs, x->x_size * sizeof(*x->x_segs)); + if (x->x_clock) clock_free(x->x_clock); +} + +static void *curve_new(t_floatarg f1, t_floatarg f2) +{ + static int initialized = 0; + t_curve *x = (t_curve *)pd_new(curve_class); + if (!initialized) + { + curve_coef = CURVE_C2 / exp(CURVE_C3); + initialized = 1; + } + x->x_value = x->x_target = f1; + x->x_ccinput = f2; + x->x_deltaset = 0; + x->x_ksr = sys_getsr() * 0.001; + x->x_nleft = 0; + x->x_retarget = 0; + x->x_size = CURVE_INISIZE; + x->x_nsegs = 0; + x->x_segs = x->x_segini; + x->x_curseg = 0; + inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1")); + floatinlet_new((t_object *)x, &x->x_ccinput); + outlet_new((t_object *)x, &s_signal); + x->x_bangout = outlet_new((t_object *)x, &s_bang); + x->x_clock = clock_new(x, (t_method)curve_tick); + return (x); +} + +void curve_tilde_setup(void) +{ + curve_class = class_new(gensym("curve~"), + (t_newmethod)curve_new, + (t_method)curve_free, + sizeof(t_curve), 0, + A_DEFFLOAT, A_DEFFLOAT, 0); + sic_setup(curve_class, curve_dsp, SIC_NOMAINSIGNALIN); + class_addfloat(curve_class, curve_float); + class_addlist(curve_class, curve_list); + class_addmethod(curve_class, (t_method)curve_ft1, + gensym("ft1"), A_FLOAT, 0); +} |