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