andrew@0: /** andrew@0: @file andrew@0: aubioOnsetDetect - an MSP object shell andrew@0: jeremy bernstein - jeremy@bootsquad.com andrew@0: andrew@0: @ingroup examples andrew@0: */ andrew@0: andrew@0: #include "ext.h" // standard Max include, always required (except in Jitter) andrew@0: #include "ext_obex.h" // required for new style objects andrew@0: #include "z_dsp.h" // required for MSP objects andrew@0: #include "AubioOnsetDetector.h" andrew@0: #include "aubio.h" andrew@0: andrew@0: ////////////////////////// object struct andrew@0: typedef struct _aubioOnsetDetect andrew@0: { andrew@0: t_pxobject ob; // the object itself (t_pxobject in MSP) andrew@0: andrew@0: t_float threshold; andrew@0: t_float threshold2; andrew@0: t_int bufsize; andrew@0: t_int hopsize; andrew@0: andrew@0: AubioOnsetDetector *onsetDetector; andrew@0: andrew@3: andrew@0: void *bangoutlet; andrew@0: void *medianBangOutlet; andrew@0: andrew@0: void *detectionFunctionOutlet; andrew@0: void *rawDetectionFunctionOutlet; andrew@0: void *medianDetectionFunctionOutlet; andrew@0: andrew@6: // bool useMedianOnsetDetection;//(true) andrew@0: //rather than Paul B's peak picking (false) andrew@0: andrew@0: } t_aubioOnsetDetect; andrew@0: andrew@0: ///////////////////////// function prototypes andrew@0: //// standard set andrew@0: void *aubioOnsetDetect_new(t_symbol *s, long argc, t_atom *argv); andrew@0: void aubioOnsetDetect_free(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_assist(t_aubioOnsetDetect *x, void *b, long m, long a, char *s); andrew@0: andrew@0: void aubioOnsetDetect_float(t_aubioOnsetDetect *x, double f); andrew@4: void aubioOnsetDetect_setBuffersize(t_aubioOnsetDetect *x, int i); andrew@4: void aubioOnsetDetect_setHopsize(t_aubioOnsetDetect *x, int i); andrew@0: void aubioOnsetDetect_energy(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_hfc(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_complex(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_phase(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_specdiff(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_kl(t_aubioOnsetDetect *x); andrew@0: void aubioOnsetDetect_mkl(t_aubioOnsetDetect *x); andrew@0: andrew@0: void aubioOnsetDetect_dsp(t_aubioOnsetDetect *x, t_signal **sp, short *count); andrew@0: t_int *aubioOnsetDetect_perform(t_int *w); andrew@0: //////////////////////// global class pointer variable andrew@0: void *aubioOnsetDetect_class; andrew@0: andrew@0: andrew@0: int main(void) andrew@0: { andrew@0: // object initialization, note the use of dsp_free for the freemethod, which is required andrew@0: // unless you need to free allocated memory, in which case you should call dsp_free from andrew@0: // your custom free function. andrew@0: andrew@0: andrew@0: // NEW METHOD andrew@0: t_class *c; andrew@0: andrew@0: c = class_new("aubioOnsetDetect~", (method)aubioOnsetDetect_new, (method)dsp_free, (long)sizeof(t_aubioOnsetDetect), 0L, A_GIMME, 0); andrew@4: class_addmethod(c, (method)aubioOnsetDetect_setBuffersize, (char*)"setBuffersize", A_LONG, 0); andrew@4: class_addmethod(c, (method)aubioOnsetDetect_setHopsize, (char*)"setHopsize", A_LONG, 0); andrew@4: andrew@0: class_addmethod(c, (method)aubioOnsetDetect_float, (char*)"float", A_FLOAT, 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_dsp, (char*) "dsp", A_CANT, 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_assist, (char*)"assist", A_CANT, 0); andrew@0: andrew@0: class_addmethod(c, (method)aubioOnsetDetect_energy, (char*)"energy", 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_hfc, (char*)"hfc", 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_phase, (char*)"phase", 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_complex, (char*)"complex", 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_specdiff, (char*)"specdiff", 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_kl, (char*)"kl", 0); andrew@0: class_addmethod(c, (method)aubioOnsetDetect_mkl, (char*)"mkl", 0); andrew@0: andrew@0: andrew@0: class_register(CLASS_BOX, c); // register class as a box class andrew@0: andrew@0: class_dspinit(c); // new style object version of dsp_initclass(); andrew@0: andrew@0: CLASS_ATTR_FLOAT(c, "threshold", 0, t_aubioOnsetDetect, threshold);//threshold is an attribute and can be set in info page for object andrew@0: andrew@0: aubioOnsetDetect_class = c; andrew@0: andrew@0: return 0; andrew@0: } andrew@0: andrew@4: andrew@4: void aubioOnsetDetect_setBuffersize(t_aubioOnsetDetect *x, int i){ andrew@4: x->bufsize = i;//using fixed buffer size here. andrew@4: x->hopsize = x->bufsize / 2; andrew@4: object_post((t_object*)x, "Buffersize in aubioonset set to %i and hopsize to %i", x->bufsize, x->hopsize); andrew@4: andrew@4: //set this up in AubioOnsetDetector class instead andrew@4: //x->onsetDetector = new AubioOnsetDetector(); andrew@6: andrew@4: x->onsetDetector->buffersize = x->bufsize; andrew@4: x->onsetDetector->hopsize = x->hopsize; andrew@5: // x->onsetDetector->threshold = x->threshold; andrew@5: // x->onsetDetector->threshold2 = x->threshold2; andrew@4: x->onsetDetector->initialise(); andrew@4: } andrew@4: andrew@4: void aubioOnsetDetect_setHopsize(t_aubioOnsetDetect *x, int i){ andrew@4: if (i < x->bufsize && x->hopsize > 0){ andrew@4: x->hopsize = i; andrew@4: object_post((t_object*)x, "Hopsize now set to %i and Buffersize set to %i", x->hopsize, x->bufsize); andrew@4: andrew@4: //set this up in AubioOnsetDetector class instead andrew@4: // x->onsetDetector = new AubioOnsetDetector(); andrew@4: x->onsetDetector->buffersize = x->bufsize; andrew@4: x->onsetDetector->hopsize = x->hopsize; andrew@5: // x->onsetDetector->threshold = x->threshold; andrew@5: // x->onsetDetector->threshold2 = x->threshold2; andrew@4: x->onsetDetector->initialise(); andrew@4: } andrew@4: } andrew@4: andrew@4: andrew@0: void aubioOnsetDetect_float(t_aubioOnsetDetect *x, double f) andrew@0: { andrew@0: if (f < 10 || f > 0.1){ andrew@0: x->threshold = f; andrew@0: x->onsetDetector->threshold = f; andrew@0: } andrew@0: //post("Threshold is %f", x->threshold); andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_energy(t_aubioOnsetDetect *x){ andrew@0: x->onsetDetector->onsetclass_energy(); andrew@0: post("Energy based onset detection now used by aubioOnsetDetect~."); andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_hfc(t_aubioOnsetDetect *x){ andrew@0: /** High Frequency Content onset detection function andrew@0: andrew@0: This method computes the High Frequency Content (HFC) of the input spectral andrew@0: frame. The resulting function is efficient at detecting percussive onsets. andrew@0: andrew@0: Paul Masri. Computer modeling of Sound for Transformation and Synthesis of andrew@0: Musical Signal. PhD dissertation, University of Bristol, UK, 1996.*/ andrew@0: x->onsetDetector->onsetclass_hfc(); andrew@0: post("High Frequency Content (Masri '96) detection now used by aubioOnsetDetect~."); andrew@0: } andrew@0: andrew@0: andrew@0: void aubioOnsetDetect_complex(t_aubioOnsetDetect *x){ andrew@0: //Complex Domain Method onset detection function andrew@0: //Christopher Duxbury, Mike E. Davies, and Mark B. Sandler. Complex domain andrew@0: //onset detection for musical signals. In Proceedings of the Digital Audio andrew@0: //Effects Conference, DAFx-03, pages 90-93, London, UK, 2003. andrew@0: x->onsetDetector->onsetclass_complex(); andrew@0: post("Complex domain onset detection (Duxbury et al., DaFx '03) now used by aubioOnsetDetect~."); andrew@0: andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_phase(t_aubioOnsetDetect *x){ andrew@0: /** Phase Based Method onset detection function andrew@0: andrew@0: Juan-Pablo Bello, Mike P. Davies, and Mark B. Sandler. Phase-based note onset andrew@0: detection for music signals. In Proceedings of the IEEE International andrew@0: Conference on Acoustics Speech and Signal Processing, pages 441­444, andrew@0: Hong-Kong, 2003.*/ andrew@0: x->onsetDetector->onsetclass_phase(); andrew@0: object_post((t_object *) x, "Phase-based detection (Bello et al., IEEE '03) now used by aubioOnsetDetect~."); andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_specdiff(t_aubioOnsetDetect *x){ andrew@0: /* Spectral difference method onset detection function andrew@0: Jonhatan Foote and Shingo Uchihashi. The beat spectrum: a new approach to andrew@0: rhythm analysis. In IEEE International Conference on Multimedia and Expo andrew@0: (ICME 2001), pages 881­884, Tokyo, Japan, August 2001. andrew@0: */ andrew@0: //aubio_onsetdetection_type andrew@0: //aubio_onsetdetection_free (x->o); andrew@0: x->onsetDetector->onsetclass_specdiff(); andrew@0: post("Spectral Difference (Foote and Shingo Uchihashi, ICME '01) detection now used by aubioOnsetDetect~."); andrew@0: andrew@0: andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_kl(t_aubioOnsetDetect *x){ andrew@0: //aubio_onsetdetection_type andrew@0: //aubio_onsetdetection_free (x->o); andrew@0: /** Kullback-Liebler onset detection function andrew@0: andrew@0: Stephen Hainsworth and Malcom Macleod. Onset detection in music audio andrew@0: signals. In Proceedings of the International Computer Music Conference andrew@0: (ICMC), Singapore, 2003. andrew@0: */ andrew@0: andrew@0: x->onsetDetector->onsetclass_kl(); andrew@0: post("Kullback-Liebler (Hainsworth and McLeod, ICMC '03) detection now used by aubioOnsetDetect~."); andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_mkl(t_aubioOnsetDetect *x){ andrew@0: /** Modified Kullback-Liebler onset detection function andrew@0: andrew@0: Paul Brossier, ``Automatic annotation of musical audio for interactive andrew@0: systems'', Chapter 2, Temporal segmentation, PhD thesis, Centre for Digital andrew@0: music, Queen Mary University of London, London, UK, 2003.*/ andrew@0: x->onsetDetector->onsetclass_mkl(); andrew@0: post("Modified Kullback-Liebler (Brossier, PhD thesis '03) detection now used by aubioOnsetDetect~."); andrew@0: } andrew@0: andrew@0: andrew@0: andrew@0: // this function is called when the DAC is enabled, and "registers" a function andrew@0: // for the signal chain. in this case, "aubioOnsetDetect_perform" andrew@0: void aubioOnsetDetect_dsp(t_aubioOnsetDetect *x, t_signal **sp, short *count) andrew@0: { andrew@0: // dsp_add andrew@0: // 1: (t_perfroutine p) perform method andrew@0: // 2: (long argc) number of args to your perform method andrew@0: // 3...: argc additional arguments, all must be sizeof(pointer) or long andrew@0: // these can be whatever, so you might want to include your object pointer in there andrew@0: // so that you have access to the info, if you need it. andrew@0: dsp_add(aubioOnsetDetect_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); andrew@0: } andrew@0: andrew@0: t_int *aubioOnsetDetect_perform(t_int *w) andrew@0: { andrew@0: // DO NOT CALL post IN HERE, but you can call defer_low (not defer) andrew@0: andrew@0: // args are in a vector, sized as specified in aubioOnsetDetect_dsp method andrew@0: // w[0] contains &aubioOnsetDetect_perform, so we start at w[1] andrew@0: t_aubioOnsetDetect *x = (t_aubioOnsetDetect *)(w[1]); andrew@0: t_float *inL = (t_float *)(w[2]); andrew@0: t_float *outL = (t_float *)(w[3]); andrew@0: int n = (int)w[4]; andrew@0: andrew@0: float frame[n]; andrew@0: int j; andrew@0: for (j=0;jonsetDetector->processframe(frame, n)){ andrew@0: //if buffer full and new result is processed (buffer is 1024 with hopsize 512 - can be set to other values) andrew@6: andrew@6: outlet_float(x->medianDetectionFunctionOutlet, x->onsetDetector->bestSlopeMedian); andrew@6: /* andrew@6: //idea for getting processed value out andrew@6: if (x->onsetDetector->rawDetectionValue > x->onsetDetector->medianDetectionValue) andrew@6: outlet_float(x->medianDetectionFunctionOutlet, x->onsetDetector->rawDetectionValue - x->onsetDetector->medianDetectionValue); andrew@6: else andrew@6: outlet_float(x->medianDetectionFunctionOutlet, 0.0); andrew@6: */ andrew@6: andrew@0: outlet_float(x->rawDetectionFunctionOutlet, x->onsetDetector->rawDetectionValue); andrew@1: // outlet_float(x->detectionFunctionOutlet, x->onsetDetector->peakPickedDetectionValue); andrew@1: outlet_float(x->detectionFunctionOutlet, x->onsetDetector->bestSlopeValue); andrew@0: andrew@0: andrew@1: if (x->onsetDetector->anrMedianProcessedOnsetFound) andrew@1: outlet_bang(x->medianBangOutlet); andrew@0: andrew@2: if (x->onsetDetector->anrBestSlopeOnset) andrew@0: outlet_bang(x->bangoutlet); andrew@0: andrew@0: andrew@0: }//end if new aubio onset detection result andrew@0: andrew@0: outL[j] = inL[j];//have added this so signal is "see through": outputting the input signal andrew@0: andrew@0: // you have to return the NEXT pointer in the array OR MAX WILL CRASH andrew@0: return w + 5; andrew@0: } andrew@0: andrew@0: void aubioOnsetDetect_assist(t_aubioOnsetDetect *x, void *b, long m, long a, char *s) andrew@0: { andrew@0: if (m == ASSIST_INLET) { //inlet andrew@0: sprintf(s, "Inlet %ld", a); andrew@0: switch (a){ andrew@0: case 0: andrew@0: sprintf(s, "Input signal and float (between 0.1 and 10) for aubio detection threshold."); andrew@0: break; andrew@0: case 1: andrew@0: sprintf(s, "no inlet 1"); andrew@0: break; andrew@0: } andrew@0: } andrew@0: else { // outlet andrew@0: andrew@0: switch (a){ andrew@0: case 0: andrew@4: sprintf(s, "Bang out when onset is detected according to best slope."); andrew@0: break; andrew@0: case 1: andrew@4: sprintf(s, "best slope detection function."); andrew@0: break; andrew@0: case 2: andrew@4: sprintf(s, "Raw aubio detection function result."); andrew@0: break; andrew@0: case 3: andrew@4: sprintf(s, "Bang outlet according to median threshold."); andrew@4: break; andrew@4: case 4: andrew@4: sprintf(s, "Median function"); andrew@4: break; andrew@4: andrew@0: } andrew@0: } andrew@0: } andrew@0: andrew@0: // NOT CALLED!, we use dsp_free for a generic free function andrew@0: void aubioOnsetDetect_free(t_aubioOnsetDetect *x) andrew@0: { andrew@0: ; andrew@0: } andrew@0: andrew@0: void *aubioOnsetDetect_new(t_symbol *s, long argc, t_atom *argv) andrew@0: { andrew@0: t_aubioOnsetDetect *x = NULL; andrew@0: andrew@0: andrew@0: // NEW VERSION andrew@4: andrew@4: //note for C++ programmers: andrew@4: //adding (t_class *) in the line below lets you use .cpp files instead andrew@4: //then just change your external code to .cpp instead of c andrew@5: andrew@0: if (x = (t_aubioOnsetDetect *)object_alloc((t_class *) aubioOnsetDetect_class)) { andrew@0: dsp_setup((t_pxobject *)x, 1); // MSP inlets: arg is # of inlets and is REQUIRED! andrew@0: // use 0 if you don't need inlets andrew@6: andrew@6: andrew@6: object_post((t_object*)x, (char*) "Aubio Onset Detect Revamp found, created by Andrew Robertson from work by Paul Brossier, Queen Mary University"); andrew@6: andrew@6: andrew@5: //set outlets, from right to left andrew@0: x->medianDetectionFunctionOutlet = floatout(x); andrew@1: x->medianBangOutlet = bangout(x); andrew@0: x->rawDetectionFunctionOutlet = floatout(x); andrew@0: x->detectionFunctionOutlet = floatout(x); andrew@0: x->bangoutlet = bangout(x); andrew@0: andrew@6: // outlet_new(x, "signal"); - no longer andrew@6: andrew@5: //aubio params andrew@0: x->threshold = 1; andrew@0: x->threshold2 = -70.; andrew@0: andrew@6: //set this up in AubioOnsetDetector class instead andrew@6: x->onsetDetector = new AubioOnsetDetector(); andrew@6: // x->onsetDetector->buffersize = x->bufsize; andrew@6: // x->onsetDetector->hopsize = x->hopsize; andrew@6: x->onsetDetector->threshold = x->threshold; andrew@6: x->onsetDetector->threshold2 = x->threshold2; andrew@4: andrew@6: andrew@0: andrew@6: if (argc > 0 && argv->a_type == A_FLOAT){//i.e. there is an argument on creation like [aubioOnsetDetect~ 0.3] andrew@4: t_atom my_atom = argv[0]; andrew@6: object_post((t_object*)x, (char*) "Threshold argument: set to %f ", atom_getfloat(&my_atom)); andrew@4: x->threshold = atom_getfloat(&my_atom); andrew@0: andrew@0: if (x->threshold > 10) andrew@0: x->threshold = 10; andrew@0: andrew@0: if (x->threshold < 0.1) andrew@0: x->threshold = 0.1; andrew@0: andrew@0: x->onsetDetector->threshold = x->threshold; andrew@0: } andrew@0: andrew@6: if (argc > 1 && (argv+1)->a_type == A_LONG){ andrew@6: t_atom my_atom = argv[1]; andrew@6: aubioOnsetDetect_setBuffersize(x, atom_getlong(&my_atom)); andrew@6: object_post((t_object*)x, (char*) "Buffersize argument: set to %ld ", atom_getlong(&my_atom)); andrew@6: } else { andrew@6: x->bufsize = 1024;//using fixed buffer size here. andrew@6: x->hopsize = x->bufsize / 2; andrew@6: } andrew@6: andrew@6: x->onsetDetector->initialise(); andrew@6: andrew@6: // x->useMedianOnsetDetection = true; andrew@0: } andrew@0: return (x); andrew@0: }