annotate Source/AubioOnsetDetector.cpp @ 3:979125db34ab

added OF visualiser src code. Added a long term median trigger threshold. New method is working very well on onsets
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Mon, 21 Nov 2011 23:22:40 +0000
parents b4c899822b4e
children eba88b84b5ca
rev   line source
andrew@0 1 /*
andrew@0 2 * AubioOnsetDetector.cpp
andrew@0 3 * aubioonset~
andrew@0 4 *
andrew@0 5 * Created by Andrew Robertson on 13/08/2010.
andrew@0 6 * Copyright 2010 __MyCompanyName__. All rights reserved.
andrew@0 7 *
andrew@0 8 */
andrew@0 9
andrew@0 10 #include "AubioOnsetDetector.h"
andrew@0 11
andrew@0 12 AubioOnsetDetector :: AubioOnsetDetector(){
andrew@0 13 buffersize = 1024;
andrew@0 14 hopsize = 512;
andrew@0 15 //aubio related setup
andrew@0 16 o = new_aubio_onsetdetection(aubio_onset_complex, buffersize, 1);//initially in complex mode
andrew@0 17 pv = (aubio_pvoc_t *)new_aubio_pvoc(buffersize, hopsize, 1);
andrew@0 18 parms = new_aubio_peakpicker(threshold);
andrew@0 19 vec = (fvec_t *)new_fvec(hopsize,1);
andrew@0 20
andrew@0 21 threshold = 1;
andrew@0 22 threshold2 = -70.;
andrew@0 23
andrew@0 24 resetValues();
andrew@0 25 thresholdRelativeToMedian = 1.3;
andrew@0 26 cutoffForRepeatOnsetsMillis = 100;
andrew@0 27 medianSpeed = 10;
andrew@0 28 pos = 0;
andrew@3 29
andrew@3 30 detectionTriggerRatio = 0.5f;
andrew@3 31 detectionTriggerThreshold = 10;
andrew@0 32 }
andrew@0 33
andrew@0 34 AubioOnsetDetector :: ~AubioOnsetDetector(){
andrew@0 35 aubio_onsetdetection_free (o);
andrew@0 36
andrew@0 37 }
andrew@0 38
andrew@0 39 void AubioOnsetDetector :: resetValues(){
andrew@0 40 rawDetectionValue = 1;
andrew@0 41 peakPickedDetectionValue = 1;
andrew@0 42 medianDetectionValue = 1;
andrew@0 43 lastMedianOnsetFrame = 0;
andrew@0 44 currentFrame = 0;
andrew@0 45 aubioLongTermAverage = 1;
andrew@2 46 lastDfValue = 0;
andrew@2 47 bestSlopeValue = 0;
andrew@2 48 recentValueIndex = 0;
andrew@2 49 lastSlopeOnsetFrame = 0;
andrew@2 50 bestSlopeMedian = 10;
andrew@2 51 slopeFallenBelowMedian = true;
andrew@2 52
andrew@2 53 for (int i = 0;i< numberOfDetectionValues;i++)
andrew@2 54 recentRawDetectionValues[i] = 1;
andrew@0 55
andrew@0 56 }
andrew@0 57
andrew@0 58
andrew@0 59 void AubioOnsetDetector :: initialise(){
andrew@0 60 //reinitialises our object
andrew@0 61 o = new_aubio_onsetdetection(aubio_onset_complex, buffersize, 1);//initially in complex mode
andrew@0 62 pv = (aubio_pvoc_t *)new_aubio_pvoc(buffersize, hopsize, 1);
andrew@0 63 parms = new_aubio_peakpicker(threshold);
andrew@0 64 vec = (fvec_t *)new_fvec(hopsize,1);
andrew@0 65 pos = 0;
andrew@0 66 fvec_write_sample(vec, 0.234, 0, pos);
andrew@0 67 fftgrain = (cvec_t *)new_cvec(buffersize,1);
andrew@0 68 onset = (fvec_t *)new_fvec(1,1);
andrew@0 69 }
andrew@0 70
andrew@0 71 bool AubioOnsetDetector :: processframe(float frame[], int n){
andrew@0 72 bool newFrameResult = false;
andrew@0 73 //Paul Brossier's aubioonsetclass~ code ported from Pd
andrew@0 74 int j,isonset;
andrew@0 75 for (j=0;j<n;j++) {
andrew@0 76 // write input to datanew
andrew@0 77 fvec_write_sample(vec, frame[j], 0, pos);//vec->data[0][pos] = frame[j]
andrew@0 78 //time for fft
andrew@0 79
andrew@0 80 if (pos == hopsize-1) { //hopsize is 512
andrew@0 81 newFrameResult = true;
andrew@0 82 aubioOnsetFound = false;
andrew@0 83 // block loop
andrew@0 84 aubio_pvoc_do (pv,vec, fftgrain);
andrew@0 85
andrew@0 86 fftgrain->norm[0][0] = fabs(fftgrain->norm[0][0]);
andrew@0 87 //added hack to solve bug that norm[0][0] is negative sometimes.
andrew@0 88
andrew@0 89 aubio_onsetdetection(o, fftgrain, onset);
andrew@0 90 rawDetectionValue = onset->data[0][0];
andrew@0 91 //Paul Brossier's method to return value of peak picking process
andrew@0 92
andrew@0 93 anrMedianProcessedOnsetFound = checkForMedianOnset(rawDetectionValue);
andrew@0 94
andrew@2 95 bestSlopeValue = getBestSlopeValue(rawDetectionValue);
andrew@2 96 anrBestSlopeOnset = checkForSlopeOnset(bestSlopeValue);
andrew@2 97
andrew@0 98 // smpl_t my_sample_value;
andrew@0 99 peakPickedDetectionValue = aubio_peakpick_pimrt_getval(parms);
andrew@0 100 //peakPickedDetectionValue = my_sample_value;
andrew@0 101
andrew@0 102 //this was what got sent from max object::
andrew@0 103 // outlet_float(x->detectionFunctionOutlet, my_sample_value);
andrew@0 104 // outlet_float(x->rawDetectionFunctionOutlet, x->onset->data[0][0]);
andrew@0 105
andrew@0 106 isonset = aubio_peakpick_pimrt(onset,parms);
andrew@0 107 if (isonset) {
andrew@0 108 // test for silence
andrew@0 109 if (aubio_silence_detection(vec, threshold2)==1)
andrew@0 110 {
andrew@0 111 isonset=0;
andrew@0 112 }
andrew@0 113 else{
andrew@0 114 // outlet_bang(x->bangoutlet);
andrew@0 115 aubioOnsetFound = true;
andrew@0 116
andrew@0 117 }
andrew@0 118 }//end if (isonset)
andrew@0 119
andrew@0 120
andrew@0 121
andrew@0 122 // end of block loop
andrew@0 123 pos = -1; // so it will be zero next j loop
andrew@0 124 }
andrew@0 125 pos++;
andrew@0 126 // outL[j] = frame[j];//have added this so signal is "see through": outputting the input signal
andrew@0 127
andrew@0 128 }
andrew@0 129 //end of Paul's code
andrew@0 130
andrew@0 131 return newFrameResult;
andrew@0 132
andrew@0 133 }
andrew@0 134
andrew@0 135
andrew@0 136 bool AubioOnsetDetector :: checkForMedianOnset(float dfvalue){
andrew@0 137 bool onsetDetected = false;
andrew@0 138 //check for onset relative to our rising and falling median threshold
andrew@0 139 if (dfvalue > medianDetectionValue * thresholdRelativeToMedian &&
andrew@0 140 dfvalue > aubioLongTermAverage &&
andrew@2 141 //lastDfValue < medianDetectionValue &&
andrew@0 142 1000*framesToSeconds(currentFrame - lastMedianOnsetFrame) > cutoffForRepeatOnsetsMillis){
andrew@2 143 printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) );
andrew@0 144 onsetDetected = true;
andrew@2 145 lastMedianOnsetFrame = currentFrame;
andrew@0 146 }
andrew@0 147
andrew@0 148 aubioLongTermAverage *= 0.999;
andrew@0 149 aubioLongTermAverage += 0.001*(dfvalue - aubioLongTermAverage);
andrew@0 150
andrew@0 151 if (dfvalue > medianDetectionValue)
andrew@0 152 medianDetectionValue = dfvalue;
andrew@0 153 else
andrew@0 154 medianDetectionValue += 0.01*medianSpeed*(dfvalue - medianDetectionValue);
andrew@0 155
andrew@0 156 currentFrame++;
andrew@2 157 lastDfValue = dfvalue;
andrew@2 158
andrew@0 159
andrew@0 160 return onsetDetected;
andrew@0 161 }
andrew@0 162
andrew@2 163 double AubioOnsetDetector::getBestSlopeValue(float dfvalue){
andrew@2 164 //the idea is we want a high slope
andrew@2 165 recentRawDetectionValues[recentValueIndex] = dfvalue;
andrew@2 166 double bestValue = 0;
andrew@2 167 for (int i = 1;i < numberOfDetectionValues;i++){
andrew@2 168 double angle = 0;
andrew@2 169 int otherIndex = (recentValueIndex - i + numberOfDetectionValues)%numberOfDetectionValues;
andrew@2 170 double testValue = 0;
andrew@2 171 if (otherIndex > 0 && recentRawDetectionValues[otherIndex] > 0){
andrew@2 172 angle = atan((float)(i * dfvalue)/ (numberOfDetectionValues*(dfvalue-recentRawDetectionValues[otherIndex])) );
andrew@2 173 testValue = (dfvalue - recentRawDetectionValues[otherIndex]) * cos(angle);
andrew@2 174 }
andrew@2 175
andrew@2 176 if (testValue > bestValue)
andrew@2 177 bestValue = testValue;
andrew@2 178 }
andrew@2 179
andrew@2 180 recentValueIndex++;
andrew@2 181
andrew@2 182 if (recentValueIndex == numberOfDetectionValues)
andrew@2 183 recentValueIndex = 0;
andrew@2 184
andrew@2 185
andrew@2 186 return bestValue;
andrew@2 187
andrew@2 188 }
andrew@2 189
andrew@2 190
andrew@2 191
andrew@2 192
andrew@2 193 bool AubioOnsetDetector :: checkForSlopeOnset(float bestValue){
andrew@2 194 bool onsetDetected = false;
andrew@2 195 //check for onset relative to our processed slope function
andrew@2 196 //a mix between increase in value and the gradient of that increase
andrew@2 197
andrew@3 198 if (bestValue > bestSlopeMedian * thresholdRelativeToMedian && //better than recent average
andrew@3 199 1000*framesToSeconds(currentFrame - lastSlopeOnsetFrame) > cutoffForRepeatOnsetsMillis //after cutoff time
andrew@3 200 && slopeFallenBelowMedian // has had onset and fall away again
andrew@3 201 && bestValue > detectionTriggerThreshold * detectionTriggerRatio //longer term ratio of winning onsets
andrew@2 202 ){
andrew@2 203 printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) );
andrew@2 204 onsetDetected = true;
andrew@2 205 lastSlopeOnsetFrame = currentFrame;
andrew@2 206 slopeFallenBelowMedian = false;
andrew@3 207
andrew@3 208 updateDetectionTriggerThreshold(bestValue);
andrew@2 209 }
andrew@2 210
andrew@2 211
andrew@2 212 if (bestValue > bestSlopeMedian)
andrew@3 213 bestSlopeMedian += (bestValue - bestSlopeMedian)*0.02;//was 1.1
andrew@2 214 else{
andrew@3 215 bestSlopeMedian *= 0.99;
andrew@2 216 slopeFallenBelowMedian = true;;
andrew@2 217 }
andrew@2 218 return onsetDetected;
andrew@2 219 }
andrew@2 220
andrew@3 221
andrew@3 222 void AubioOnsetDetector::updateDetectionTriggerThreshold(const float& val){
andrew@3 223 float detectionAdaptSpeed = 0.05;//moving average, roughly last twenty onsets
andrew@3 224 detectionTriggerThreshold *= 1- detectionAdaptSpeed;
andrew@3 225 detectionTriggerThreshold += (val * detectionAdaptSpeed);
andrew@3 226 }
andrew@3 227
andrew@0 228 double AubioOnsetDetector::framesToSeconds(float frames){
andrew@0 229 double seconds = frames * buffersize / 44100.;
andrew@0 230 return seconds;
andrew@0 231 }
andrew@0 232
andrew@0 233 float AubioOnsetDetector :: getRawDetectionFrame(){
andrew@0 234 return rawDetectionValue;
andrew@0 235 }
andrew@0 236
andrew@0 237 float AubioOnsetDetector :: getPeakPickedDetectionFrame(){
andrew@0 238 return peakPickedDetectionValue;
andrew@0 239 }
andrew@0 240
andrew@0 241
andrew@0 242 void AubioOnsetDetector :: onsetclass_energy(){
andrew@0 243 //aubio_onsetdetection_type
andrew@0 244 aubio_onsetdetection_free (o);
andrew@0 245 o = new_aubio_onsetdetection(aubio_onset_energy, buffersize, 1);
andrew@0 246 }
andrew@0 247
andrew@0 248 void AubioOnsetDetector :: onsetclass_hfc(){
andrew@0 249 /** High Frequency Content onset detection function
andrew@0 250
andrew@0 251 This method computes the High Frequency Content (HFC) of the input spectral
andrew@0 252 frame. The resulting function is efficient at detecting percussive onsets.
andrew@0 253
andrew@0 254 Paul Masri. Computer modeling of Sound for Transformation and Synthesis of
andrew@0 255 Musical Signal. PhD dissertation, University of Bristol, UK, 1996.*/
andrew@0 256 aubio_onsetdetection_free (o);
andrew@0 257 o = new_aubio_onsetdetection(aubio_onset_hfc, buffersize, 1);
andrew@0 258 }
andrew@0 259
andrew@0 260
andrew@0 261 void AubioOnsetDetector :: onsetclass_complex(){
andrew@0 262 //aubio_onsetdetection_type
andrew@0 263 //Complex Domain Method onset detection function
andrew@0 264 //Christopher Duxbury, Mike E. Davies, and Mark B. Sandler. Complex domain
andrew@0 265 //onset detection for musical signals. In Proceedings of the Digital Audio
andrew@0 266 //Effects Conference, DAFx-03, pages 90-93, London, UK, 2003.
andrew@0 267 aubio_onsetdetection_free (o);
andrew@0 268 o = new_aubio_onsetdetection(aubio_onset_complex, buffersize, 1);
andrew@0 269 }
andrew@0 270
andrew@0 271 void AubioOnsetDetector :: onsetclass_phase(){
andrew@0 272 /** Phase Based Method onset detection function
andrew@0 273
andrew@0 274 Juan-Pablo Bello, Mike P. Davies, and Mark B. Sandler. Phase-based note onset
andrew@0 275 detection for music signals. In Proceedings of the IEEE International
andrew@0 276 Conference on Acoustics Speech and Signal Processing, pages 441­444,
andrew@0 277 Hong-Kong, 2003.*/
andrew@0 278 aubio_onsetdetection_free (o);
andrew@0 279 o = new_aubio_onsetdetection(aubio_onset_phase, buffersize, 1);
andrew@0 280
andrew@0 281 }
andrew@0 282
andrew@0 283 void AubioOnsetDetector :: onsetclass_specdiff(){
andrew@0 284 /* Spectral difference method onset detection function
andrew@0 285 Jonhatan Foote and Shingo Uchihashi. The beat spectrum: a new approach to
andrew@0 286 rhythm analysis. In IEEE International Conference on Multimedia and Expo
andrew@0 287 (ICME 2001), pages 881­884, Tokyo, Japan, August 2001.
andrew@0 288 */
andrew@0 289 //aubio_onsetdetection_type
andrew@0 290 aubio_onsetdetection_free (o);
andrew@0 291 o = new_aubio_onsetdetection(aubio_onset_specdiff, buffersize, 1);
andrew@0 292 }
andrew@0 293
andrew@0 294 void AubioOnsetDetector :: onsetclass_kl(){
andrew@0 295 /** Kullback-Liebler onset detection function
andrew@0 296
andrew@0 297 Stephen Hainsworth and Malcom Macleod. Onset detection in music audio
andrew@0 298 signals. In Proceedings of the International Computer Music Conference
andrew@0 299 (ICMC), Singapore, 2003.
andrew@0 300 */
andrew@0 301 aubio_onsetdetection_free (o);
andrew@0 302 o = new_aubio_onsetdetection(aubio_onset_kl, buffersize, 1);
andrew@0 303 }
andrew@0 304
andrew@0 305 void AubioOnsetDetector :: onsetclass_mkl(){
andrew@0 306
andrew@0 307 /** Modified Kullback-Liebler onset detection function
andrew@0 308
andrew@0 309 Paul Brossier, ``Automatic annotation of musical audio for interactive
andrew@0 310 systems'', Chapter 2, Temporal segmentation, PhD thesis, Centre for Digital
andrew@0 311 music, Queen Mary University of London, London, UK, 2003.*/
andrew@0 312 aubio_onsetdetection_free (o);
andrew@0 313 o = new_aubio_onsetdetection(aubio_onset_hfc, buffersize, 1);
andrew@0 314 }
andrew@0 315