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
|
Venetian@8
|
12 const bool printingOn = false;//true;//false;//true;//false;
|
Venetian@8
|
13
|
Venetian@8
|
14
|
Venetian@8
|
15 /*
|
Venetian@8
|
16 how it works
|
Venetian@8
|
17 detectionTriggerThreshold is a fairly fdast moving average (every twenty frames)
|
Venetian@8
|
18 tend to require newValue > detectionTriggerRatio * detectionTriggerThreshold
|
Venetian@8
|
19 */
|
andrew@2
|
20
|
andrew@0
|
21 PeakProcessor::PeakProcessor(){
|
andrew@0
|
22
|
andrew@0
|
23 recentDFsamples.assign(vectorSize, 0.0);
|
andrew@0
|
24 recentDFonsetFound.assign(vectorSize, false);
|
andrew@0
|
25 recentDFslopeValues.assign(vectorSize, 0.0);
|
Venetian@8
|
26 /*
|
Venetian@8
|
27 //all in reset
|
andrew@0
|
28 numberOfDetectionValuesToTest = 10;
|
andrew@0
|
29 currentFrame = 0;
|
Venetian@8
|
30 cutoffForRepeatOnsetsFrames = 8;
|
andrew@4
|
31 detectionTriggerRatio = 0.34f;//was 0.5
|
Venetian@8
|
32 detectionTriggerThreshold = 0.2;//was 1.5 is trigger?
|
andrew@4
|
33 bestSlopeMedian = 3;
|
andrew@0
|
34 thresholdRelativeToMedian = 1.1;
|
andrew@0
|
35 slopeFallenBelowMedian = true;
|
andrew@0
|
36 lastSlopeOnsetFrame = 0;
|
Venetian@8
|
37 */
|
Venetian@8
|
38 initialise();
|
Venetian@8
|
39 reset();
|
Venetian@8
|
40 minimumThreshold = 15.;
|
andrew@0
|
41 }
|
andrew@0
|
42
|
andrew@0
|
43 PeakProcessor::~PeakProcessor(){
|
andrew@0
|
44
|
andrew@0
|
45 recentDFsamples.clear();
|
andrew@0
|
46 recentDFonsetFound.clear();
|
andrew@0
|
47 recentDFslopeValues.clear();
|
andrew@0
|
48 }
|
andrew@0
|
49
|
andrew@0
|
50
|
Venetian@8
|
51 void PeakProcessor::initialise(){
|
Venetian@8
|
52 numberOfDetectionValuesToTest = 10;
|
Venetian@8
|
53 cutoffForRepeatOnsetsFrames = 8;
|
Venetian@8
|
54 detectionTriggerRatio = 0.234f;//was 0.5
|
Venetian@8
|
55 thresholdRelativeToMedian = 1.01;//need to be this multiple above median value
|
Venetian@8
|
56 //median tends to move relatively slowly
|
Venetian@8
|
57 reset();
|
Venetian@8
|
58 /* slopeFallenBelowMedian = true;
|
Venetian@8
|
59 lastSlopeOnsetFrame = 0;
|
Venetian@8
|
60 currentFrame = 0;
|
Venetian@8
|
61 */
|
Venetian@8
|
62 }
|
Venetian@8
|
63
|
Venetian@8
|
64 void PeakProcessor::reset(){
|
Venetian@8
|
65 /*
|
Venetian@8
|
66 numberOfDetectionValuesToTest = 10;
|
Venetian@8
|
67
|
Venetian@8
|
68 cutoffForRepeatOnsetsFrames = 8;
|
Venetian@8
|
69 detectionTriggerRatio = 0.234f;//was 0.5
|
Venetian@8
|
70 detectionTriggerThreshold = 0.2;
|
Venetian@8
|
71 bestSlopeMedian = 3;
|
Venetian@8
|
72 thresholdRelativeToMedian = 1.01;//need to be this multiple above median value
|
Venetian@8
|
73 */
|
Venetian@8
|
74 //median tends to move relatively slowly
|
Venetian@8
|
75 slopeFallenBelowMedian = true;
|
Venetian@8
|
76 lastSlopeOnsetFrame = 0;
|
Venetian@8
|
77 currentFrame = 0;
|
Venetian@8
|
78 detectionTriggerThreshold = 0.4;
|
Venetian@8
|
79 bestSlopeMedian = 16;
|
Venetian@8
|
80 }
|
Venetian@8
|
81
|
andrew@0
|
82 bool PeakProcessor::peakProcessing(const double& newDFval){
|
andrew@0
|
83 recentDFsamples.erase (recentDFsamples.begin(), recentDFsamples.begin()+1);//erase first val
|
andrew@0
|
84 recentDFsamples.push_back(newDFval);
|
andrew@0
|
85
|
andrew@0
|
86 double slopeVal = getBestSlopeValue(newDFval);
|
andrew@0
|
87
|
andrew@0
|
88 newOnsetFound = checkForSlopeOnset(slopeVal);
|
andrew@0
|
89
|
andrew@2
|
90 if (printingOn)
|
andrew@2
|
91 printf("PeakProcessor: slope %f best slope median %f det trigger thresh median %f\n", slopeVal, bestSlopeMedian, detectionTriggerThreshold);
|
andrew@0
|
92
|
andrew@2
|
93 if (newOnsetFound && printingOn){
|
andrew@2
|
94 printf("PeakProcessor: BANG!\n");
|
andrew@0
|
95 }
|
andrew@0
|
96
|
andrew@0
|
97 recentDFslopeValues.erase (recentDFslopeValues.begin(), recentDFslopeValues.begin()+1);//erase first val
|
andrew@0
|
98 recentDFslopeValues.push_back(slopeVal);
|
andrew@0
|
99
|
andrew@0
|
100 recentDFonsetFound.erase (recentDFonsetFound.begin(), recentDFonsetFound.begin()+1);//erase first val
|
andrew@0
|
101 recentDFonsetFound.push_back(newOnsetFound);
|
andrew@0
|
102
|
andrew@0
|
103
|
andrew@0
|
104 //printf("\n");
|
andrew@0
|
105 // for (int i = 0;i < recentDFsamples.size();i++){
|
andrew@0
|
106 // printf("rdf[%i] %f\n", i, recentDFsamples[i]);
|
andrew@0
|
107 // }
|
andrew@0
|
108 //printf("SLOPE %f\n", slopeVal);
|
andrew@0
|
109
|
andrew@0
|
110 return newOnsetFound;
|
andrew@0
|
111 }
|
andrew@0
|
112
|
andrew@0
|
113
|
andrew@0
|
114 double PeakProcessor::getBestSlopeValue(const float& dfvalue){
|
andrew@0
|
115
|
andrew@0
|
116 //the idea is we want a high slope
|
andrew@0
|
117 double bestValue = 0;
|
Venetian@8
|
118 double bestCosAngle = 0;
|
andrew@0
|
119
|
andrew@0
|
120 for (int i = 1;i < min(numberOfDetectionValuesToTest, (int)recentDFsamples.size() - 1);i++){
|
andrew@0
|
121 double angle = 0;
|
andrew@0
|
122 int otherIndex = recentDFsamples.size() - i + 1;
|
andrew@0
|
123 double testValue = 0;
|
andrew@0
|
124
|
andrew@0
|
125 if (otherIndex > 0 && recentDFsamples[otherIndex] > 0
|
andrew@0
|
126 && recentDFsamples[otherIndex] < dfvalue
|
andrew@0
|
127 ){
|
andrew@0
|
128 angle = atan((float)(i * dfvalue)/ (numberOfDetectionValuesToTest*(dfvalue-recentDFsamples[otherIndex])) );
|
andrew@0
|
129 testValue = (dfvalue - recentDFsamples[otherIndex]) * cos(angle);
|
andrew@0
|
130 }
|
andrew@0
|
131
|
Venetian@8
|
132 if (testValue > bestValue){
|
andrew@0
|
133 bestValue = testValue;
|
Venetian@8
|
134 bestCosAngle = cos(angle);
|
Venetian@8
|
135 }
|
andrew@0
|
136 }
|
Venetian@8
|
137 if (printingOn)
|
Venetian@8
|
138 printf("best value %f dfValue %f cosAngle %f\n", bestValue, dfvalue, bestCosAngle);
|
Venetian@8
|
139
|
andrew@0
|
140 return bestValue;
|
andrew@0
|
141
|
andrew@0
|
142 }
|
andrew@0
|
143
|
andrew@0
|
144
|
andrew@0
|
145
|
andrew@0
|
146 bool PeakProcessor :: checkForSlopeOnset(const float& bestValue){
|
andrew@0
|
147 bool onsetDetected = false;
|
andrew@0
|
148 //check for onset relative to our processed slope function
|
andrew@0
|
149 //a mix between increase in value and the gradient of that increase
|
andrew@0
|
150
|
andrew@0
|
151 currentFrame++;
|
andrew@0
|
152
|
andrew@0
|
153 if (bestValue > bestSlopeMedian * thresholdRelativeToMedian && //better than recent average
|
andrew@0
|
154 (currentFrame - lastSlopeOnsetFrame) > cutoffForRepeatOnsetsFrames //after cutoff time
|
andrew@0
|
155 && slopeFallenBelowMedian // has had onset and fall away again
|
andrew@0
|
156 && bestValue > detectionTriggerThreshold * detectionTriggerRatio //longer term ratio of winning onsets
|
Venetian@8
|
157 && bestValue > minimumThreshold//fixed minimum requirement
|
andrew@0
|
158 ){
|
andrew@0
|
159 // printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) );
|
andrew@0
|
160 onsetDetected = true;
|
andrew@0
|
161 lastSlopeOnsetFrame = currentFrame;
|
andrew@0
|
162 slopeFallenBelowMedian = false;
|
andrew@0
|
163
|
andrew@0
|
164 updateDetectionTriggerThreshold(bestValue);
|
Venetian@8
|
165
|
Venetian@8
|
166 if (printingOn)
|
Venetian@8
|
167 printf("ONSET, best value %f, min %f\n", bestValue, minimumThreshold);
|
andrew@0
|
168 }
|
andrew@0
|
169
|
andrew@0
|
170
|
andrew@0
|
171 if (bestValue > bestSlopeMedian){
|
andrew@0
|
172 bestSlopeMedian += (bestValue - bestSlopeMedian)*0.04;//was 1.1
|
andrew@0
|
173 }
|
andrew@0
|
174 else{
|
andrew@0
|
175 bestSlopeMedian *= 0.99;
|
andrew@0
|
176 slopeFallenBelowMedian = true;;
|
andrew@0
|
177 }
|
andrew@0
|
178
|
andrew@0
|
179 //bestSlopeMedian += 0.02* (bestValue - bestSlopeMedian);
|
andrew@0
|
180
|
andrew@0
|
181 return onsetDetected;
|
andrew@0
|
182 }
|
andrew@0
|
183
|
andrew@0
|
184 void PeakProcessor :: updateDetectionTriggerThreshold(const float& val){
|
andrew@0
|
185 float detectionAdaptSpeed = 0.05;//moving average, roughly last twenty onsets
|
andrew@0
|
186 detectionTriggerThreshold *= 1- detectionAdaptSpeed;
|
andrew@0
|
187 detectionTriggerThreshold += (val * detectionAdaptSpeed);
|
andrew@0
|
188 }
|