aboutsummaryrefslogtreecommitdiff
path: root/externals/signal/bonk~
diff options
context:
space:
mode:
Diffstat (limited to 'externals/signal/bonk~')
-rw-r--r--externals/signal/bonk~/bonk~.c1083
-rw-r--r--externals/signal/bonk~/help-bonk~.pd162
2 files changed, 1245 insertions, 0 deletions
diff --git a/externals/signal/bonk~/bonk~.c b/externals/signal/bonk~/bonk~.c
new file mode 100644
index 00000000..138d5fae
--- /dev/null
+++ b/externals/signal/bonk~/bonk~.c
@@ -0,0 +1,1083 @@
+/* Copyright 1997-1999 Miller Puckette (msp@ucsd.edu) and Ted Apel
+(tapel@ucsd.edu). Permission is granted to use this software for any
+noncommercial purpose. For commercial licensing please contact the UCSD
+Technology Transfer Office.
+
+THE AUTHORS AND THEIR EMPLOYERS MAKE NO WARRANTY, EXPRESS OR IMPLIED,
+IN CONNECTION WITH THIS SOFTWARE!
+*/
+
+#include <math.h>
+#include <stdio.h>
+
+#ifdef NT
+#pragma warning (disable: 4305 4244)
+#endif
+
+#ifdef MSP
+
+#include "ext.h"
+#include "z_dsp.h"
+#include "math.h"
+//#include "stdio.h"
+#include "ext_support.h"
+//#include "ext_strings.h"
+#include "ext_proto.h"
+
+typedef double t_floatarg; // from m_pd.h
+#define flog log
+#define fexp exp
+#define fsqrt sqrt
+#define t_resizebytes(a, b, c) t_resizebytes((char *)(a), (b), (c))
+
+#define flog log
+#define fexp exp
+#define fsqrt sqrt
+
+#define FILE_DIALOG 1 /* use dialogs to get file name */
+#define FILE_NAMED 2 /* symbol specifies file name */
+
+#define DUMTAB1SIZE 256
+#define DUMTAB2SIZE 1024
+
+static float rsqrt_exptab[DUMTAB1SIZE], rsqrt_mantissatab[DUMTAB2SIZE];
+
+void *bonk_class;
+#define getbytes t_getbytes
+#define freebytes t_freebytes
+#endif /* MSP */
+
+#ifdef PD
+#include "m_pd.h"
+static t_class *bonk_class;
+#endif
+
+/* ------------------------ bonk~ ----------------------------- */
+
+#define NPOINTS 256
+#define MAXCHANNELS 8
+#define DEFPERIOD 128
+#define DEFHITHRESH 60
+#define DEFLOTHRESH 50
+#define DEFMASKTIME 4
+#define DEFMASKDECAY 0.7
+#define DEFDEBOUNCEDECAY 0
+#define DEFMINVEL 7
+
+
+
+typedef struct _filterkernel
+{
+ int k_npoints;
+ float k_freq;
+ float k_normalize;
+ float *k_stuff;
+} t_filterkernel;
+
+#if 0 /* this is the design for 1.0: */
+static t_filterkernel bonk_filterkernels[] =
+ {{256, 2, .01562}, {256, 4, .01562}, {256, 6, .01562}, {180, 6, .02222},
+ {128, 6, .01803}, {90, 6, .02222}, {64, 6, .02362}, {46, 6, .02773},
+ {32, 6, .03227}, {22, 6, .03932}, {16, 6, .04489}};
+#endif
+
+ /* here's the 1.1 rev: */
+static t_filterkernel bonk_filterkernels[] =
+ {{256, 1, .01562, 0}, {256, 3, .01562, 0}, {256, 5, .01562, 0},
+ {212, 6, .01886, 0}, {150, 6, .01885, 0}, {106, 6, .02179, 0},
+ {76, 6, .0236, 0}, {54, 6, .02634, 0}, {38, 6, .03047, 0},
+ {26, 6, .03667, 0}, {18, 6, .04458, 0}};
+
+#define NFILTERS ((int)(sizeof(bonk_filterkernels)/ \
+ sizeof(bonk_filterkernels[0])))
+
+static float bonk_hanningwindow[NPOINTS];
+
+typedef struct _hist
+{
+ float h_power;
+ float h_mask;
+ float h_before;
+ int h_countup;
+} t_hist;
+
+typedef struct template
+{
+ float t_amp[NFILTERS];
+} t_template;
+
+typedef struct _insig
+{
+ t_hist g_hist[NFILTERS]; /* history for each filter */
+#ifdef PD
+ t_outlet *g_outlet; /* outlet for raw data */
+#endif
+#ifdef MSP
+ void *g_outlet; /* outlet for raw data */
+#endif
+ float *g_inbuf; /* buffered input samples */
+ t_float *g_invec; /* new input samples */
+} t_insig;
+
+typedef struct _bonk
+{
+#ifdef PD
+ t_object x_obj;
+ t_outlet *x_cookedout;
+ t_clock *x_clock;
+#endif /* PD */
+#ifdef MSP
+ t_pxobject x_obj;
+ void *x_cookedout; // t_outlet *x_cookedout;
+ void *x_clock; // t_clock *x_clock;
+ short x_vol; // to store the volume reference number.
+#endif /* MSP */
+
+ t_hist x_hist[NFILTERS];
+ t_template *x_template;
+ t_insig *x_insig;
+ int x_ninsig;
+ int x_ntemplate;
+ int x_infill;
+ int x_countdown;
+ int x_period;
+ int x_willattack;
+ int x_debug;
+ float x_hithresh;
+ float x_lothresh;
+ int x_masktime;
+ float x_maskdecay;
+ int x_learn;
+ double x_learndebounce; /* debounce time for "learn" mode */
+ int x_learncount; /* countup for "learn" mode */
+ float x_debouncedecay;
+ float x_minvel; /* minimum velocity we output */
+ float x_debouncevel;
+} t_bonk;
+
+#ifdef MSP
+static void *bonk_new(int period, int bonk2);
+void bonk_tick(t_bonk *x);
+void bonk_doit(t_bonk *x);
+static t_int *bonk_perform(t_int *w);
+void bonk_dsp(t_bonk *x, t_signal **sp);
+void bonk_assist(t_bonk *x, void *b, long m, long a, char *s);
+void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2);
+void bonk_mask(t_bonk *x, t_floatarg f1, t_floatarg f2);
+void bonk_debounce(t_bonk *x, t_floatarg f1);
+static void bonk_print(t_bonk *x, t_floatarg f);
+static void bonk_learn(t_bonk *x, t_floatarg f);
+void bonk_bang(t_bonk *x);
+void bonk_setupkernels(void);
+void bonk_free(t_bonk *x);
+void bonk_setup(void);
+void main();
+float qrsqrt(float f);
+static void bonk_write(t_bonk *x, t_symbol *s);
+static void bonk_read(t_bonk *x, t_symbol *s);
+
+double clock_getsystime();
+double clock_gettimesince(double prevsystime);
+static char *strcpy(char *s1, const char *s2);
+static int ilog2(int n);
+#endif
+
+static void bonk_tick(t_bonk *x);
+
+static void bonk_donew(t_bonk *x, int period, int nsig)
+{
+ int i, j;
+ t_hist *h;
+ float *fp;
+ t_insig *g;
+
+ for (j = 0, g = x->x_insig; j < nsig; j++, g++)
+ {
+ for (i = 0, h = g->g_hist; i--; h++)
+ h->h_power = h->h_mask = h->h_before = 0, h->h_countup = 0;
+ /* we ought to check for failure to allocate memory here */
+ g->g_inbuf = (float *)getbytes(NPOINTS * sizeof(float));
+ for (i = NPOINTS, fp = g->g_inbuf; i--; fp++) *fp = 0;
+ }
+ x->x_ninsig = nsig;
+ x->x_template = (t_template *)getbytes(0);
+ x->x_ntemplate = 0;
+ x->x_infill = 0;
+ x->x_countdown = 0;
+ if (!period) period = NPOINTS/2;
+ x->x_period = 1 << ilog2(period);
+ x->x_willattack = 0;
+ x->x_debug = 0;
+ x->x_hithresh = DEFHITHRESH;
+ x->x_lothresh = DEFLOTHRESH;
+ x->x_masktime = DEFMASKTIME;
+ x->x_maskdecay = DEFMASKDECAY;
+ x->x_learn = 0;
+ x->x_learndebounce = clock_getsystime();
+ x->x_learncount = 0;
+ x->x_debouncedecay = DEFDEBOUNCEDECAY;
+ x->x_minvel = DEFMINVEL;
+ x->x_debouncevel = 0;
+}
+
+
+static void bonk_print(t_bonk *x, t_floatarg f);
+
+static void bonk_dotick(t_bonk *x, int hit)
+{
+ t_atom at[NFILTERS], *ap, at2[3];
+ int i, j, k, n;
+ t_hist *h;
+ float powerout[NFILTERS*MAXCHANNELS], *pp, vel = 0, temperature = 0;
+ float *fp;
+ t_template *tp;
+ int nfit, ninsig = x->x_ninsig, ntemplate = x->x_ntemplate;
+ t_insig *gp;
+ int totalbins = NFILTERS * ninsig;
+
+ x->x_willattack = 0;
+
+ for (i = ninsig, pp = powerout, gp = x->x_insig; i--; gp++)
+ {
+ for (j = 0, h = gp->g_hist; j < NFILTERS; j++, h++, pp++)
+ {
+ float power = (hit ? h->h_mask - h->h_before : h->h_power);
+ float intensity = *pp =
+ (power > 0 ? 100. * qrsqrt(qrsqrt(power)) : 0);
+ vel += intensity;
+ temperature += intensity * (float)j;
+ }
+ }
+ if (vel > 0) temperature /= vel;
+ else temperature = 0;
+ vel *= 0.5 / ninsig; /* fudge factor */
+ if (hit)
+ {
+ /* if hit nonzero it's a clock callback. if in "learn" mode update the
+ template list; in any event match the hit to known templates. */
+
+ if (vel < x->x_debouncevel)
+ {
+ if (x->x_debug)
+ post("bounce cancelled: vel %f debounce %f",
+ vel, x->x_debouncevel);
+ return;
+ }
+ if (vel < x->x_minvel)
+ {
+ if (x->x_debug)
+ post("low velocity cancelled: vel %f, minvel %f",
+ vel, x->x_minvel);
+ return;
+ }
+ x->x_debouncevel = vel;
+ if (x->x_learn)
+ {
+ double lasttime = x->x_learndebounce;
+ double msec = clock_gettimesince(lasttime);
+ if ((!ntemplate) || (msec > 200))
+ {
+ int countup = x->x_learncount;
+ /* normalize to 100 */
+ float norm;
+ for (i = NFILTERS * ninsig, norm = 0, pp = powerout; i--; pp++)
+ norm += *pp * *pp;
+ if (norm < 1.0e-15) norm = 1.0e-15;
+ norm = 100.f * qrsqrt(norm);
+ /* check if this is the first strike for a new template */
+ if (!countup)
+ {
+ int oldn = ntemplate;
+ x->x_ntemplate = ntemplate = oldn + ninsig;
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+ oldn * sizeof(x->x_template[0]),
+ ntemplate * sizeof(x->x_template[0]));
+ for (i = ninsig, pp = powerout; i--; oldn++)
+ for (j = NFILTERS, fp = x->x_template[oldn].t_amp; j--;
+ pp++, fp++)
+ *fp = *pp * norm;
+ }
+ else
+ {
+ int oldn = ntemplate - ninsig;
+ if (oldn < 0) post("bonk_tick bug");
+ for (i = ninsig, pp = powerout; i--; oldn++)
+ {
+ for (j = NFILTERS, fp = x->x_template[oldn].t_amp; j--;
+ pp++, fp++)
+ *fp = (countup * *fp + *pp * norm)
+ /(countup + 1.0f);
+ }
+ }
+ countup++;
+ if (countup == x->x_learn) countup = 0;
+ x->x_learncount = countup;
+ }
+ else return;
+ }
+ x->x_learndebounce = clock_getsystime();
+ if (ntemplate)
+ {
+ float bestfit = -1e30;
+ int templatecount;
+ nfit = -1;
+ for (i = 0, templatecount = 0, tp = x->x_template;
+ templatecount < ntemplate; i++)
+ {
+ float dotprod = 0;
+ for (k = 0, pp = powerout;
+ k < ninsig && templatecount < ntemplate;
+ k++, tp++, templatecount++)
+ {
+ for (j = NFILTERS, fp = tp->t_amp;
+ j--; fp++, pp++)
+ {
+ if (*fp < 0 || *pp < 0) post("bonk_tick bug 2");
+ dotprod += *fp * *pp;
+ }
+ }
+ if (dotprod > bestfit)
+ {
+ bestfit = dotprod;
+ nfit = i;
+ }
+ }
+ if (nfit < 0) post("bonk_tick bug");
+ }
+ else nfit = 0;
+ }
+ else nfit = -1; /* hit is zero; this is the "bang" method. */
+
+ if (x->x_debug)
+ post("bonk out: number %d, vel %f, temperature %f",
+ nfit, vel, temperature);
+ SETFLOAT(at2, nfit);
+ SETFLOAT(at2+1, vel);
+ SETFLOAT(at2+2, temperature);
+ outlet_list(x->x_cookedout, 0, 3, at2);
+
+ for (n = 0, gp = x->x_insig + (ninsig-1),
+ pp = powerout + NFILTERS * (ninsig-1);
+ n < ninsig; n++, gp--, pp -= NFILTERS)
+ {
+ float *pp2;
+ for (i = 0, ap = at, pp2 = pp; i < NFILTERS;
+ i++, ap++, pp2++)
+ {
+ ap->a_type = A_FLOAT;
+ ap->a_w.w_float = *pp2;
+ }
+ outlet_list(gp->g_outlet, 0, NFILTERS, at);
+ }
+}
+
+static void bonk_tick(t_bonk *x)
+{
+ bonk_dotick(x, 1);
+}
+
+static void bonk_doit(t_bonk *x)
+{
+ int i, j, n;
+ t_filterkernel *k;
+ t_hist *h;
+ float growth = 0, *fp1, *fp2, *fp3, *fp4;
+ float windowbuf[NPOINTS];
+ static int poodle;
+ int ninsig = x->x_ninsig;
+ t_insig *gp;
+
+ for (n = 0, gp = x->x_insig; n < ninsig; n++, gp++)
+ {
+ for (i = NPOINTS, fp1 = gp->g_inbuf, fp2 = bonk_hanningwindow,
+ fp3 = windowbuf; i--; fp1++, fp2++, fp3++)
+ *fp3 = *fp1 * *fp2;
+
+ for (i = 0, k = bonk_filterkernels, h = gp->g_hist;
+ i < NFILTERS; i++, k++, h++)
+ {
+ float power = 0, maskpow = h->h_mask;
+ int countup = h->h_countup;
+ int npoints = k->k_npoints;
+ /* special case: the fourth filter is centered */
+ float *inbuf = gp->g_inbuf +
+ (i == 3 ? ((NPOINTS - npoints) / 2) : 0);
+
+ /* run the filter repeatedly, sliding it forward by half its
+ length, stopping when it runs past the end of the buffer */
+ for (fp1 = inbuf, fp2 = fp1 + NPOINTS - k->k_npoints;
+ fp1 <= fp2; fp1 += npoints/2)
+ {
+ float rsum = 0, isum = 0;
+ for (fp3 = fp1, fp4 = k->k_stuff, j = npoints; j--;)
+ {
+ float g = *fp3++;
+ rsum += g * *fp4++;
+ isum += g * *fp4++;
+ }
+ power += rsum * rsum + isum * isum;
+ }
+
+ if (!x->x_willattack) h->h_before = maskpow;
+
+ if (power > maskpow)
+ growth += power/(maskpow + 1.0e-15) - 1.f;
+ if (!x->x_willattack && countup >= x->x_masktime)
+ maskpow *= x->x_maskdecay;
+
+ if (power > maskpow)
+ {
+ maskpow = power;
+ countup = 0;
+ }
+ countup++;
+ h->h_countup = countup;
+ h->h_mask = maskpow;
+ h->h_power = power;
+ }
+ }
+ if (x->x_willattack > 4)
+ {
+ /* if it takes more than 4 analyses for the energy to stop growing,
+ forget it; we would rather miss the note than report it late. */
+ if (x->x_debug) post("soft attack cancelled");
+ x->x_willattack = 0;
+ }
+ else if (x->x_willattack)
+ {
+ if (growth < x->x_lothresh)
+ clock_delay(x->x_clock, 0);
+ else x->x_willattack++;
+ }
+ else if (growth > x->x_hithresh)
+ {
+ if (x->x_debug) post("attack; growth = %f", growth);
+ x->x_willattack = 1;
+ for (n = 0, gp = x->x_insig; n < ninsig; n++, gp++)
+ for (i = NFILTERS, h = gp->g_hist; i--; h++)
+ h->h_mask = h->h_power, h->h_countup = 0;
+ }
+
+ x->x_debouncevel *= x->x_debouncedecay;
+
+ /* shift the input buffer and update counters */
+ if (x->x_period > NPOINTS) x->x_countdown = x->x_period - NPOINTS;
+ else x->x_countdown = 0;
+ if (x->x_period < NPOINTS)
+ {
+ int overlap = NPOINTS - x->x_period;
+
+ for (n = 0, gp = x->x_insig; n < ninsig; n++, gp++)
+ for (i = overlap, fp1 = gp->g_inbuf, fp2 = fp1 + x->x_period; i--;)
+ *fp1++ = *fp2++;
+ x->x_infill = overlap;
+ }
+ else x->x_infill = 0;
+ poodle = 1;
+}
+
+static t_int *bonk_perform(t_int *w)
+{
+ t_bonk *x = (t_bonk *)(w[1]);
+ int n = (int)(w[2]);
+ int onset = (int)(w[3]);
+ if (x->x_countdown > 0) x->x_countdown -= n;
+ else
+ {
+ int i, j, infill = x->x_infill, ninsig = x->x_ninsig;
+ t_insig *gp;
+ for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)
+ {
+ float *fp = gp->g_inbuf + infill;
+ t_float *in1 = gp->g_invec + onset;
+ for (j = 0; j < n; j++)
+ *fp++ = *in1++;
+ }
+ infill += n;
+ x->x_infill = infill;
+ if (infill == NPOINTS) bonk_doit(x);
+ }
+ return (w+4);
+}
+
+static void bonk_dsp(t_bonk *x, t_signal **sp)
+{
+ int i, n = sp[0]->s_n, vsize = x->x_period, ninsig = x->x_ninsig;
+ t_insig *gp;
+ if (vsize > n) vsize = n;
+
+ for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)
+ gp->g_invec = (*(sp++))->s_vec;
+
+ for (i = 0; i < n; i += vsize)
+ dsp_add(bonk_perform, 3, x, vsize, i);
+}
+
+static void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2)
+{
+ if (f1 > f2)
+ post("bonk: warning: low threshold greater than hi threshold");
+ x->x_lothresh = f1;
+ x->x_hithresh = f2;
+}
+
+static void bonk_mask(t_bonk *x, t_floatarg f1, t_floatarg f2)
+{
+ int ticks = f1;
+ if (ticks < 0) ticks = 0;
+ if (f2 < 0) f2 = 0;
+ else if (f2 > 1) f2 = 1;
+ x->x_masktime = ticks;
+ x->x_maskdecay = f2;
+}
+
+static void bonk_debounce(t_bonk *x, t_floatarg f1)
+{
+ if (f1 < 0) f1 = 0;
+ else if (f1 > 1) f1 = 1;
+ x->x_debouncedecay = f1;
+}
+
+static void bonk_minvel(t_bonk *x, t_floatarg f)
+{
+ if (f < 0) f = 0;
+ x->x_minvel = f;
+}
+
+static void bonk_print(t_bonk *x, t_floatarg f)
+{
+ int i;
+ post("thresh %f %f", x->x_lothresh, x->x_hithresh);
+ post("mask %d %f", x->x_masktime, x->x_maskdecay);
+ post("debounce %f", x->x_debouncedecay);
+ post("minvel %f", x->x_minvel);
+ if (x->x_ntemplate)
+ {
+ post("templates:");
+ for (i = 0; i < x->x_ntemplate; i++)
+ post("%2d \
+%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f", i,
+ x->x_template[i].t_amp[0],
+ x->x_template[i].t_amp[1],
+ x->x_template[i].t_amp[2],
+ x->x_template[i].t_amp[3],
+ x->x_template[i].t_amp[4],
+ x->x_template[i].t_amp[5],
+ x->x_template[i].t_amp[6],
+ x->x_template[i].t_amp[7],
+ x->x_template[i].t_amp[8],
+ x->x_template[i].t_amp[9],
+ x->x_template[i].t_amp[10]);
+ }
+ else post("no templates");
+ if (x->x_learn) post("learn mode");
+ if (f != 0)
+ {
+ int j, ninsig = x->x_ninsig;
+ t_insig *gp;
+ for (j = 0, gp = x->x_insig; j < ninsig; j++, gp++)
+ {
+ t_hist *h;
+ if (ninsig > 1) post("input %d:", j+1);
+ for (i = NFILTERS, h = gp->g_hist; i--; h++)
+ post("pow %f mask %f before %f count %d",
+ h->h_power, h->h_mask, h->h_before, h->h_countup);
+ }
+ }
+ if (x->x_debug) post("debug mode");
+}
+
+static void bonk_debug(t_bonk *x, t_floatarg f)
+{
+ x->x_debug = (f != 0);
+}
+
+static void bonk_learn(t_bonk *x, t_floatarg f)
+{
+ int n = f;
+ if (n < 0) n = 0;
+ if (n)
+ {
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+ x->x_ntemplate * sizeof(x->x_template[0]), 0);
+ x->x_ntemplate = 0;
+ }
+ x->x_learn = n;
+ x->x_learncount = 0;
+}
+
+static void bonk_forget(t_bonk *x)
+{
+ int ntemplate = x->x_ntemplate, newn = ntemplate - x->x_ninsig;
+ if (newn < 0) newn = 0;
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+ x->x_ntemplate * sizeof(x->x_template[0]),
+ newn * sizeof(x->x_template[0]));
+ x->x_ntemplate = newn;
+ x->x_learncount = 0;
+}
+
+#if 0
+static void bonk_bang(t_bonk *x)
+{
+ t_atom at[NFILTERS];
+ int i, j, ninsig = x->x_ninsig;
+ t_insig *gp;
+
+ SETFLOAT(at2, nfit);
+ SETFLOAT(at2+1, vel);
+ SETFLOAT(at2+2, temperature);
+ outlet_list(x->x_cookedout, 0L, 3, at2);
+ for (i = 0, gp = x->x_insig + (ninsig-1); i < ninsig; i++, gp--)
+ {
+ for (j = 0; j < NFILTERS; j++)
+ {
+ at[j].a_type = A_FLOAT;
+ at[j].a_w.w_float = 100 * qrsqrt(qrsqrt(gp->g_hist[j].h_power));
+ }
+ outlet_list(gp->g_outlet, 0L, NFILTERS, at);
+ }
+}
+#endif
+
+static void bonk_bang(t_bonk *x)
+{
+ bonk_dotick(x, 0);
+}
+
+static void bonk_setupkernels(void)
+{
+ int i, j;
+ float *fp;
+ for (i = 0; i < NFILTERS; i++)
+ {
+ int npoints = bonk_filterkernels[i].k_npoints;
+ float freq = bonk_filterkernels[i].k_freq;
+ float normalize = bonk_filterkernels[i].k_normalize;
+ float phaseinc = (2.f * 3.14159f) / npoints;
+ bonk_filterkernels[i].k_stuff =
+ (float *)getbytes(2 * sizeof(float) * npoints);
+ for (fp = bonk_filterkernels[i].k_stuff, j = npoints; j--;)
+ {
+ float phase = j * phaseinc;
+ float window = normalize * (0.5f - 0.5f * cos(phase));
+ *fp++ = window * cos(freq * phase);
+ *fp++ = window * sin(freq * phase);
+ }
+ }
+ for (i = 0; i < NPOINTS; i++)
+ bonk_hanningwindow[i] = (0.5f - 0.5f * cos(i * (2*3.14159)/NPOINTS));
+}
+
+#ifdef PD
+static void bonk_read(t_bonk *x, t_symbol *s)
+{
+ FILE *fd = fopen(s->s_name, "r");
+ float vec[NFILTERS];
+ int i, ntemplate = 0, remaining;
+ float *fp, *fp2;
+ if (!fd)
+ {
+ post("%s: open failed", s->s_name);
+ return;
+ }
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+ x->x_ntemplate * sizeof(t_template), 0);
+ while (1)
+ {
+ for (i = NFILTERS, fp = vec; i--; fp++)
+ if (fscanf(fd, "%f", fp) < 1) goto nomore;
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+ ntemplate * sizeof(t_template),
+ (ntemplate + 1) * sizeof(t_template));
+ for (i = NFILTERS, fp = vec,
+ fp2 = x->x_template[ntemplate].t_amp; i--;)
+ *fp2++ = *fp++;
+ ntemplate++;
+ }
+nomore:
+ if (remaining = (ntemplate % x->x_ninsig))
+ {
+ post("bonk_read: %d templates not a multiple of %d; dropping extras");
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+ ntemplate * sizeof(t_template),
+ (ntemplate - remaining) * sizeof(t_template));
+ ntemplate = ntemplate - remaining;
+ }
+ post("bonk: read %d templates\n", ntemplate);
+ x->x_ntemplate = ntemplate;
+ fclose(fd);
+}
+#endif /* PD */
+
+#ifdef MSP
+static void bonk_read(t_bonk *x, t_symbol *s) // MSP
+{
+ SFTypeList types;
+ short vol = 0;
+ OSType type;
+ char name[256];
+ char **buf;
+ int eaten;
+ long size = 100;
+ int i, ntemplate = 0;
+ float vec[NFILTERS];
+ float *fp, *fp2;
+ if (s->s_name[0]) // if it is named
+ {
+ vol = defvolume();
+ strcpy (name, s->s_name);
+
+ if (readtohandle (name, vol, &buf, &size) != 0)
+
+ {
+ post("bonk~: problem with reading file.");
+ return;
+
+ }
+ else
+ {
+ post("bonk~: template read successfully.");
+ //post("bonk~: size of file is %d", size);
+ }
+ for (eaten = 0; ;)
+ {
+ for (i = NFILTERS, fp = vec; i--; fp++)
+ {
+ while (eaten < size && (
+ (*buf)[eaten] == ' ' ||
+ (*buf)[eaten] == '\t' ||
+ (*buf)[eaten] == '\n' ||
+ (*buf)[eaten] == ';' ||
+ (*buf)[eaten] == '\r'))
+ eaten++;
+ if (eaten >= size) goto nomore;
+ if (sscanf(&(*buf)[eaten], "%f", fp) < 1) goto nomore;
+
+ while (eaten < size && !(
+ (*buf)[eaten] == ' ' ||
+ (*buf)[eaten] == '\t' ||
+ (*buf)[eaten] == '\n' ||
+ (*buf)[eaten] == ';' ||
+ (*buf)[eaten] == '\r'))
+ eaten++;
+ }
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+
+ ntemplate * sizeof(t_template),
+ (ntemplate + 1) * sizeof(t_template));
+
+ for (i = NFILTERS, fp = vec,
+ fp2 = x->x_template[ntemplate].t_amp; i--;)
+ *fp2++ = *fp++;
+ ntemplate++;
+ post("bonk~: fp = %f", fp);
+ }
+ }
+ else
+ {
+ name[0] = 0;
+ types[0]='TEXT';
+ types[1]='maxb';
+
+ open_promptset("Select template for reading.");
+
+ if (open_dialog(name, &vol, &type, types, 2))
+ {
+ post("bonk~: open canceled");
+ return;
+ }
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+
+ x->x_ntemplate * sizeof(t_template), 0);
+
+ // post("bonk~: the file name is %s", name);
+
+ if (readtohandle (name, vol, &buf, &size) != 0)
+
+ {
+ post("bonk~: problem with reading file.");
+ return;
+
+ }
+ else
+ {
+ post("bonk~: template read successfully.");
+ // post("bonk~: size of file is %d", size);
+ }
+ for (eaten = 0; ;)
+ {
+ for (i = NFILTERS, fp = vec; i--; fp++)
+ {
+ while (eaten < size && (
+ (*buf)[eaten] == ' ' ||
+ (*buf)[eaten] == '\t' ||
+ (*buf)[eaten] == '\n' ||
+ (*buf)[eaten] == ';' ||
+ (*buf)[eaten] == '\r'))
+ eaten++;
+ if (eaten >= size) goto nomore;
+ if (sscanf(&(*buf)[eaten], "%f", fp) < 1) goto nomore;
+
+ while (eaten < size && !(
+ (*buf)[eaten] == ' ' ||
+ (*buf)[eaten] == '\t' ||
+ (*buf)[eaten] == '\n' ||
+ (*buf)[eaten] == ';' ||
+ (*buf)[eaten] == '\r'))
+ eaten++;
+ }
+ x->x_template = (t_template *)t_resizebytes(x->x_template,
+
+ ntemplate * sizeof(t_template),
+ (ntemplate + 1) * sizeof(t_template));
+
+ for (i = NFILTERS, fp = vec,
+ fp2 = x->x_template[ntemplate].t_amp; i--;)
+ *fp2++ = *fp++;
+ ntemplate++;
+ }
+ nomore:
+ post("bonk~: read %d templates", ntemplate);
+
+ x->x_ntemplate = ntemplate;
+ } // end of else
+}
+#endif /* MSP */
+
+#ifdef PD
+static void bonk_write(t_bonk *x, t_symbol *s)
+{
+ FILE *fd = fopen(s->s_name, "w");
+ int i, ntemplate = x->x_ntemplate;
+ t_template *tp = x->x_template;
+ float *fp;
+ if (!fd)
+ {
+ post("%s: couldn't create", s->s_name);
+ return;
+ }
+ for (; ntemplate--; tp++)
+ {
+ for (i = NFILTERS, fp = tp->t_amp; i--; fp++)
+ fprintf(fd, "%6.2f ", *fp);
+ fprintf(fd, "\n");
+ }
+ post("bonk: wrote %d templates\n", x->x_ntemplate);
+ fclose(fd);
+}
+#endif /* PD */
+
+#ifdef MSP
+static void bonk_write(t_bonk *x, t_symbol *s) // MSP
+{
+
+ char fn[236];
+ short vol;
+ short bin = 0; // 0 = text
+ void* b;
+ int i, ntemplate = x->x_ntemplate;
+ t_template *tp = x->x_template;
+ if (s->s_name[0]) // if it is named
+ {
+ strcpy (fn, s->s_name);
+ vol = defvolume();
+ b = binbuf_new();
+ for (; ntemplate--; tp++)
+ {
+ int i;
+ Atom at[11];
+ for (i = 0; i < 11; i++)
+ at[i].a_type = A_FLOAT, at[i].a_w.w_float = tp->t_amp[i];
+ binbuf_insert(b, 0L, 11, at);
+ }
+ binbuf_write(b, fn, vol, bin);
+ freeobject(b);
+ post("bonk~: wrote file %s", fn);
+ }
+
+ else
+ {
+ saveas_promptset("Save Template file as");
+ strcpy(fn, "");
+ if (!saveas_dialog(fn, &vol, 0L))
+ {
+ b = binbuf_new();
+ for (; ntemplate--; tp++)
+ {
+ int i;
+ Atom at[11];
+ for (i = 0; i < 11; i++)
+ at[i].a_type = A_FLOAT, at[i].a_w.w_float =
+ tp->t_amp[i];
+ binbuf_insert(b, 0L, 11, at);
+ }
+ binbuf_write(b, fn, vol, bin);
+ freeobject(b);
+ post("bonk~: wrote file %s", fn);
+ }
+ } // end of else
+}
+#endif /* MSP */
+
+static void bonk_free(t_bonk *x)
+{
+ int i, ninsig = x->x_ninsig;
+ t_insig *gp = x->x_insig;
+ for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)
+ freebytes(gp->g_inbuf, NPOINTS * sizeof(float));
+ clock_free(x->x_clock);
+}
+
+/* -------------------------- Pd glue ------------------------- */
+#ifdef PD
+
+static void *bonk_new(t_floatarg fperiod, t_floatarg fnsig)
+{
+ t_bonk *x = (t_bonk *)pd_new(bonk_class);
+ int nsig = fnsig, j;
+ t_insig *g;
+ if (nsig < 1) nsig = 1;
+ if (nsig > MAXCHANNELS) nsig = MAXCHANNELS;
+
+ x->x_clock = clock_new(x, (t_method)bonk_tick);
+ x->x_insig = (t_insig *)getbytes(nsig * sizeof(*x->x_insig));
+ for (j = 0, g = x->x_insig; j < nsig; j++, g++)
+ {
+ g->g_outlet = outlet_new(&x->x_obj, gensym("list"));
+ if (j)
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ }
+ x->x_cookedout = outlet_new(&x->x_obj, gensym("list"));
+ bonk_donew(x, fperiod, nsig);
+ return (x);
+}
+
+void bonk_tilde_setup(void)
+{
+ bonk_class = class_new(gensym("bonk~"), (t_newmethod)bonk_new, 0,
+ sizeof(t_bonk), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addmethod(bonk_class, nullfn, gensym("signal"), 0);
+ class_addmethod(bonk_class, (t_method)bonk_dsp, gensym("dsp"), 0);
+ class_addbang(bonk_class, bonk_bang);
+ class_addmethod(bonk_class, (t_method)bonk_learn, gensym("learn"),
+ A_FLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_forget, gensym("forget"), 0);
+ class_addmethod(bonk_class, (t_method)bonk_thresh, gensym("thresh"),
+ A_FLOAT, A_FLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_mask, gensym("mask"),
+ A_FLOAT, A_FLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_debounce, gensym("debounce"),
+ A_FLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_minvel, gensym("minvel"),
+ A_FLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_print, gensym("print"),
+ A_DEFFLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_debug, gensym("debug"),
+ A_DEFFLOAT, 0);
+ class_addmethod(bonk_class, (t_method)bonk_read, gensym("read"),
+ A_SYMBOL, 0);
+ class_addmethod(bonk_class, (t_method)bonk_write, gensym("write"),
+ A_SYMBOL, 0);
+ bonk_setupkernels();
+ post("bonk version 1.1 TEST 3");
+}
+#endif
+
+/* -------------------------- MSP glue ------------------------- */
+#ifdef MSP
+
+static int ilog2(int n)
+{
+ int ret = -1;
+ while (n)
+ {
+ n >>= 1;
+ ret++;
+ }
+ return (ret);
+}
+
+static char *strcpy(char *s1, const char *s2)
+{
+ char *ret = s1;
+
+ while ((*s1++ = *s2++) != 0)
+ ;
+
+ return ret;
+}
+
+static void *bonk_new(int period, int nsig)
+{
+ int i, j;
+ t_hist *h;
+ t_bonk *x = (t_bonk *)newobject(bonk_class);
+ float *fp;
+ t_insig *g;
+
+ if (nsig < 1) nsig = 1;
+ if (nsig > MAXCHANNELS) nsig = MAXCHANNELS;
+ x->x_insig = (t_insig *)getbytes(nsig * sizeof(*x->x_insig));
+ dsp_setup((t_pxobject *)x, nsig); // nsig inputs
+ x->x_cookedout = listout((t_object *)x);
+ for (j = 0, g = x->x_insig + nsig-1; j < nsig; j++, g--)
+ {
+ g->g_outlet = listout((t_object *)x);
+ }
+ x->x_cookedout = listout((t_object *)x);
+ x->x_clock = clock_new(x, (method)bonk_tick);
+
+ bonk_donew(x, period, nsig);
+ return (x);
+}
+
+void main()
+{
+ setup(&bonk_class, bonk_new, (method)bonk_free,
+ (short)sizeof(t_bonk), 0L, A_DEFLONG, A_DEFLONG, 0);
+ addmess((method)bonk_dsp, "dsp", 0);
+ addbang((method)bonk_bang);
+ addmess((method)bonk_forget, "forget", 0);
+ addmess((method)bonk_learn, "learn", A_FLOAT, 0);
+ addmess((method)bonk_thresh, "thresh", A_FLOAT, A_FLOAT, 0);
+ addmess((method)bonk_mask, "mask", A_FLOAT, A_FLOAT, 0);
+ addmess((method)bonk_minvel, "minvel", A_FLOAT, 0);
+ addmess((method)bonk_debounce, "debounce", A_FLOAT, 0);
+ addmess((method)bonk_print, "print", A_DEFFLOAT, 0);
+ addmess((method)bonk_read, "read", A_DEFSYM, 0);
+ addmess((method)bonk_write, "write", A_DEFSYM, 0);
+ addmess((method)bonk_assist, "assist", A_CANT, 0);
+ addmess((method)bonk_debug, "debug", A_FLOAT, 0);
+ bonk_setupkernels();
+// post("bonk~ v1.00 Miller Puckette, Ted Apel");
+ post("bonk~ v1.00");
+ dsp_initclass();
+ rescopy('STR#',3747);
+}
+
+void bonk_assist(t_bonk *x, void *b, long m, long a, char *s)
+{
+ assist_string(3747,m,a,1,2,s);
+}
+
+ /* get current system time */
+double clock_getsystime()
+{
+
+ return gettime();
+}
+
+ /* elapsed time in milliseconds since the given system time */
+double clock_gettimesince(double prevsystime)
+{
+ return ((gettime() - prevsystime));
+}
+
+
+float qrsqrt(float f)
+{
+ return 1/sqrt(f);
+
+}
+#endif /* MSP */
diff --git a/externals/signal/bonk~/help-bonk~.pd b/externals/signal/bonk~/help-bonk~.pd
new file mode 100644
index 00000000..52427964
--- /dev/null
+++ b/externals/signal/bonk~/help-bonk~.pd
@@ -0,0 +1,162 @@
+#N canvas 107 94 958 626 10;
+#X obj 320 579 print;
+#X floatatom 314 501 0 0 0;
+#X obj 320 549 spigot;
+#X msg 314 471 0;
+#X msg 351 471 1;
+#X msg 442 427 bang;
+#X obj 429 518 bonk~;
+#X msg 442 244 learn 1;
+#X msg 442 304 learn 0;
+#X msg 437 486 print;
+#X obj 390 467 adc~;
+#X text 320 597 cooked;
+#X msg 565 76 \; pd dsp 1;
+#X obj 257 579 print;
+#X floatatom 251 501 0 0 0;
+#X obj 257 549 spigot;
+#X msg 251 471 0;
+#X msg 282 471 1;
+#X text 257 597 raw;
+#N canvas 366 126 600 400 synth 0;
+#X obj 112 24 r bonk-cooked;
+#X obj 112 49 unpack;
+#X obj 112 99 * 12;
+#X obj 112 124 div 7;
+#X obj 112 74 + 1;
+#X obj 112 174 mtof;
+#X obj 112 224 osc~;
+#X obj 112 249 cos~;
+#X obj 112 149 + 47;
+#X obj 209 247 line~;
+#X obj 209 272 *~;
+#X obj 209 297 lop~ 500;
+#X obj 112 274 *~;
+#X obj 103 361 dac~;
+#X obj 253 165 dbtorms;
+#X obj 253 115 * 0.5;
+#X obj 253 140 + 50;
+#X obj 211 189 f;
+#X msg 173 159 bang;
+#X obj 258 83 inlet;
+#X obj 111 307 hip~ 5;
+#X msg 34 24 0 60;
+#X obj 112 199 sig~;
+#X msg 209 222 \$1 \, 0 200;
+#X connect 0 0 1 0;
+#X connect 1 0 4 0;
+#X connect 2 0 3 0;
+#X connect 3 0 8 0;
+#X connect 4 0 2 0;
+#X connect 5 0 18 0;
+#X connect 5 0 22 0;
+#X connect 6 0 7 0;
+#X connect 7 0 12 0;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 9 0 10 1;
+#X connect 10 0 11 0;
+#X connect 11 0 12 1;
+#X connect 12 0 20 0;
+#X connect 14 0 17 1;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
+#X connect 17 0 23 0;
+#X connect 18 0 17 0;
+#X connect 19 0 15 0;
+#X connect 20 0 13 1;
+#X connect 20 0 13 0;
+#X connect 21 0 1 0;
+#X connect 22 0 6 0;
+#X connect 23 0 9 0;
+#X restore 804 86 pd synth;
+#X obj 454 549 s bonk-cooked;
+#X floatatom 804 63 0 0 0;
+#X msg 804 33 0;
+#X msg 442 274 learn 10;
+#X msg 442 334 forget;
+#X msg 442 364 write templates.txt;
+#X msg 442 394 read templates.txt;
+#X msg 835 33 90;
+#X msg 442 120 thresh 6 50;
+#X text 8 70 The Bonk object takes an audio signal input and looks
+for "attacks" defined as sharp changes in the spectral envelope of
+the incoming sound. Optionally \, and less reliably \, you can have
+Bonk check the attack against a collection of stored templates to try
+to guess which of two or more instruments was hit. Bonk is described
+theoretically in the 1998 ICMC proceedings \, reprinted on http://man104nfs.ucsd.edu/~mpuckett.
+;
+#X text 470 70 click here;
+#X text 471 83 to start DSP;
+#X text 8 191 Bonk's two outputs are the raw spectrum of the attack
+(provided as a list of 11 numbers giving the signal "loudness" in the
+11 frequency bands used) \, and the "cooked" output which gives only
+an instrument number (counting up from zero) and a "velocity". The
+instrumnent number is significant only if Bonk has a "template set"
+in memory.;
+#X text 8 368 In this patch \, after starting DSP \, you can print
+out the raw or cooked output using the two "spigots" or listen to a
+synthesizer output by raising its volume.;
+#X text 259 448 enable printout;
+#X text 705 32 output volume;
+#X text 719 50 (0-100);
+#X text 533 118 Set low and high thresholds. Signal growth must exceed
+the high one and then fall to the low one to make an attack.;
+#X text 533 151 Minimum velocity to output (quieter notes are ignored.)
+;
+#X msg 442 180 mask 4 0.7;
+#X msg 442 214 debounce 0;
+#X text 8 299 Bonk's analysis is carried out on a 256-point window
+(6 msec at 44.1 kHz) and by default the analysis period is 128 samples.
+The analysis period can be specified as Bonk's creation argument but
+must be a multiple of 64;
+#X text 532 219 Minimum time (msec) between attacks;
+#X text 532 170 Describes how energy in each frequency band masks later
+energy in the band. Here the masking is total for 4 analysis periods
+and then drops by 0.7 each period.;
+#X text 530 244 Forget all templates and start learning new ones. The
+argument gives the number of times you will hit each instrument (10
+recommended.) Turn on the output volume above for audible feedback
+as you train Bonk. "Learn 0" exits learn mode.;
+#X text 530 328 Forget the last template. In Learn mode \, use "forget"
+to erase and record over a template.;
+#X text 595 368 Write templates to a file in text-editable format.
+;
+#X text 596 398 Read templates from a file.;
+#X text 538 493 Print out all settings and templates.;
+#X msg 442 150 minvel 10;
+#X text 538 426 Poll the current spectrum via "raw" outlet \, You can
+set a very high threshold if you don't want attacks mixed in.;
+#X text 218 12 BONK - an attack detector for small percussion instruments
+;
+#X msg 634 517 print;
+#X msg 437 456 debug 0;
+#X text 538 466 turn debugging on or off.;
+#X connect 1 0 2 1;
+#X connect 2 0 0 0;
+#X connect 3 0 1 0;
+#X connect 4 0 1 0;
+#X connect 5 0 6 0;
+#X connect 6 0 15 0;
+#X connect 6 1 2 0;
+#X connect 6 1 20 0;
+#X connect 7 0 6 0;
+#X connect 8 0 6 0;
+#X connect 9 0 6 0;
+#X connect 10 0 6 0;
+#X connect 14 0 15 1;
+#X connect 15 0 13 0;
+#X connect 16 0 14 0;
+#X connect 17 0 14 0;
+#X connect 21 0 19 0;
+#X connect 22 0 21 0;
+#X connect 23 0 6 0;
+#X connect 24 0 6 0;
+#X connect 25 0 6 0;
+#X connect 26 0 6 0;
+#X connect 27 0 21 0;
+#X connect 28 0 6 0;
+#X connect 39 0 6 0;
+#X connect 40 0 6 0;
+#X connect 49 0 6 0;
+#X connect 53 0 6 0;