andrew@0: /* andrew@0: * AubioOnsetDetector.cpp andrew@0: * aubioonset~ andrew@0: * andrew@0: * Created by Andrew Robertson on 13/08/2010. andrew@0: * Copyright 2010 __MyCompanyName__. All rights reserved. andrew@0: * andrew@0: */ andrew@0: andrew@0: #include "AubioOnsetDetector.h" andrew@0: andrew@0: AubioOnsetDetector :: AubioOnsetDetector(){ andrew@0: buffersize = 1024; andrew@0: hopsize = 512; andrew@0: //aubio related setup andrew@0: o = new_aubio_onsetdetection(aubio_onset_complex, buffersize, 1);//initially in complex mode andrew@0: pv = (aubio_pvoc_t *)new_aubio_pvoc(buffersize, hopsize, 1); andrew@0: parms = new_aubio_peakpicker(threshold); andrew@0: vec = (fvec_t *)new_fvec(hopsize,1); andrew@0: andrew@0: threshold = 1; andrew@0: threshold2 = -70.; andrew@0: andrew@0: resetValues(); andrew@0: thresholdRelativeToMedian = 1.3; andrew@0: cutoffForRepeatOnsetsMillis = 100; andrew@0: medianSpeed = 10; andrew@0: pos = 0; andrew@3: andrew@3: detectionTriggerRatio = 0.5f; andrew@3: detectionTriggerThreshold = 10; andrew@0: } andrew@0: andrew@0: AubioOnsetDetector :: ~AubioOnsetDetector(){ andrew@0: aubio_onsetdetection_free (o); andrew@0: andrew@0: } andrew@0: andrew@0: void AubioOnsetDetector :: resetValues(){ andrew@0: rawDetectionValue = 1; andrew@0: peakPickedDetectionValue = 1; andrew@0: medianDetectionValue = 1; andrew@0: lastMedianOnsetFrame = 0; andrew@0: currentFrame = 0; andrew@0: aubioLongTermAverage = 1; andrew@2: lastDfValue = 0; andrew@2: bestSlopeValue = 0; andrew@2: recentValueIndex = 0; andrew@2: lastSlopeOnsetFrame = 0; andrew@2: bestSlopeMedian = 10; andrew@2: slopeFallenBelowMedian = true; andrew@2: andrew@2: for (int i = 0;i< numberOfDetectionValues;i++) andrew@2: recentRawDetectionValues[i] = 1; andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: void AubioOnsetDetector :: initialise(){ andrew@0: //reinitialises our object andrew@0: o = new_aubio_onsetdetection(aubio_onset_complex, buffersize, 1);//initially in complex mode andrew@0: pv = (aubio_pvoc_t *)new_aubio_pvoc(buffersize, hopsize, 1); andrew@0: parms = new_aubio_peakpicker(threshold); andrew@0: vec = (fvec_t *)new_fvec(hopsize,1); andrew@0: pos = 0; andrew@0: fvec_write_sample(vec, 0.234, 0, pos); andrew@0: fftgrain = (cvec_t *)new_cvec(buffersize,1); andrew@0: onset = (fvec_t *)new_fvec(1,1); andrew@5: andrew@5: resetValues(); andrew@5: } andrew@0: andrew@0: bool AubioOnsetDetector :: processframe(float frame[], int n){ andrew@0: bool newFrameResult = false; andrew@0: //Paul Brossier's aubioonsetclass~ code ported from Pd andrew@0: int j,isonset; andrew@0: for (j=0;jdata[0][pos] = frame[j] andrew@0: //time for fft andrew@0: andrew@0: if (pos == hopsize-1) { //hopsize is 512 andrew@0: newFrameResult = true; andrew@0: aubioOnsetFound = false; andrew@0: // block loop andrew@0: aubio_pvoc_do (pv,vec, fftgrain); andrew@0: andrew@0: fftgrain->norm[0][0] = fabs(fftgrain->norm[0][0]); andrew@0: //added hack to solve bug that norm[0][0] is negative sometimes. andrew@0: andrew@0: aubio_onsetdetection(o, fftgrain, onset); andrew@0: rawDetectionValue = onset->data[0][0]; andrew@0: //Paul Brossier's method to return value of peak picking process andrew@0: andrew@0: anrMedianProcessedOnsetFound = checkForMedianOnset(rawDetectionValue); andrew@0: andrew@2: bestSlopeValue = getBestSlopeValue(rawDetectionValue); andrew@2: anrBestSlopeOnset = checkForSlopeOnset(bestSlopeValue); andrew@2: andrew@0: // smpl_t my_sample_value; andrew@0: peakPickedDetectionValue = aubio_peakpick_pimrt_getval(parms); andrew@0: //peakPickedDetectionValue = my_sample_value; andrew@0: andrew@0: //this was what got sent from max object:: andrew@0: // outlet_float(x->detectionFunctionOutlet, my_sample_value); andrew@0: // outlet_float(x->rawDetectionFunctionOutlet, x->onset->data[0][0]); andrew@0: andrew@0: isonset = aubio_peakpick_pimrt(onset,parms); andrew@0: if (isonset) { andrew@0: // test for silence andrew@0: if (aubio_silence_detection(vec, threshold2)==1) andrew@0: { andrew@0: isonset=0; andrew@0: } andrew@0: else{ andrew@0: // outlet_bang(x->bangoutlet); andrew@0: aubioOnsetFound = true; andrew@0: andrew@0: } andrew@0: }//end if (isonset) andrew@0: andrew@0: andrew@0: andrew@0: // end of block loop andrew@0: pos = -1; // so it will be zero next j loop andrew@0: } andrew@0: pos++; andrew@0: // outL[j] = frame[j];//have added this so signal is "see through": outputting the input signal andrew@0: andrew@0: } andrew@0: //end of Paul's code andrew@0: andrew@0: return newFrameResult; andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: bool AubioOnsetDetector :: checkForMedianOnset(float dfvalue){ andrew@0: bool onsetDetected = false; andrew@0: //check for onset relative to our rising and falling median threshold andrew@0: if (dfvalue > medianDetectionValue * thresholdRelativeToMedian && andrew@0: dfvalue > aubioLongTermAverage && andrew@2: //lastDfValue < medianDetectionValue && andrew@0: 1000*framesToSeconds(currentFrame - lastMedianOnsetFrame) > cutoffForRepeatOnsetsMillis){ andrew@2: printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) ); andrew@0: onsetDetected = true; andrew@2: lastMedianOnsetFrame = currentFrame; andrew@0: } andrew@0: andrew@0: aubioLongTermAverage *= 0.999; andrew@0: aubioLongTermAverage += 0.001*(dfvalue - aubioLongTermAverage); andrew@0: andrew@0: if (dfvalue > medianDetectionValue) andrew@0: medianDetectionValue = dfvalue; andrew@0: else andrew@0: medianDetectionValue += 0.01*medianSpeed*(dfvalue - medianDetectionValue); andrew@0: andrew@0: currentFrame++; andrew@2: lastDfValue = dfvalue; andrew@2: andrew@0: andrew@0: return onsetDetected; andrew@0: } andrew@0: andrew@2: double AubioOnsetDetector::getBestSlopeValue(float dfvalue){ andrew@2: //the idea is we want a high slope andrew@2: recentRawDetectionValues[recentValueIndex] = dfvalue; andrew@2: double bestValue = 0; andrew@2: for (int i = 1;i < numberOfDetectionValues;i++){ andrew@2: double angle = 0; andrew@2: int otherIndex = (recentValueIndex - i + numberOfDetectionValues)%numberOfDetectionValues; andrew@2: double testValue = 0; andrew@2: if (otherIndex > 0 && recentRawDetectionValues[otherIndex] > 0){ andrew@2: angle = atan((float)(i * dfvalue)/ (numberOfDetectionValues*(dfvalue-recentRawDetectionValues[otherIndex])) ); andrew@2: testValue = (dfvalue - recentRawDetectionValues[otherIndex]) * cos(angle); andrew@2: } andrew@2: andrew@2: if (testValue > bestValue) andrew@2: bestValue = testValue; andrew@2: } andrew@2: andrew@2: recentValueIndex++; andrew@2: andrew@2: if (recentValueIndex == numberOfDetectionValues) andrew@2: recentValueIndex = 0; andrew@2: andrew@2: return bestValue; andrew@2: andrew@2: } andrew@2: andrew@2: andrew@2: andrew@2: andrew@2: bool AubioOnsetDetector :: checkForSlopeOnset(float bestValue){ andrew@2: bool onsetDetected = false; andrew@2: //check for onset relative to our processed slope function andrew@2: //a mix between increase in value and the gradient of that increase andrew@2: andrew@3: if (bestValue > bestSlopeMedian * thresholdRelativeToMedian && //better than recent average andrew@3: 1000*framesToSeconds(currentFrame - lastSlopeOnsetFrame) > cutoffForRepeatOnsetsMillis //after cutoff time andrew@3: && slopeFallenBelowMedian // has had onset and fall away again andrew@3: && bestValue > detectionTriggerThreshold * detectionTriggerRatio //longer term ratio of winning onsets andrew@2: ){ andrew@2: printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) ); andrew@2: onsetDetected = true; andrew@2: lastSlopeOnsetFrame = currentFrame; andrew@2: slopeFallenBelowMedian = false; andrew@3: andrew@3: updateDetectionTriggerThreshold(bestValue); andrew@2: } andrew@2: andrew@2: andrew@2: if (bestValue > bestSlopeMedian) andrew@3: bestSlopeMedian += (bestValue - bestSlopeMedian)*0.02;//was 1.1 andrew@2: else{ andrew@3: bestSlopeMedian *= 0.99; andrew@2: slopeFallenBelowMedian = true;; andrew@2: } andrew@2: return onsetDetected; andrew@2: } andrew@2: andrew@3: andrew@3: void AubioOnsetDetector::updateDetectionTriggerThreshold(const float& val){ andrew@3: float detectionAdaptSpeed = 0.05;//moving average, roughly last twenty onsets andrew@3: detectionTriggerThreshold *= 1- detectionAdaptSpeed; andrew@3: detectionTriggerThreshold += (val * detectionAdaptSpeed); andrew@3: } andrew@3: andrew@0: double AubioOnsetDetector::framesToSeconds(float frames){ andrew@0: double seconds = frames * buffersize / 44100.; andrew@0: return seconds; andrew@0: } andrew@0: andrew@0: float AubioOnsetDetector :: getRawDetectionFrame(){ andrew@0: return rawDetectionValue; andrew@0: } andrew@0: andrew@0: float AubioOnsetDetector :: getPeakPickedDetectionFrame(){ andrew@0: return peakPickedDetectionValue; andrew@0: } andrew@0: andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_energy(){ andrew@0: //aubio_onsetdetection_type andrew@0: aubio_onsetdetection_free (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_energy, buffersize, 1); andrew@0: } andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_hfc(){ 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: aubio_onsetdetection_free (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_hfc, buffersize, 1); andrew@0: } andrew@0: andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_complex(){ andrew@0: //aubio_onsetdetection_type 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: aubio_onsetdetection_free (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_complex, buffersize, 1); andrew@0: } andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_phase(){ 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: aubio_onsetdetection_free (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_phase, buffersize, 1); andrew@0: andrew@0: } andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_specdiff(){ 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 (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_specdiff, buffersize, 1); andrew@0: } andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_kl(){ 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: aubio_onsetdetection_free (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_kl, buffersize, 1); andrew@0: } andrew@0: andrew@0: void AubioOnsetDetector :: onsetclass_mkl(){ andrew@0: 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: aubio_onsetdetection_free (o); andrew@0: o = new_aubio_onsetdetection(aubio_onset_hfc, buffersize, 1); andrew@0: } andrew@0: