comparison hackday/midiEventHolder.cpp @ 24:5a11b19906c7

hackday code is added.
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Sat, 03 Dec 2011 17:19:43 +0000
parents
children 2a025ea7c793
comparison
equal deleted inserted replaced
23:032edf186a68 24:5a11b19906c7
1 /*
2 * midiEventHolder.cpp
3 * midiCannamReader3
4 *
5 * Created by Andrew on 19/07/2011.
6 * Copyright 2011 QMUL. All rights reserved.
7 *
8 */
9
10 #include "midiEventHolder.h"
11
12 midiEventHolder::midiEventHolder(){
13 // recordedNoteOnIndex = 0;
14
15 useTempoPrior = false;//puts sine wave round tempo
16 confidenceWeightingUsed = true;
17
18 //there is option to use MAP estinate or integral in beayesianarraystricture class
19
20 runningInRealTime = true;
21 bayesStruct.realTimeMode = &runningInRealTime;
22
23 minimumMatchSpeed = 0.0;
24 maximumMatchSpeed = 2.0;
25 minimumTimeIntervalForTempoUpdate = 150;
26
27 width = ofGetWidth();
28 height = ofGetHeight();
29 screenWidth= &width;
30 screenHeight = &height;
31
32 ticksPerScreen = 4000;
33 tickLocation = 0;
34 pulsesPerQuarternote = 240;
35 noteArrayIndex = 0;
36 noteMinimum = 30;
37 noteMaximum = 96;
38
39
40
41 likelihoodWidth = 100;//using 100 is good
42 likelihoodToNoiseRatio = 0.08;//was 0.02 on 18/11/11, changing to give more weight to observations
43
44 bayesStruct.speedLikelihoodNoise = 0.1;//was 0.05
45 bayesStruct.speedDecayWidth = 20;
46 bayesStruct.speedDecayAmount = 10;
47
48
49
50 speedPriorValue = 1.0;
51
52 matchWindowWidth = 12000;//window size for matching in ms
53
54 bayesStruct.resetSize(matchWindowWidth);
55 bayesStruct.setPositionDistributionScalar(1);
56
57 bayesStruct.resetSpeedSize(200);
58 bayesStruct.setRelativeSpeedScalar(0.01);
59 bayesStruct.relativeSpeedPrior.getMaximum();
60 //bayesStruct.simpleExample();
61
62
63 speedWindowWidthMillis = 4000;
64 speedPriorValue = 1.0;
65 noteHeight = (*screenHeight) / (float)(noteMaximum - noteMinimum);
66
67
68
69 drawPhaseMode = true;
70
71 printf("lookup index %f value %f\n", bayesStruct.prior.getLookupIndex(100, 30., 10.0), bayesStruct.prior.gaussianLookupTable[(int)bayesStruct.prior.getLookupIndex(100, 30., 10.0)]);
72 }
73
74
75
76 void midiEventHolder::reset(){
77 //called when we start playing
78
79 noteArrayIndex = 0;
80 tickLocation = 0;
81 lastPeriodUpdateTime = getTimeNow(0);//ofGetElapsedTimeMillis();
82 bayesStruct.lastEventTime = getTimeNow(0);//ofGetElapsedTimeMillis();
83 numberOfScreensIn = 0;
84 // recordedNoteOnIndex = 0;
85 bayesStruct.setNewDistributionOffsets(0);
86 bayesStruct.posterior.offset = 0;
87
88 playedEventTimes.clear();
89 playedNoteOnMatrix.clear();
90 matchMatrix.clear();
91 bestMatchIndex = 0;
92
93 bayesStruct.resetSpeedToOne();
94 bayesStruct.setSpeedPrior(speedPriorValue);
95 setMatchedNotesBackToFalse();
96 }
97
98 void midiEventHolder::setMatchedNotesBackToFalse(){
99 for (int i = 0;i < noteOnMatches.size();i++)
100 noteOnMatches[i] = false;
101 }
102
103 void midiEventHolder::clearAllEvents(){
104 recordedNoteOnMatrix.clear();
105 matchesFound.clear();
106 noteOnMatches.clear();
107 recordedEventTimes.clear();
108
109 //played events:
110 playedEventTimes.clear();
111 playedNoteOnMatrix.clear();
112 matchMatrix.clear();
113 bestMatchFound.clear();
114 }
115
116 void midiEventHolder::printNotes(){
117 printf("RECORDED MATRIX\n");
118 for (int i = 0;i < recordedNoteOnMatrix.size();i++){
119 printf("ticktime %i :: pitch %i @ millis %f\n", recordedNoteOnMatrix[i][0], recordedNoteOnMatrix[i][1], recordedEventTimes[i]);
120 }
121 }
122
123
124 double midiEventHolder::getEventTimeTicks(double millis){
125 return (millis * pulsesPerQuarternote / period);
126 }
127
128 double midiEventHolder::getEventTimeMillis(double ticks){
129 return (period * ticks / (double) pulsesPerQuarternote);
130 }
131
132 void midiEventHolder::newNoteOnEvent(int pitch, int velocity, double timePlayed){
133 // tempoSpeedString = "";
134
135 //MOVE INTO BAYESSTRUCT?? XXX
136 //bayesStruct.copyPriorToPosterior();
137 //why was this here??
138 bayesStruct.prior.copyFromDynamicVector(bayesStruct.posterior);//try the otehr way
139 //bayesStruct.copyPriorToPosterior();
140 //need to get new MAP position and set the offset of the arrays
141 //currently bestEstimate is the approx for the new MAP position
142
143
144 //add the new event to our played information matrix
145 IntVector v;
146 v.push_back(pitch);
147 v.push_back(velocity);
148 playedNoteOnMatrix.push_back(v);
149
150
151 //would update the arrays at this point to show where out current location (phase) and tempo is.
152 // double timeNow = ofGetElapsedTimeMillis() - startTime;
153 double timeNow = timePlayed;// - startTime;
154 recentNoteOnTime = timePlayed;
155
156 // printf("Max time %f OF time %f \n", timePlayed, timeNow);
157
158 playedEventTimes.push_back(timePlayed);
159
160 // double timeDifference = ofGetElapsedTimeMillis() - bayesStruct.lastEventTime;
161 double timeDifference = timePlayed - bayesStruct.lastEventTime;
162
163
164
165 //printf("note %i played at %f and last event %f time difference %f and current best estmate %f\n", pitch, timePlayed, bayesStruct.lastEventTime, timeDifference, bayesStruct.bestEstimate);
166
167 //addnoise to the tempo distribution
168 //bayesStruct.decaySpeedDistribution(timeDifference);
169 if (timeDifference > 50){
170 bayesStruct.addGaussianNoiseToSpeedPosterior(timeDifference * 10 / 100.);
171 // bayesStruct.addTriangularNoiseToSpeedPosterior(timeDifference * 10 / 100.);
172 }
173
174 bayesStruct.updateTmpBestEstimate(timeDifference);// debug - didnt work bayesStruct.bestEstimate = bayesStruct.tmpBestEstimate;
175 bayesStruct.updateBestEstimate(timeDifference);
176 bayesStruct.lastBestEstimateUpdateTime = getTimeNow(timePlayed);
177
178 // double newMAPestimateTime = bayesStruct.posterior.getIndexInRealTerms(bayesStruct.posterior.MAPestimate);
179 //was offset + bayesStruct.posterior.MAPestimate; but this doesnt include scalar to convert to millis
180
181 timeString = "Pitch:"+ofToString(pitch);
182 timeString += ", time now:"+ofToString(timeNow, 1);
183 timeString += " TD "+ofToString(timeDifference, 1);
184 timeString += " offset "+ofToString(bayesStruct.posterior.offset , 0);
185 timeString += " map Est: "+ofToString(bayesStruct.posterior.MAPestimate, 0);
186 // timeString += " Previous time" + ofToString(newMAPestimateTime,0);
187 timeString += " speedMap "+ofToString(bayesStruct.relativeSpeedPosterior.integratedEstimate, 2);
188 timeString += " :: "+ofToString(bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.integratedEstimate), 2);
189
190 // newMAPestimateTime += (timeDifference * bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate));
191 // timeString += " : Predicted MAP time" + ofToString(newMAPestimateTime,0);
192
193 //then we recalculate the window start based on MAP being central
194 //then we do the matches on these and the likelihood on these.
195
196 bayesStruct.setNewDistributionOffsets(max(0., bayesStruct.bestEstimate - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2)));
197 // bayesStruct.prior.offset = max(0.,newMAPestimateTime - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2));
198
199 timeString += " \n : new offset " + ofToString(bayesStruct.prior.offset , 0);
200 timeString += " \n best estimate "+ofToString(bayesStruct.bestEstimate, 1);
201 timeString += " error "+ofToString(minimumMatchError, 0);
202 timeString += " map "+ofToString(bayesStruct.relativeSpeedPosterior.integratedEstimate, 1);
203 timeString += " rel speed "+ofToString(bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.integratedEstimate), 2);
204
205
206 //be able to draw the prior in correct location relative to the midi notes
207 //this calculates the cross update of all possible speeds and all possible positions
208 bayesStruct.crossUpdateArrays(bayesStruct.posterior, bayesStruct.relativeSpeedPosterior, timeDifference);
209
210
211 timeString += " new OFF "+ofToString(bayesStruct.bestEstimate - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2), 1);
212 timeString += " notearrayindex "+ofToString(noteArrayIndex, 0);
213 //when this is off teh screen there is a problem somehow XXX
214
215 bayesStruct.posterior.offset = max(0., bayesStruct.bestEstimate - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2));// bayesStruct.prior.offset = max(0., bayesStruct.bestEstimate - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2));
216
217 //trying to switch to prior
218
219
220 bayesStruct.lastEventTime = timePlayed;//bayesStruct.lastEventTime = ofGetElapsedTimeMillis();
221
222 //do the cross update to find current posterior for location
223 // totalConfidence= 0;
224 int numberOfMatchesFound = findLocalMatches(pitch);
225 setMatchLikelihoods(numberOfMatchesFound);
226 bayesStruct.calculatePosterior();
227
228 //having found matches we have matches for new note and matches for previous notes
229 if (!confidenceWeightingUsed)
230 findLocalTempoPairs();
231 else
232 findLocalTempoPairsWeightedForConfidence();
233
234 //bayesStruct.addGaussianNoiseToSpeedPosterior(10);
235
236 }
237
238 double midiEventHolder::getTimeNow(double eventTime){
239 double timeNow = eventTime;
240 if (runningInRealTime)
241 timeNow = ofGetElapsedTimeMillis();
242 return timeNow;
243 }
244
245 int midiEventHolder::findLocalMatches(int notePitch){
246
247 //here we find the matches to the new note within appropriate range
248
249 matchString = "";
250
251 windowStartTime = max(0.0,(bayesStruct.bestEstimate - matchWindowWidth/2));//was playPositionInMillis
252 // cout << "best estimate is " << bayesStruct.bestEstimate << endl;
253 int numberOfMatches = findMatch(notePitch, windowStartTime, windowStartTime + matchWindowWidth);
254
255
256 //matchString += " pitch: "+ofToString(notePitch)+" matches "+ofToString(numberOfMatches)+" win start "+ofToString(windowStartTime);
257
258 return numberOfMatches;
259
260
261 }
262
263
264 void midiEventHolder::setMatchLikelihoods(int numberOfMatches){
265 //reset the offset to match the prior
266 bayesStruct.likelihood.offset = bayesStruct.prior.offset;
267 bayesStruct.likelihood.zero();//set to zero
268
269 double quantity = likelihoodToNoiseRatio / numberOfMatches;
270
271 for (int i = 0;i < numberOfMatches && matchesFound[i] >= 0 && matchesFound[i] < recordedEventTimes.size();i++){
272 // printf("match times %i of %i::%f adding likelihood to %f\n", i, numberOfMatches, recordedEventTimes[matchesFound[i]], recordedEventTimes[matchesFound[i]] - bayesStruct.likelihood.offset);
273 //this is the vent time since start of file
274 if (recordedEventTimes[matchesFound[i]] - bayesStruct.likelihood.offset < bayesStruct.likelihood.arraySize){
275 // double confidenceMeasure = 0;
276 // if (totalConfidence > 0)
277 // confidenceMeasure = bayesStruct.posterior.getValueAtMillis(recordedEventTimes[matchesFound[i]])/totalConfidence;
278
279 bayesStruct.likelihood.addGaussianShape(recordedEventTimes[matchesFound[i]] - bayesStruct.likelihood.offset, likelihoodWidth, quantity);//* confidenceMeasure
280 }//end if
281 }
282 bayesStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesStruct.likelihood.length);
283 }
284
285 int midiEventHolder::findMatch(const int& notePitch, const int& startTime, const int& endTime){
286
287 matchesFound.clear();
288 int startIndex = 0;
289
290 if (recordedEventTimes.size() > 0){
291
292 //get to the right range of events to check in
293 while (startIndex < recordedEventTimes.size() && recordedEventTimes[startIndex] < startTime)
294 startIndex++;
295
296 }
297
298 IntVector v;
299 DoubleVector d;
300 double tmpError = 100000.;//v high error
301
302 double minimumConfidence = 0;
303 while (startIndex < recordedEventTimes.size() && recordedEventTimes[startIndex] < endTime){
304 if (recordedNoteOnMatrix[startIndex][1] == notePitch){
305
306 matchesFound.push_back(startIndex);
307 v.push_back(startIndex);
308 //so startIndex is registered as a match
309
310 double eventConfidence = bayesStruct.posterior.getValueAtMillis(recordedEventTimes[startIndex]);
311 if (eventConfidence > minimumConfidence){
312 minimumConfidence = eventConfidence;
313 bestMatchIndex = startIndex;
314 }
315 d.push_back(eventConfidence);
316
317 double confidence = eventConfidence;//bayesStruct.posterior.getValueAtMillis(mouseX);
318 // recordedEventTimes[startIndex]);
319 // matchString += "["+ofToString(startIndex)+"] = "+ofToString(confidence, 3)+" .";
320
321 if (abs(recordedEventTimes[startIndex] - bayesStruct.bestEstimate) < tmpError){
322 //record the error between expected and observed times
323 tmpError = abs(recordedEventTimes[startIndex] - bayesStruct.bestEstimate);
324 minimumMatchError = tmpError;//recordedEventTimes[startIndex] - bayesStruct.bestEstimate;
325 }
326
327 }
328 startIndex++;
329 }
330
331
332 // printf("%i MATCHES TO Note %i found\n", (int)matchesFound.size(), notePitch);
333 int size = matchesFound.size();
334 if (size > 0)
335 noteOnMatches[bestMatchIndex] = true;
336
337 v.insert(v.begin() , (int)size);//at beginning, we list how many matches there are that we have found
338 d.insert(d.begin() , (double)size);
339
340 //v.push_back(size);
341 //d.push_back(size);
342 //for (int i = 0;i < matchesFound.size()+1;i++){
343 // v.push_back(matchesFound[i]);
344 // printf("match %i,[%i] is %i\n", startIndex, i, v[i]);
345 //}
346
347
348 matchMatrix.push_back(v);
349 matchConfidence.push_back(d);
350
351 //bringing in way to list only the best matches and use these in tempo process
352 bestMatchFound.push_back(bestMatchIndex);
353
354 // printf("BEST MATCH TO note %i, start time %i, endtime %i, time %i is recorded time %i, confidence %0.2f\n", notePitch, startTime, endTime, (int) recordedEventTimes[bestMatchIndex], minimumConfidence);
355
356 return size;
357 }
358
359 bool midiEventHolder::checkIfMatchedNote(const int& tmpIndex){
360 for (int i = 0;i < matchesFound.size();i++){
361 if (matchesFound[i] == tmpIndex)
362 return true;
363 }
364 return false;
365 }
366
367
368
369 void midiEventHolder::findLocalTempoPairs(){
370
371 int currentPlayedIndex = playedNoteOnMatrix.size()-1;
372 // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]);
373 // printMatchesFound();
374 // printMatchMatrix();
375 // printf("possible notes \n");
376 bool needToUpdate = false;
377 bayesStruct.setLikelihoodToConstant();
378
379
380 for (int i = 0;i < matchMatrix[currentPlayedIndex][0];i++){
381 //iterate through the recently matched events - even dodgy matches included
382 //size, index of match0, index of match1, ....
383
384
385 int recordedCurrentIndex = matchMatrix[currentPlayedIndex][i+1];
386
387 int previousIndex = currentPlayedIndex-1;
388
389
390 while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) {
391 double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex];
392
393 for (int k = 0;k < matchMatrix[previousIndex][0];k++){
394
395 int recordedPreviousIndex = matchMatrix[previousIndex][k+1];
396
397 double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex];
398
399
400 //we want the speed of the recording relative to that of the playing live
401
402 double speedRatio = recordedTimeDifference / playedTimeDifference;
403 if (recordedTimeDifference > minimumTimeIntervalForTempoUpdate &&
404 speedRatio <= maximumMatchSpeed && speedRatio >= minimumMatchSpeed){
405
406 //adding in a prior that prefers 1
407 double priorWeighting = 1;
408 if (useTempoPrior)
409 priorWeighting = sin(speedRatio * PI/2);
410
411
412
413 /*
414 printf("(%i)", matchMatrix[currentPlayedIndex][i+1]);
415 printf("[%i] :: ", recordedPreviousIndex);
416 printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference);
417 printf("update on speed ratio %f\n", speedRatio);
418 */
419 // matchString += " speed: "+ofToString(speedRatio, 3);
420 // commented for debug
421
422 //bayesStruct.updateTempoDistribution(speedRatio, 0.1);//second paramter is confidence in the match
423 double amount = (1-bayesStruct.speedLikelihoodNoise)/10;
424 amount *= priorWeighting;
425 bayesStruct.updateTempoLikelihood(speedRatio, amount);
426 // tempoSpeedString += ofToString(recordedPreviousIndex) + " "+ ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n";
427 needToUpdate = true;
428 }
429 // printf("\n");
430 }
431
432 previousIndex--;
433 }//end while previousindex countdown
434 }//end for loop through possible current matches
435
436 if (needToUpdate)
437 bayesStruct.updateTempoDistribution();
438
439 //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate));
440 }
441
442
443 void midiEventHolder::findLocalTempoPairsWeightedForConfidence(){
444 bool needToUpdate = false;
445
446 DoubleVector speedIntervalsFound;
447
448 //adapted this to just use the best match for each note
449
450 int currentPlayedIndex = playedNoteOnMatrix.size()-1;
451 // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]);
452 // printMatchesFound();
453 // printMatchMatrix();
454 // printf("possible notes \n");
455
456 bayesStruct.setLikelihoodToConstant();
457
458 int recordedCurrentIndex = bestMatchFound[currentPlayedIndex];
459 //we only look at intervals between the current best match and other recent best matched notes
460 //that is the difference in confidence method
461
462
463 //printf("BEST MATCH FOUND for index %i is %i ", currentPlayedIndex, bestMatchFound[currentPlayedIndex]);
464
465 int previousIndex = currentPlayedIndex-1;
466
467 //withing speedwindow i.e. 4 seconds
468 while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) {
469 double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex];
470
471 int recordedPreviousIndex = bestMatchFound[previousIndex];
472
473 double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex];
474
475
476 //we want the speed of the recording relative to that of the playing live
477
478 double speedRatio = recordedTimeDifference / playedTimeDifference;
479 if (recordedTimeDifference > minimumTimeIntervalForTempoUpdate
480 && speedRatio < maximumMatchSpeed && speedRatio > minimumMatchSpeed){
481
482 /* printf("(%i)", previousIndex);
483 printf("[%i] :: ", recordedPreviousIndex);
484 // printf(" conf %f & %f ", currentMatchConfidence, previousMatchConfidence);
485 printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference);
486 printf("update on speed ratio %f\n", speedRatio);
487 */
488 // matchString += " speed: "+ofToString(speedRatio, 3);
489 // commented for debug
490
491
492 double priorWeighting = 1;
493
494 if (useTempoPrior)
495 priorWeighting = sin(speedRatio * PI/2);//adding in a prior that prefers 1.0 speed
496
497
498 // double weighting = previousMatchConfidence * currentMatchConfidence ;
499 double amount = (1-bayesStruct.speedLikelihoodNoise)*priorWeighting/16;//was 9
500
501 speedIntervalsFound.push_back(speedRatio);
502 // bayesStruct.updateTempoLikelihood(speedRatio, amount);//second paramter is confidence in the match
503
504 // tempoSpeedString += ofToString(recordedCurrentIndex) + " :: " + ofToString(recordedPreviousIndex);
505 // tempoSpeedString += " " + ofToString(recordedTimeDifference)+ " " + ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n";
506
507 needToUpdate = true;
508 }
509 // printf("\n");
510
511
512 previousIndex--;
513 }//end while previousindex countdown
514
515 if (speedIntervalsFound.size() > 0){
516 double amount = (1 - bayesStruct.speedLikelihoodNoise) / speedIntervalsFound.size();
517 for (int i = 0;i < speedIntervalsFound.size();i++)
518 bayesStruct.updateTempoLikelihood(speedIntervalsFound[i], amount);
519 }
520
521
522 if (needToUpdate)
523 bayesStruct.updateTempoDistribution();
524 //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate));
525 }
526
527 /*
528 void midiEventHolder::findLocalTempoPairsWeightedForConfidence(){
529 bool needToUpdate = false;
530
531 //adapted this to just use the best match for each note
532
533 int currentPlayedIndex = playedNoteOnMatrix.size()-1;
534 // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]);
535 // printMatchesFound();
536 // printMatchMatrix();
537 // printf("possible notes \n");
538
539 bayesStruct.setLikelihoodToConstant();
540
541 for (int i = 0;i < matchMatrix[currentPlayedIndex][0];i++){
542
543 //iterate through the recently matched events - even dodgy matches included
544 //size, index of match0, index of match1, ....
545 int recordedCurrentIndex = matchMatrix[currentPlayedIndex][i+1];
546
547 double currentMatchConfidence = matchConfidence[currentPlayedIndex][i+1];//new confidence
548
549 int previousIndex = currentPlayedIndex-1;
550
551 while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) {
552 double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex];
553
554 // for (int k = 0;k < matchMatrix[previousIndex][0];k++)
555 int recordedPreviousIndex = bestMatchFound[previousIndex];//matchMatrix[previousIndex][k+1];
556
557 //double previousMatchConfidence = matchConfidence[previousIndex][k+1];
558
559
560 double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex];
561
562
563 //we want the speed of the recording relative to that of the playing live
564
565 double speedRatio = recordedTimeDifference / playedTimeDifference;
566 if (speedRatio <= maximumMatchSpeed && speedRatio >= minimumMatchSpeed){
567
568 printf("(%i)", matchMatrix[currentPlayedIndex][i+1]);
569 printf("[%i] :: ", recordedPreviousIndex);
570 // printf(" conf %f & %f ", currentMatchConfidence, previousMatchConfidence);
571 printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference);
572 printf("update on speed ratio %f\n", speedRatio);
573
574 // matchString += " speed: "+ofToString(speedRatio, 3);
575 // commented for debug
576
577
578 double priorWeighting = 1;
579
580 if (useTempoPrior)
581 priorWeighting = sin(speedRatio * PI/2);//adding in a prior that prefers 1.0 speed
582
583
584 // double weighting = previousMatchConfidence * currentMatchConfidence ;
585 double amount = (1-bayesStruct.speedLikelihoodNoise)*priorWeighting/10;
586 bayesStruct.updateTempoLikelihood(speedRatio, amount);//second paramter is confidence in the match
587 tempoSpeedString += ofToString(recordedPreviousIndex) + " " + ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n";
588
589 needToUpdate = true;
590 }
591 // printf("\n");
592
593
594 previousIndex--;
595 }//end while previousindex countdown
596 }//end for loop through possible current matches
597
598 if (needToUpdate)
599 bayesStruct.updateTempoDistribution();
600 //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate));
601 }
602 */
603
604
605 void midiEventHolder::updatePlayPosition(){
606
607 //in actual fact if we are changing the speed of the play position
608 //we will need to update this via the file
609
610 //actually time since beginning of file i think
611
612 double timeDifference = 0;
613 if (runningInRealTime)
614 timeDifference = ofGetElapsedTimeMillis() - lastPeriodUpdateTime;//elpased - lastperiodupdatetime
615
616 //this is time diff in milliseconds
617 //then we have
618 double quarterNoteIntervals = (timeDifference / period);
619 tickLocation = quarterNoteIntervals * pulsesPerQuarternote;
620
621 playPositionInMillis = timeDifference;//based on updating from when we change period
622 //this to be added
623
624 if (runningInRealTime)
625 bayesStruct.updateBestEstimate(timeDifference);
626
627 }
628
629
630 void midiEventHolder::drawMidiFile(){
631
632 //draws midi file on scrolling screen
633 int size = recordedNoteOnMatrix.size();
634 if (size > 0){
635
636 numberOfScreensIn = floor(bayesStruct.bestEstimate / getEventTimeMillis(ticksPerScreen));//rpounds down on no screens in
637
638 // numberOfScreensIn = tickLocation / ticksPerScreen;//rounds down
639 timeOffsetForScreen = getEventTimeMillis(numberOfScreensIn * ticksPerScreen);
640
641 while (noteArrayIndex < recordedNoteOnMatrix.size()-1 && tickLocation > recordedNoteOnMatrix[noteArrayIndex][0] )
642 noteArrayIndex++;
643
644
645 while (noteArrayIndex > 0 && noteArrayIndex < size && tickLocation < recordedNoteOnMatrix[noteArrayIndex][0])
646 noteArrayIndex--;
647
648 //need to start where we currently are in file
649 int maxNoteIndexToPrint = noteArrayIndex;
650 int minNoteIndexToPrint = min(size-1,noteArrayIndex);//not needed as changed above
651
652 while (maxNoteIndexToPrint < recordedNoteOnMatrix.size() && recordedNoteOnMatrix[maxNoteIndexToPrint][0] < (numberOfScreensIn+1)*ticksPerScreen )
653 maxNoteIndexToPrint++;
654
655 while (minNoteIndexToPrint > 0 && recordedNoteOnMatrix[minNoteIndexToPrint][0] > numberOfScreensIn*ticksPerScreen)//&& minNoteIndexToPrint < size
656 minNoteIndexToPrint--;
657
658 for (int tmpIndex = max(0,minNoteIndexToPrint);tmpIndex < min(maxNoteIndexToPrint, (int)recordedNoteOnMatrix.size());tmpIndex++){
659
660 ofSetColor(255,255,255);
661 if (checkIfMatchedNote(tmpIndex))
662 ofSetColor(100,100,100);//0,0,255);
663 else if(noteOnMatches[tmpIndex]){
664 ofSetColor(255,0,255);//dark grey
665 }
666 else{
667 ofSetColor(255,255,255);//255,255,255);
668 }
669
670 //ofSetColor(255,255,255);
671 if (tmpIndex == bestMatchIndex)
672 ofSetColor(255,0,0);//best recent match is in red
673
674 // XXX replace ofgetwidth below
675 //if (tmpIndex >= 0 && tmpIndex < size)
676 int xLocation = (float)(recordedNoteOnMatrix[tmpIndex][0] - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen;
677 int duration = (float)(recordedNoteOnMatrix[tmpIndex][3]*(*screenWidth))/(float)ticksPerScreen;
678
679
680 int yLocation = (*screenHeight) - ((recordedNoteOnMatrix[tmpIndex][1] - noteMinimum )*(*screenHeight)/ (float)(noteMaximum - noteMinimum));
681 ofRect(xLocation,yLocation, duration, noteHeight);
682
683 }
684
685
686 int xLocation;// = getLocationFromTicks(tickLocation);
687 // ofLine(xLocation, 0, xLocation, (*screenHeight));
688
689 //orange line at best estimate
690 xLocation = getLocationFromMillis(bayesStruct.bestEstimate);
691 ofSetColor(80,80,80);//250,100,0);
692 ofLine(xLocation, 0, xLocation, (*screenHeight));
693
694 xLocation = getLocationFromMillis(bayesStruct.tmpBestEstimate);
695 ofSetColor(150,150,150);//250,100,0);
696 ofLine(xLocation, 0, xLocation, (*screenHeight));
697
698
699 //lines where matching window start and end are
700 ofSetColor(0);//0,100,255);
701 xLocation = getLocationFromMillis(windowStartTime);
702 ofLine(xLocation, 0, xLocation, (*screenHeight));
703 xLocation = getLocationFromMillis(windowStartTime+matchWindowWidth);
704 ofLine(xLocation, 0, xLocation, (*screenHeight));
705
706
707 int maxSize = recordedNoteOnMatrix[size-1][0];
708
709 // ofDrawBitmapString(tempoSpeedString, 20, 20);
710 /* string indexString = "num screens in "+ofToString(numberOfScreensIn)+"; min index to print "+ofToString(minNoteIndexToPrint)+", max index to print "+ofToString(maxNoteIndexToPrint);
711 indexString += " size "+ofToString(size)+" tick loc "+ofToString(tickLocation)+" max size "+ofToString(maxSize);
712 ofDrawBitmapString(indexString, 20, 40);
713 */
714 }
715
716 //ofDrawBitmapString(ofToString(timeOffsetForScreen, 1), 20,20);
717
718 //ofDrawBitmapString(timeString, 20, 60);
719
720
721 }
722
723
724
725 void midiEventHolder::drawMidiFile(IntMatrix& midiFileToDraw){
726
727 //draws midi file on scrolling screen
728 int size = midiFileToDraw.size();
729 if (size > 0){
730
731 numberOfScreensIn = floor(bayesStruct.bestEstimate / getEventTimeMillis(ticksPerScreen));//rpounds down on no screens in
732
733 // numberOfScreensIn = tickLocation / ticksPerScreen;//rounds down
734 timeOffsetForScreen = getEventTimeMillis(numberOfScreensIn * ticksPerScreen);
735
736 while (noteArrayIndex < midiFileToDraw.size()-1 && tickLocation > midiFileToDraw[noteArrayIndex][0] )
737 noteArrayIndex++;
738
739
740 while (noteArrayIndex > 0 && noteArrayIndex < size && tickLocation < midiFileToDraw[noteArrayIndex][0])
741 noteArrayIndex--;
742
743 //need to start where we currently are in file
744 int maxNoteIndexToPrint = noteArrayIndex;
745 int minNoteIndexToPrint = min(size-1,noteArrayIndex);//not needed as changed above
746
747 while (maxNoteIndexToPrint < midiFileToDraw.size() && midiFileToDraw[maxNoteIndexToPrint][0] < (numberOfScreensIn+1)*ticksPerScreen )
748 maxNoteIndexToPrint++;
749
750 while (minNoteIndexToPrint > 0 && midiFileToDraw[minNoteIndexToPrint][0] > numberOfScreensIn*ticksPerScreen)//&& minNoteIndexToPrint < size
751 minNoteIndexToPrint--;
752
753 for (int tmpIndex = max(0,minNoteIndexToPrint);tmpIndex < min(maxNoteIndexToPrint, (int)midiFileToDraw.size());tmpIndex++){
754
755 ofSetColor(255,0,255);
756 /*
757 if (checkIfMatchedNote(tmpIndex))
758 ofSetColor(100,100,100);//0,0,255);
759 else if(noteOnMatches[tmpIndex]){
760 ofSetColor(255,0,255);//dark grey
761 }
762 else{
763 ofSetColor(255,255,255);//255,255,255);
764 }
765 */
766 //ofSetColor(255,255,255);
767 if (tmpIndex == bestMatchIndex)
768 ofSetColor(255,0,0);//best recent match is in red
769
770 // XXX replace ofgetwidth below
771 //if (tmpIndex >= 0 && tmpIndex < size)
772 int xLocation = (float)(midiFileToDraw[tmpIndex][0] - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen;
773 int duration = (float)(midiFileToDraw[tmpIndex][3]*(*screenWidth))/(float)ticksPerScreen;
774
775
776 int yLocation = (*screenHeight) - ((midiFileToDraw[tmpIndex][1] - noteMinimum )*(*screenHeight)/ (float)(noteMaximum - noteMinimum));
777 ofRect(xLocation,yLocation, duration, noteHeight);
778
779 }
780
781
782
783 }
784
785
786
787 }
788
789
790
791 void midiEventHolder::drawFile(){
792 drawMidiFile();
793
794
795 // bayesStruct.drawArrays();
796
797 // ofSetColor(200,200,0);
798 // bayesStruct.prior.drawConstrainedVector(0, bayesStruct.prior.arraySize, 400, 800);
799
800 //need to draw arrays within correct timescope
801 if (drawPhaseMode)
802 bayesStruct.drawArraysRelativeToTimeframe(timeOffsetForScreen, timeOffsetForScreen + getEventTimeMillis(ticksPerScreen));
803
804 if (drawTempoMode)
805 bayesStruct.drawTempoArrays();
806
807
808 ofSetColor(0, 0, 0);
809 //ofDrawBitmapString(matchString, 20, ofGetHeight() - 20);
810
811 double confidence = bayesStruct.posterior.getValueAtMillis(mouseX);
812 /*
813 string mouseString = "mouseX "+ofToString(confidence, 3)+" .";
814 ofDrawBitmapString(mouseString, 20 , ofGetHeight() - 40);
815
816 string mouseString = "updateCounter "+ofToString(bayesStruct.updateCounter);
817 ofDrawBitmapString(mouseString, 20 , ofGetHeight() - 40);
818
819 string infostring = "speed "+ofToString(bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate), 3);
820 ofDrawBitmapString(infostring, 20 , ofGetHeight() - 60);
821 */
822 }
823
824 int midiEventHolder::getLocationFromTicks(double tickPosition){
825 return (int)((float)(tickPosition - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen);
826 }
827
828 int midiEventHolder::getLocationFromMillis(double millisPosition){
829 //(getEventTimeTicks(windowStartTime+matchWindowWidth) - numberOfScreensIn*ticksPerScreen)*(*screenWidth) / (double)ticksPerScreen
830 return (millisPosition - timeOffsetForScreen)*(*screenWidth)/getEventTimeMillis(ticksPerScreen);
831 }
832
833
834 void midiEventHolder::exampleCrossUpdate(){
835
836 bayesStruct.crossUpdateArrays(bayesStruct.posterior, bayesStruct.relativeSpeedPosterior, 200);
837
838 }
839
840
841 void midiEventHolder::setStartPlayingTimes(){
842 lastPeriodUpdateTime = getTimeNow(0);//ofGetElapsedTimeMillis();
843 startTime = lastPeriodUpdateTime;
844
845 /*
846 bayesStruct.lastEventTime = 0;//ofGetElapsedTimeMillis();
847 bayesStruct.bestEstimate = 0;
848 bayesStruct.resetArrays();
849 bayesStruct.lastBestEstimateUpdateTime = ofGetElapsedTimeMillis();
850 */
851 bayesStruct.setStartPlaying();
852 matchString = "";
853 }
854
855
856 void midiEventHolder::printMatchMatrix(){
857 printf("match matrix:\n");
858 for (int i = 0;i < matchMatrix.size();i++){
859 for (int k = 0;k < matchMatrix[i].size();k++){
860 printf("%i , ", matchMatrix[i][k]);
861 }
862 printf("\n");
863 }
864
865 }
866
867
868
869 void midiEventHolder::printRecordedEvents(){
870 printf("Recorded Events:\n");
871 for (int i = 0;i < recordedNoteOnMatrix.size();i++){
872 for (int k = 0;k < recordedNoteOnMatrix[i].size();k++){
873 printf("[%i] = %i ,", i, recordedNoteOnMatrix[i][k]);
874 }
875 if (i < recordedEventTimes.size())
876 printf("time %f \n", recordedEventTimes[i]);
877 else
878 printf("\n");
879 }
880
881 }
882
883
884
885 void midiEventHolder::reorderMatrixFromNoteTimes(IntMatrix& noteOnMatrix){
886 double currentTime = -19999.;
887 for (int i = 0;i < noteOnMatrix.size();i++){
888 int nextIndex = getIndexOfMinimumAboveTime(currentTime, noteOnMatrix);
889 // cout << "index of min time " << currentTime << " is " << nextIndex << " at time " << noteOnMatrix[nextIndex][0] << endl;
890
891 if (nextIndex >= 0 && nextIndex > i && noteOnMatrix[nextIndex][0] < noteOnMatrix[i][0] ){
892 //which it should be
893 // cout << " index " << nextIndex << " at time " << noteOnMatrix[nextIndex][0] << " swaps with inex " << i << " at time " << noteOnMatrix[i][0] << endl;
894 noteOnMatrix[i].swap(noteOnMatrix[nextIndex]);
895 currentTime = noteOnMatrix[i][0];
896 }
897
898 }
899
900 //printRecordedEvents();
901
902 }
903
904
905
906
907 void midiEventHolder::doublecheckOrder(IntMatrix& noteOnMatrix){
908
909 for (int i = 0;i < noteOnMatrix.size();i++){
910 int nextIndex = getIndexOfMinimumAboveIndex(i, noteOnMatrix);
911 if (nextIndex > i){
912 noteOnMatrix[i].swap(noteOnMatrix[nextIndex]);
913 }
914 }
915 }
916
917 int midiEventHolder::getIndexOfMinimumAboveIndex(const int& index, IntMatrix& noteOnMatrix){
918 int returnIndex = index;
919 int min = noteOnMatrix[index][0];
920 for (int i = index;i < noteOnMatrix.size();i++){
921 if (noteOnMatrix[i][0] < min){
922 returnIndex = i;
923 min = noteOnMatrix[i][0];
924 }
925 }
926 return returnIndex;
927 }
928
929
930 int midiEventHolder::getIndexOfMinimumAboveTime(const double& time, IntMatrix& noteOnMatrix){
931 int index = 0;
932 double minimumTime = 100000000.;
933 int bestIndex = -1;
934 while (index < noteOnMatrix.size()){
935
936 if (noteOnMatrix[index][0] > time && noteOnMatrix[index][0] < minimumTime){
937 bestIndex = index;
938 minimumTime = noteOnMatrix[index][0];
939 }
940 index++;
941 }
942 return bestIndex;
943 }
944
945
946 void midiEventHolder::correctTiming(IntMatrix& noteOnMatrix){
947
948 if (noteOnMatrix.size() > 0 && noteOnMatrix[0][0] < 0) {
949 int offset = noteOnMatrix[0][0];
950 for (int i = 0;i < noteOnMatrix.size();i++){
951 noteOnMatrix[i][0] -= offset;
952 }
953 }
954
955 }