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