andrew@0: /* andrew@0: * PeakProcessor.cpp andrew@0: * peakOnsetDetector andrew@0: * andrew@2: * Created by Andrew Robertson on 07/09/2012. andrew@0: * Copyright 2012 QMUL. All rights reserved. andrew@0: * andrew@0: */ andrew@0: andrew@0: #include "PeakProcessor.h" andrew@0: Venetian@8: const bool printingOn = false;//true;//false;//true;//false; Venetian@8: Venetian@8: Venetian@8: /* Venetian@8: how it works Venetian@8: detectionTriggerThreshold is a fairly fdast moving average (every twenty frames) Venetian@8: tend to require newValue > detectionTriggerRatio * detectionTriggerThreshold Venetian@8: */ andrew@2: andrew@0: PeakProcessor::PeakProcessor(){ andrew@0: andrew@0: recentDFsamples.assign(vectorSize, 0.0); andrew@0: recentDFonsetFound.assign(vectorSize, false); andrew@0: recentDFslopeValues.assign(vectorSize, 0.0); Venetian@8: /* Venetian@8: //all in reset andrew@0: numberOfDetectionValuesToTest = 10; andrew@0: currentFrame = 0; Venetian@8: cutoffForRepeatOnsetsFrames = 8; andrew@4: detectionTriggerRatio = 0.34f;//was 0.5 Venetian@8: detectionTriggerThreshold = 0.2;//was 1.5 is trigger? andrew@4: bestSlopeMedian = 3; andrew@0: thresholdRelativeToMedian = 1.1; andrew@0: slopeFallenBelowMedian = true; andrew@0: lastSlopeOnsetFrame = 0; Venetian@8: */ Venetian@8: initialise(); Venetian@8: reset(); Venetian@8: minimumThreshold = 15.; andrew@0: } andrew@0: andrew@0: PeakProcessor::~PeakProcessor(){ andrew@0: andrew@0: recentDFsamples.clear(); andrew@0: recentDFonsetFound.clear(); andrew@0: recentDFslopeValues.clear(); andrew@0: } andrew@0: andrew@0: Venetian@8: void PeakProcessor::initialise(){ Venetian@8: numberOfDetectionValuesToTest = 10; Venetian@8: cutoffForRepeatOnsetsFrames = 8; Venetian@8: detectionTriggerRatio = 0.234f;//was 0.5 Venetian@8: thresholdRelativeToMedian = 1.01;//need to be this multiple above median value Venetian@8: //median tends to move relatively slowly Venetian@8: reset(); Venetian@8: /* slopeFallenBelowMedian = true; Venetian@8: lastSlopeOnsetFrame = 0; Venetian@8: currentFrame = 0; Venetian@8: */ Venetian@8: } Venetian@8: Venetian@8: void PeakProcessor::reset(){ Venetian@8: /* Venetian@8: numberOfDetectionValuesToTest = 10; Venetian@8: Venetian@8: cutoffForRepeatOnsetsFrames = 8; Venetian@8: detectionTriggerRatio = 0.234f;//was 0.5 Venetian@8: detectionTriggerThreshold = 0.2; Venetian@8: bestSlopeMedian = 3; Venetian@8: thresholdRelativeToMedian = 1.01;//need to be this multiple above median value Venetian@8: */ Venetian@8: //median tends to move relatively slowly Venetian@8: slopeFallenBelowMedian = true; Venetian@8: lastSlopeOnsetFrame = 0; Venetian@8: currentFrame = 0; Venetian@8: detectionTriggerThreshold = 0.4; Venetian@8: bestSlopeMedian = 16; Venetian@8: } Venetian@8: andrew@0: bool PeakProcessor::peakProcessing(const double& newDFval){ andrew@0: recentDFsamples.erase (recentDFsamples.begin(), recentDFsamples.begin()+1);//erase first val andrew@0: recentDFsamples.push_back(newDFval); andrew@0: andrew@0: double slopeVal = getBestSlopeValue(newDFval); andrew@0: andrew@0: newOnsetFound = checkForSlopeOnset(slopeVal); andrew@0: andrew@2: if (printingOn) andrew@2: printf("PeakProcessor: slope %f best slope median %f det trigger thresh median %f\n", slopeVal, bestSlopeMedian, detectionTriggerThreshold); andrew@0: andrew@2: if (newOnsetFound && printingOn){ andrew@2: printf("PeakProcessor: BANG!\n"); andrew@0: } andrew@0: andrew@0: recentDFslopeValues.erase (recentDFslopeValues.begin(), recentDFslopeValues.begin()+1);//erase first val andrew@0: recentDFslopeValues.push_back(slopeVal); andrew@0: andrew@0: recentDFonsetFound.erase (recentDFonsetFound.begin(), recentDFonsetFound.begin()+1);//erase first val andrew@0: recentDFonsetFound.push_back(newOnsetFound); andrew@0: andrew@0: andrew@0: //printf("\n"); andrew@0: // for (int i = 0;i < recentDFsamples.size();i++){ andrew@0: // printf("rdf[%i] %f\n", i, recentDFsamples[i]); andrew@0: // } andrew@0: //printf("SLOPE %f\n", slopeVal); andrew@0: andrew@0: return newOnsetFound; andrew@0: } andrew@0: andrew@0: andrew@0: double PeakProcessor::getBestSlopeValue(const float& dfvalue){ andrew@0: andrew@0: //the idea is we want a high slope andrew@0: double bestValue = 0; Venetian@8: double bestCosAngle = 0; andrew@0: andrew@0: for (int i = 1;i < min(numberOfDetectionValuesToTest, (int)recentDFsamples.size() - 1);i++){ andrew@0: double angle = 0; andrew@0: int otherIndex = recentDFsamples.size() - i + 1; andrew@0: double testValue = 0; andrew@0: andrew@0: if (otherIndex > 0 && recentDFsamples[otherIndex] > 0 andrew@0: && recentDFsamples[otherIndex] < dfvalue andrew@0: ){ andrew@0: angle = atan((float)(i * dfvalue)/ (numberOfDetectionValuesToTest*(dfvalue-recentDFsamples[otherIndex])) ); andrew@0: testValue = (dfvalue - recentDFsamples[otherIndex]) * cos(angle); andrew@0: } andrew@0: Venetian@8: if (testValue > bestValue){ andrew@0: bestValue = testValue; Venetian@8: bestCosAngle = cos(angle); Venetian@8: } andrew@0: } Venetian@8: if (printingOn) Venetian@8: printf("best value %f dfValue %f cosAngle %f\n", bestValue, dfvalue, bestCosAngle); Venetian@8: andrew@0: return bestValue; andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: andrew@0: bool PeakProcessor :: checkForSlopeOnset(const float& bestValue){ andrew@0: bool onsetDetected = false; andrew@0: //check for onset relative to our processed slope function andrew@0: //a mix between increase in value and the gradient of that increase andrew@0: andrew@0: currentFrame++; andrew@0: andrew@0: if (bestValue > bestSlopeMedian * thresholdRelativeToMedian && //better than recent average andrew@0: (currentFrame - lastSlopeOnsetFrame) > cutoffForRepeatOnsetsFrames //after cutoff time andrew@0: && slopeFallenBelowMedian // has had onset and fall away again andrew@0: && bestValue > detectionTriggerThreshold * detectionTriggerRatio //longer term ratio of winning onsets Venetian@8: && bestValue > minimumThreshold//fixed minimum requirement andrew@0: ){ andrew@0: // printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) ); andrew@0: onsetDetected = true; andrew@0: lastSlopeOnsetFrame = currentFrame; andrew@0: slopeFallenBelowMedian = false; andrew@0: andrew@0: updateDetectionTriggerThreshold(bestValue); Venetian@8: Venetian@8: if (printingOn) Venetian@8: printf("ONSET, best value %f, min %f\n", bestValue, minimumThreshold); andrew@0: } andrew@0: andrew@0: andrew@0: if (bestValue > bestSlopeMedian){ andrew@0: bestSlopeMedian += (bestValue - bestSlopeMedian)*0.04;//was 1.1 andrew@0: } andrew@0: else{ andrew@0: bestSlopeMedian *= 0.99; andrew@0: slopeFallenBelowMedian = true;; andrew@0: } andrew@0: andrew@0: //bestSlopeMedian += 0.02* (bestValue - bestSlopeMedian); andrew@0: andrew@0: return onsetDetected; andrew@0: } andrew@0: andrew@0: void PeakProcessor :: updateDetectionTriggerThreshold(const float& val){ andrew@0: float detectionAdaptSpeed = 0.05;//moving average, roughly last twenty onsets andrew@0: detectionTriggerThreshold *= 1- detectionAdaptSpeed; andrew@0: detectionTriggerThreshold += (val * detectionAdaptSpeed); andrew@0: }