annotate modules-and-plug-ins/max-external/btrack~.cpp @ 117:ca2d83d29814 tip master

Merge branch 'release/1.0.5'
author Adam Stark <adamstark.uk@gmail.com>
date Fri, 18 Aug 2023 20:07:33 +0200
parents d812bf72d928
children
rev   line source
adamstark@78 1 //===========================================================================
adamstark@78 2 /** @file btrack~.cpp
adamstark@78 3 * @brief The btrack~ Max external
adamstark@78 4 * @author Adam Stark
adamstark@78 5 * @copyright Copyright (C) 2008-2014 Queen Mary University of London
adamstark@78 6 *
adamstark@78 7 * This program is free software: you can redistribute it and/or modify
adamstark@78 8 * it under the terms of the GNU General Public License as published by
adamstark@78 9 * the Free Software Foundation, either version 3 of the License, or
adamstark@78 10 * (at your option) any later version.
adamstark@78 11 *
adamstark@78 12 * This program is distributed in the hope that it will be useful,
adamstark@78 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
adamstark@78 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
adamstark@78 15 * GNU General Public License for more details.
adamstark@78 16 *
adamstark@78 17 * You should have received a copy of the GNU General Public License
adamstark@78 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
adamstark@78 19 */
adamstark@78 20 //===========================================================================
adamstark@78 21
adamstark@78 22 //===========================================================================
adamstark@78 23 #include "ext.h" // standard Max include, always required (except in Jitter)
adamstark@78 24 #include "ext_obex.h" // required for "new" style objects
adamstark@78 25 #include "z_dsp.h" // required for MSP objects
adamstark@78 26
adamstark@78 27 //===========================================================================
adamstark@78 28 // BTrack includes
adamstark@78 29 #include "../../src/BTrack.h"
adamstark@78 30 #include "../../src/OnsetDetectionFunction.h"
adamstark@78 31
adamstark@78 32 //===========================================================================
adamstark@78 33 // struct to represent the object's state
adamstark@78 34 typedef struct _btrack {
adamstark@78 35
adamstark@78 36 // The object itself (t_pxobject in MSP instead of t_object)
adamstark@78 37 t_pxobject ob;
adamstark@78 38
adamstark@78 39 // An instance of the BTrack beat tracker
adamstark@78 40 BTrack *b;
adamstark@78 41
adamstark@79 42 // Indicates whether the beat tracker should output beats
adamstark@79 43 bool should_output_beats;
adamstark@79 44
adamstark@80 45 // the time of the last bang received in milliseconds
adamstark@80 46 long time_of_last_bang_ms;
adamstark@80 47
adamstark@80 48 // a count in counter
adamstark@80 49 long count_in;
adamstark@80 50
adamstark@80 51 // the recent tempi observed during count ins
adamstark@80 52 double count_in_tempi[3];
adamstark@80 53
adamstark@78 54 // An outlet for beats
adamstark@78 55 void *beat_outlet;
adamstark@78 56
adamstark@78 57 // An outlet for tempo estimates
adamstark@78 58 void *tempo_outlet;
adamstark@78 59
adamstark@78 60 } t_btrack;
adamstark@78 61
adamstark@78 62
adamstark@78 63 //===========================================================================
adamstark@78 64 // method prototypes
adamstark@78 65 void *btrack_new(t_symbol *s, long argc, t_atom *argv);
adamstark@78 66 void btrack_free(t_btrack *x);
adamstark@78 67 void btrack_assist(t_btrack *x, void *b, long m, long a, char *s);
adamstark@78 68 void btrack_float(t_btrack *x, double f);
adamstark@78 69 void btrack_dsp(t_btrack *x, t_signal **sp, short *count);
adamstark@78 70 void btrack_dsp64(t_btrack *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags);
adamstark@78 71 t_int *btrack_perform(t_int *w);
adamstark@78 72 void btrack_perform64(t_btrack *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam);
adamstark@78 73
adamstark@78 74 //===========================================================================
adamstark@78 75 void btrack_process(t_btrack *x,double* audioFrame);
adamstark@79 76
adamstark@79 77 void btrack_on(t_btrack *x);
adamstark@79 78 void btrack_off(t_btrack *x);
adamstark@79 79
adamstark@80 80 void btrack_fixtempo(t_btrack *x, double f);
adamstark@80 81 void btrack_unfixtempo(t_btrack *x);
adamstark@80 82
adamstark@80 83 void btrack_bang(t_btrack *x);
adamstark@80 84 void btrack_countin(t_btrack *x);
adamstark@80 85
adamstark@78 86 void outlet_beat(t_btrack *x, t_symbol *s, long argc, t_atom *argv);
adamstark@78 87
adamstark@78 88 // global class pointer variable
adamstark@78 89 static t_class *btrack_class = NULL;
adamstark@78 90
adamstark@78 91
adamstark@78 92
adamstark@78 93
adamstark@78 94 //===========================================================================
adamstark@78 95 int C74_EXPORT main(void)
adamstark@78 96 {
adamstark@80 97 //--------------------------------------------------------------
adamstark@80 98 t_class *c = class_new("btrack~", (method)btrack_new, (method)btrack_free, (long)sizeof(t_btrack), 0L, A_GIMME, 0);
adamstark@78 99
adamstark@80 100 //--------------------------------------------------------------
adamstark@78 101 class_addmethod(c, (method)btrack_float, "float", A_FLOAT, 0);
adamstark@78 102 class_addmethod(c, (method)btrack_dsp, "dsp", A_CANT, 0); // Old 32-bit MSP dsp chain compilation for Max 5 and earlier
adamstark@78 103 class_addmethod(c, (method)btrack_dsp64, "dsp64", A_CANT, 0); // New 64-bit MSP dsp chain compilation for Max 6
adamstark@78 104 class_addmethod(c, (method)btrack_assist, "assist", A_CANT, 0);
adamstark@80 105
adamstark@80 106 //--------------------------------------------------------------
adamstark@79 107 class_addmethod(c, (method)btrack_on, "on", 0);
adamstark@79 108 class_addmethod(c, (method)btrack_off, "off", 0);
adamstark@80 109
adamstark@80 110 //--------------------------------------------------------------
adamstark@80 111 class_addmethod(c, (method)btrack_fixtempo, "fixtempo", A_FLOAT, 0);
adamstark@80 112 class_addmethod(c, (method)btrack_unfixtempo, "unfixtempo", 0);
adamstark@79 113
adamstark@80 114 //--------------------------------------------------------------
adamstark@80 115 class_addmethod(c, (method)btrack_bang, "bang", 0);
adamstark@80 116 class_addmethod(c, (method)btrack_countin, "countin", 0);
adamstark@80 117
adamstark@80 118 //--------------------------------------------------------------
adamstark@78 119 class_dspinit(c);
adamstark@78 120 class_register(CLASS_BOX, c);
adamstark@78 121 btrack_class = c;
adamstark@78 122
adamstark@78 123 return 0;
adamstark@78 124 }
adamstark@78 125
adamstark@78 126 //===========================================================================
adamstark@78 127 void *btrack_new(t_symbol *s, long argc, t_atom *argv)
adamstark@78 128 {
adamstark@78 129 t_btrack *x = (t_btrack *)object_alloc(btrack_class);
adamstark@78 130
adamstark@78 131 if (x) {
adamstark@78 132 dsp_setup((t_pxobject *)x, 1); // MSP inlets: arg is # of inlets and is REQUIRED!
adamstark@78 133 // use 0 if you don't need inlets
adamstark@78 134
adamstark@78 135 // create detection function and beat tracking objects
adamstark@78 136 x->b = new BTrack();
adamstark@78 137
adamstark@80 138 // create outlets for bpm and beats
adamstark@78 139 x->tempo_outlet = floatout(x);
adamstark@78 140 x->beat_outlet = bangout(x);
adamstark@78 141
adamstark@80 142 // initialise variables
adamstark@79 143 x->should_output_beats = true;
adamstark@80 144 x->time_of_last_bang_ms = 0;
adamstark@80 145 x->count_in = 4;
adamstark@80 146 x->count_in_tempi[0] = 120;
adamstark@80 147 x->count_in_tempi[1] = 120;
adamstark@80 148 x->count_in_tempi[2] = 120;
adamstark@80 149
adamstark@78 150 }
adamstark@78 151 return (x);
adamstark@78 152 }
adamstark@78 153
adamstark@78 154
adamstark@78 155 //===========================================================================
adamstark@78 156 void btrack_free(t_btrack *x)
adamstark@78 157 {
adamstark@80 158 // delete the beat tracker
adamstark@80 159 delete x->b;
adamstark@80 160 x->b = NULL;
adamstark@80 161
adamstark@80 162 // call the dsp free function on our object
adamstark@80 163 dsp_free((t_pxobject *)x);
adamstark@78 164 }
adamstark@78 165
adamstark@78 166
adamstark@78 167 //===========================================================================
adamstark@78 168 void btrack_assist(t_btrack *x, void *b, long m, long a, char *s)
adamstark@78 169 {
adamstark@79 170 if (m == ASSIST_INLET) { //inlet
adamstark@79 171 if (a == 0)
adamstark@79 172 {
adamstark@79 173 sprintf(s, "(signal) Audio In");
adamstark@79 174 }
adamstark@79 175 }
adamstark@79 176 else { // outlet
adamstark@79 177 if (a == 0)
adamstark@79 178 {
adamstark@79 179 sprintf(s, "Beats Out");
adamstark@79 180 }
adamstark@79 181 if (a == 1)
adamstark@79 182 {
adamstark@79 183 sprintf(s, "Tempo (bpm)");
adamstark@79 184 }
adamstark@79 185
adamstark@79 186 }
adamstark@78 187 }
adamstark@78 188
adamstark@78 189
adamstark@78 190 //===========================================================================
adamstark@78 191 void btrack_float(t_btrack *x, double f)
adamstark@78 192 {
adamstark@78 193
adamstark@78 194
adamstark@78 195 }
adamstark@78 196
adamstark@78 197 //===========================================================================
adamstark@78 198 // this function is called when the DAC is enabled, and "registers" a function for the signal chain in Max 5 and earlier.
adamstark@78 199 // In this case we register the 32-bit, "btrack_perform" method.
adamstark@78 200 void btrack_dsp(t_btrack *x, t_signal **sp, short *count)
adamstark@78 201 {
adamstark@80 202 // get hop size and frame size
adamstark@78 203 int hopSize = (int) sp[0]->s_n;
adamstark@78 204 int frameSize = hopSize*2;
adamstark@78 205
adamstark@80 206 // initialise the beat tracker
adamstark@78 207 x->b->updateHopAndFrameSize(hopSize, frameSize);
adamstark@78 208
adamstark@80 209 // set up dsp
adamstark@78 210 dsp_add(btrack_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
adamstark@78 211 }
adamstark@78 212
adamstark@78 213
adamstark@78 214 //===========================================================================
adamstark@78 215 // this is the Max 6 version of the dsp method -- it registers a function for the signal chain in Max 6,
adamstark@78 216 // which operates on 64-bit audio signals.
adamstark@78 217 void btrack_dsp64(t_btrack *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags)
adamstark@78 218 {
adamstark@80 219 // get hop size and frame size
adamstark@78 220 int hopSize = (int) maxvectorsize;
adamstark@78 221 int frameSize = hopSize*2;
adamstark@78 222
adamstark@80 223 // initialise the beat tracker
adamstark@78 224 x->b->updateHopAndFrameSize(hopSize, frameSize);
adamstark@78 225
adamstark@80 226 // set up dsp
adamstark@78 227 object_method(dsp64, gensym("dsp_add64"), x, btrack_perform64, 0, NULL);
adamstark@78 228 }
adamstark@78 229
adamstark@78 230
adamstark@78 231 //===========================================================================
adamstark@78 232 // this is the 32-bit perform method for Max 5 and earlier
adamstark@78 233 t_int *btrack_perform(t_int *w)
adamstark@78 234 {
adamstark@78 235 t_btrack *x = (t_btrack *)(w[1]);
adamstark@78 236 t_float *inL = (t_float *)(w[2]);
adamstark@78 237 int n = (int)w[3];
adamstark@78 238
adamstark@78 239 double audioFrame[n];
adamstark@78 240
adamstark@78 241 for (int i = 0;i < n;i++)
adamstark@78 242 {
adamstark@78 243 audioFrame[i] = (double) inL[i];
adamstark@78 244 }
adamstark@78 245
adamstark@78 246 btrack_process(x,audioFrame);
adamstark@78 247
adamstark@78 248 // you have to return the NEXT pointer in the array OR MAX WILL CRASH
adamstark@78 249 return w + 4;
adamstark@78 250 }
adamstark@78 251
adamstark@78 252 //===========================================================================
adamstark@78 253 // this is 64-bit perform method for Max 6
adamstark@78 254 void btrack_perform64(t_btrack *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam)
adamstark@78 255 {
adamstark@78 256 t_double *inL = ins[0]; // we get audio for each inlet of the object from the **ins argument
adamstark@78 257 int n = sampleframes;
adamstark@78 258
adamstark@78 259 double audioFrame[n];
adamstark@78 260
adamstark@78 261 for (int i = 0;i < n;i++)
adamstark@78 262 {
adamstark@78 263 audioFrame[i] = (double) inL[i];
adamstark@78 264 }
adamstark@78 265
adamstark@78 266 btrack_process(x,audioFrame);
adamstark@78 267 }
adamstark@78 268
adamstark@78 269 //===========================================================================
adamstark@78 270 void btrack_process(t_btrack *x,double* audioFrame)
adamstark@78 271 {
adamstark@78 272 // process the audio frame
adamstark@78 273 x->b->processAudioFrame(audioFrame);
adamstark@78 274
adamstark@78 275
adamstark@78 276 // if there is a beat in this frame
adamstark@78 277 if (x->b->beatDueInCurrentFrame())
adamstark@78 278 {
adamstark@78 279 // outlet a beat
adamstark@78 280 defer_low((t_object *)x, (method)outlet_beat, NULL, 0, NULL);
adamstark@78 281 }
adamstark@78 282 }
adamstark@78 283
adamstark@78 284 //===========================================================================
adamstark@78 285 void outlet_beat(t_btrack *x, t_symbol *s, long argc, t_atom *argv)
adamstark@78 286 {
adamstark@79 287 if (x->should_output_beats)
adamstark@79 288 {
adamstark@79 289 // send a bang out of the beat outlet
adamstark@79 290 outlet_bang(x->beat_outlet);
adamstark@78 291
adamstark@79 292 // send the tempo out of the tempo outlet
adamstark@79 293 outlet_float(x->tempo_outlet, (float) x->b->getCurrentTempoEstimate());
adamstark@79 294 }
adamstark@78 295 }
adamstark@78 296
adamstark@78 297
adamstark@79 298 //===========================================================================
adamstark@79 299 void btrack_on(t_btrack *x)
adamstark@79 300 {
adamstark@79 301 x->should_output_beats = true;
adamstark@79 302 }
adamstark@78 303
adamstark@79 304 //===========================================================================
adamstark@79 305 void btrack_off(t_btrack *x)
adamstark@79 306 {
adamstark@79 307 x->should_output_beats = false;
adamstark@80 308 x->count_in = 4;
adamstark@79 309 }
adamstark@78 310
adamstark@80 311 //===========================================================================
adamstark@80 312 void btrack_fixtempo(t_btrack *x, double f)
adamstark@80 313 {
adamstark@80 314 x->b->fixTempo(f);
adamstark@80 315 object_post((t_object *) x,"Tempo fixed to %f BPM",f);
adamstark@80 316 }
adamstark@78 317
adamstark@80 318 //===========================================================================
adamstark@80 319 void btrack_unfixtempo(t_btrack *x)
adamstark@80 320 {
adamstark@80 321 x->b->doNotFixTempo();
adamstark@80 322 object_post((t_object *) x,"Tempo no longer fixed");
adamstark@80 323 }
adamstark@78 324
adamstark@80 325 //===========================================================================
adamstark@80 326 void btrack_countin(t_btrack *x)
adamstark@80 327 {
adamstark@80 328 x->count_in = x->count_in-1;
adamstark@80 329
adamstark@80 330 btrack_bang(x);
adamstark@80 331 if (x->count_in == 0)
adamstark@80 332 {
adamstark@80 333 x->should_output_beats = 1;
adamstark@80 334 }
adamstark@80 335 }
adamstark@78 336
adamstark@80 337 //===========================================================================
adamstark@80 338 void btrack_bang(t_btrack *x)
adamstark@80 339 {
adamstark@80 340 double bperiod;
adamstark@80 341 double tempo;
adamstark@80 342 double mean_tempo;
adamstark@80 343
adamstark@80 344 // get current time in milliseconds
adamstark@80 345 long ms = systime_ms();
adamstark@80 346
adamstark@80 347 // calculate beat period
adamstark@80 348 bperiod = ((double) (ms - x->time_of_last_bang_ms))/1000.0;
adamstark@80 349
adamstark@80 350 // store time since last bang
adamstark@80 351 x->time_of_last_bang_ms = ms;
adamstark@80 352
adamstark@80 353 // if beat period is between 0 and 1
adamstark@80 354 if ((bperiod < 1.0) && (bperiod > 0.0))
adamstark@80 355 {
adamstark@80 356 // calculate tempo from beat period
adamstark@80 357 tempo = (1/bperiod)*60;
adamstark@80 358
adamstark@80 359 double sum = 0;
adamstark@80 360
adamstark@80 361 // move back elements in tempo history and sum remaining elements
adamstark@80 362 for (int i = 0;i < 2;i++)
adamstark@80 363 {
adamstark@80 364 x->count_in_tempi[i] = x->count_in_tempi[i+1];
adamstark@80 365 sum = sum+x->count_in_tempi[i];
adamstark@80 366 }
adamstark@80 367
adamstark@80 368 // set final element to be the newly calculated tempo
adamstark@80 369 x->count_in_tempi[2] = tempo;
adamstark@80 370
adamstark@80 371 // add the new tempo to the sum
adamstark@80 372 sum = sum+x->count_in_tempi[2];
adamstark@80 373
adamstark@80 374 // calculate the mean tempo by dividing the tempo by 3
adamstark@80 375 mean_tempo = sum/3;
adamstark@80 376
adamstark@80 377 // set the tempo in the beat tracker
adamstark@80 378 x->b->setTempo(mean_tempo);
adamstark@80 379 }
adamstark@80 380 }
adamstark@78 381
adamstark@78 382
adamstark@78 383