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