Mercurial > hg > midi-score-follower
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 } |