aboutsummaryrefslogtreecommitdiff
path: root/pd/src/d_ctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'pd/src/d_ctl.c')
-rw-r--r--pd/src/d_ctl.c496
1 files changed, 496 insertions, 0 deletions
diff --git a/pd/src/d_ctl.c b/pd/src/d_ctl.c
new file mode 100644
index 00000000..e143a067
--- /dev/null
+++ b/pd/src/d_ctl.c
@@ -0,0 +1,496 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* The sig~ and line~ routines; possibly fancier envelope generators to
+ come later.
+*/
+
+#include "m_pd.h"
+#include "math.h"
+
+/* -------------------------- sig~ ------------------------------ */
+static t_class *sig_class;
+
+typedef struct _sig
+{
+ t_object x_obj;
+ float x_f;
+} t_sig;
+
+static t_int *sig_perform(t_int *w)
+{
+ t_float f = *(t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ while (n--) *out++ = f;
+ return (w+4);
+}
+
+static t_int *sig_perf8(t_int *w)
+{
+ t_float f = *(t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+
+ for (; n; n -= 8, out += 8)
+ {
+ out[0] = f;
+ out[1] = f;
+ out[2] = f;
+ out[3] = f;
+ out[4] = f;
+ out[5] = f;
+ out[6] = f;
+ out[7] = f;
+ }
+ return (w+4);
+}
+
+void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n)
+{
+ if (n&7)
+ dsp_add(sig_perform, 3, in, out, n);
+ else
+ dsp_add(sig_perf8, 3, in, out, n);
+}
+
+static void sig_float(t_sig *x, t_float f)
+{
+ x->x_f = f;
+}
+
+static void sig_dsp(t_sig *x, t_signal **sp)
+{
+ dsp_add(sig_perform, 3, &x->x_f, sp[0]->s_vec, sp[0]->s_n);
+}
+
+static void *sig_new(t_floatarg f)
+{
+ t_sig *x = (t_sig *)pd_new(sig_class);
+ x->x_f = f;
+ outlet_new(&x->x_obj, gensym("signal"));
+ return (x);
+}
+
+static void sig_setup(void)
+{
+ sig_class = class_new(gensym("sig~"), (t_newmethod)sig_new, 0,
+ sizeof(t_sig), 0, A_DEFFLOAT, 0);
+ class_addfloat(sig_class, (t_method)sig_float);
+ class_addmethod(sig_class, (t_method)sig_dsp, gensym("dsp"), 0);
+}
+
+/* -------------------------- line~ ------------------------------ */
+static t_class *line_class;
+
+typedef struct _line
+{
+ t_object x_obj;
+ float x_target;
+ float x_value;
+ float x_biginc;
+ float x_inc;
+ float x_1overn;
+ float x_msectodsptick;
+ float x_inletvalue;
+ float x_inletwas;
+ int x_ticksleft;
+ int x_retarget;
+} t_line;
+
+static t_int *line_perform(t_int *w)
+{
+ t_line *x = (t_line *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ float f = x->x_value;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ x->x_value = f = 0;
+ if (x->x_retarget)
+ {
+ int nticks = x->x_inletwas * x->x_msectodsptick;
+ if (!nticks) nticks = 1;
+ x->x_ticksleft = nticks;
+ x->x_biginc = (x->x_target - x->x_value)/(float)nticks;
+ x->x_inc = x->x_1overn * x->x_biginc;
+ x->x_retarget = 0;
+ }
+ if (x->x_ticksleft)
+ {
+ float f = x->x_value;
+ while (n--) *out++ = f, f += x->x_inc;
+ x->x_value += x->x_biginc;
+ x->x_ticksleft--;
+ }
+ else
+ {
+ x->x_value = x->x_target;
+ while (n--) *out++ = x->x_value;
+ }
+ return (w+4);
+}
+
+static void line_float(t_line *x, t_float f)
+{
+ if (x->x_inletvalue <= 0)
+ {
+ x->x_target = x->x_value = f;
+ x->x_ticksleft = x->x_retarget = 0;
+ }
+ else
+ {
+ x->x_target = f;
+ x->x_retarget = 1;
+ x->x_inletwas = x->x_inletvalue;
+ x->x_inletvalue = 0;
+ }
+}
+
+static void line_stop(t_line *x)
+{
+ x->x_target = x->x_value;
+ x->x_ticksleft = x->x_retarget = 0;
+}
+
+static void line_dsp(t_line *x, t_signal **sp)
+{
+ dsp_add(line_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+ x->x_1overn = 1./sp[0]->s_n;
+ x->x_msectodsptick = sp[0]->s_sr / (1000 * sp[0]->s_n);
+}
+
+static void *line_new(void)
+{
+ t_line *x = (t_line *)pd_new(line_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ floatinlet_new(&x->x_obj, &x->x_inletvalue);
+ x->x_ticksleft = x->x_retarget = 0;
+ x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0;
+ return (x);
+}
+
+static void line_setup(void)
+{
+ line_class = class_new(gensym("line~"), line_new, 0,
+ sizeof(t_line), 0, 0);
+ class_addfloat(line_class, (t_method)line_float);
+ class_addmethod(line_class, (t_method)line_dsp, gensym("dsp"), 0);
+ class_addmethod(line_class, (t_method)line_stop, gensym("stop"), 0);
+}
+
+/* -------------------------- snapshot~ ------------------------------ */
+static t_class *snapshot_class;
+
+typedef struct _snapshot
+{
+ t_object x_obj;
+ t_sample x_value;
+ float x_f;
+} t_snapshot;
+
+static void *snapshot_new(void)
+{
+ t_snapshot *x = (t_snapshot *)pd_new(snapshot_class);
+ x->x_value = 0;
+ outlet_new(&x->x_obj, &s_float);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *snapshot_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ *out = *in;
+ return (w+3);
+}
+
+static void snapshot_dsp(t_snapshot *x, t_signal **sp)
+{
+ dsp_add(snapshot_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), &x->x_value);
+}
+
+static void snapshot_bang(t_snapshot *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_value);
+}
+
+static void snapshot_setup(void)
+{
+ snapshot_class = class_new(gensym("snapshot~"), snapshot_new, 0,
+ sizeof(t_snapshot), 0, 0);
+ CLASS_MAINSIGNALIN(snapshot_class, t_snapshot, x_f);
+ class_addmethod(snapshot_class, (t_method)snapshot_dsp, gensym("dsp"), 0);
+ class_addbang(snapshot_class, snapshot_bang);
+}
+
+/* ---------------- env~ - simple envelope follower. ----------------- */
+
+#define MAXOVERLAP 10
+#define MAXVSTAKEN 64
+
+typedef struct sigenv
+{
+ t_object x_obj; /* header */
+ void *x_outlet; /* a "float" outlet */
+ void *x_clock; /* a "clock" object */
+ float *x_buf; /* a Hanning window */
+ int x_phase; /* number of points since last output */
+ int x_period; /* requested period of output */
+ int x_realperiod; /* period rounded up to vecsize multiple */
+ int x_npoints; /* analysis window size in samples */
+ float x_result; /* result to output */
+ float x_sumbuf[MAXOVERLAP]; /* summing buffer */
+ float x_f;
+} t_sigenv;
+
+t_class *sigenv_class;
+static void sigenv_tick(t_sigenv *x);
+
+static void *sigenv_new(t_floatarg fnpoints, t_floatarg fperiod)
+{
+ int npoints = fnpoints;
+ int period = fperiod;
+ t_sigenv *x;
+ float *buf;
+ int i;
+
+ if (npoints < 1) npoints = 1024;
+ if (period < 1) period = npoints/2;
+ if (period < npoints / MAXOVERLAP + 1)
+ period = npoints / MAXOVERLAP + 1;
+ if (!(buf = getbytes(sizeof(float) * (npoints + MAXVSTAKEN))))
+ {
+ error("env: couldn't allocate buffer");
+ return (0);
+ }
+ x = (t_sigenv *)pd_new(sigenv_class);
+ x->x_buf = buf;
+ x->x_npoints = npoints;
+ x->x_phase = 0;
+ x->x_period = period;
+ for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0;
+ for (i = 0; i < npoints; i++)
+ buf[i] = (1. - cos((2 * 3.14159 * i) / npoints))/npoints;
+ for (; i < npoints+MAXVSTAKEN; i++) buf[i] = 0;
+ x->x_clock = clock_new(x, (t_method)sigenv_tick);
+ x->x_outlet = outlet_new(&x->x_obj, gensym("float"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigenv_perform(t_int *w)
+{
+ t_sigenv *x = (t_sigenv *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ int count;
+ float *sump;
+ in += n;
+ for (count = x->x_phase, sump = x->x_sumbuf;
+ count < x->x_npoints; count += x->x_realperiod, sump++)
+ {
+ float *hp = x->x_buf + count;
+ float *fp = in;
+ float sum = *sump;
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ fp--;
+ sum += *hp++ * (*fp * *fp);
+ }
+ *sump = sum;
+ }
+ sump[0] = 0;
+ x->x_phase -= n;
+ if (x->x_phase < 0)
+ {
+ x->x_result = x->x_sumbuf[0];
+ for (count = x->x_realperiod, sump = x->x_sumbuf;
+ count < x->x_npoints; count += x->x_realperiod, sump++)
+ sump[0] = sump[1];
+ sump[0] = 0;
+ x->x_phase = x->x_realperiod - n;
+ clock_delay(x->x_clock, 0L);
+ }
+ return (w+4);
+}
+
+static void sigenv_dsp(t_sigenv *x, t_signal **sp)
+{
+ if (x->x_period % sp[0]->s_n) x->x_realperiod =
+ x->x_period + sp[0]->s_n - (x->x_period % sp[0]->s_n);
+ else x->x_realperiod = x->x_period;
+ dsp_add(sigenv_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+ if (sp[0]->s_n > MAXVSTAKEN) bug("sigenv_dsp");
+}
+
+static void sigenv_tick(t_sigenv *x) /* callback function for the clock */
+{
+ outlet_float(x->x_outlet, powtodb(x->x_result));
+}
+
+static void sigenv_ff(t_sigenv *x) /* cleanup on free */
+{
+ clock_free(x->x_clock);
+ freebytes(x->x_buf, (x->x_npoints + MAXVSTAKEN) * sizeof(float));
+}
+
+
+void sigenv_setup(void )
+{
+ sigenv_class = class_new(gensym("env~"), (t_newmethod)sigenv_new,
+ (t_method)sigenv_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sigenv_class, t_sigenv, x_f);
+ class_addmethod(sigenv_class, (t_method)sigenv_dsp, gensym("dsp"), 0);
+}
+
+/* --------------------- threshold~ ----------------------------- */
+
+static t_class *threshold_tilde_class;
+
+typedef struct _threshold_tilde
+{
+ t_object x_obj;
+ t_outlet *x_outlet1; /* bang out for high thresh */
+ t_outlet *x_outlet2; /* bang out for low thresh */
+ t_clock *x_clock; /* wakeup for message output */
+ float x_f; /* scalar inlet */
+ int x_state; /* 1 = high, 0 = low */
+ float x_hithresh; /* value of high threshold */
+ float x_lothresh; /* value of low threshold */
+ float x_deadwait; /* msec remaining in dead period */
+ float x_msecpertick; /* msec per DSP tick */
+ float x_hideadtime; /* hi dead time in msec */
+ float x_lodeadtime; /* lo dead time in msec */
+} t_threshold_tilde;
+
+static void threshold_tilde_tick(t_threshold_tilde *x);
+static void threshold_tilde_set(t_threshold_tilde *x,
+ t_floatarg hithresh, t_floatarg hideadtime,
+ t_floatarg lothresh, t_floatarg lodeadtime);
+
+static t_threshold_tilde *threshold_tilde_new(t_floatarg hithresh,
+ t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime)
+{
+ t_threshold_tilde *x = (t_threshold_tilde *)
+ pd_new(threshold_tilde_class);
+ x->x_state = 0; /* low state */
+ x->x_deadwait = 0; /* no dead time */
+ x->x_clock = clock_new(x, (t_method)threshold_tilde_tick);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_bang);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_bang);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ x->x_msecpertick = 0.;
+ x->x_f = 0;
+ threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime);
+ return (x);
+}
+
+ /* "set" message to specify thresholds and dead times */
+static void threshold_tilde_set(t_threshold_tilde *x,
+ t_floatarg hithresh, t_floatarg hideadtime,
+ t_floatarg lothresh, t_floatarg lodeadtime)
+{
+ if (lothresh > hithresh)
+ lothresh = hithresh;
+ x->x_hithresh = hithresh;
+ x->x_hideadtime = hideadtime;
+ x->x_lothresh = lothresh;
+ x->x_lodeadtime = lodeadtime;
+}
+
+ /* number in inlet sets state -- note incompatible with JMAX which used
+ "int" message for this, impossible here because of auto signal conversion */
+static void threshold_tilde_ft1(t_threshold_tilde *x, t_floatarg f)
+{
+ x->x_state = (f != 0);
+ x->x_deadwait = 0;
+}
+
+static void threshold_tilde_tick(t_threshold_tilde *x)
+{
+ if (x->x_state)
+ outlet_bang(x->x_outlet1);
+ else outlet_bang(x->x_outlet2);
+}
+
+static t_int *threshold_tilde_perform(t_int *w)
+{
+ float *in1 = (float *)(w[1]);
+ t_threshold_tilde *x = (t_threshold_tilde *)(w[2]);
+ int n = (t_int)(w[3]);
+ if (x->x_deadwait > 0)
+ x->x_deadwait -= x->x_msecpertick;
+ else if (x->x_state)
+ {
+ /* we're high; look for low sample */
+ for (; n--; in1++)
+ {
+ if (*in1 < x->x_lothresh)
+ {
+ clock_delay(x->x_clock, 0L);
+ x->x_state = 0;
+ x->x_deadwait = x->x_lodeadtime;
+ goto done;
+ }
+ }
+ }
+ else
+ {
+ /* we're low; look for high sample */
+ for (; n--; in1++)
+ {
+ if (*in1 >= x->x_hithresh)
+ {
+ clock_delay(x->x_clock, 0L);
+ x->x_state = 1;
+ x->x_deadwait = x->x_hideadtime;
+ goto done;
+ }
+ }
+ }
+done:
+ return (w+4);
+}
+
+void threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp)
+{
+ x->x_msecpertick = 1000. * sp[0]->s_n / sp[0]->s_sr;
+ dsp_add(threshold_tilde_perform, 3, sp[0]->s_vec, x, sp[0]->s_n);
+}
+
+static void threshold_tilde_ff(t_threshold_tilde *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void threshold_tilde_setup( void)
+{
+ threshold_tilde_class = class_new(gensym("threshold~"),
+ (t_newmethod)threshold_tilde_new, (t_method)threshold_tilde_ff,
+ sizeof(t_threshold_tilde), 0,
+ A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(threshold_tilde_class, t_threshold_tilde, x_f);
+ class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_set,
+ gensym("set"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_dsp,
+ gensym("dsp"), 0);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_ctl_setup(void)
+{
+ sig_setup();
+ line_setup();
+ snapshot_setup();
+ sigenv_setup();
+ threshold_tilde_setup();
+}
+