aboutsummaryrefslogtreecommitdiff
path: root/cyclone/sickle/curve.c
diff options
context:
space:
mode:
Diffstat (limited to 'cyclone/sickle/curve.c')
-rw-r--r--cyclone/sickle/curve.c462
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);
+}