From 5af7081222ef801b23857fb904c78d0798f5d0df Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 10 Jan 2011 06:03:02 +0000 Subject: build Pd's extra as a libdir svn path=/trunk/; revision=14694 --- externals/extra/bonk~/bonk~-help.pd | 209 +++++ externals/extra/bonk~/bonk~.c | 1474 +++++++++++++++++++++++++++++++++++ externals/extra/bonk~/templates.txt | 4 + 3 files changed, 1687 insertions(+) create mode 100644 externals/extra/bonk~/bonk~-help.pd create mode 100644 externals/extra/bonk~/bonk~.c create mode 100644 externals/extra/bonk~/templates.txt (limited to 'externals/extra/bonk~') diff --git a/externals/extra/bonk~/bonk~-help.pd b/externals/extra/bonk~/bonk~-help.pd new file mode 100644 index 00000000..e52b7c50 --- /dev/null +++ b/externals/extra/bonk~/bonk~-help.pd @@ -0,0 +1,209 @@ +#N canvas 43 123 1054 583 12; +#X obj 382 492 spigot; +#X msg 484 293 bang; +#X obj 483 454 bonk~; +#X msg 483 357 print; +#X obj 435 428 adc~; +#X msg 637 506 \; pd dsp 1; +#X obj 300 492 spigot; +#N canvas 366 126 604 404 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 869 523 pd synth; +#X floatatom 869 500 0 0 0 0 - - -; +#X msg 869 470 0; +#X msg 900 470 90; +#X text 625 472 click here; +#X text 626 485 to start DSP; +#X text 5 285 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 770 469 output volume; +#X text 784 487 (0-100); +#X msg 483 138 mask 4 0.7; +#X text 578 120 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 528 286 Poll the current spectrum via "raw" outlet \, You can +set a very high threshold if you don't want attacks mixed in.; +#X msg 483 331 debug 0; +#X text 561 331 turn debugging on or off.; +#X obj 349 493 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; +#X obj 431 493 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; +#X obj 382 522 print cooked; +#X obj 300 522 print raw; +#X text 162 491 enable printout:; +#X text 560 202 Minimum "velocity" to output (quieter notes are ignored.) +; +#X obj 485 481 s bonk-cooked; +#X text 8 145 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". This +"velocity" is the sum of the square roots of the amplitudes of the +bands \, normalized so that 100 is an attack of amplitude of about +1 The instrument number is significant only if Bonk has a "template +set" in memory.; +#X text 580 35 Set low and high thresholds. Signal growth must exceed +the high one and then fall to the low one to make an attack. The unit +is the sum of the proportional growth in the 11 filter bands. Proportional +growth is essentially the logarithmic time derivative.; +#X msg 483 384 print 1; +#X text 551 386 print out filterbank settings; +#X text 9 33 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 crca.ucsd.edu/~msp +.; +#N canvas 723 241 701 719 creation-arguments 0; +#X text 228 14 creation arguments for bonk~; +#X text 70 272 -npts 256; +#X text 44 244 default value:; +#X text 70 308 -hop 128; +#X text 70 342 -nfilters 11; +#X text 68 380 -halftones 6; +#X text 76 514 -overlap 1; +#X text 79 567 -firstbin 1; +#X text 71 454 -minbandwidth 1.5; +#X text 122 147 All frequency parameters are specified in 'bins'. One +bin is the sample rate divided by the window size. The minimum possible +bandwidth is 1.5 bins. Higher bandwidths give numerically more robust +outputs.; +#X text 43 229 Arguments and; +#X text 212 270 window size in points; +#X text 210 306 analysis period ("hop size") in points; +#X text 212 340 number of filters to use; +#X text 212 379 desired bandwidth of filters in halftones \, effective +in the exponentially spaced region. (At lower center frequencies the +bandwidth is supported by the "minbandwidth" parameter below).; +#X text 212 511 overlap factor between filters. If 1 \, the filters +are spaced to line up at their half-power points. Other values specify +more or fewer filters proportionally.; +#X text 121 49 bonk~ uses a filterbank whose center frequencies are +spaced equally at low frequencies and proportionally at high ones - +i.e. \, they increase linearly \, then exponentially. They are determined +by the filters' bandwidths and overlap. The bandwidths are specified +proportionally to frequency but bounded below by a specified minimum. +; +#X text 210 455 minimum bandwidth in bins. If the bandwidth specified +by "halftones" is smaller than this \, this value is used. This must +be at least 1.5.; +#X text 212 567 center frequency \, in bins \, of the lowest filter. +The others are computed from this.; +#X restore 147 414 pd creation-arguments; +#N canvas 660 173 579 589 templates 0; +#X msg 76 197 learn 0; +#X msg 76 227 forget; +#X msg 76 257 write templates.txt; +#X msg 76 287 read templates.txt; +#X msg 76 107 debounce 0; +#X msg 76 137 learn 10; +#X obj 62 431 outlet; +#X text 155 133 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 155 217 Forget the last template. In Learn mode \, use "forget" +to erase and record over a template.; +#X text 220 253 Write templates to a file in text-editable format. +; +#X text 221 283 Read templates from a file.; +#X text 157 104 Minimum time (msec) between attacks in learn mode; +#X connect 0 0 6 0; +#X connect 1 0 6 0; +#X connect 2 0 6 0; +#X connect 3 0 6 0; +#X connect 4 0 6 0; +#X connect 5 0 6 0; +#X restore 500 421 pd templates; +#X msg 483 68 thresh 2.5 5; +#X msg 483 173 attack-frames 1; +#X text 608 174 number of frames over which to measure growth; +#X text 605 422 more messages for managing templates; +#X msg 483 201 minvel 7; +#X msg 483 228 spew 0; +#X text 550 230 Turn spew mode on/off; +#X msg 483 255 useloudness 0; +#X text 597 254 experimental: use alternative loudness units; +#X text 212 9 BONK~ - attack detection and spectral envelope measurement +; +#X text 734 552 Updated for Pd version 0.42; +#X text 5 344 By default bonk's analysis is carried out on a 256-point +window (6 msec at 44.1 kHz) and the analysis period is 128 samples. +These and other parameters may be overridden using creation arguments +as shown in the subpatch below:; +#X text 552 356 Print out settings and templates.; +#X connect 0 0 23 0; +#X connect 1 0 2 0; +#X connect 2 0 6 0; +#X connect 2 1 0 0; +#X connect 2 1 27 0; +#X connect 3 0 2 0; +#X connect 4 0 2 0; +#X connect 6 0 24 0; +#X connect 8 0 7 0; +#X connect 9 0 8 0; +#X connect 10 0 8 0; +#X connect 16 0 2 0; +#X connect 19 0 2 0; +#X connect 21 0 6 1; +#X connect 22 0 0 1; +#X connect 30 0 2 0; +#X connect 34 0 2 0; +#X connect 35 0 2 0; +#X connect 36 0 2 0; +#X connect 39 0 2 0; +#X connect 40 0 2 0; +#X connect 42 0 2 0; diff --git a/externals/extra/bonk~/bonk~.c b/externals/extra/bonk~/bonk~.c new file mode 100644 index 00000000..6776b2e2 --- /dev/null +++ b/externals/extra/bonk~/bonk~.c @@ -0,0 +1,1474 @@ +/* + ########################################################################### + # bonk~ - a Max/MSP external + # by miller puckette and ted apel + # http://crca.ucsd.edu/~msp/ + # Max/MSP port by barry threw + # http://www.barrythrew.com + # me@barrythrew.com + # San Francisco, CA + # (c) 2008 + # for Kesumo - http://www.kesumo.com + ########################################################################### + // bonk~ detects attacks in an audio signal + ########################################################################### + This software is copyrighted by Miller Puckette and others. The following + terms (the "Standard Improved BSD License") apply to all files associated with + the software unless explicitly disclaimed in individual files: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +dolist: +decay and other times in msec +*/ + +#include +#include +#include + +#ifdef NT +#pragma warning (disable: 4305 4244) +#endif + +#ifdef MSP +#include "ext.h" +#include "z_dsp.h" +#include "math.h" +#include "ext_support.h" +#include "ext_proto.h" +#include "ext_obex.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)) + +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 + +#ifndef _MSC_VER +#include +#endif + +/* ------------------------ bonk~ ----------------------------- */ + +#define DEFNPOINTS 256 +#define MAXCHANNELS 8 +#define MINPOINTS 64 +#define DEFPERIOD 128 +#define DEFNFILTERS 11 +#define DEFHALFTONES 6 +#define DEFOVERLAP 1 +#define DEFFIRSTBIN 1 +#define DEFMINBANDWIDTH 1.5 +#define DEFHITHRESH 5 +#define DEFLOTHRESH 2.5 +#define DEFMASKTIME 4 +#define DEFMASKDECAY 0.7 +#define DEFDEBOUNCEDECAY 0 +#define DEFMINVEL 7 +#define DEFATTACKBINS 1 +#define MAXATTACKWAIT 4 + +typedef struct _filterkernel +{ + int k_filterpoints; + int k_hoppoints; + int k_skippoints; + int k_nhops; + float k_centerfreq; /* center frequency, bins */ + float k_bandwidth; /* bandwidth, bins */ + float *k_stuff; +} t_filterkernel; + +typedef struct _filterbank +{ + int b_nfilters; /* number of filters in bank */ + int b_npoints; /* input vector size */ + float b_halftones; /* filter bandwidth in halftones */ + float b_overlap; /* overlap; default 1 for 1/2-power pts */ + float b_firstbin; /* freq of first filter in bins, default 1 */ + float b_minbandwidth; /* minimum bandwidth, default 1.5 */ + t_filterkernel *b_vec; /* filter kernels */ + int b_refcount; /* number of bonk~ objects using this */ + struct _filterbank *b_next; /* next in linked list */ +} t_filterbank; + +#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 + +#if 0 + /* 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]))) + +#endif + +#if 0 + /* and 1.2 */ +#define NFILTERS 11 +static t_filterkernel bonk_filterkernels[NFILTERS]; +#endif + + /* and 1.3 */ +#define MAXNFILTERS 50 +#define MASKHIST 8 + +static t_filterbank *bonk_filterbanklist; + +typedef struct _hist +{ + float h_power; + float h_before; + float h_outpower; + int h_countup; + float h_mask[MASKHIST]; +} t_hist; + +typedef struct template +{ + float t_amp[MAXNFILTERS]; +} t_template; + +typedef struct _insig +{ + t_hist g_hist[MAXNFILTERS]; /* 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 *obex; + void *x_cookedout; + void *x_clock; +#endif /* MSP */ + /* parameters */ + int x_npoints; /* number of points in input buffer */ + int x_period; /* number of input samples between analyses */ + int x_nfilters; /* number of filters requested */ + float x_halftones; /* nominal halftones between filters */ + float x_overlap; + float x_firstbin; + float x_minbandwidth; + float x_hithresh; /* threshold for total growth to trigger */ + float x_lothresh; /* threshold for total growth to re-arm */ + float x_minvel; /* minimum velocity we output */ + float x_maskdecay; + int x_masktime; + int x_useloudness; /* use loudness spectra instead of power */ + float x_debouncedecay; + float x_debouncevel; + double x_learndebounce; /* debounce time (in "learn" mode only) */ + int x_attackbins; /* number of bins to wait for attack */ + + t_filterbank *x_filterbank; + t_hist x_hist[MAXNFILTERS]; + t_template *x_template; + t_insig *x_insig; + int x_ninsig; + int x_ntemplate; + int x_infill; + int x_countdown; + int x_willattack; + int x_attacked; + int x_debug; + int x_learn; + int x_learncount; /* countup for "learn" mode */ + int x_spew; /* if true, always generate output! */ + int x_maskphase; /* phase, 0 to MASKHIST-1, for mask history */ + float x_sr; /* current sample rate in Hz. */ + int x_hit; /* next "tick" called because of a hit, not a poll */ +} t_bonk; + +#ifdef MSP +static void *bonk_new(t_symbol *s, long ac, t_atom *av); +static void bonk_tick(t_bonk *x); +static void bonk_doit(t_bonk *x); +static t_int *bonk_perform(t_int *w); +static void bonk_dsp(t_bonk *x, t_signal **sp); +void bonk_assist(t_bonk *x, void *b, long m, long a, char *s); +static void bonk_free(t_bonk *x); +void bonk_setup(void); +int main(); + +static void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2); +static void bonk_print(t_bonk *x, t_floatarg f); +static void bonk_bang(t_bonk *x); + +static void bonk_write(t_bonk *x, t_symbol *s); +static void bonk_read(t_bonk *x, t_symbol *s); + +void bonk_minvel_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_lothresh_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_hithresh_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_masktime_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_maskdecay_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_debouncedecay_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_debug_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_spew_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_useloudness_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_attackbins_set(t_bonk *x, void *attr, long ac, t_atom *av); +void bonk_learn_set(t_bonk *x, void *attr, long ac, t_atom *av); + +float qrsqrt(float f); +double clock_getsystime(); +double clock_gettimesince(double prevsystime); +char *strcpy(char *s1, const char *s2); +#endif + +static void bonk_tick(t_bonk *x); + +#define HALFWIDTH 0.75 /* half peak bandwidth at half power point in bins */ +#define SLIDE 0.25 /* relative slide between filter subwindows */ + +static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, + float halftones, float overlap, float firstbin, float minbandwidth) +{ + int i, j; + float cf, bw, h, relspace; + t_filterbank *b = (t_filterbank *)getbytes(sizeof(*b)); + b->b_npoints = npoints; + b->b_nfilters = nfilters; + b->b_halftones = halftones; + b->b_overlap = overlap; + b->b_firstbin = firstbin; + b->b_minbandwidth = minbandwidth; + b->b_refcount = 0; + b->b_next = bonk_filterbanklist; + bonk_filterbanklist = b; + b->b_vec = (t_filterkernel *)getbytes(nfilters * sizeof(*b->b_vec)); + + h = exp((log(2.)/12.)*halftones); /* specced interval between filters */ + relspace = (h - 1)/(h + 1); /* nominal spacing-per-f for fbank */ + + if (minbandwidth < 2*HALFWIDTH) + minbandwidth = 2*HALFWIDTH; + if (firstbin < minbandwidth/(2*HALFWIDTH)) + firstbin = minbandwidth/(2*HALFWIDTH); + cf = firstbin; + bw = cf * relspace * overlap; + if (bw < (0.5*minbandwidth)) + bw = (0.5*minbandwidth); + for (i = 0; i < nfilters; i++) + { + float *fp, newcf, newbw; + float normalizer = 0; + int filterpoints, skippoints, hoppoints, nhops; + + filterpoints = npoints * HALFWIDTH/bw; + if (cf > npoints/2) + { + post("bonk~: only using %d filters (ran past Nyquist)", i+1); + break; + } + if (filterpoints < 4) + { + post("bonk~: only using %d filters (kernels got too short)", i+1); + break; + } + else if (filterpoints > npoints) + filterpoints = npoints; + + hoppoints = SLIDE * npoints * HALFWIDTH/bw; + + nhops = 1. + (npoints-filterpoints)/(float)hoppoints; + skippoints = 0.5 * (npoints-filterpoints - (nhops-1) * hoppoints); + + b->b_vec[i].k_stuff = + (float *)getbytes(2 * sizeof(float) * filterpoints); + b->b_vec[i].k_filterpoints = filterpoints; + b->b_vec[i].k_nhops = nhops; + b->b_vec[i].k_hoppoints = hoppoints; + b->b_vec[i].k_skippoints = skippoints; + b->b_vec[i].k_centerfreq = cf; + b->b_vec[i].k_bandwidth = bw; + + for (fp = b->b_vec[i].k_stuff, j = 0; j < filterpoints; j++, fp+= 2) + { + float phase = j * cf * (2*3.14159/ npoints); + float wphase = j * (2*3.14159 / filterpoints); + float window = sin(0.5*wphase); + fp[0] = window * cos(phase); + fp[1] = window * sin(phase); + normalizer += window; + } + normalizer = 1/(normalizer * sqrt(nhops)); + for (fp = b->b_vec[i].k_stuff, j = 0; + j < filterpoints; j++, fp+= 2) + fp[0] *= normalizer, fp[1] *= normalizer; +#if 0 + post("i %d cf %.2f bw %.2f nhops %d, hop %d, skip %d, npoints %d", + i, cf, bw, nhops, hoppoints, skippoints, filterpoints); +#endif + newcf = (cf + bw/overlap)/(1 - relspace); + newbw = newcf * overlap * relspace; + if (newbw < 0.5*minbandwidth) + { + newbw = 0.5*minbandwidth; + newcf = cf + minbandwidth / overlap; + } + cf = newcf; + bw = newbw; + } + for (; i < nfilters; i++) + b->b_vec[i].k_stuff = 0, b->b_vec[i].k_filterpoints = 0; + return (b); +} + +static void bonk_freefilterbank(t_filterbank *b) +{ + t_filterbank *b2, *b3; + int i; + if (bonk_filterbanklist == b) + bonk_filterbanklist = b->b_next; + else for (b2 = bonk_filterbanklist; b3 = b2->b_next; b2 = b3) + if (b3 == b) + { + b2->b_next = b3->b_next; + break; + } + for (i = 0; i < b->b_nfilters; i++) + if (b->b_vec[i].k_stuff) + freebytes(b->b_vec[i].k_stuff, + b->b_vec[i].k_filterpoints * sizeof(float)); + freebytes(b, sizeof(*b)); +} + +static void bonk_donew(t_bonk *x, int npoints, int period, int nsig, + int nfilters, float halftones, float overlap, float firstbin, + float minbandwidth, float samplerate) +{ + int i, j; + t_hist *h; + float *fp; + t_insig *g; + t_filterbank *fb; + 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_before = 0, h->h_countup = 0; + for (j = 0; j < MASKHIST; j++) + h->h_mask[j] = 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; + } + if (!period) period = npoints/2; + x->x_npoints = npoints; + x->x_period = period; + x->x_ninsig = nsig; + x->x_nfilters = nfilters; + x->x_halftones = halftones; + x->x_template = (t_template *)getbytes(0); + x->x_ntemplate = 0; + x->x_infill = 0; + x->x_countdown = 0; + x->x_willattack = 0; + x->x_attacked = 0; + x->x_maskphase = 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_useloudness = 0; + x->x_debouncevel = 0; + x->x_attackbins = DEFATTACKBINS; + x->x_sr = samplerate; + x->x_filterbank = 0; + x->x_hit = 0; + for (fb = bonk_filterbanklist; fb; fb = fb->b_next) + if (fb->b_nfilters == x->x_nfilters && + fb->b_halftones == x->x_halftones && + fb->b_firstbin == firstbin && + fb->b_overlap == overlap && + fb->b_npoints == x->x_npoints && + fb->b_minbandwidth == minbandwidth) + { + fb->b_refcount++; + x->x_filterbank = fb; + break; + } + if (!x->x_filterbank) + x->x_filterbank = bonk_newfilterbank(npoints, nfilters, + halftones, overlap, firstbin, minbandwidth), + x->x_filterbank->b_refcount++; +} + +static void bonk_tick(t_bonk *x) +{ + t_atom at[MAXNFILTERS], *ap, at2[3]; + int i, j, k, n; + t_hist *h; + float *pp, vel = 0, temperature = 0; + float *fp; + t_template *tp; + int nfit, ninsig = x->x_ninsig, ntemplate = x->x_ntemplate, nfilters = x->x_nfilters; + t_insig *gp; +#ifdef _MSC_VER + float powerout[MAXNFILTERS*MAXCHANNELS]; +#else + float *powerout = alloca(x->x_nfilters * x->x_ninsig * sizeof(*powerout)); +#endif + + 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 = h->h_outpower; + 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 (x->x_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. */ + + x->x_attacked = 1; + 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_doit(t_bonk *x) +{ + int i, j, ch, n; + t_filterkernel *k; + t_hist *h; + float growth = 0, *fp1, *fp3, *fp4, hithresh, lothresh; + int ninsig = x->x_ninsig, nfilters = x->x_nfilters, + maskphase = x->x_maskphase, nextphase, oldmaskphase; + t_insig *gp; + nextphase = maskphase + 1; + if (nextphase >= MASKHIST) + nextphase = 0; + x->x_maskphase = nextphase; + oldmaskphase = nextphase - x->x_attackbins; + if (oldmaskphase < 0) + oldmaskphase += MASKHIST; + if (x->x_useloudness) + hithresh = qrsqrt(qrsqrt(x->x_hithresh)), + lothresh = qrsqrt(qrsqrt(x->x_lothresh)); + else hithresh = x->x_hithresh, lothresh = x->x_lothresh; + for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) + { + for (i = 0, k = x->x_filterbank->b_vec, h = gp->g_hist; + i < nfilters; i++, k++, h++) + { + float power = 0, maskpow = h->h_mask[maskphase]; + float *inbuf= gp->g_inbuf + k->k_skippoints; + int countup = h->h_countup; + int filterpoints = k->k_filterpoints; + /* if the user asked for more filters that fit under the + Nyquist frequency, some filters won't actually be filled in + so we skip running them. */ + if (!filterpoints) + { + h->h_countup = 0; + h->h_mask[nextphase] = 0; + h->h_power = 0; + continue; + } + /* run the filter repeatedly, sliding it forward by hoppoints, + for nhop times */ + for (fp1 = inbuf, n = 0; + n < k->k_nhops; fp1 += k->k_hoppoints, n++) + { + float rsum = 0, isum = 0; + for (fp3 = fp1, fp4 = k->k_stuff, j = filterpoints; 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 > h->h_mask[oldmaskphase]) + { + if (x->x_useloudness) + growth += qrsqrt(qrsqrt( + power/(h->h_mask[oldmaskphase] + 1.0e-15))) - 1.f; + else growth += power/(h->h_mask[oldmaskphase] + 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[nextphase] = maskpow; + h->h_power = power; + } + } + if (x->x_willattack) + { + if (x->x_willattack > MAXATTACKWAIT || growth < x->x_lothresh) + { + /* if haven't yet, and if not in spew mode, report a hit */ + if (!x->x_spew && !x->x_attacked) + { + for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) + for (i = nfilters, h = gp->g_hist; i--; h++) + h->h_outpower = h->h_mask[nextphase]; + x->x_hit = 1; + clock_delay(x->x_clock, 0); + } + } + if (growth < x->x_lothresh) + x->x_willattack = 0; + else x->x_willattack++; + } + else if (growth > x->x_hithresh) + { + if (x->x_debug) post("attack: growth = %f", growth); + x->x_willattack = 1; + x->x_attacked = 0; + for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) + for (i = nfilters, h = gp->g_hist; i--; h++) + h->h_mask[nextphase] = h->h_power, h->h_countup = 0; + } + + /* if in "spew" mode just always output */ + if (x->x_spew) + { + for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) + for (i = nfilters, h = gp->g_hist; i--; h++) + h->h_outpower = h->h_power; + x->x_hit = 0; + clock_delay(x->x_clock, 0); + } + x->x_debouncevel *= x->x_debouncedecay; +} + +static t_int *bonk_perform(t_int *w) +{ + t_bonk *x = (t_bonk *)(w[1]); + int n = (int)(w[2]); + int onset = 0; + if (x->x_countdown >= n) + x->x_countdown -= n; + else + { + int i, j, ninsig = x->x_ninsig; + t_insig *gp; + if (x->x_countdown > 0) + { + n -= x->x_countdown; + onset += x->x_countdown; + x->x_countdown = 0; + } + while (n > 0) + { + int infill = x->x_infill; + int m = (n < (x->x_npoints - infill) ? + n : (x->x_npoints - infill)); + 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 < m; j++) + *fp++ = *in1++; + } + infill += m; + x->x_infill = infill; + if (infill == x->x_npoints) + { + bonk_doit(x); + + /* shift or clear the input buffer and update counters */ + if (x->x_period > x->x_npoints) + x->x_countdown = x->x_period - x->x_npoints; + else x->x_countdown = 0; + if (x->x_period < x->x_npoints) + { + int overlap = x->x_npoints - x->x_period; + float *fp1, *fp2; + 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; + } + n -= m; + onset += m; + } + } + return (w+3); +} + +static void bonk_dsp(t_bonk *x, t_signal **sp) +{ + int i, n = sp[0]->s_n, ninsig = x->x_ninsig; + t_insig *gp; + + x->x_sr = sp[0]->s_sr; + + for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++) + gp->g_invec = (*(sp++))->s_vec; + + dsp_add(bonk_perform, 2, x, n); +} + +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 <= 0 ? 0.0001 : f1); + x->x_hithresh = (f2 <= 0 ? 0.0001 : f2); +} + +#ifdef PD +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_debug(t_bonk *x, t_floatarg f) +{ + x->x_debug = (f != 0); +} + +static void bonk_spew(t_bonk *x, t_floatarg f) +{ + x->x_spew = (f != 0); +} + +static void bonk_useloudness(t_bonk *x, t_floatarg f) +{ + x->x_useloudness = (f != 0); +} + +static void bonk_attackbins(t_bonk *x, t_floatarg f) +{ + if (f < 1) + f = 1; + else if (f > MASKHIST) + f = MASKHIST; + x->x_attackbins = f; +} + +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; +} +#endif + +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("attack-frames %d", x->x_attackbins); + post("debounce %f", x->x_debouncedecay); + post("minvel %f", x->x_minvel); + post("spew %d", x->x_spew); + post("useloudness %d", x->x_useloudness); + +#if 0 /* LATER rewrite without hard-coded 11 filters */ + 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"); +#endif + post("number of templates %d", x->x_ntemplate); + 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 = x->x_nfilters, h = gp->g_hist; i--; h++) + post("pow %f mask %f before %f count %d", + h->h_power, h->h_mask[x->x_maskphase], + h->h_before, h->h_countup); + } + post("filter details (frequencies are in units of %.2f-Hz. bins):", + x->x_sr/x->x_npoints); + for (j = 0; j < x->x_nfilters; j++) + post("%2d cf %.2f bw %.2f nhops %d hop %d skip %d npoints %d", + j, + x->x_filterbank->b_vec[j].k_centerfreq, + x->x_filterbank->b_vec[j].k_bandwidth, + x->x_filterbank->b_vec[j].k_nhops, + x->x_filterbank->b_vec[j].k_hoppoints, + x->x_filterbank->b_vec[j].k_skippoints, + x->x_filterbank->b_vec[j].k_filterpoints); + } + if (x->x_debug) post("debug mode"); +} + +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; +} + +static void bonk_bang(t_bonk *x) +{ + int i, ch; + t_insig *gp; + x->x_hit = 0; + for (ch = 0, gp = x->x_insig; ch < x->x_ninsig; ch++, gp++) + { + t_hist *h; + for (i = 0, h = gp->g_hist; i < x->x_nfilters; i++, h++) + h->h_outpower = h->h_power; + } + bonk_tick(x); +} + +static void bonk_read(t_bonk *x, t_symbol *s) +{ + FILE *fd = fopen(s->s_name, "r"); + float vec[MAXNFILTERS]; + 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 = x->x_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 = x->x_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); +} + +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 = x->x_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); +} + +static void bonk_free(t_bonk *x) +{ + + int i, ninsig = x->x_ninsig; + t_insig *gp = x->x_insig; +#ifdef MSP + dsp_free((t_pxobject *)x); +#endif + for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++) + freebytes(gp->g_inbuf, x->x_npoints * sizeof(float)); + clock_free(x->x_clock); + if (!--(x->x_filterbank->b_refcount)) + bonk_freefilterbank(x->x_filterbank); + +} + +/* -------------------------- Pd glue ------------------------- */ +#ifdef PD + +static void *bonk_new(t_symbol *s, int argc, t_atom *argv) +{ + t_bonk *x = (t_bonk *)pd_new(bonk_class); + int nsig = 1, period = DEFPERIOD, npts = DEFNPOINTS, + nfilters = DEFNFILTERS, j; + float halftones = DEFHALFTONES, overlap = DEFOVERLAP, + firstbin = DEFFIRSTBIN, minbandwidth = DEFMINBANDWIDTH; + t_insig *g; + + if (argc > 0 && argv[0].a_type == A_FLOAT) + { + /* old style args for compatibility */ + period = atom_getfloatarg(0, argc, argv); + nsig = atom_getfloatarg(1, argc, argv); + } + else while (argc > 0) + { + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->s_name, "-npts") && argc > 1) + { + npts = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-hop") && argc > 1) + { + period = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-nsigs") && argc > 1) + { + nsig = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-nfilters") && argc > 1) + { + nfilters = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-halftones") && argc > 1) + { + halftones = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-overlap") && argc > 1) + { + overlap = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-firstbin") && argc > 1) + { + firstbin = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-minbandwidth") && argc > 1) + { + minbandwidth = atom_getfloatarg(1, argc, argv); + argc -= 2; argv += 2; + } + else if (!strcmp(firstarg->s_name, "-spew") && argc > 1) + { + x->x_spew = (atom_getfloatarg(1, argc, argv) != 0); + argc -= 2; argv += 2; + } + else + { + pd_error(x, +"usage is: bonk [-npts #] [-hop #] [-nsigs #] [-nfilters #] [-halftones #]"); + post( +"... [-overlap #] [-firstbin #] [-spew #]"); + argc = 0; + } + } + + x->x_npoints = (npts >= MINPOINTS ? npts : DEFNPOINTS); + x->x_period = (period >= 1 ? period : npts/2); + x->x_nfilters = (nfilters >= 1 ? nfilters : DEFNFILTERS); + if (halftones < 0.01) + halftones = DEFHALFTONES; + else if (halftones > 12) + halftones = 12; + if (nsig < 1) + nsig = 1; + else if (nsig > MAXCHANNELS) + nsig = MAXCHANNELS; + if (firstbin < 0.5) + firstbin = 0.5; + if (overlap < 1) + overlap = 1; + + 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, npts, period, nsig, nfilters, halftones, overlap, + firstbin, minbandwidth, sys_getsr()); + return (x); +} + +void bonk_tilde_setup(void) +{ + bonk_class = class_new(gensym("bonk~"), (t_newmethod)bonk_new, + (t_method)bonk_free, sizeof(t_bonk), 0, A_GIMME, 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_spew, + gensym("spew"), A_DEFFLOAT, 0); + class_addmethod(bonk_class, (t_method)bonk_useloudness, + gensym("useloudness"), A_DEFFLOAT, 0); + class_addmethod(bonk_class, (t_method)bonk_attackbins, + gensym("attack-bins"), A_DEFFLOAT, 0); + class_addmethod(bonk_class, (t_method)bonk_attackbins, + gensym("attack-frames"), 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); + post("bonk version 1.3"); +} +#endif + +/* -------------------------- MSP glue ------------------------- */ +#ifdef MSP + +int main() +{ + t_class *c; + t_object *attr; + long attrflags = 0; + t_symbol *sym_long = gensym("long"), *sym_float32 = gensym("float32"); + + c = class_new("bonk~", (method)bonk_new, (method)bonk_free, sizeof(t_bonk), (method)0L, A_GIMME, 0); + + class_obexoffset_set(c, calcoffset(t_bonk, obex)); + + attr = attr_offset_new("npoints", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_npoints)); + class_addattr(c, attr); + + attr = attr_offset_new("hop", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_period)); + class_addattr(c, attr); + + attr = attr_offset_new("nfilters", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_nfilters)); + class_addattr(c, attr); + + attr = attr_offset_new("halftones", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_halftones)); + class_addattr(c, attr); + + attr = attr_offset_new("overlap", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_overlap)); + class_addattr(c, attr); + + attr = attr_offset_new("firstbin", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_firstbin)); + class_addattr(c, attr); + + attr = attr_offset_new("minbandwidth", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_minbandwidth)); + class_addattr(c, attr); + + attr = attr_offset_new("minvel", sym_float32, attrflags, (method)0L, (method)bonk_minvel_set, calcoffset(t_bonk, x_minvel)); + class_addattr(c, attr); + + attr = attr_offset_new("lothresh", sym_float32, attrflags, (method)0L, (method)bonk_lothresh_set, calcoffset(t_bonk, x_lothresh)); + class_addattr(c, attr); + + attr = attr_offset_new("hithresh", sym_float32, attrflags, (method)0L, (method)bonk_hithresh_set, calcoffset(t_bonk, x_hithresh)); + class_addattr(c, attr); + + attr = attr_offset_new("masktime", sym_long, attrflags, (method)0L, (method)bonk_masktime_set, calcoffset(t_bonk, x_masktime)); + class_addattr(c, attr); + + attr = attr_offset_new("maskdecay", sym_float32, attrflags, (method)0L, (method)bonk_maskdecay_set, calcoffset(t_bonk, x_maskdecay)); + class_addattr(c, attr); + + attr = attr_offset_new("debouncedecay", sym_float32, attrflags, (method)0L, (method)bonk_debouncedecay_set, calcoffset(t_bonk, x_debouncedecay)); + class_addattr(c, attr); + + attr = attr_offset_new("debug", sym_long, attrflags, (method)0L, (method)bonk_debug_set, calcoffset(t_bonk, x_debug)); + class_addattr(c, attr); + + attr = attr_offset_new("spew", sym_long, attrflags, (method)0L, (method)bonk_spew_set, calcoffset(t_bonk, x_spew)); + class_addattr(c, attr); + + attr = attr_offset_new("useloudness", sym_long, attrflags, (method)0L, (method)bonk_useloudness_set, calcoffset(t_bonk, x_useloudness)); + class_addattr(c, attr); + + attr = attr_offset_new("attackframes", sym_long, attrflags, (method)0L, (method)bonk_attackbins_set, calcoffset(t_bonk, x_attackbins)); + class_addattr(c, attr); + + attr = attr_offset_new("learn", sym_long, attrflags, (method)0L, (method)bonk_learn_set, calcoffset(t_bonk, x_learn)); + class_addattr(c, attr); + + class_addmethod(c, (method)bonk_dsp, "dsp", A_CANT, 0); + class_addmethod(c, (method)bonk_bang, "bang", A_CANT, 0); + class_addmethod(c, (method)bonk_forget, "forget", 0); + class_addmethod(c, (method)bonk_thresh, "thresh", A_FLOAT, A_FLOAT, 0); + class_addmethod(c, (method)bonk_print, "print", A_DEFFLOAT, 0); + class_addmethod(c, (method)bonk_read, "read", A_DEFSYM, 0); + class_addmethod(c, (method)bonk_write, "write", A_DEFSYM, 0); + class_addmethod(c, (method)bonk_assist, "assist", A_CANT, 0); + + class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0); + class_addmethod(c, (method)object_obex_quickref, "quickref", A_CANT, 0); + + class_dspinit(c); + + class_register(CLASS_BOX, c); + bonk_class = c; + + post("bonk~ v1.3"); + return (0); +} + +static void *bonk_new(t_symbol *s, long ac, t_atom *av) +{ + short j; + t_bonk *x; + + if (x = (t_bonk *)object_alloc(bonk_class)) { + + t_insig *g; + + x->x_npoints = DEFNPOINTS; + x->x_period = DEFPERIOD; + x->x_nfilters = DEFNFILTERS; + x->x_halftones = DEFHALFTONES; + x->x_firstbin = DEFFIRSTBIN; + x->x_minbandwidth = DEFMINBANDWIDTH; + x->x_overlap = DEFOVERLAP; + x->x_ninsig = 1; + + x->x_hithresh = DEFHITHRESH; + x->x_lothresh = DEFLOTHRESH; + x->x_masktime = DEFMASKTIME; + x->x_maskdecay = DEFMASKDECAY; + x->x_debouncedecay = DEFDEBOUNCEDECAY; + x->x_minvel = DEFMINVEL; + x->x_attackbins = DEFATTACKBINS; + + if (!x->x_period) x->x_period = x->x_npoints/2; + x->x_template = (t_template *)getbytes(0); + x->x_ntemplate = 0; + x->x_infill = 0; + x->x_countdown = 0; + x->x_willattack = 0; + x->x_attacked = 0; + x->x_maskphase = 0; + x->x_debug = 0; + x->x_learn = 0; + x->x_learndebounce = clock_getsystime(); + x->x_learncount = 0; + x->x_useloudness = 0; + x->x_debouncevel = 0; + x->x_sr = sys_getsr(); + + if (ac) { + switch (av[0].a_type) { + case A_LONG: + x->x_ninsig = av[0].a_w.w_long; + break; + } + } + + if (x->x_ninsig < 1) x->x_ninsig = 1; + if (x->x_ninsig > MAXCHANNELS) x->x_ninsig = MAXCHANNELS; + + attr_args_process(x, ac, av); + + x->x_insig = (t_insig *)getbytes(x->x_ninsig * sizeof(*x->x_insig)); + + dsp_setup((t_pxobject *)x, x->x_ninsig); + + object_obex_store(x, gensym("dumpout"), outlet_new(x, NULL)); + + x->x_cookedout = listout((t_object *)x); + + for (j = 0, g = x->x_insig + x->x_ninsig-1; j < x->x_ninsig; j++, g--) { + g->g_outlet = listout((t_object *)x); + } + + x->x_clock = clock_new(x, (method)bonk_tick); + + bonk_donew(x, x->x_npoints, x->x_period, x->x_ninsig, x->x_nfilters, + x->x_halftones, x->x_overlap, x->x_firstbin, x->x_minbandwidth, + sys_getsr()); + } + return (x); +} + +/* Attribute setters. */ +void bonk_minvel_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + float f = atom_getfloat(av); + if (f < 0) f = 0; + x->x_minvel = f; + } +} + +void bonk_lothresh_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + float f = atom_getfloat(av); + if (f > x->x_hithresh) + post("bonk: warning: low threshold greater than hi threshold"); + x->x_lothresh = (f <= 0 ? 0.0001 : f); + } +} + +void bonk_hithresh_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + float f = atom_getfloat(av); + if (f < x->x_lothresh) + post("bonk: warning: low threshold greater than hi threshold"); + x->x_hithresh = (f <= 0 ? 0.0001 : f); + } +} + +void bonk_masktime_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + int n = atom_getlong(av); + x->x_masktime = (n < 0) ? 0 : n; + } +} + +void bonk_maskdecay_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + float f = atom_getfloat(av); + f = (f < 0) ? 0 : f; + f = (f > 1) ? 1 : f; + x->x_maskdecay = f; + } +} + +void bonk_debouncedecay_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + float f = atom_getfloat(av); + f = (f < 0) ? 0 : f; + f = (f > 1) ? 1 : f; + x->x_debouncedecay = f; + } +} + +void bonk_debug_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + int n = atom_getlong(av); + x->x_debug = (n != 0); + } +} + +void bonk_spew_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + int n = atom_getlong(av); + x->x_spew = (n != 0); + } +} + +void bonk_useloudness_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + int n = atom_getlong(av); + x->x_useloudness = (n != 0); + } +} + +void bonk_attackbins_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + int n = atom_getlong(av); + n = (n < 1) ? 1 : n; + n = (n > MASKHIST) ? MASKHIST : n; + x->x_attackbins = n; + } +} + +void bonk_learn_set(t_bonk *x, void *attr, long ac, t_atom *av) +{ + if (ac && av) { + int n = atom_getlong(av); + if (n != 0) { + 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 != 0); + x->x_learncount = 0; + } +} +/* end attr setters */ + +void bonk_assist(t_bonk *x, void *b, long m, long a, char *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/extra/bonk~/templates.txt b/externals/extra/bonk~/templates.txt new file mode 100644 index 00000000..f3528d78 --- /dev/null +++ b/externals/extra/bonk~/templates.txt @@ -0,0 +1,4 @@ + 10.47 9.65 14.95 23.77 28.32 38.84 53.21 41.20 31.25 21.70 16.48 + 6.52 13.93 27.82 58.05 24.11 35.26 35.98 37.78 22.54 13.56 10.75 + 30.45 28.86 29.42 21.94 29.92 35.70 38.49 32.01 28.19 27.38 22.10 + 66.77 46.27 28.82 25.95 22.84 20.61 20.33 14.18 6.86 8.92 7.37 -- cgit v1.2.1