comparison DrumTimingLoader_OF/PerformanceAnalyserSrc/TimingAnalyser.cpp @ 1:106bc2d4f702

added timing analyser file
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Sat, 23 Nov 2013 15:44:47 +0000
parents
children 50ba55abea8c
comparison
equal deleted inserted replaced
0:82352cfc0b23 1:106bc2d4f702
1 /*
2 * TimingAnalyser.cpp
3 * performanceTimingAnalyser
4 *
5 * Created by Andrew on 17/12/2011.
6 * Copyright 2011 QMUL. All rights reserved.
7 *
8 */
9
10 #include "TimingAnalyser.h"
11
12 //FROM TestApp.cpp
13 //change these to your preferred filepaths for exporting the data
14 //timingDataFilepath = "/Users/andrew/outputFile.txt";//here as decoded tempo and phase
15 //change this to your filepath for storing the final path as output
16 //can then load it back in easily without reanalysing using this...
17 // importFileFromStoredData(fileToLoad);
18 //processBeatTimesFilepath = "//Users/andrew/processedBeats";//here as a list of beat times
19 //this latter will be a smoothed version of the original
20
21 //RESTRICT PHASE HOP POSSIBLE
22
23
24 //Correct or DELETE this BeatPosition variable - what is it? Why used?
25
26
27 TimingAnalyser::TimingAnalyser(){
28
29 moveMatrixToOptimalCostPosition = true;
30
31 tempoScalar = 2.0;
32 phaseScalar = 2.0;
33
34 phaseCost = 1000.4;//a phase jump is 1.5 (y-x) where y now becomes the predicted position
35 tempoCost = 2.8;//a tempo jump of z msec per period is charged at 2z units
36
37 drawIOIintervals = false;
38
39 drawExpressiveTimingData = true;
40
41 printHistory = false;
42 mozartTriplets = false;
43
44 movementFactor = 0.5;
45 drawBPM = true;
46
47 //COST VARIABLES - Assuming that a simple error is costing linear (t-x) where x is predicted time
48
49
50 blackAndWhite = true;
51
52 setTempoLimits(400);
53
54
55 //index between which we look at the variation
56 tempoVariationStartIndex = 4;
57
58
59 DoubleVector v;
60 v.assign(phaseRange, 0.0);
61 logProbabilityMatrix.assign(tempoRange, v);
62 previousLogMatrix = logProbabilityMatrix;
63
64 //[tempo][phase]
65 phaseMinimumOffset = -1 * phaseScalar*(phaseRange - 1)/2;//offset in millis
66 globalTimeOffset = 0;//global offset in millis
67
68 lastBeatPosition = 0;
69
70
71 numberOfPointsPerPage = 80;
72 startPoint = 0;
73
74 // initialiseRoutes();
75
76 recentPhaseShift = 0;
77 recentTempoShift = 0;
78
79 /* meanGlobalTempo = 440;
80 tempoMinimumOffset = meanGlobalTempo - (tempoRange-1)/2;
81 meanTempoIndex = (tempoRange - 1)/2;
82 meanTempo = tempoMinimumOffset + meanTempoIndex;
83 */
84 meanPhaseIndex = (phaseRange - 1)/2;
85
86 currentBestTempo = meanTempoIndex;
87 currentBestPhase = meanPhaseIndex;
88
89 printf("Mean tempo initialised at %i and mean index %i\n", meanTempo, meanTempoIndex);
90 int minDrawTempo = minimumTempo + minimumPhaseHop - 4;
91 int maxDrawTempo = maximumTempo + maximumPhaseHop + 4;
92
93 ofTrueTypeFont::setGlobalDpi(72);
94 timesFont.loadFont("TimesNewRoman.ttf", 18, true, true);
95 timesFont.setLineHeight(18.0f);
96 timesFont.setLetterSpacing(1.037);
97
98 }
99
100 void TimingAnalyser::initialiseRoutes(){
101 }
102
103 void TimingAnalyser::clearData(){
104 timingData.clear();
105 basicInterOnsetInterval.clear();
106 beatPosition.clear();
107 }
108
109 void TimingAnalyser::setTempoLimits(const int& newGlobalTempo){
110
111 meanGlobalTempo = newGlobalTempo;
112 tempoMinimumOffset = meanGlobalTempo - (tempoScalar*(tempoRange-1)/2);
113 meanTempoIndex = (tempoRange - 1)/2;
114 meanTempo = tempoMinimumOffset + meanTempoIndex*tempoScalar;
115
116 printf("Setting tempo to %i units i.e. %f ms\n", newGlobalTempo, getTempo(meanTempoIndex));
117 }
118
119
120 double TimingAnalyser::getPhaseIndexFromEventTime(const double& eventTime){
121 return eventTime / phaseScalar;
122 }
123
124 double TimingAnalyser::getTempoInUnits(const double& tempoInMs){
125 return tempoInMs / tempoScalar;
126 }
127
128 //NEW UPDATE METHOD
129 double TimingAnalyser::getBestMinimumCost(const int& newTempoInUnits, const int& newPhaseInUnits, Route& r){
130 //new method to check all points in last matrix
131 //new phase is the phase point from zero in units
132 //including any offsets etc
133
134 double cost = 0;
135 double bestCost = INFINITY;
136 Path* lastPath;
137 int minPhase = 0;
138 int minTempo = 0;
139 r.tempoHop = 0;
140 r.phaseHop = 0;
141
142 //int tempoHop, phaseHop; no need
143 if (pathHistory.size() > 0)
144 lastPath = &pathHistory[pathHistory.size()-1];
145 else{
146 // printf("Initiating first path...\n");
147 }
148
149 for (int tempo = 0;tempo < tempoRange;tempo++){
150
151 for (int phase = 0;phase < phaseRange;phase++){
152 //change tempo
153 cost = 0;
154 int phasePoint = 0;
155
156 if (pathHistory.size() > 0){
157 cost = (*lastPath).currentRoute[tempo][phase].totalCost;
158 cost += tempoCost * abs(newTempoInUnits - (tempo + (*lastPath).tempoOffset));
159
160 //our point then projects forwards at the new tempo
161 phasePoint = phase + (*lastPath).phaseOffset + newTempoInUnits;
162 //phase cost is
163 cost += phaseHopCost(newPhaseInUnits - phasePoint);//i.e. phaseCost * abs(newPhaseInUnits - phasePoint);
164 }//else it's the first point and all previous cost is zero
165
166 if (cost < bestCost){
167 bestCost = cost;
168 minPhase = phase;
169 minTempo = tempo;
170
171 if (pathHistory.size() > 0){
172 r.tempoHop = newTempoInUnits - (tempo + (*lastPath).tempoOffset);
173 r.phaseHop = newPhaseInUnits - phasePoint;
174 } else{
175 r.tempoHop = 0;
176 r.phaseHop = 0;
177 }
178
179 r.previousTempoIndex = tempo;
180 r.previousPhaseIndex = phase;
181
182 }//end if mew minimum
183 }
184 }
185
186 /* printf("best cost for tempo-phase pair %i, %i from tempo %i and phase %i, hop %i, %i is %.2f\n",
187 newTempoInUnits, newPhaseInUnits,
188 minTempo, minPhase,
189 r.tempoHop, r.phaseHop, bestCost);
190 */ return bestCost;
191 }
192
193
194 void TimingAnalyser::printCostMatrix(const RouteMatrix& m){
195 printf("\n");
196 for (int i = 0; i< m.size();i++){
197 printf("Tempo %.1f (index %i)\n", getTempo(i), i);
198 for (int k = 0;k < m[i].size();k++){
199 printf("%.1f (new %.1f + prev %.1f (%i,%i)), ", m[i][k].totalCost,
200 m[i][k].addedCost,m[i][k].previousCost,
201 m[i][k].previousTempoIndex, m[i][k].previousPhaseIndex );
202 }
203 printf("\n");
204 }
205 }
206
207 void TimingAnalyser::updateCostToPoint(const int& eventTime, const int& eventBeatLocation){
208
209 // previousLogMatrix = logProbabilityMatrix;//store previous matrix - settings are in pathHistory vector
210 printf("\nTiming Analyser: update cost to %i, \n", eventTime);
211 //event beat location %i is 1 so don't bother printing
212 // printf("old phase range is %.1f to %.1f\n", getPhase(0), getPhase(phaseRange-1));
213 // printf("old tempo range is %.1f to %.1f\n", getTempo(0), getTempo(tempoRange-1));
214
215 int interval = 1;
216
217 //setting the new path offset points
218 RouteMatrix r_mat;
219 Path* lastPath;
220
221 Path p;//the new path point
222 p.tempoOffset = tempoMinimumOffset;//for now keep it the same
223
224 if (pathHistory.size() > 0){
225 lastPath = &pathHistory[pathHistory.size()-1];
226 int ioi = (int) getTempoInUnits(eventTime - (*lastPath).eventTimeObserved);
227 p.tempoOffset = ioi - ((tempoRange-1)/2);
228 tempoMinimumOffset = p.tempoOffset;
229 printf("New time %i, last time %i diff %i: IOI is %i\n",
230 eventTime, (*lastPath).eventTimeObserved,
231 (eventTime - (*lastPath).eventTimeObserved), ioi);
232 }
233
234 //and in tempo units!
235 p.phaseOffset = round(getPhaseIndexFromEventTime(eventTime)) + phaseMinimumOffset;//in units
236 //abandoned - now using the offset too - dont need - (phaseRange/2) as in the get phase fn
237 printf("NEW PHASE and tempo offsets %i and %i, \n", p.phaseOffset, p.tempoOffset);
238 //end set offset pts
239
240 for (int tempoIndex = 0;tempoIndex < logProbabilityMatrix.size();tempoIndex++){
241 RouteVector r_vec;
242
243 for (int phase = 0;phase < logProbabilityMatrix[tempoIndex].size();phase++){
244
245
246 Route r;
247
248 double bestCost = getBestMinimumCost(p.tempoOffset + tempoIndex, phase + p.phaseOffset, r);
249 double newCost = fabs(getPhaseIndexFromEventTime(eventTime) - (p.phaseOffset + phase));
250
251 //this is the phase position that would lead to this current tempo and phase iwthout any hopping
252 //the bestPreviousCost calculates the best oposition including hopping
253
254 //then we compare the past cost
255
256 r.previousCost = bestCost;
257
258 //this is the best past cost that can get us to current location
259 //then we need new cost
260
261 r.addedCost = newCost;
262
263 //r.globalPhaseOffset = globalTimeOffset;
264
265 logProbabilityMatrix[tempoIndex][phase] = bestCost + newCost;
266 r.totalCost = bestCost + newCost;
267
268 //just to check we get this right!
269 r.tempoIndex = tempoIndex;
270 r.phaseIndex = phase;
271
272 r_vec.push_back(r);
273 }
274 r_mat.push_back(r_vec);
275 }
276 //history.push_back(r_mat);
277
278 //printHistory(); - defunct as using pathHistory instead
279 //printBestPath();
280 //printf("Global offset %i ph min off %i\n", globalTimeOffset, phaseMinimumOffset);
281
282 //new method using Path class
283 p.currentRoute = r_mat;
284
285 globalTimeOffset = p.phaseOffset;
286
287 //store the settings for the matrix at this time step
288 //p.globalPhaseOffset = globalTimeOffset;
289 // p.tempoOffset = tempoMinimumOffset;
290
291 setBestTempoAndPhase(p);
292 lastBeatPosition = eventBeatLocation;
293 printf(" error %i ", (int)(eventTime - getPhase(currentBestPhase, globalTimeOffset)));
294 p.phaseShiftApplied = recentPhaseShift;//this is what we did in the recent step
295 p.tempoShiftApplied = recentTempoShift;
296 //so is the shift by which current stored matrix and indices differ from the last path
297
298 p.eventTimeObserved = eventTime;
299 p.costMatrix = logProbabilityMatrix;
300
301 if (pathHistory.size() > 0){
302 //then can calculate inter-onset interval
303 int ioi = eventTime - pathHistory[pathHistory.size()-1].eventTimeObserved;
304 basicInterOnsetInterval.push_back(ioi);
305 printf("ioi for time %i is %i\n", eventTime, ioi);
306 }
307 printf("New PATH offset is %i\n", p.phaseOffset);
308 pathHistory.push_back(p);
309
310 //prints the complete cost matrix at every step
311 //printCostMatrix(p.currentRoute);
312
313
314 if (printHistory)
315 printPathHistory();
316
317 // printf(", SHIFT (%i, %i)\n", p.phaseShiftApplied, p.tempoShiftApplied);
318
319 //loaded the new prob matrix to the main log one
320 //now looking to shift
321
322 //change global offset to match new position
323 // printLogMatrix();
324
325
326 previousLogMatrix = logProbabilityMatrix;//store previous matrix - settings are in pathHistory vector
327
328 // printLogMatrix();
329
330 }
331
332
333
334 void TimingAnalyser::printIOIdata(){
335 for (int i = 0;i < basicInterOnsetInterval.size();i++){
336 printf("IOI[%i] : %i\n", i, basicInterOnsetInterval[i]);
337 }
338 }
339
340
341 void TimingAnalyser::checkShiftMatrix(){
342
343 if (moveMatrixToOptimalCostPosition){
344 int phaseShiftIndex = (int)(meanPhaseIndex + movementFactor*(currentBestPhase - meanPhaseIndex));
345 int tempoShiftIndex = (int)(meanTempoIndex + movementFactor*(currentBestTempo - meanTempoIndex));
346 printf("current best tempo %i, best phase %i, mean tempo %i, shift of %i\n", currentBestTempo, currentBestPhase, meanTempoIndex, tempoShiftIndex);
347 shiftMatrixToMatchBestPhase(phaseShiftIndex);
348 shiftMatrixToMatchBestTempo(tempoShiftIndex);
349 }
350
351 }
352
353
354
355 void TimingAnalyser::shiftMatrixToMatchBestPhase(const int& bestPhaseIndex){
356
357 // int bestPhaseIndex = pathHistory[pathHistory.size()-1].bestPhase;
358 printf("SHIFT:: BEST PHASE %i, ", bestPhaseIndex);
359
360 int shift = bestPhaseIndex - meanPhaseIndex;
361 DoubleMatrix tmpMat = logProbabilityMatrix;
362
363 for (int t = 0;t < tempoRange;t++){
364 for (int p = 0;p < phaseRange;p++){
365 int switchIndex = p + shift;
366 if (switchIndex < phaseRange && switchIndex >= 0){
367 logProbabilityMatrix[t][p] = tmpMat[t][switchIndex];
368 }else{
369 logProbabilityMatrix[t][p] = INFINITY;
370 }
371 }
372 }
373 globalTimeOffset += shift * phaseScalar;
374 printf("new phase shift of %i and offset is %i \n", shift, globalTimeOffset);
375 recentPhaseShift = shift;
376 }
377
378 void TimingAnalyser::shiftMatrixToMatchBestTempo(const int& bestTempoIndex){
379
380 // int bestPhaseIndex = pathHistory[pathHistory.size()-1].bestPhase;
381 printf("SHIFT:: BEST TEMPO %i, ", bestTempoIndex);
382
383 int shift = bestTempoIndex - meanTempoIndex;
384 DoubleMatrix tmpMat = logProbabilityMatrix;
385
386 for (int t = 0;t < tempoRange;t++){
387 for (int p = 0;p < phaseRange;p++){
388 int switchIndex = t + shift;
389 if (switchIndex < tempoRange && switchIndex >= 0){
390 logProbabilityMatrix[t][p] = tmpMat[switchIndex][p];
391 }else{
392 logProbabilityMatrix[t][p] = INFINITY;
393 }
394 }
395 }
396
397 tempoMinimumOffset += shift * tempoScalar;
398 printf("new tempo shift of %i and tempo offset is %i \n", shift, tempoMinimumOffset);
399 recentTempoShift = shift;
400 }
401
402
403 bool TimingAnalyser::checkPhaseRange(const int& phaseToCheck){
404
405 if (phaseToCheck < phaseRange && phaseToCheck >= 0)
406 return true;
407 else
408 return false;
409 }
410
411 bool TimingAnalyser::checkTempoRange(const int& tempoToCheck){
412
413 if (tempoToCheck < tempoRange && tempoToCheck >= 0)
414 return true;
415 else
416 return false;
417 }
418
419 double TimingAnalyser::phaseHopCost(const int& phaseHop){
420 //printf("phase hop %i returns %f", phaseHop, phaseCost*abs(phaseHop));
421 return phaseCost*abs(phaseHop);
422 }
423
424 double TimingAnalyser::tempoHopCost(const int& tempoHop){
425 return tempoCost*abs(tempoHop);
426 }
427
428 double TimingAnalyser::getTempo(const int& tempoIndex){
429 return (tempoMinimumOffset + tempoIndex*tempoScalar);
430 }
431
432 double TimingAnalyser::getPhase(const int& phaseIndex){
433 return globalTimeOffset + (phaseIndex*phaseScalar);
434 }
435
436 double TimingAnalyser::getTempo(const int& tempoIndex, const int& tempoOffset){
437 return ((tempoOffset + tempoIndex)*tempoScalar);
438 }
439
440 double TimingAnalyser::getPhase(const int& phaseIndex, const int& phaseOffset){
441 //phase offset for middle of matrix
442 return (phaseOffset + phaseIndex)*phaseScalar;
443 }
444
445 int TimingAnalyser::getTempoIndex(const double& tempo){
446 double index = tempo;
447 index -= tempoMinimumOffset;
448 index /= tempoScalar;
449 return (int)round(index);
450 }
451
452
453 int TimingAnalyser::getPhaseIndex(const double& location){
454 double index = location;
455 index -= globalTimeOffset;
456 index -= phaseMinimumOffset;
457 index /= phaseScalar;
458 return (int)round(index);
459 }
460
461
462 double TimingAnalyser::getMinimumTransitionCost(const int& interval, const int& tempoIndex, const int& phaseIndex){
463
464 double minimumCost = 100000000.0;//very big
465
466 //need to get the location of where we are now
467 int endLocation = 0;//getBeatLocation()
468
469 // int zeroLocation = getBeatLocation();
470 // getLocation(0, 0, beatPosition + interval);
471 int tempo = 0;
472
473 // for (int tempo = 0;tempo < logProbabilityMatrix.size();tempo++){
474 for (int phase = 0;phase < logProbabilityMatrix[tempo].size();phase++){
475
476 //double tempoTransition = getTempoTransitionCost(tempoIndex - tempo);
477 //double phaseTransition = getPhaseTransitionCost(phaseindex - phase);
478
479
480
481 }
482 // }
483
484 return minimumCost;
485 }
486
487
488 double TimingAnalyser::getMaximum(){
489 double maxVal = 1.0;
490 for (int tempo = 0;tempo < logProbabilityMatrix.size();tempo++){
491 for (int phase = 0;phase < logProbabilityMatrix[tempo].size();phase++){
492 if (logProbabilityMatrix[tempo][phase] > maxVal && logProbabilityMatrix[tempo][phase] < INFINITY)
493 maxVal = logProbabilityMatrix[tempo][phase];
494 }
495 }
496 return maxVal;
497 }
498
499 void TimingAnalyser::drawCostMatrix(){
500 double maximum = getMaximum();
501 double width = ofGetWidth() / phaseRange;
502 double height = ofGetHeight() / tempoRange;
503
504 for (int tempo = 0;tempo < logProbabilityMatrix.size();tempo++){
505 for (int phase = 0;phase < logProbabilityMatrix[tempo].size();phase++){
506
507 ofSetColor(0,0, 255.0*(maximum - logProbabilityMatrix[tempo][phase])/maximum );
508 ofRect(phase*width, tempo*height, width, height);
509 ofSetColor(255,255,255);
510
511 string infoStr = "T "+ofToString(getTempo(tempo, tempoMinimumOffset));
512 infoStr += "\nP "+ofToString(phase)+" ("+ofToString(getPhase(phase, globalTimeOffset))+")";
513 infoStr += "\n"+ofToString(logProbabilityMatrix[tempo][phase]);
514
515 // ofDrawBitmapString(infoStr, phase*width + 10, tempo*height + 10);
516 }
517 }
518
519 }
520
521
522 void TimingAnalyser::setBestTempoAndPhase(Path& newPath){
523
524 double bestCost = newPath.currentRoute[currentBestTempo][currentBestPhase].totalCost;// INFINITY;
525 for (int t = 0;t < tempoRange;t++){
526 for (int p = 0;p < phaseRange;p++){
527 if (newPath.currentRoute[t][p].totalCost < bestCost){
528 bestCost = newPath.currentRoute[t][p].totalCost;
529 currentBestPhase = p;
530 currentBestTempo = t;
531 }
532 }
533 }
534
535 newPath.bestTempo = currentBestTempo;// bestTempo;
536 newPath.bestPhase = currentBestPhase;// bestPhase;
537
538 printf("BEST TEMPO %i, PHASE %i :: ", currentBestTempo, currentBestPhase);
539 string tString = "Tempo "+ofToString(getTempo(currentBestTempo, tempoMinimumOffset));
540 tString += ", Phase "+ofToString(getPhase(currentBestPhase, globalTimeOffset));
541 tString += ", BEST COST "+ofToString(bestCost);
542 //tstring += ", error "+ofToString()
543 printf("i.e. %s", tString.c_str());
544
545 }
546
547 void TimingAnalyser::printPathHistory(){
548 /* printf("tempo %i (%3.0f), phase %i (%3.0f)\n", pathHistory[i].bestTempo,
549 getTempo(pathHistory[i].bestTempo, pathHistory[i].tempoOffset),
550 pathHistory[i].bestPhase,
551 getPhase(pathHistory[i].bestPhase, pathHistory[i].globalPhaseOffset)
552 );
553 */
554 int tempo, phase;
555
556 RouteMatrix* tmpRoute;
557 int pathIndex = pathHistory.size()-1;
558 if ( pathHistory.size() > 0){
559 pathHistory[0].phaseShiftApplied = 0;//hack to fix non zero
560 tempo = pathHistory[pathHistory.size()-1].bestTempo;
561 phase = pathHistory[pathHistory.size()-1].bestPhase;
562
563 printf("PATH BEST[%i] tempo %i (%3.0f), phase %i (%3.0f) at COST %3.3f preCOST %3.3f addCOST %3.3f\n",
564 pathIndex,
565 tempo, getTempo(tempo, pathHistory[pathHistory.size()-1].tempoOffset),
566 phase, getPhase(phase, pathHistory[pathHistory.size()-1].phaseOffset),
567 pathHistory[pathHistory.size()-1].currentRoute[tempo][phase].totalCost,
568 pathHistory[pathHistory.size()-1].currentRoute[tempo][phase].previousCost,
569 pathHistory[pathHistory.size()-1].currentRoute[tempo][phase].addedCost
570 );
571 }//end if
572
573
574 while (pathIndex >= 0 && pathIndex < pathHistory.size()){
575 // printf("index %i, history size %i\n", pathIndex, pathHistory.size());
576 tmpRoute = &(pathHistory[pathIndex].currentRoute);
577 int previousTempo = (*tmpRoute)[tempo][phase].previousTempoIndex;
578 int previousPhase = (*tmpRoute)[tempo][phase].previousPhaseIndex;// + pathHistory[pathIndex].phaseShiftApplied;
579 //above was error on 24/12 that created wrong results - it is already included!
580
581 printf("PTH[%i](%i,%i)\total{%3.2f}\t(prevCost %3.2f (%3.2f, %3.2f, %3.2f)\t:: adddedCost %3.2f\t: hop (%i, %i)\tprevious (%i, %i) applied (%i, %i){%i, %i}, ",
582 pathIndex,
583 tempo, phase,
584 (*tmpRoute)[tempo][phase].totalCost,
585 (*tmpRoute)[tempo][phase].previousCost,
586 (*tmpRoute)[tempo][phase].preHopCost,
587 (*tmpRoute)[tempo][phase].tempoHopCost,
588 (*tmpRoute)[tempo][phase].phaseHopCost,
589 (*tmpRoute)[tempo][phase].addedCost,
590 (*tmpRoute)[tempo][phase].tempoHop,
591 (*tmpRoute)[tempo][phase].phaseHop,
592 previousTempo, previousPhase,
593 pathHistory[pathIndex].tempoShiftApplied, pathHistory[pathIndex].phaseShiftApplied,
594 pathHistory[pathIndex].tempoOffset, pathHistory[pathIndex].phaseOffset
595
596 );
597
598 if (pathIndex > 0){
599 printf("Tempo %3.0f Phase %3.0f (PRED %3.0f) @event %i (%3.0f)\n",
600 getTempo(previousTempo, pathHistory[pathIndex-1].tempoOffset),
601 getPhase(previousPhase, pathHistory[pathIndex-1].phaseOffset),
602 getPhase(previousPhase, pathHistory[pathIndex-1].phaseOffset) + getTempo(previousTempo, pathHistory[pathIndex-1].tempoOffset),
603 pathHistory[pathIndex].eventTimeObserved,
604 getPhase(phase, pathHistory[pathIndex].phaseOffset)
605 );
606 }
607
608 tempo = previousTempo;
609 phase = previousPhase;
610
611 pathIndex--;
612 }
613
614
615 }
616
617
618 void TimingAnalyser::processPathHistory(){
619
620 printf("Process path history...\n");
621
622 timingData.clear();
623
624 maximumTempo = 0;
625 minimumTempo = INFINITY;
626
627 minimumPhaseHop = 0;
628 maximumPhaseHop = 0;
629
630 printf("\n");
631 int tempo, phase;
632
633 RouteMatrix* tmpRoute;
634 int pathIndex = pathHistory.size()-1;
635 int observedPhasePoint;
636 int observedTempo, observedEventTime;
637 int storedTempoHop = 0;
638 int storedTempo = 0;
639
640 if ( pathHistory.size() > 0){
641 pathHistory[0].phaseShiftApplied = 0;//hack to fix non zero
642 tempo = pathHistory[pathIndex].bestTempo;
643 phase = pathHistory[pathIndex].bestPhase;
644 observedPhasePoint = getPhase(phase, pathHistory[pathIndex].phaseOffset);
645
646 }
647
648 while (pathIndex >= 0){
649 tmpRoute = &(pathHistory[pathIndex].currentRoute);
650 int previousTempo = (*tmpRoute)[tempo][phase].previousTempoIndex;
651 int previousPhase = (*tmpRoute)[tempo][phase].previousPhaseIndex;
652 IntVector v;
653
654 observedTempo = storedTempo;
655 observedPhasePoint = (int)round(getPhase(phase, pathHistory[pathIndex].phaseOffset));
656 observedEventTime = pathHistory[pathIndex].eventTimeObserved;
657
658 if (observedTempo > maximumTempo)
659 maximumTempo = observedTempo;
660
661 if (observedTempo < minimumTempo && observedTempo > 0)
662 minimumTempo = observedTempo;
663
664 int phaseHop = (*tmpRoute)[tempo][phase].phaseHop;
665
666 if (phaseHop < minimumPhaseHop)
667 minimumPhaseHop = phaseHop;
668
669 if (phaseHop > maximumPhaseHop)
670 maximumPhaseHop = phaseHop;
671
672 v.push_back(observedTempo);//timingdata[][0]
673 v.push_back(observedPhasePoint);//timingdata[][1]
674 v.push_back(observedEventTime);//timingdata[][2]
675 v.push_back(storedTempoHop);
676 v.push_back( (*tmpRoute)[tempo][phase].phaseHop);//timingData[][4]
677 v.push_back(observedEventTime - observedPhasePoint);//timingdata[][5]
678
679 timingData.push_back(v);
680
681
682 printf("Index %i TEMPO %i, PHASE %i, EVENT %i HOP (%i, %i) error %i \n",
683 (int) timingData.size(),
684 observedTempo, observedPhasePoint,
685 observedEventTime, storedTempoHop,
686 (*tmpRoute)[tempo][phase].phaseHop,
687 (observedEventTime - observedPhasePoint));
688 //checking this ned to align tempo, observed phase and observed events
689
690 //N.B. MUST STORE THIS FROM FUTURE POINT
691 storedTempo = (int)round(getTempo(tempo, pathHistory[pathIndex].tempoOffset));
692 storedTempoHop = (*tmpRoute)[tempo][phase].tempoHop;
693
694 tempo = previousTempo;
695 phase = previousPhase;
696
697 pathIndex--;
698 }
699
700 reverse(timingData.begin(), timingData.end());
701
702 clearHistogram();
703
704 for (int i = 0;i < timingData.size();i++){
705 printf("Rev index %i tempo %i, phase hop %i, error %i, beat location %i\n", i,
706 timingData[i][0], timingData[i][4], timingData[i][5], (int) beatPosition[i]);
707
708 int error = (int) timingData[i][4] + timingData[i][5];
709
710 if (abs(error) < 30 && (int)beatPosition[i] - 1 < timingHistogram.size())
711 timingHistogram[(int)beatPosition[i] - 1].push_back(error);
712 }
713
714 printf("Maximum is %f and min %f hopMin %i hopMax %i\n", maximumTempo, minimumTempo, minimumPhaseHop, maximumPhaseHop);
715 minDrawTempo = minimumTempo + minimumPhaseHop - 4;
716 maxDrawTempo = maximumTempo + maximumPhaseHop + 4;
717
718 getHistogramResults();
719
720 }
721
722 void TimingAnalyser::clearHistogram(){
723
724 timingHistogram.clear();
725 IntVector v;
726 for (int i = 0;i < 4;i++){
727 v.clear();
728 timingHistogram.push_back(v);
729 }
730 }
731
732
733 void TimingAnalyser::getHistogramResults(){
734 DoubleVector results;
735 for (int i = 0;i < 4;i++){
736 double total;
737 for (int k = 0; k < timingHistogram[i].size();k++){
738 total += timingHistogram[i][k];
739 }
740 total /= timingHistogram[i].size();
741 results.push_back(total);
742 printf("Mean at beat %i is %.2f\n", i, results[i]);
743 }
744 }
745
746 #pragma mark -drawTempoCurve
747 void TimingAnalyser::drawTempoCurve(){
748 if (timingData.size() > 0){
749 screenWidth = ofGetWidth();
750 screenHeight = ofGetHeight();
751 double height = screenHeight;
752 // int minDrawTempo = minimumTempo + minimumPhaseHop - 4;
753 // int maxDrawTempo = maximumTempo + maximumPhaseHop + 4;
754
755 if (maximumTempo > 0)
756 height /= (maxDrawTempo - minDrawTempo);
757
758 double bpmMaxTempo = msToBpm(minDrawTempo);
759 double bpmMinTempo = msToBpm(maxDrawTempo);
760 double bpmHeight = screenHeight / (bpmMaxTempo - bpmMinTempo);
761
762 //int minHeightPixels = minHeight * height;
763
764 ofBackground(255);
765 double width = ofGetWidth()/numberOfPointsPerPage;
766
767 int lastHeightPoint = getHeightPoint(height * (timingData[0][0] - minDrawTempo) );
768 int newHeightPoint;
769
770 double error;
771 int Xpoint;
772
773 //horizonal lines for tempo
774 ofSetColor(150);
775 if(!drawBPM){
776 int horizontalTempo = minDrawTempo + 20 - minDrawTempo%20;
777 // while (horizontalTempo > minDrawTempo)
778 // horizontalTempo -= 20;
779 while (horizontalTempo < maxDrawTempo){
780 ofSetColor(150);
781 ofLine(0, getHeightPoint(height*(horizontalTempo - minDrawTempo)), screenWidth, getHeightPoint(height*(horizontalTempo - minDrawTempo)));
782 ofSetColor(50);
783 ofDrawBitmapString(ofToString(horizontalTempo), 20, getHeightPoint(height*(horizontalTempo - minDrawTempo)));
784 // timesFont.drawString(ofToString(horizontalTempo), 20, getHeightPoint(height*(horizontalTempo - minDrawTempo)));//FONT
785 horizontalTempo += 20;
786 }//end while
787
788 } else{
789 int resolution = 2;
790
791 if (maxDrawTempo - minDrawTempo > 150)
792 resolution = 4;
793 if (maxDrawTempo - minDrawTempo > 400)
794 resolution = 8;
795
796 int horizontalTempo = (int)bpmMinTempo + resolution - ((int)bpmMinTempo % resolution);//mod ten above min
797 while (horizontalTempo < bpmMaxTempo) {
798 int tmp = getHeightPoint(bpmHeight * (horizontalTempo - bpmMinTempo));
799 ofSetColor(150);
800 ofLine(0, tmp , screenWidth, tmp);
801 ofSetColor(50);
802 ofDrawBitmapString(ofToString(horizontalTempo), 20, tmp+2);
803 std::string number = ofToString(horizontalTempo);
804 // timesFont.drawString(number, 20, tmp+2);//FONT
805 horizontalTempo += resolution;
806 }
807
808 }
809
810 //vertical bars
811 int solidBarEvery = 4;//normally every four beats
812
813 int counter = 0;
814 for (int i = 0;i < timingData.size();i++){
815 ofSetColor(200);
816 bool drawLine = false;
817
818
819 //this useful for drawing on ISMIR paper
820 if (mozartTriplets){
821 //stronger lines on the bars
822 solidBarEvery = 12;//- pollini triplets
823
824 if ((i % solidBarEvery == 0)){// || beatPosition[i] == 0 ){
825 ofSetColor(80);
826 drawLine = true;
827 counter = 0;
828 }
829
830 //normal every three (eighth notes)
831 if (i % 3 == 0 && mozartTriplets){
832 drawLine = true;
833 }
834 }
835
836
837 int beatsPerBar = 4;//set 3 for some beatles tracks
838 int mainBeat = 0;
839
840 if ((int)i % beatsPerBar == mainBeat && !mozartTriplets){
841 //was BeatPosoiton[i] - dodgy
842 drawLine = true;
843 }
844 /*
845 if (i == tempoVariationEndIndex){
846 ofSetColor(200, 0,0);
847 drawLine = true;
848 }
849 */
850
851 if (drawLine){
852 ofLine((i-startPoint) * width, 0, (i-startPoint) * width, ofGetHeight());
853 }
854 }
855
856
857 for (int i = startPoint+1;i < min((int)timingData.size(), startPoint + numberOfPointsPerPage);i++){
858 if (!drawIOIintervals){
859 ofSetColor(0,0,0);
860 Xpoint = (i-startPoint) * width ;
861 if (!drawBPM)
862 newHeightPoint = getHeightPoint(height * (timingData[i][0] - minDrawTempo));
863 else
864 newHeightPoint = getHeightPoint(bpmHeight * (msToBpm(timingData[i][0]) - bpmMinTempo));
865
866 ofLine((i-startPoint-1) * width, lastHeightPoint,Xpoint, newHeightPoint);
867 lastHeightPoint = newHeightPoint;
868
869 error = timingData[i][2] - timingData[i][1];
870 int phaseHop = timingData[i][4];
871
872 if (!blackAndWhite)
873 ofSetColor(255,0,0);
874 else
875 ofSetColor(155);
876
877 if (drawExpressiveTimingData)
878 ofLine(Xpoint, newHeightPoint, Xpoint, newHeightPoint - (phaseHop * height));
879 //line up from tempo to where we observe point
880
881 if (!blackAndWhite)
882 ofSetColor(0,0, 155);
883 else
884 ofSetColor(60);
885
886 if (drawExpressiveTimingData)
887 ofCircle(Xpoint, newHeightPoint - ((phaseHop + error) * height), 2);
888 // ofLine((i-startPoint) * width , screenHeight/2, (i-startPoint) * width , (screenHeight/2)*(1+(timingData[i][3]/60.0)));
889
890 if (!blackAndWhite)
891 ofSetColor(0,0,255);
892 ofLine(((playingIndex)-startPoint) * width, 0, ((playingIndex)-startPoint) * width, screenHeight);
893
894 }//if not draw IOI
895
896 if (drawIOIintervals){
897 ofSetColor(160,160,160);
898 ofLine((i-startPoint-1) * width, getHeightPoint(height * (basicInterOnsetInterval[i-1]- minDrawTempo)),
899 (i-startPoint) * width, getHeightPoint(height * (basicInterOnsetInterval[i]- minDrawTempo)) );
900 }
901 // ofLine((i-startPoint) * width , screenHeight/2, (i-startPoint) * width , (screenHeight/2)*(1+(timingData[i][4]/60.0)));
902
903 }//end for
904
905 }
906
907 }
908
909
910 void TimingAnalyser::widenDrawWindow(){
911 maxDrawTempo += 2;
912 minDrawTempo -= 2;
913 }
914
915
916
917
918 void TimingAnalyser::narrowDrawWindow(){
919 maxDrawTempo -= 2;
920 minDrawTempo += 2;
921 }
922
923 void TimingAnalyser::moveDrawWindowUp(){
924 maxDrawTempo += 2;
925 minDrawTempo += 2;
926 }
927
928 void TimingAnalyser::moveDrawWindowDown(){
929 maxDrawTempo -= 2;
930 minDrawTempo -= 2;
931 }
932
933 int TimingAnalyser::getIndexAtMouseXposition(const int& Xpos){
934 int numberPointsIn = (int)round(Xpos * numberOfPointsPerPage / ofGetWidth());
935
936 printf("number pts %i size %i , start %i no. in from left %i\n",
937 numberOfPointsPerPage, (int)timingData.size(),
938 startPoint, numberPointsIn);
939
940 numberPointsIn += startPoint;
941
942 printf("MOUSE X IS %i == %i\n", Xpos, numberPointsIn);
943
944 return min((int)timingData.size(), numberPointsIn);
945 }
946
947 int TimingAnalyser::getHeightPoint(float f){
948 return (int)round(screenHeight - f);
949 }
950
951
952
953 void TimingAnalyser::printMatrix(DoubleMatrix& logMatrix){
954 for (int t = 0;t < logMatrix.size();t++){
955 for (int p = 0;p < logMatrix[t].size();p++){
956 printf("(%i,%i)= %2.1f ", t, p, logMatrix[t][p]);
957 }
958 printf("\n");
959 }
960
961 }
962
963 void TimingAnalyser::printLogMatrix(){
964 printf("\n");
965 for (int p = 0;p < phaseRange;p++){
966 printf("LOG M [%i] %2.2f, ", p, logProbabilityMatrix[0][p]);
967 }
968 printf("offset %i\n", globalTimeOffset);
969 }
970
971 void TimingAnalyser::exportTimingData(){
972 ofstream ofs(timingDataFilepath.c_str());
973 // ofs << "output timng data size " << (int) timingData.size() << "IOI size " << (int)basicInterOnsetInterval.size() << endl;
974
975 for (int i = 0;i < min((int)timingData.size(), (int) basicInterOnsetInterval.size());i++){
976 for (int k = 0; k < 5;k++){
977 ofs << timingData[i][k] << "\t";
978 }
979 ofs << basicInterOnsetInterval[i] << "\t";
980 ofs << endl;
981 }
982
983 }
984
985 void TimingAnalyser::importTimingData(std::string importFileName){
986 ifstream ifs(importFileName.c_str());
987 printf("IMPORT FILE %s\n", importFileName.c_str());
988 timingData.clear();
989 string value;
990 int count = 0;
991 //Notation n;
992 IntVector v;
993 while ( ifs.good() )
994 {
995 getline ( ifs, value, '\n' ); // read a string until next enter command
996 // cout << "disp " << string( value, 0, value.length()-1 ) << endl; // display value removing the first and the last character from it
997
998 //printf("size of line is %i\n", (int)value.size());
999 v.clear();
1000
1001 if (value.size() > 0){
1002
1003 string::size_type start = value.find_first_not_of(" \t\v");
1004 string part = value.substr(start, string::npos);
1005 string otherPart = value;
1006 printf("input line%i : '%s'\n", count, otherPart.c_str());
1007 size_t found;
1008 found=part.find_first_of("\t\n");
1009 size_t startC = 0;
1010 int r = 0;
1011 while (found!=string::npos){
1012 string section = otherPart.substr(startC, found - startC);
1013 int my_int = atoi(section.c_str());
1014 v.push_back(my_int);
1015 printf("timing[%i][%i] ", count, r, my_int);
1016 printf("section '%s'\n", section.c_str());
1017
1018 startC= otherPart.find_first_not_of("\t\n",found);
1019 found = otherPart.find_first_of("\t\n",found+1);
1020 r++;
1021 }
1022 timingData.push_back(v);
1023 }//end if > 0
1024
1025 count++;
1026
1027 }
1028
1029
1030 basicInterOnsetInterval.clear();
1031
1032 maximumTempo = 0;
1033 minimumTempo = INFINITY;
1034
1035 for (int i = 0;i < max(0, (int)timingData.size()-1);i++){
1036 printf("TEMPO %i %i IOI %i\n", timingData[i][0], timingData[i][1], timingData[i][4]);
1037
1038 basicInterOnsetInterval.push_back(timingData[i][5]);
1039
1040 int observedTempo = timingData[i][0];
1041 // printf("observed %i\n", observedTempo);
1042
1043 if (observedTempo > maximumTempo)
1044 maximumTempo = observedTempo;
1045
1046 if (observedTempo < minimumTempo && observedTempo > 0)
1047 minimumTempo = observedTempo;
1048
1049 }
1050
1051
1052 printf("min t %f, Max t %f\n", minimumTempo, maximumTempo);
1053 }
1054
1055
1056 void TimingAnalyser::exportProcessedBeatTimes(const double& firstBeatTime){
1057 ofstream ofs(processedBeatTimesFilepath.c_str());
1058
1059 for (int i = 0;i < min((int)timingData.size(), (int) basicInterOnsetInterval.size());i++){
1060 double beatTime = (timingData[i][1] + firstBeatTime)/1000.0;
1061 printf("Beat %i : %f\n", i, beatTime);
1062 ofs << beatTime;
1063 ofs << endl;
1064 }
1065 }
1066
1067
1068 double TimingAnalyser::msToBpm(const double& ms){
1069 return 60000./ms;
1070 }
1071
1072
1073 void TimingAnalyser::calculateTempoLimits(){
1074
1075 maximumTempo = 0;
1076 minimumTempo = INFINITY;
1077
1078 for (int i = 0;i < max(0, (int)timingData.size()-1);i++){
1079
1080 int observedTempo = timingData[i][0];
1081
1082 if (observedTempo > maximumTempo)
1083 maximumTempo = observedTempo;
1084
1085 if (observedTempo < minimumTempo && observedTempo > 0)
1086 minimumTempo = observedTempo;
1087
1088 }
1089
1090 tempoVariationStartIndex = min(4, (int)timingData.size());
1091 tempoVariationEndIndex = max(0, (int)timingData.size() - 4);
1092 printf("VARIATION TO BE CALCULATED BETWEEN %i and %i\n", tempoVariationStartIndex, tempoVariationEndIndex);
1093 }
1094
1095 double TimingAnalyser::calculateTempoVariation(){
1096 if (timingData.size() > tempoVariationStartIndex && tempoVariationEndIndex + 1< timingData.size()){
1097 double mean = 0;
1098
1099 for (int i = tempoVariationStartIndex;i <= tempoVariationEndIndex;i++){
1100 mean += timingData[i][0];
1101 }
1102 mean /= (tempoVariationEndIndex - tempoVariationStartIndex);
1103 double variation = 0;
1104 double fluctuation = 0;
1105 int lastValue = timingData[tempoVariationStartIndex][0];
1106
1107 for (int i = tempoVariationStartIndex;i <= tempoVariationEndIndex;i++){
1108 variation += (timingData[i][0] - mean)*(timingData[i][0] - mean);
1109 fluctuation += abs(timingData[i][0] - lastValue);
1110 lastValue = timingData[i][0];
1111 }
1112 variation /= (tempoVariationEndIndex - tempoVariationStartIndex);
1113 variation = sqrt(variation);
1114 fluctuation /= (tempoVariationEndIndex - tempoVariationStartIndex);
1115
1116 double slope = (double) timingData[tempoVariationStartIndex][0] / timingData[tempoVariationEndIndex][0];
1117 printf("TEMPO STATS:\nMean: %.2f\nVariance %.2f \nSlope %.1f per cent\nAverage delta Tempo %.2f",
1118 mean, variation, (slope*100.0), fluctuation);
1119 }
1120
1121 }
1122
1123
1124 void TimingAnalyser::zoomIn(){
1125 numberOfPointsPerPage /= 2;
1126 }
1127
1128
1129 void TimingAnalyser::zoomOut(){
1130 numberOfPointsPerPage *= 2;
1131 }
1132
1133
1134 /*
1135 double TimingAnalyser::getCost(const int& eventTime, const int& eventLocation, const int& tempoIndex, const int& phaseIndex){
1136
1137 double interval = eventLocation - lastBeatPosition;
1138 double newLocation = getPhase(phaseIndex) + interval*getTempo(tempoIndex);
1139 double phaseCost = fabs(newLocation - eventTime);
1140
1141 return phaseCost;
1142 }
1143 */