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