andrew@2
|
1 /*
|
andrew@2
|
2 * PreciseOnsetDetectorOffline.cpp
|
andrew@2
|
3 * ofxPreciseOnsetDetectionOffline
|
andrew@2
|
4 *
|
andrew@2
|
5 * Created by Andrew Robertson on 25/12/2013.
|
andrew@2
|
6 * Copyright 2013 QMUL. All rights reserved.
|
andrew@2
|
7 *
|
andrew@2
|
8 */
|
andrew@2
|
9
|
andrew@2
|
10
|
andrew@2
|
11 //add a retrigger threshold
|
andrew@2
|
12
|
andrew@2
|
13 #include "PreciseOnsetDetectorOffline.h"
|
andrew@2
|
14
|
Venetian@7
|
15 const bool printingOn = false;//true
|
Venetian@7
|
16
|
Venetian@7
|
17 //can work just by calling initialise, then recursive call to processAudioFrame(float* frame, in numSamples)
|
Venetian@7
|
18 //set the dfType - initialises to energy - good for bass and drums, not so good for harmonic instruments
|
andrew@6
|
19
|
andrew@2
|
20 PreciseOnsetDetectorOffline::PreciseOnsetDetectorOffline(){
|
andrew@2
|
21 frameSize = 1024;
|
andrew@2
|
22 hopSize = 512;
|
andrew@2
|
23 preciseLocator.setup(frameSize);
|
andrew@2
|
24 writeOutput = true;//write output to txt when loading file - stored at sample location as file
|
Venetian@7
|
25
|
Venetian@7
|
26 dfType = 5;//see onset detection class
|
Venetian@7
|
27 //0 energy
|
Venetian@7
|
28 //5 is csd
|
Venetian@7
|
29
|
Venetian@7
|
30 ringbuffer = new AudioRingBuffer(frameSize, hopSize);
|
Venetian@7
|
31 sampleCount = 0;
|
Venetian@7
|
32 frameCount = 0;
|
Venetian@7
|
33
|
Venetian@7
|
34 initialised = false;
|
Venetian@7
|
35
|
Venetian@7
|
36 //some params for peak processing
|
Venetian@7
|
37 peakProcess.cutoffForRepeatOnsetsFrames = 8;
|
Venetian@7
|
38 peakProcess.detectionTriggerRatio = 0.5f;//was 0.5
|
Venetian@7
|
39 peakProcess.bestSlopeMedian = 3;
|
Venetian@7
|
40 peakProcess.thresholdRelativeToMedian = 1.81;
|
Venetian@7
|
41
|
Venetian@7
|
42 isBass = false;
|
andrew@2
|
43 }
|
andrew@2
|
44
|
andrew@2
|
45 PreciseOnsetDetectorOffline::~PreciseOnsetDetectorOffline(){
|
Venetian@7
|
46
|
Venetian@7
|
47 if (initialised)
|
Venetian@7
|
48 delete detectionFunction;
|
Venetian@7
|
49
|
Venetian@7
|
50 delete ringbuffer;
|
Venetian@7
|
51
|
andrew@2
|
52 }
|
andrew@2
|
53
|
Venetian@7
|
54 void PreciseOnsetDetectorOffline::initialise(){
|
Venetian@7
|
55 clearAll();
|
Venetian@7
|
56 sampleCount = 0;
|
Venetian@7
|
57 frameCount = 0;
|
Venetian@7
|
58 // initialises with hopsize = 512, framesize = 1024, complex spectral difference DF and hanning window
|
Venetian@7
|
59 detectionFunction = new OnsetDetectionFunction(hopSize, frameSize, dfType, 1);
|
Venetian@7
|
60
|
Venetian@7
|
61 setDfType(5);//for csd
|
Venetian@7
|
62 initialised = true;
|
Venetian@7
|
63
|
Venetian@7
|
64 peakProcess.reset();
|
Venetian@7
|
65 }
|
Venetian@7
|
66
|
Venetian@7
|
67 void PreciseOnsetDetectorOffline::endProcessing(){
|
Venetian@7
|
68 delete detectionFunction;
|
Venetian@7
|
69 detectionFunction = NULL;
|
Venetian@7
|
70 initialised = false;
|
Venetian@7
|
71
|
Venetian@7
|
72 printf("frames in seconds %f\n", secondsToFrameIndex(samples/44100.));
|
Venetian@7
|
73 printf("frames %i\n", (int) dfValues.size());
|
Venetian@7
|
74
|
Venetian@7
|
75 }
|
Venetian@7
|
76
|
Venetian@7
|
77 #pragma mark AudioProcessing
|
Venetian@7
|
78 void PreciseOnsetDetectorOffline::processAudioFrame(float* frame, int n){
|
Venetian@7
|
79 //call initialise before starting here
|
Venetian@7
|
80 assert(initialised);
|
Venetian@7
|
81
|
Venetian@7
|
82 sampleCount += n;
|
Venetian@7
|
83
|
Venetian@7
|
84 if (ringbuffer->addToBuffer(frame, n)){
|
Venetian@7
|
85 //for (int i = 0; i < ringbuffer->buffersize; i++)
|
Venetian@7
|
86 // printf("ring[%i] %.6f\n", i, ringbuffer->audiobuffer[i]);
|
Venetian@7
|
87
|
Venetian@7
|
88
|
Venetian@7
|
89 //needed to make sure df fn is same framesize as our ringbuffer
|
Venetian@7
|
90 double dfval = detectionFunction->getDFsample(ringbuffer->audiobuffer); // compute detection function sample
|
Venetian@7
|
91
|
Venetian@7
|
92 dfValues.push_back(dfval);
|
Venetian@7
|
93
|
Venetian@7
|
94 if (printingOn)
|
Venetian@7
|
95 printf("det val %i: %f\n", frameCount, dfval);
|
Venetian@7
|
96
|
Venetian@7
|
97 if (peakProcess.peakProcessing(dfval)){
|
Venetian@7
|
98
|
Venetian@7
|
99 //onsetPositionFrames.push_back(frameCount);
|
Venetian@7
|
100
|
Venetian@7
|
101 int precisesample = preciseLocator.findExactOnset(ringbuffer->audiobuffer);
|
Venetian@7
|
102
|
Venetian@7
|
103 //printf("precise sample is %i\n", precisesample);
|
Venetian@7
|
104 //so exact sample is
|
Venetian@7
|
105 //int exactsample = (frameCount-1)*hopSize;//chunks in from beginning of frame
|
Venetian@7
|
106 //as we have just added hopsize samples in, the beginning of the frame is -1 from counter
|
Venetian@7
|
107 //exactsample += precisesample;
|
Venetian@7
|
108
|
Venetian@7
|
109 //above was old way, but not as neat
|
Venetian@7
|
110
|
Venetian@7
|
111 //number of samples in, but to beginning of frame, then precisesample in from there
|
Venetian@7
|
112 int alternativePreciseSample = sampleCount - frameSize + precisesample;
|
Venetian@7
|
113
|
Venetian@7
|
114 //printf("PreciseSample %i, %i == %i\n", precisesample, exactsample, alternativePrecise);
|
Venetian@7
|
115
|
Venetian@7
|
116 OnsetInfo newOnsetInfo;
|
Venetian@7
|
117 newOnsetInfo.onsetLocation = alternativePreciseSample/44100.;
|
Venetian@7
|
118 newOnsetInfo.positionSamples = alternativePreciseSample;
|
Venetian@7
|
119 newOnsetInfo.positionFrames = frameCount;
|
Venetian@7
|
120 newOnsetInfo.pitch = 0;
|
Venetian@7
|
121 newOnsetInfo.matchIndicator = 0;
|
Venetian@7
|
122 newOnsetInfo.onBeat = false;
|
Venetian@7
|
123 newOnsetInfo.expressiveTiming = 0;
|
Venetian@7
|
124 // onsetLocations.push_back(alternativePreciseSample/44100.);
|
Venetian@7
|
125 // onsetPositionSamples.push_back(alternativePreciseSample);
|
Venetian@7
|
126 onsetList.push_back(newOnsetInfo);
|
Venetian@7
|
127
|
Venetian@7
|
128
|
Venetian@7
|
129 if (printingOn)
|
Venetian@7
|
130 printf("BANG\n");
|
Venetian@7
|
131 }
|
Venetian@7
|
132
|
Venetian@7
|
133 frameCount++;
|
Venetian@7
|
134
|
Venetian@7
|
135 }
|
Venetian@7
|
136
|
Venetian@7
|
137 }
|
Venetian@7
|
138
|
Venetian@7
|
139
|
Venetian@7
|
140 void PreciseOnsetDetectorOffline::setDfType(int t){
|
Venetian@7
|
141 if (t >= 0 && t <= 9)
|
Venetian@7
|
142 dfType = t;
|
Venetian@7
|
143
|
Venetian@7
|
144 /*
|
Venetian@7
|
145 switch (df_type){
|
Venetian@7
|
146 case 0:
|
Venetian@7
|
147 df_sample = energy_envelope(); // calculate energy envelope detection function sample
|
Venetian@7
|
148 break;
|
Venetian@7
|
149 case 1:
|
Venetian@7
|
150 df_sample = energy_difference(); // calculate half-wave rectified energy difference detection function sample
|
Venetian@7
|
151 break;
|
Venetian@7
|
152 case 2:
|
Venetian@7
|
153 df_sample = spectral_difference(); // calculate spectral difference detection function sample
|
Venetian@7
|
154 break;
|
Venetian@7
|
155 case 3:
|
Venetian@7
|
156 df_sample = spectral_difference_hwr(); // calculate spectral difference detection function sample (half wave rectified)
|
Venetian@7
|
157 break;
|
Venetian@7
|
158 case 4:
|
Venetian@7
|
159 df_sample = phase_deviation(); // calculate phase deviation detection function sample (half wave rectified)
|
Venetian@7
|
160 break;
|
Venetian@7
|
161 case 5:
|
Venetian@7
|
162 df_sample = complex_spectral_difference(); // calcualte complex spectral difference detection function sample
|
Venetian@7
|
163 break;
|
Venetian@7
|
164 case 6:
|
Venetian@7
|
165 df_sample = complex_spectral_difference_hwr(); // calcualte complex spectral difference detection function sample (half-wave rectified)
|
Venetian@7
|
166 break;
|
Venetian@7
|
167 case 7:
|
Venetian@7
|
168 df_sample = high_frequency_content(); // calculate high frequency content detection function sample
|
Venetian@7
|
169 break;
|
Venetian@7
|
170 case 8:
|
Venetian@7
|
171 df_sample = high_frequency_spectral_difference(); // calculate high frequency spectral difference detection function sample
|
Venetian@7
|
172 break;
|
Venetian@7
|
173 case 9:
|
Venetian@7
|
174 df_sample = high_frequency_spectral_difference_hwr(); // calculate high frequency spectral difference detection function (half-wave rectified)
|
Venetian@7
|
175 break;
|
Venetian@7
|
176 */
|
Venetian@7
|
177 }
|
andrew@2
|
178
|
andrew@4
|
179 int PreciseOnsetDetectorOffline::load(std::string filename){
|
andrew@2
|
180
|
andrew@2
|
181 //name for output file is same dir and filename as aif/wav but with _preciseOnsets.txt added
|
andrew@2
|
182 //eg 'song.wav' is 'song.wav_preciseOnsets.txt'
|
andrew@2
|
183
|
Venetian@7
|
184 //printf("PODO: load '%s'\n", filename.c_str());
|
Venetian@7
|
185 return processAudioFileForBeatTimes(filename);
|
andrew@2
|
186 }
|
andrew@2
|
187
|
andrew@2
|
188
|
Venetian@7
|
189 void PreciseOnsetDetectorOffline::clearAll(){
|
Venetian@7
|
190
|
Venetian@7
|
191 dfValues.clear();
|
Venetian@7
|
192 onsetList.clear();
|
Venetian@7
|
193 // onsetLocations.clear();
|
Venetian@7
|
194 // onsetPositionFrames.clear();
|
Venetian@7
|
195 // onsetPositionSamples.clear();
|
Venetian@7
|
196 }
|
andrew@2
|
197
|
Venetian@7
|
198
|
Venetian@7
|
199 int PreciseOnsetDetectorOffline::processAudioFileForBeatTimes(std::string audiofile){
|
andrew@2
|
200 //originally from BeatAnnotationViewer project
|
Venetian@7
|
201 printf("PODO: ProcessFunction %s\n", audiofile.c_str());
|
Venetian@7
|
202
|
Venetian@7
|
203 initialise();// - needed but elsewhere
|
Venetian@7
|
204
|
andrew@3
|
205
|
andrew@2
|
206
|
andrew@2
|
207 // static double frame[FRAMESIZE]; // to hold a single frame
|
andrew@2
|
208 double buffer[frameSize];
|
andrew@2
|
209 double dfval;
|
andrew@2
|
210
|
andrew@2
|
211 for (int i = 0;i < frameSize;i++)
|
andrew@2
|
212 {
|
andrew@2
|
213 buffer[i] = 0;
|
andrew@2
|
214 }
|
andrew@2
|
215
|
andrew@2
|
216 //detfun = new df(1,(FRAMESIZE*2),0);
|
andrew@2
|
217
|
Venetian@7
|
218
|
andrew@2
|
219
|
andrew@2
|
220 SNDFILE *infile, *outfile ; // define input and output sound files
|
andrew@2
|
221
|
andrew@2
|
222 SF_INFO sfinfo ; // struct to hold info about sound file
|
andrew@2
|
223 int readcount ; // counts number of samples read from sound file
|
andrew@2
|
224 const char *infilename = audiofile.c_str();
|
andrew@2
|
225 //"/Users/andrew/Music/Station To Station 2/3-03 Panic In Detroit (Live Nassau Coliseum '76).wav";//"ledzep.wav" ; // input file name
|
andrew@2
|
226 // const char *outfilename = "output.wav" ; // output file name
|
andrew@2
|
227
|
andrew@2
|
228
|
andrew@2
|
229 // Open Input File
|
andrew@2
|
230 if (! (infile = sf_open (infilename, SFM_READ, &sfinfo)))
|
andrew@2
|
231 { // Open failed
|
andrew@2
|
232 printf ("Not able to open input file %s.\n", infilename) ;
|
andrew@2
|
233 // Print the error message from libsndfile.
|
andrew@2
|
234 puts (sf_strerror (NULL)) ;
|
andrew@2
|
235 return 1;
|
andrew@2
|
236 } ;
|
andrew@2
|
237
|
andrew@2
|
238 printf("opened '%s'\n", audiofile.c_str());
|
andrew@2
|
239 loadedFilename = audiofile;
|
andrew@2
|
240 //STEREO OKAY
|
andrew@2
|
241
|
andrew@2
|
242 //HERE IS THE CLASSIC LOADING FILE CODE
|
andrew@2
|
243 //DEALS WITH MORE THAN MONO
|
andrew@2
|
244 int channels = sfinfo.channels;
|
andrew@2
|
245 samples = sfinfo.frames;
|
andrew@2
|
246 printf("Number of channels %i, samples %i\n", channels, samples);
|
andrew@2
|
247
|
andrew@2
|
248
|
andrew@2
|
249 int blocksize = hopSize;//FRAMESIZE;
|
andrew@2
|
250 float buf [channels * blocksize] ;
|
andrew@2
|
251 float frame[blocksize];
|
andrew@2
|
252
|
andrew@2
|
253 int k, m;
|
andrew@2
|
254 readcount = 1;
|
Venetian@7
|
255
|
andrew@2
|
256
|
andrew@2
|
257 //DoubleVector d;
|
andrew@2
|
258 while ((readcount = sf_readf_float (infile, buf, blocksize)) > 0){
|
andrew@2
|
259 for (k = 0 ; k < readcount ; k++){
|
andrew@2
|
260 //d.clear();
|
andrew@2
|
261 frame[k] = 0;
|
andrew@2
|
262
|
andrew@2
|
263 for (m = 0 ; m < channels ; m++){
|
andrew@2
|
264 frame[k] += buf[k*channels + 0];//sum the channels together
|
andrew@2
|
265 //d.push_back(buf [k * channels + m]);
|
andrew@2
|
266 }
|
andrew@2
|
267
|
andrew@2
|
268 frame[k] /= channels;//average of the channels
|
andrew@2
|
269 }
|
andrew@2
|
270
|
Venetian@7
|
271 processAudioFrame(frame, blocksize);
|
Venetian@7
|
272
|
andrew@2
|
273 //add to our buffer for pocessing
|
andrew@2
|
274 for (int i = 0; i< frameSize-hopSize;i++)
|
andrew@2
|
275 {
|
andrew@2
|
276 buffer[i] = buffer[i+hopSize];
|
andrew@2
|
277 buffer[i+hopSize] = frame[i];
|
andrew@2
|
278 }
|
Venetian@7
|
279 /*
|
Venetian@7
|
280 for (int i = 0; i < frameSize; i++)
|
Venetian@7
|
281 printf("buffer[%i] %.6f\n", i, buffer[i]);
|
andrew@2
|
282
|
andrew@2
|
283 dfval = detectionFunction->getDFsample(buffer); // compute detection function sample
|
andrew@2
|
284
|
andrew@2
|
285 dfValues.push_back(dfval);
|
andrew@2
|
286
|
Venetian@7
|
287 // if (printingOn)
|
Venetian@7
|
288 // printf("det val %i: %f\n", counter, dfval);
|
andrew@2
|
289
|
andrew@2
|
290 if (peakProcess.peakProcessing(dfval)){
|
andrew@6
|
291
|
andrew@2
|
292 onsetPositionFrames.push_back(counter);
|
andrew@2
|
293 int precisesample = preciseLocator.findExactOnset(&buffer[0]);
|
Venetian@7
|
294 printf("precise sample is %i\n", precisesample);
|
andrew@2
|
295 //so exact sample is
|
Venetian@7
|
296 int exactsample = (counter-1)*hopSize;//chunks in from beginning of frame
|
andrew@2
|
297 //as we have just added hopsize samples in, the beginning of the frame is -1 from counter
|
andrew@2
|
298 exactsample += precisesample;
|
andrew@2
|
299 //printf("PreciseSample %i, %i\n", precisesample, exactsample);
|
andrew@3
|
300
|
andrew@2
|
301
|
andrew@2
|
302 onsetLocations.push_back(exactsample/44100.);
|
Venetian@7
|
303 onsetPositionSamples.push_back(exactsample);
|
Venetian@7
|
304
|
Venetian@7
|
305
|
andrew@6
|
306
|
andrew@6
|
307 if (printingOn)
|
andrew@6
|
308 printf("BANG\n");
|
andrew@2
|
309 }
|
andrew@2
|
310
|
andrew@2
|
311 counter++;
|
Venetian@7
|
312 */
|
andrew@4
|
313 //printf("read %i samples\n", readcount);
|
andrew@2
|
314 //was sf_write_double(outfile, frame, readcount) ;
|
andrew@2
|
315
|
andrew@2
|
316 }//end readcount
|
andrew@2
|
317 //END STEREO OKAY
|
andrew@2
|
318
|
andrew@2
|
319 // Close input file
|
andrew@2
|
320 sf_close (infile);
|
andrew@2
|
321
|
Venetian@7
|
322 endProcessing();
|
andrew@6
|
323
|
andrew@2
|
324 return 0;
|
andrew@2
|
325
|
andrew@2
|
326 }
|
andrew@2
|
327
|
andrew@4
|
328 double PreciseOnsetDetectorOffline::frameIndexToSeconds(const int& frameIndex){
|
andrew@4
|
329 return ((double)(frameIndex*hopSize) /44100.);//- (detectionFunction.framesize/2)?;
|
andrew@4
|
330 }
|
andrew@4
|
331
|
andrew@4
|
332 double PreciseOnsetDetectorOffline::secondsToFrameIndex(const double& seconds){
|
andrew@4
|
333 return (seconds*44100./(double)hopSize );//- (detectionFunction.framesize/2)?;
|
andrew@4
|
334 }
|
andrew@4
|
335
|
andrew@4
|
336
|
andrew@3
|
337 void PreciseOnsetDetectorOffline::exportOnsetTimes(){
|
andrew@3
|
338 exportOnsetTimes(0.0, samples/44100.0);//i.e. the whole file
|
andrew@3
|
339 }
|
andrew@2
|
340
|
andrew@2
|
341
|
andrew@3
|
342 void PreciseOnsetDetectorOffline::exportOnsetTimes(double startTime, double endTime){
|
andrew@3
|
343 //for writing output
|
andrew@3
|
344 BeatWriter writer;
|
andrew@3
|
345 std::string outputFilename = loadedFilename+"_preciseOnsets.txt";
|
andrew@3
|
346
|
andrew@3
|
347 if (writeOutput)
|
andrew@3
|
348 writer.openFile(outputFilename);
|
andrew@3
|
349
|
andrew@3
|
350
|
andrew@3
|
351 int index = 0;
|
Venetian@7
|
352 while (index < onsetList.size() && onsetList[index].onsetLocation < startTime)
|
andrew@3
|
353 index++;
|
andrew@3
|
354
|
Venetian@7
|
355 while (index < onsetList.size() && onsetList[index].onsetLocation <= endTime){
|
Venetian@7
|
356 writer.writeBeatTime(onsetList[index].onsetLocation);
|
andrew@3
|
357 index++;
|
andrew@3
|
358 }
|
andrew@3
|
359
|
andrew@3
|
360 if (writeOutput)
|
andrew@3
|
361 writer.closeFile();
|
andrew@3
|
362 }
|
andrew@2
|
363
|
andrew@2
|
364 void PreciseOnsetDetectorOffline::update(){
|
andrew@2
|
365
|
andrew@2
|
366 }
|
andrew@2
|
367
|
andrew@2
|
368
|
andrew@2
|
369 void PreciseOnsetDetectorOffline::draw(){
|
andrew@2
|
370 //do vizualisation in a specialised class
|
andrew@2
|
371 }
|
andrew@2
|
372
|
andrew@2
|
373 void PreciseOnsetDetectorOffline::printOnsetLocations(){
|
Venetian@7
|
374 for (int i = 0; i < (int)onsetList.size(); i++)
|
Venetian@7
|
375 printf("Onset[%i]: %.3f, samples %i\n", i, onsetList[i].onsetLocation, onsetList[i].positionSamples);
|
andrew@2
|
376 }
|
andrew@3
|
377
|
andrew@3
|
378 double PreciseOnsetDetectorOffline::closestOnset(double& targetVal){
|
andrew@3
|
379 int bestIndex = 0;
|
andrew@3
|
380 double bestDiff = 99999;
|
andrew@3
|
381 double bestVal = -1;
|
andrew@3
|
382
|
Venetian@7
|
383 for (int testIndex = 0; testIndex < (int)onsetList.size(); testIndex++){
|
Venetian@7
|
384 double testDiff = (onsetList[testIndex].onsetLocation - targetVal);
|
andrew@3
|
385 if (fabs(testDiff) < bestDiff){
|
andrew@3
|
386 bestDiff = fabs(testDiff);
|
Venetian@7
|
387 bestVal = onsetList[testIndex].onsetLocation;
|
andrew@3
|
388 bestIndex = testIndex;
|
andrew@3
|
389 }
|
andrew@3
|
390 }
|
andrew@3
|
391 return bestVal;
|
andrew@3
|
392 }
|
andrew@3
|
393
|
andrew@4
|
394 void PreciseOnsetDetectorOffline::loadOnsetLocations(DoubleVector& beats){
|
andrew@4
|
395 //replaces onset locations with new vector
|
Venetian@7
|
396 onsetList.clear();
|
Venetian@7
|
397
|
Venetian@7
|
398 //onsetLocations.clear();
|
andrew@4
|
399 for (int i = 0; i < beats.size(); i++){
|
Venetian@7
|
400 //onsetLocations.push_back(beats[i]);
|
Venetian@7
|
401 OnsetInfo newInfo;
|
Venetian@7
|
402 newInfo.onsetLocation = beats[i];
|
Venetian@7
|
403 onsetList.push_back(newInfo);
|
andrew@4
|
404 }
|
Venetian@7
|
405 }
|
Venetian@7
|
406
|
Venetian@7
|
407 double PreciseOnsetDetectorOffline::onsetAtIndex(int index){
|
Venetian@7
|
408 if (index < onsetList.size())
|
Venetian@7
|
409 return onsetList[index].onsetLocation;
|
Venetian@7
|
410 else
|
Venetian@7
|
411 return 0;
|
Venetian@7
|
412 /*
|
Venetian@7
|
413 if (index < onsetLocations.size())
|
Venetian@7
|
414 return onsetLocations[index];
|
Venetian@7
|
415 else
|
Venetian@7
|
416 return 0;
|
Venetian@7
|
417 */
|
Venetian@7
|
418 }
|
Venetian@7
|
419
|
Venetian@7
|
420 void PreciseOnsetDetectorOffline::cropStartTo(double startTime){
|
Venetian@7
|
421 int index = 0;
|
Venetian@7
|
422 while (index < onsetList.size() && onsetList[index].onsetLocation < startTime){
|
Venetian@7
|
423 onsetList.erase(onsetList.begin());
|
Venetian@7
|
424 // onsetLocations.erase(onsetLocations.begin());
|
Venetian@7
|
425 // onsetPositionSamples.erase(onsetPositionSamples.begin());
|
Venetian@7
|
426 // onsetPositionFrames.erase(onsetPositionFrames.begin());
|
Venetian@7
|
427 }
|
Venetian@7
|
428 /*
|
Venetian@7
|
429 int index = 0;
|
Venetian@7
|
430 while (index < onsetLocations.size() && onsetLocations[index] < startTime){
|
Venetian@7
|
431 onsetLocations.erase(onsetLocations.begin());
|
Venetian@7
|
432 onsetPositionSamples.erase(onsetPositionSamples.begin());
|
Venetian@7
|
433 onsetPositionFrames.erase(onsetPositionFrames.begin());
|
Venetian@7
|
434 }
|
Venetian@7
|
435 */
|
Venetian@7
|
436 }
|
Venetian@7
|
437
|
Venetian@7
|
438 void PreciseOnsetDetectorOffline::setMinimumThreshold(float fVal){
|
Venetian@7
|
439 peakProcess.minimumThreshold = fVal;
|
Venetian@7
|
440 }
|
Venetian@7
|
441
|
Venetian@7
|
442
|
Venetian@7
|
443
|
Venetian@7
|
444 double PreciseOnsetDetectorOffline::beatAtIndex(std::vector<double> beatTimes, int beatIndex){
|
Venetian@7
|
445 if (beatIndex >= 0 && beatIndex < beatTimes.size()){
|
Venetian@7
|
446 return beatTimes[beatIndex];
|
Venetian@7
|
447 } else {
|
Venetian@7
|
448 printf("OUT OF RANGE\n");
|
Venetian@7
|
449 return 0;
|
Venetian@7
|
450 }
|
Venetian@7
|
451 }
|
Venetian@7
|
452
|
Venetian@7
|
453
|
Venetian@7
|
454
|
Venetian@7
|
455 #pragma mark Quantisation
|
Venetian@7
|
456 void PreciseOnsetDetectorOffline::categoriseOnsets(std::vector<double> beatTimes){
|
Venetian@7
|
457 double onsetTime;
|
Venetian@7
|
458 double cutoff = 0.06;//seconds - is it within this range of given beat times
|
Venetian@7
|
459
|
Venetian@7
|
460 for (int i = 0; i < onsetList.size(); i++){
|
Venetian@7
|
461
|
Venetian@7
|
462 onsetTime = onsetList[i].onsetLocation;
|
Venetian@7
|
463
|
Venetian@7
|
464 int beatIndex = 0;
|
Venetian@7
|
465 while(beatIndex < beatTimes.size() && beatTimes[beatIndex] < onsetTime){
|
Venetian@7
|
466 beatIndex++;
|
Venetian@7
|
467 }
|
Venetian@7
|
468 while(beatIndex > 0 && beatTimes[beatIndex] > onsetTime){
|
Venetian@7
|
469 beatIndex--;
|
Venetian@7
|
470 }
|
Venetian@7
|
471 //beatIndex now either side of onset, or onset before first beat
|
Venetian@7
|
472
|
Venetian@7
|
473 printf("beat %.2f, beat+1 %.2f, onset %.2f\n", beatAtIndex(beatTimes, beatIndex),
|
Venetian@7
|
474 beatAtIndex(beatTimes, beatIndex+1), onsetTime);
|
Venetian@7
|
475
|
Venetian@7
|
476 double beatTime = beatAtIndex(beatTimes, beatIndex);
|
Venetian@7
|
477 double nextBeatTime = beatAtIndex(beatTimes, beatIndex+1);
|
Venetian@7
|
478
|
Venetian@7
|
479 //new cutoff part
|
Venetian@7
|
480 onsetList[i].onBeat = false;
|
Venetian@7
|
481
|
Venetian@7
|
482
|
Venetian@7
|
483
|
Venetian@7
|
484 if (fabs(onsetTime-beatTime) < cutoff || fabs(onsetTime-nextBeatTime) < cutoff){
|
Venetian@7
|
485 onsetList[i].onBeat = true;
|
Venetian@7
|
486 }
|
Venetian@7
|
487 //end cutoff part
|
Venetian@7
|
488
|
Venetian@7
|
489 double diff = onsetTime - beatTime;
|
Venetian@7
|
490
|
Venetian@7
|
491 double period;
|
Venetian@7
|
492 if (nextBeatTime){
|
Venetian@7
|
493 period = nextBeatTime - beatTime;
|
Venetian@7
|
494 } else {
|
Venetian@7
|
495 period = beatAtIndex(beatTimes, beatIndex) - beatAtIndex(beatTimes, beatIndex-1);
|
Venetian@7
|
496 }
|
Venetian@7
|
497
|
Venetian@7
|
498 while (onsetTime < beatTime){
|
Venetian@7
|
499 //bug if onset is before th first beat
|
Venetian@7
|
500 beatTime -= period;
|
Venetian@7
|
501 nextBeatTime -= period;
|
Venetian@7
|
502 beatIndex--;
|
Venetian@7
|
503 printf("FIXING: beat index %i time %f onsest %f\n", beatIndex, beatTime, onsetTime);
|
Venetian@7
|
504 }
|
Venetian@7
|
505
|
Venetian@7
|
506 if (period > 0){
|
Venetian@7
|
507 while (diff < 0){
|
Venetian@7
|
508 diff += period;
|
Venetian@7
|
509 //i.e. add the beat period to bring it in range
|
Venetian@7
|
510 }
|
Venetian@7
|
511 //now can look which point it is nearest
|
Venetian@7
|
512 double ratio = diff/period;
|
Venetian@7
|
513 ratio *= 12;
|
Venetian@7
|
514 int beattype = round(ratio);
|
Venetian@7
|
515 if (beattype == 12){
|
Venetian@7
|
516 beattype = 0;
|
Venetian@7
|
517
|
Venetian@7
|
518 beatIndex++;//added need to test
|
Venetian@7
|
519 }
|
Venetian@7
|
520
|
Venetian@7
|
521 doCorrection(beattype, beatIndex);
|
Venetian@7
|
522
|
Venetian@7
|
523
|
Venetian@7
|
524
|
Venetian@7
|
525 // Onset newOnset;
|
Venetian@7
|
526 // newOnset.time = onsetTime;
|
Venetian@7
|
527 // newOnset.onsetType = beattype;
|
Venetian@7
|
528 //int position = beatIndex%4;
|
Venetian@7
|
529
|
Venetian@7
|
530 // if (onsetTime > beatTime + (period/2.))
|
Venetian@7
|
531 // pos++;
|
Venetian@7
|
532 // newOnset.position = pos%4;
|
Venetian@7
|
533
|
Venetian@7
|
534 onsetList[i].beatPosition = beatIndex;
|
Venetian@7
|
535 onsetList[i].onsetType = beattype;
|
Venetian@7
|
536
|
Venetian@7
|
537 printf("Position %i,%i\n", onsetList[i].beatPosition, beattype);
|
Venetian@7
|
538 }
|
Venetian@7
|
539
|
Venetian@7
|
540 }
|
Venetian@7
|
541
|
Venetian@7
|
542 }
|
Venetian@7
|
543
|
Venetian@7
|
544
|
Venetian@7
|
545 void PreciseOnsetDetectorOffline::doCorrection(int& beattype, int& beatPosition){
|
Venetian@7
|
546 switch (beattype) {
|
Venetian@7
|
547 case 1:
|
Venetian@7
|
548 beattype = 0;//on beat
|
Venetian@7
|
549 break;
|
Venetian@7
|
550 case 2:
|
Venetian@7
|
551 beattype = 3;//16th
|
Venetian@7
|
552 break;
|
Venetian@7
|
553 case 5: case 7:
|
Venetian@7
|
554 beattype = 6;//8th note
|
Venetian@7
|
555 break;
|
Venetian@7
|
556 case 10:
|
Venetian@7
|
557 beattype = 9;
|
Venetian@7
|
558 break;
|
Venetian@7
|
559 case 11:
|
Venetian@7
|
560 beattype = 0;//on the beat
|
Venetian@7
|
561 beatPosition++;
|
Venetian@7
|
562 break;
|
Venetian@7
|
563 default:
|
Venetian@7
|
564 break;
|
Venetian@7
|
565 }
|
Venetian@7
|
566
|
Venetian@7
|
567 }
|
Venetian@7
|
568
|
Venetian@7
|
569
|
Venetian@7
|
570
|
Venetian@7
|
571
|
Venetian@7
|
572 int PreciseOnsetDetectorOffline::onsetAtBeat(int testPosition){
|
Venetian@7
|
573 //find onset bear at particular beat
|
Venetian@7
|
574 int index = 0;
|
Venetian@7
|
575 int foundIndex = 0;
|
Venetian@7
|
576 bool found = false;
|
Venetian@7
|
577 double tmpDiff = 10000;
|
Venetian@7
|
578 while (index < onsetList.size() && onsetList[index].beatPosition <= testPosition){
|
Venetian@7
|
579
|
Venetian@7
|
580 if (onsetList[index].beatPosition == testPosition && onsetList[index].onsetType == 0){
|
Venetian@7
|
581 found = true;
|
Venetian@7
|
582 foundIndex = index;
|
Venetian@7
|
583 //tmpDiff = fabs(onsetList[index].onsetLocation -
|
Venetian@7
|
584 //break; - could break here - would find the first onset
|
Venetian@7
|
585 //commented out finds the last one - really we'd just want the closest one to beat time
|
Venetian@7
|
586 }
|
Venetian@7
|
587 index++;
|
Venetian@7
|
588 }
|
Venetian@7
|
589 if (found)
|
Venetian@7
|
590 return foundIndex;
|
Venetian@7
|
591 else
|
Venetian@7
|
592 return -1;
|
Venetian@7
|
593 }
|
Venetian@7
|
594
|