andrew@0
|
1 /*
|
andrew@0
|
2 * PeakProcessor.cpp
|
andrew@0
|
3 * peakOnsetDetector
|
andrew@0
|
4 *
|
andrew@2
|
5 * Created by Andrew Robertson on 07/09/2012.
|
andrew@0
|
6 * Copyright 2012 QMUL. All rights reserved.
|
andrew@0
|
7 *
|
andrew@0
|
8 */
|
andrew@0
|
9
|
andrew@0
|
10 #include "PeakProcessor.h"
|
andrew@0
|
11
|
andrew@2
|
12 const bool printingOn = true;//false;
|
andrew@2
|
13
|
andrew@0
|
14 PeakProcessor::PeakProcessor(){
|
andrew@0
|
15
|
andrew@0
|
16 recentDFsamples.assign(vectorSize, 0.0);
|
andrew@0
|
17 recentDFonsetFound.assign(vectorSize, false);
|
andrew@0
|
18 recentDFslopeValues.assign(vectorSize, 0.0);
|
andrew@0
|
19
|
andrew@0
|
20 numberOfDetectionValuesToTest = 10;
|
andrew@0
|
21 currentFrame = 0;
|
andrew@0
|
22 cutoffForRepeatOnsetsFrames = 4;
|
andrew@0
|
23 detectionTriggerRatio = 0.5f;
|
andrew@2
|
24 detectionTriggerThreshold = 5;//0.1;
|
andrew@2
|
25 bestSlopeMedian = 8;
|
andrew@0
|
26 thresholdRelativeToMedian = 1.1;
|
andrew@0
|
27 slopeFallenBelowMedian = true;
|
andrew@0
|
28 lastSlopeOnsetFrame = 0;
|
andrew@0
|
29 }
|
andrew@0
|
30
|
andrew@0
|
31 PeakProcessor::~PeakProcessor(){
|
andrew@0
|
32
|
andrew@0
|
33 recentDFsamples.clear();
|
andrew@0
|
34 recentDFonsetFound.clear();
|
andrew@0
|
35 recentDFslopeValues.clear();
|
andrew@0
|
36 }
|
andrew@0
|
37
|
andrew@0
|
38
|
andrew@0
|
39 bool PeakProcessor::peakProcessing(const double& newDFval){
|
andrew@0
|
40 recentDFsamples.erase (recentDFsamples.begin(), recentDFsamples.begin()+1);//erase first val
|
andrew@0
|
41 recentDFsamples.push_back(newDFval);
|
andrew@0
|
42
|
andrew@0
|
43 double slopeVal = getBestSlopeValue(newDFval);
|
andrew@0
|
44
|
andrew@0
|
45 newOnsetFound = checkForSlopeOnset(slopeVal);
|
andrew@0
|
46
|
andrew@2
|
47 if (printingOn)
|
andrew@2
|
48 printf("PeakProcessor: slope %f best slope median %f det trigger thresh median %f\n", slopeVal, bestSlopeMedian, detectionTriggerThreshold);
|
andrew@0
|
49
|
andrew@2
|
50 if (newOnsetFound && printingOn){
|
andrew@2
|
51 printf("PeakProcessor: BANG!\n");
|
andrew@0
|
52 }
|
andrew@0
|
53
|
andrew@0
|
54 recentDFslopeValues.erase (recentDFslopeValues.begin(), recentDFslopeValues.begin()+1);//erase first val
|
andrew@0
|
55 recentDFslopeValues.push_back(slopeVal);
|
andrew@0
|
56
|
andrew@0
|
57 recentDFonsetFound.erase (recentDFonsetFound.begin(), recentDFonsetFound.begin()+1);//erase first val
|
andrew@0
|
58 recentDFonsetFound.push_back(newOnsetFound);
|
andrew@0
|
59
|
andrew@0
|
60
|
andrew@0
|
61 //printf("\n");
|
andrew@0
|
62 // for (int i = 0;i < recentDFsamples.size();i++){
|
andrew@0
|
63 // printf("rdf[%i] %f\n", i, recentDFsamples[i]);
|
andrew@0
|
64 // }
|
andrew@0
|
65 //printf("SLOPE %f\n", slopeVal);
|
andrew@0
|
66
|
andrew@0
|
67 return newOnsetFound;
|
andrew@0
|
68 }
|
andrew@0
|
69
|
andrew@0
|
70
|
andrew@0
|
71 double PeakProcessor::getBestSlopeValue(const float& dfvalue){
|
andrew@0
|
72
|
andrew@0
|
73 //the idea is we want a high slope
|
andrew@0
|
74 double bestValue = 0;
|
andrew@0
|
75
|
andrew@0
|
76 for (int i = 1;i < min(numberOfDetectionValuesToTest, (int)recentDFsamples.size() - 1);i++){
|
andrew@0
|
77 double angle = 0;
|
andrew@0
|
78 int otherIndex = recentDFsamples.size() - i + 1;
|
andrew@0
|
79 double testValue = 0;
|
andrew@0
|
80
|
andrew@0
|
81 if (otherIndex > 0 && recentDFsamples[otherIndex] > 0
|
andrew@0
|
82 && recentDFsamples[otherIndex] < dfvalue
|
andrew@0
|
83 ){
|
andrew@0
|
84 angle = atan((float)(i * dfvalue)/ (numberOfDetectionValuesToTest*(dfvalue-recentDFsamples[otherIndex])) );
|
andrew@0
|
85 testValue = (dfvalue - recentDFsamples[otherIndex]) * cos(angle);
|
andrew@0
|
86 }
|
andrew@0
|
87
|
andrew@0
|
88 if (testValue > bestValue)
|
andrew@0
|
89 bestValue = testValue;
|
andrew@0
|
90 }
|
andrew@0
|
91
|
andrew@0
|
92 return bestValue;
|
andrew@0
|
93
|
andrew@0
|
94 }
|
andrew@0
|
95
|
andrew@0
|
96
|
andrew@0
|
97
|
andrew@0
|
98 bool PeakProcessor :: checkForSlopeOnset(const float& bestValue){
|
andrew@0
|
99 bool onsetDetected = false;
|
andrew@0
|
100 //check for onset relative to our processed slope function
|
andrew@0
|
101 //a mix between increase in value and the gradient of that increase
|
andrew@0
|
102
|
andrew@0
|
103 currentFrame++;
|
andrew@0
|
104
|
andrew@0
|
105 if (bestValue > bestSlopeMedian * thresholdRelativeToMedian && //better than recent average
|
andrew@0
|
106 (currentFrame - lastSlopeOnsetFrame) > cutoffForRepeatOnsetsFrames //after cutoff time
|
andrew@0
|
107 && slopeFallenBelowMedian // has had onset and fall away again
|
andrew@0
|
108 && bestValue > detectionTriggerThreshold * detectionTriggerRatio //longer term ratio of winning onsets
|
andrew@0
|
109 ){
|
andrew@0
|
110 // printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) );
|
andrew@0
|
111 onsetDetected = true;
|
andrew@0
|
112 lastSlopeOnsetFrame = currentFrame;
|
andrew@0
|
113 slopeFallenBelowMedian = false;
|
andrew@0
|
114
|
andrew@0
|
115 updateDetectionTriggerThreshold(bestValue);
|
andrew@0
|
116 }
|
andrew@0
|
117
|
andrew@0
|
118
|
andrew@0
|
119 if (bestValue > bestSlopeMedian){
|
andrew@0
|
120 bestSlopeMedian += (bestValue - bestSlopeMedian)*0.04;//was 1.1
|
andrew@0
|
121 }
|
andrew@0
|
122 else{
|
andrew@0
|
123 bestSlopeMedian *= 0.99;
|
andrew@0
|
124 slopeFallenBelowMedian = true;;
|
andrew@0
|
125 }
|
andrew@0
|
126
|
andrew@0
|
127 //bestSlopeMedian += 0.02* (bestValue - bestSlopeMedian);
|
andrew@0
|
128
|
andrew@0
|
129 return onsetDetected;
|
andrew@0
|
130 }
|
andrew@0
|
131
|
andrew@0
|
132 void PeakProcessor :: updateDetectionTriggerThreshold(const float& val){
|
andrew@0
|
133 float detectionAdaptSpeed = 0.05;//moving average, roughly last twenty onsets
|
andrew@0
|
134 detectionTriggerThreshold *= 1- detectionAdaptSpeed;
|
andrew@0
|
135 detectionTriggerThreshold += (val * detectionAdaptSpeed);
|
andrew@0
|
136 }
|