aboutsummaryrefslogtreecommitdiff
path: root/beat.c
diff options
context:
space:
mode:
Diffstat (limited to 'beat.c')
-rw-r--r--beat.c806
1 files changed, 403 insertions, 403 deletions
diff --git a/beat.c b/beat.c
index a2f565e..1bf5a76 100644
--- a/beat.c
+++ b/beat.c
@@ -1,403 +1,403 @@
-/* --------------------------- beat ------------------------------------------ */
-/* */
-/* Detect the beats per minute of a MIDI stream. */
-/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
-/* Based on code written by Robert Rowe. */
-/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */
-/* */
-/* This program is free software; you can redistribute it and/or */
-/* modify it under the terms of the GNU General Public License */
-/* as published by the Free Software Foundation; either version 2 */
-/* of the License, or (at your option) any later version. */
-/* */
-/* This program is distributed in the hope that it will be useful, */
-/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
-/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
-/* GNU General Public License for more details. */
-/* */
-/* You should have received a copy of the GNU General Public License */
-/* along with this program; if not, write to the Free Software */
-/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
-/* */
-/* Based on PureData by Miller Puckette and others. */
-/* */
-/* ---------------------------------------------------------------------------- */
-
-#include "m_pd.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define BEAT_LONG 1500 /* longest time we take into concideration (40 bpm) */
-#define BEAT_SHORT 300 /* shortest time we take into concideration (200 bpm) */
-
-static char *version = "beat v0.1, written by Olaf Matthes <olaf.matthes@gmx.de>";
-
-typedef struct
-{
- t_int points; /* number of points assigned to this theory */
- double expect; /* time of next expected hit */
- t_int onbeat; /* whether (1) or not (0) it was on the beat */
-} beat_theory;
-
-typedef struct /* used for sorting theories */
-{
- t_int points;
- t_int theory;
-} beat_sort_record;
-
-
-typedef struct beat
-{
- t_object x_ob;
- t_clock *x_clock;
- t_outlet *x_outbpm; /* beat as MIDI note number */
- t_outlet *x_outms; /* beat in milliseconds */
- t_outlet *x_outbeat; /* send a bang whenever beat is 'on beat' */
- t_int x_print; /* switch printing to console window on / off */
-
- t_int x_num_beats; /* number of beats we've received */
- double x_beat_period; /* time in ms until next expected beat / beat pulse */
- beat_theory x_beats[BEAT_LONG];
- double x_beatexpect; /* expected time for next beat */
- t_int x_on_beat; /* indicate if last event was on beat */
- t_int x_band_percent;
-
- t_int x_pitch;
- t_int x_velo;
- /* helpers needed to do the time calculations */
- double x_this_input;
- double x_last_input;
- double x_lasttime;
- double x_lastlasttime;
-} t_beat;
-
-/* ---------------- mathematical functions to work with doubles -------------- */
-static double double_abs(double value)
-{
- if(value < 0)
- return (value * -1);
- else
- return (value);
-}
-
-/* --------------- beat stuff ------------------------------------------------ */
- /* evaluate results: find theory that is the most likely one and
- print out internal data to console window if print is enabled */
-static int beat_evaluate(t_beat *x)
-{
- int i, j, K;
- char string[256];
- char info[40];
- beat_sort_record theories[BEAT_LONG], *sortp, R;
- int value; /* the result of the sorting */
-
- for (i = 0; i < BEAT_LONG; i++)
- { /* prepare sort records */
- sortp = &(theories[i]);
- sortp->points = x->x_beats[i].points;
- sortp->theory = i;
- }
- for (j = 2; j < BEAT_LONG; j++)
- { /* sort */
- i = j - 1;
- K = theories[j].points;
- R = theories[j];
- while (i > 0)
- {
- if (K >= theories[i].points)
- {
- theories[i+1] = R;
- break;
- }
- else
- {
- theories[i+1] = theories[i];
- i -= 1;
- }
- }
- if (i==0) theories[i+1] = R;
- }
- /* get leading result */
- sortp = &(theories[BEAT_LONG - 1]);
- value = sortp->theory; /* get our resulting theory */
-
- if(x->x_print)
- {
- post(" 0 1 2 3 4 R E");
- *string = '\0'; /* print out five leading theories */
- sprintf(info, "%4g", x->x_this_input);
- strcat(string, info);
- for(i = 1; i < 6; i++)
- {
- sortp = &(theories[BEAT_LONG - i]);
- sprintf(info, " %4d[%3d]", (int) sortp->theory, (int) sortp->points);
- strcat(string, info);
- }
- sprintf(info, " %g %g", clock_getlogicaltime(), x->x_beatexpect);
- strcat(string, info);
- post(string);
- }
-
- return value;
-}
-
- /* reduce duration to fit into our processing window */
- /* some sort of 'double modulo'... */
-static double beat_reduce_offset(double duration)
-{
- double temp = duration;
- int divisor = 2; /* first try dividing by two */
- while (temp > BEAT_LONG) /* while duration is too long */
- temp = duration / divisor++; /* divide by progressively higher divisors */
- return temp; /* return a value in bounds */
-}
-
-/*
- * beat_eligible: determine whether an event is eligible for consideration
- * as a beat theory
- */
-static int beat_eligible(double candidate, int* offsets, int num_offsets)
-{
- double diff;
- int i;
-
- if (candidate >= BEAT_LONG) /* if too long try subharmonics */
- candidate = beat_reduce_offset(candidate);
-
- /* if candidate is close to one already found */
- for(i = 0; i < num_offsets; i++)
- {
- diff = double_abs((candidate - offsets[i]));
- if (diff < offsets[i]/20) {
- if (candidate > offsets[i])
- ++offsets[i]; else /* pull existing one */
- if (candidate < offsets[i]) /* toward new candidate */
- --offsets[i];
- return 0; /* declare candidate ineligible */
- }
- }
- return candidate; /* otherwise return legal candidate */
-}
-
-static void beat_float(t_beat *x, t_floatarg f)
-{
- t_int velo = x->x_velo;
- int i, j, indx;
- int num_offsets, candidate;
- int low_limit, high_limit, width, deviation;
- int points, band, center_offset, period;
- beat_theory* t;
- int offsets[7];
- static int factors[10] =
- { 200, 50, 300, 150, 66, 400, 600, 133, 33, 75 };
- double now = clock_getlogicaltime();
- t_float outvalue;
-
- x->x_pitch = (t_int)f;
- x->x_this_input = clock_gettimesince(x->x_last_input);
-
- if(velo != 0) /* note-on received */
- {
- if(++x->x_num_beats == 1)
- {
- goto time; /* only one event, no beats yet */
- }
-
- num_offsets = 0;
- candidate = beat_eligible(x->x_this_input, offsets, num_offsets);
- if(candidate)
- offsets[num_offsets++] = candidate; /* offset[0] set to incoming offset */
-
- if(x->x_num_beats > 2)
- { /* if three events */
- /* check previous for eligibility */
- candidate = beat_eligible(x->x_lasttime, offsets, num_offsets);
- if (candidate)
- offsets[num_offsets++] = candidate;
- candidate = x->x_this_input + x->x_lasttime; /* add current and previous offsets */
- candidate = beat_eligible(candidate, offsets, num_offsets);
- if (candidate) /* add to list if eligible */
- offsets[num_offsets++] = candidate;
- }
-
- if(x->x_num_beats > 3)
- {
- candidate = beat_eligible(x->x_lastlasttime, offsets, num_offsets);
- if (candidate)
- offsets[num_offsets++] = candidate;
- candidate += x->x_lasttime;
- candidate = beat_eligible(candidate, offsets, num_offsets);
- if (candidate)
- offsets[num_offsets++] = candidate;
- }
-
- indx = 0;
- for(i = num_offsets; i < 7; i++)
- {
- offsets[i] = 0;
- if (indx >= 10) break;
- candidate = 0;
- while ((indx < 10) && (!candidate))
- candidate = beat_eligible((x->x_this_input * factors[indx++])/100, offsets, num_offsets);
- if (candidate)
- offsets[num_offsets++] = candidate;
- }
-
- for(i = 0; i < num_offsets; i++)
- {
- band = offsets[i] * x->x_band_percent / 100;
- if ((low_limit = offsets[i] - band) < 0) /* add points in a critical band */
- low_limit = 0; /* around calculated offset */
- if ((high_limit = offsets[i] + band) > BEAT_LONG)
- high_limit = BEAT_LONG;
- center_offset = offsets[i]; /* midpoint of increment */
- points = 0;
- for (j = low_limit; j < high_limit; j++)
- {
- if ((points = x->x_beats[j].points) > 0)
- { /* if there is already activation */
- deviation = j - center_offset; /* record deviation from midpoint */
- x->x_beats[j].points = 0;
- if (deviation < 0) { /* if there is activation below midpoint */
- t = &(x->x_beats[j+1]); /* take theory one above prior */
- } else
- if (deviation > 0) { /* if there is activation above midpoint */
- t = &(x->x_beats[j-1]); /* take theory one below prior */
- } else
- t = &(x->x_beats[j]); /* landed right on it */
- t->points = points + (num_offsets-i);
- break;
- }
- }
- if (!points)
- x->x_beats[center_offset].points = num_offsets - i;
- }
-
- /* boost hits, and suppress theories with missed beats */
- period = 0;
- points = 0;
- for (i = BEAT_SHORT; i < BEAT_LONG; i++)
- {
- t = &(x->x_beats[i]);
- width = 5 > (t->expect / 7) ? 5 : (t->expect / 7);
- t->expect -= x->x_this_input;
- t->onbeat = 0;
- if(double_abs(t->expect) <= width) /* lies within range */
- {
- t->expect = i;
- t->onbeat = 1;
- if (t->points > 0)
- t->points += 4; /* add 4 points */
- }
- else if(t->expect < 0)
- {
- t->points -= 8;
- t->expect = i;
- }
- if (t->points < 0) t->points = 0; else
- if (t->points > 200) t->points = 200;
- if (t->points > points)
- {
- points = t->points;
- period = i;
- }
- }
-
-
-
- x->x_beat_period = (double)period;
- t = &(x->x_beats[period]);
- x->x_beatexpect = now + (double)t->expect;
- x->x_on_beat = t->onbeat;
-
-time:
- x->x_lastlasttime = x->x_lasttime;
- x->x_lasttime = x->x_this_input; //now;
- x->x_last_input = now;
-
- if(x->x_on_beat)outlet_bang(x->x_outbeat);
- outvalue = (t_float)beat_evaluate(x);
- outlet_float(x->x_outms, outvalue);
- if(x->x_beat_period)outlet_float(x->x_outbpm, (t_float)(60000.0 / outvalue));
- }
- return;
-}
-
-static void beat_ft1(t_beat *x, t_floatarg f)
-{
- x->x_velo = (t_int)f;
-}
-
- /* toggle printing on/off */
-static void beat_print(t_beat *x)
-{
- if(x->x_print)x->x_print = 0;
- else x->x_print = 1;
-}
-
-static void beat_reset(t_beat *x)
-{
- int i;
-
- for(i = 0; i < BEAT_LONG; i++)
- {
- x->x_beats[i].points = 0;
- x->x_beats[i].expect = i;
- x->x_beats[i].onbeat = 0;
- }
- x->x_lastlasttime = 0;
- x->x_lasttime = 0;
- x->x_num_beats = 0;
- x->x_beat_period = 0;
- x->x_on_beat = 0;
-}
-
-static t_class *beat_class;
-
-static void beat_free(t_beat *x)
-{
- /* nothing to do */
-}
-
-static void *beat_new(t_floatarg f)
-{
- t_beat *x = (t_beat *)pd_new(beat_class);
- inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1"));
- x->x_outbpm = outlet_new(&x->x_ob, gensym("float"));
- x->x_outms = outlet_new(&x->x_ob, gensym("float"));
- x->x_outbeat = outlet_new(&x->x_ob, gensym("bang"));
-
- beat_reset(x);
- x->x_band_percent = 4; /* allow 4% 'jitter' by default */
- if(f)x->x_band_percent = (t_int)f;
-
- post("beat: band percentage set to %d", x->x_band_percent);
-
- return (void *)x;
-}
-
-#ifndef MAXLIB
-void beat_setup(void)
-{
- beat_class = class_new(gensym("beat"), (t_newmethod)beat_new,
- (t_method)beat_free, sizeof(t_beat), 0, A_DEFFLOAT, 0);
-#else
-void maxlib_beat_setup(void)
-{
- beat_class = class_new(gensym("maxlib_beat"), (t_newmethod)beat_new,
- (t_method)beat_free, sizeof(t_beat), 0, A_DEFFLOAT, 0);
-#endif
- class_addfloat(beat_class, beat_float);
- class_addmethod(beat_class, (t_method)beat_ft1, gensym("ft1"), A_FLOAT, 0);
- class_addmethod(beat_class, (t_method)beat_reset, gensym("reset"), 0);
- class_addmethod(beat_class, (t_method)beat_print, gensym("print"), 0);
-#ifndef MAXLIB
-
- post(version);
-#else
- class_addcreator((t_newmethod)beat_new, gensym("beat"), A_DEFFLOAT, 0);
- class_sethelpsymbol(beat_class, gensym("maxlib/beat-help.pd"));
-#endif
-}
-
+/* --------------------------- beat ------------------------------------------ */
+/* */
+/* Detect the beats per minute of a MIDI stream. */
+/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
+/* Based on code written by Robert Rowe. */
+/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+#include "m_pd.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BEAT_LONG 1500 /* longest time we take into concideration (40 bpm) */
+#define BEAT_SHORT 300 /* shortest time we take into concideration (200 bpm) */
+
+static char *version = "beat v0.1, written by Olaf Matthes <olaf.matthes@gmx.de>";
+
+typedef struct
+{
+ t_int points; /* number of points assigned to this theory */
+ double expect; /* time of next expected hit */
+ t_int onbeat; /* whether (1) or not (0) it was on the beat */
+} beat_theory;
+
+typedef struct /* used for sorting theories */
+{
+ t_int points;
+ t_int theory;
+} beat_sort_record;
+
+
+typedef struct beat
+{
+ t_object x_ob;
+ t_clock *x_clock;
+ t_outlet *x_outbpm; /* beat as MIDI note number */
+ t_outlet *x_outms; /* beat in milliseconds */
+ t_outlet *x_outbeat; /* send a bang whenever beat is 'on beat' */
+ t_int x_print; /* switch printing to console window on / off */
+
+ t_int x_num_beats; /* number of beats we've received */
+ double x_beat_period; /* time in ms until next expected beat / beat pulse */
+ beat_theory x_beats[BEAT_LONG];
+ double x_beatexpect; /* expected time for next beat */
+ t_int x_on_beat; /* indicate if last event was on beat */
+ t_int x_band_percent;
+
+ t_int x_pitch;
+ t_int x_velo;
+ /* helpers needed to do the time calculations */
+ double x_this_input;
+ double x_last_input;
+ double x_lasttime;
+ double x_lastlasttime;
+} t_beat;
+
+/* ---------------- mathematical functions to work with doubles -------------- */
+static double double_abs(double value)
+{
+ if(value < 0)
+ return (value * -1);
+ else
+ return (value);
+}
+
+/* --------------- beat stuff ------------------------------------------------ */
+ /* evaluate results: find theory that is the most likely one and
+ print out internal data to console window if print is enabled */
+static int beat_evaluate(t_beat *x)
+{
+ int i, j, K;
+ char string[256];
+ char info[40];
+ beat_sort_record theories[BEAT_LONG], *sortp, R;
+ int value; /* the result of the sorting */
+
+ for (i = 0; i < BEAT_LONG; i++)
+ { /* prepare sort records */
+ sortp = &(theories[i]);
+ sortp->points = x->x_beats[i].points;
+ sortp->theory = i;
+ }
+ for (j = 2; j < BEAT_LONG; j++)
+ { /* sort */
+ i = j - 1;
+ K = theories[j].points;
+ R = theories[j];
+ while (i > 0)
+ {
+ if (K >= theories[i].points)
+ {
+ theories[i+1] = R;
+ break;
+ }
+ else
+ {
+ theories[i+1] = theories[i];
+ i -= 1;
+ }
+ }
+ if (i==0) theories[i+1] = R;
+ }
+ /* get leading result */
+ sortp = &(theories[BEAT_LONG - 1]);
+ value = sortp->theory; /* get our resulting theory */
+
+ if(x->x_print)
+ {
+ post(" 0 1 2 3 4 R E");
+ *string = '\0'; /* print out five leading theories */
+ sprintf(info, "%4g", x->x_this_input);
+ strcat(string, info);
+ for(i = 1; i < 6; i++)
+ {
+ sortp = &(theories[BEAT_LONG - i]);
+ sprintf(info, " %4d[%3d]", (int) sortp->theory, (int) sortp->points);
+ strcat(string, info);
+ }
+ sprintf(info, " %g %g", clock_getlogicaltime(), x->x_beatexpect);
+ strcat(string, info);
+ post(string);
+ }
+
+ return value;
+}
+
+ /* reduce duration to fit into our processing window */
+ /* some sort of 'double modulo'... */
+static double beat_reduce_offset(double duration)
+{
+ double temp = duration;
+ int divisor = 2; /* first try dividing by two */
+ while (temp > BEAT_LONG) /* while duration is too long */
+ temp = duration / divisor++; /* divide by progressively higher divisors */
+ return temp; /* return a value in bounds */
+}
+
+/*
+ * beat_eligible: determine whether an event is eligible for consideration
+ * as a beat theory
+ */
+static int beat_eligible(double candidate, int* offsets, int num_offsets)
+{
+ double diff;
+ int i;
+
+ if (candidate >= BEAT_LONG) /* if too long try subharmonics */
+ candidate = beat_reduce_offset(candidate);
+
+ /* if candidate is close to one already found */
+ for(i = 0; i < num_offsets; i++)
+ {
+ diff = double_abs((candidate - offsets[i]));
+ if (diff < offsets[i]/20) {
+ if (candidate > offsets[i])
+ ++offsets[i]; else /* pull existing one */
+ if (candidate < offsets[i]) /* toward new candidate */
+ --offsets[i];
+ return 0; /* declare candidate ineligible */
+ }
+ }
+ return candidate; /* otherwise return legal candidate */
+}
+
+static void beat_float(t_beat *x, t_floatarg f)
+{
+ t_int velo = x->x_velo;
+ int i, j, indx;
+ int num_offsets, candidate;
+ int low_limit, high_limit, width, deviation;
+ int points, band, center_offset, period;
+ beat_theory* t;
+ int offsets[7];
+ static int factors[10] =
+ { 200, 50, 300, 150, 66, 400, 600, 133, 33, 75 };
+ double now = clock_getlogicaltime();
+ t_float outvalue;
+
+ x->x_pitch = (t_int)f;
+ x->x_this_input = clock_gettimesince(x->x_last_input);
+
+ if(velo != 0) /* note-on received */
+ {
+ if(++x->x_num_beats == 1)
+ {
+ goto time; /* only one event, no beats yet */
+ }
+
+ num_offsets = 0;
+ candidate = beat_eligible(x->x_this_input, offsets, num_offsets);
+ if(candidate)
+ offsets[num_offsets++] = candidate; /* offset[0] set to incoming offset */
+
+ if(x->x_num_beats > 2)
+ { /* if three events */
+ /* check previous for eligibility */
+ candidate = beat_eligible(x->x_lasttime, offsets, num_offsets);
+ if (candidate)
+ offsets[num_offsets++] = candidate;
+ candidate = x->x_this_input + x->x_lasttime; /* add current and previous offsets */
+ candidate = beat_eligible(candidate, offsets, num_offsets);
+ if (candidate) /* add to list if eligible */
+ offsets[num_offsets++] = candidate;
+ }
+
+ if(x->x_num_beats > 3)
+ {
+ candidate = beat_eligible(x->x_lastlasttime, offsets, num_offsets);
+ if (candidate)
+ offsets[num_offsets++] = candidate;
+ candidate += x->x_lasttime;
+ candidate = beat_eligible(candidate, offsets, num_offsets);
+ if (candidate)
+ offsets[num_offsets++] = candidate;
+ }
+
+ indx = 0;
+ for(i = num_offsets; i < 7; i++)
+ {
+ offsets[i] = 0;
+ if (indx >= 10) break;
+ candidate = 0;
+ while ((indx < 10) && (!candidate))
+ candidate = beat_eligible((x->x_this_input * factors[indx++])/100, offsets, num_offsets);
+ if (candidate)
+ offsets[num_offsets++] = candidate;
+ }
+
+ for(i = 0; i < num_offsets; i++)
+ {
+ band = offsets[i] * x->x_band_percent / 100;
+ if ((low_limit = offsets[i] - band) < 0) /* add points in a critical band */
+ low_limit = 0; /* around calculated offset */
+ if ((high_limit = offsets[i] + band) > BEAT_LONG)
+ high_limit = BEAT_LONG;
+ center_offset = offsets[i]; /* midpoint of increment */
+ points = 0;
+ for (j = low_limit; j < high_limit; j++)
+ {
+ if ((points = x->x_beats[j].points) > 0)
+ { /* if there is already activation */
+ deviation = j - center_offset; /* record deviation from midpoint */
+ x->x_beats[j].points = 0;
+ if (deviation < 0) { /* if there is activation below midpoint */
+ t = &(x->x_beats[j+1]); /* take theory one above prior */
+ } else
+ if (deviation > 0) { /* if there is activation above midpoint */
+ t = &(x->x_beats[j-1]); /* take theory one below prior */
+ } else
+ t = &(x->x_beats[j]); /* landed right on it */
+ t->points = points + (num_offsets-i);
+ break;
+ }
+ }
+ if (!points)
+ x->x_beats[center_offset].points = num_offsets - i;
+ }
+
+ /* boost hits, and suppress theories with missed beats */
+ period = 0;
+ points = 0;
+ for (i = BEAT_SHORT; i < BEAT_LONG; i++)
+ {
+ t = &(x->x_beats[i]);
+ width = 5 > (t->expect / 7) ? 5 : (t->expect / 7);
+ t->expect -= x->x_this_input;
+ t->onbeat = 0;
+ if(double_abs(t->expect) <= width) /* lies within range */
+ {
+ t->expect = i;
+ t->onbeat = 1;
+ if (t->points > 0)
+ t->points += 4; /* add 4 points */
+ }
+ else if(t->expect < 0)
+ {
+ t->points -= 8;
+ t->expect = i;
+ }
+ if (t->points < 0) t->points = 0; else
+ if (t->points > 200) t->points = 200;
+ if (t->points > points)
+ {
+ points = t->points;
+ period = i;
+ }
+ }
+
+
+
+ x->x_beat_period = (double)period;
+ t = &(x->x_beats[period]);
+ x->x_beatexpect = now + (double)t->expect;
+ x->x_on_beat = t->onbeat;
+
+time:
+ x->x_lastlasttime = x->x_lasttime;
+ x->x_lasttime = x->x_this_input; //now;
+ x->x_last_input = now;
+
+ if(x->x_on_beat)outlet_bang(x->x_outbeat);
+ outvalue = (t_float)beat_evaluate(x);
+ outlet_float(x->x_outms, outvalue);
+ if(x->x_beat_period)outlet_float(x->x_outbpm, (t_float)(60000.0 / outvalue));
+ }
+ return;
+}
+
+static void beat_ft1(t_beat *x, t_floatarg f)
+{
+ x->x_velo = (t_int)f;
+}
+
+ /* toggle printing on/off */
+static void beat_print(t_beat *x)
+{
+ if(x->x_print)x->x_print = 0;
+ else x->x_print = 1;
+}
+
+static void beat_reset(t_beat *x)
+{
+ int i;
+
+ for(i = 0; i < BEAT_LONG; i++)
+ {
+ x->x_beats[i].points = 0;
+ x->x_beats[i].expect = i;
+ x->x_beats[i].onbeat = 0;
+ }
+ x->x_lastlasttime = 0;
+ x->x_lasttime = 0;
+ x->x_num_beats = 0;
+ x->x_beat_period = 0;
+ x->x_on_beat = 0;
+}
+
+static t_class *beat_class;
+
+static void beat_free(t_beat *x)
+{
+ /* nothing to do */
+}
+
+static void *beat_new(t_floatarg f)
+{
+ t_beat *x = (t_beat *)pd_new(beat_class);
+ inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1"));
+ x->x_outbpm = outlet_new(&x->x_ob, gensym("float"));
+ x->x_outms = outlet_new(&x->x_ob, gensym("float"));
+ x->x_outbeat = outlet_new(&x->x_ob, gensym("bang"));
+
+ beat_reset(x);
+ x->x_band_percent = 4; /* allow 4% 'jitter' by default */
+ if(f)x->x_band_percent = (t_int)f;
+
+ post("beat: band percentage set to %d", x->x_band_percent);
+
+ return (void *)x;
+}
+
+#ifndef MAXLIB
+void beat_setup(void)
+{
+ beat_class = class_new(gensym("beat"), (t_newmethod)beat_new,
+ (t_method)beat_free, sizeof(t_beat), 0, A_DEFFLOAT, 0);
+#else
+void maxlib_beat_setup(void)
+{
+ beat_class = class_new(gensym("maxlib_beat"), (t_newmethod)beat_new,
+ (t_method)beat_free, sizeof(t_beat), 0, A_DEFFLOAT, 0);
+#endif
+ class_addfloat(beat_class, beat_float);
+ class_addmethod(beat_class, (t_method)beat_ft1, gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(beat_class, (t_method)beat_reset, gensym("reset"), 0);
+ class_addmethod(beat_class, (t_method)beat_print, gensym("print"), 0);
+#ifndef MAXLIB
+
+ logpost(NULL, 4, version);
+#else
+ class_addcreator((t_newmethod)beat_new, gensym("beat"), A_DEFFLOAT, 0);
+ class_sethelpsymbol(beat_class, gensym("maxlib/beat-help.pd"));
+#endif
+}
+