Mercurial > hg > midi-score-follower
diff hackday/midiEventHolder.cpp @ 24:5a11b19906c7
hackday code is added.
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Sat, 03 Dec 2011 17:19:43 +0000 |
parents | |
children | 2a025ea7c793 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hackday/midiEventHolder.cpp Sat Dec 03 17:19:43 2011 +0000 @@ -0,0 +1,955 @@ +/* + * midiEventHolder.cpp + * midiCannamReader3 + * + * Created by Andrew on 19/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +#include "midiEventHolder.h" + +midiEventHolder::midiEventHolder(){ +// recordedNoteOnIndex = 0; + + useTempoPrior = false;//puts sine wave round tempo + confidenceWeightingUsed = true; + + //there is option to use MAP estinate or integral in beayesianarraystricture class + + runningInRealTime = true; + bayesStruct.realTimeMode = &runningInRealTime; + + minimumMatchSpeed = 0.0; + maximumMatchSpeed = 2.0; + minimumTimeIntervalForTempoUpdate = 150; + + width = ofGetWidth(); + height = ofGetHeight(); + screenWidth= &width; + screenHeight = &height; + + ticksPerScreen = 4000; + tickLocation = 0; + pulsesPerQuarternote = 240; + noteArrayIndex = 0; + noteMinimum = 30; + noteMaximum = 96; + + + + likelihoodWidth = 100;//using 100 is good + likelihoodToNoiseRatio = 0.08;//was 0.02 on 18/11/11, changing to give more weight to observations + + bayesStruct.speedLikelihoodNoise = 0.1;//was 0.05 + bayesStruct.speedDecayWidth = 20; + bayesStruct.speedDecayAmount = 10; + + + + speedPriorValue = 1.0; + + matchWindowWidth = 12000;//window size for matching in ms + + bayesStruct.resetSize(matchWindowWidth); + bayesStruct.setPositionDistributionScalar(1); + + bayesStruct.resetSpeedSize(200); + bayesStruct.setRelativeSpeedScalar(0.01); + bayesStruct.relativeSpeedPrior.getMaximum(); + //bayesStruct.simpleExample(); + + + speedWindowWidthMillis = 4000; + speedPriorValue = 1.0; + noteHeight = (*screenHeight) / (float)(noteMaximum - noteMinimum); + + + + drawPhaseMode = true; + + 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)]); +} + + + +void midiEventHolder::reset(){ + //called when we start playing + + noteArrayIndex = 0; + tickLocation = 0; + lastPeriodUpdateTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + bayesStruct.lastEventTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + numberOfScreensIn = 0; +// recordedNoteOnIndex = 0; + bayesStruct.setNewDistributionOffsets(0); + bayesStruct.posterior.offset = 0; + + playedEventTimes.clear(); + playedNoteOnMatrix.clear(); + matchMatrix.clear(); + bestMatchIndex = 0; + + bayesStruct.resetSpeedToOne(); + bayesStruct.setSpeedPrior(speedPriorValue); + setMatchedNotesBackToFalse(); +} + +void midiEventHolder::setMatchedNotesBackToFalse(){ + for (int i = 0;i < noteOnMatches.size();i++) + noteOnMatches[i] = false; +} + +void midiEventHolder::clearAllEvents(){ + recordedNoteOnMatrix.clear(); + matchesFound.clear(); + noteOnMatches.clear(); + recordedEventTimes.clear(); + + //played events: + playedEventTimes.clear(); + playedNoteOnMatrix.clear(); + matchMatrix.clear(); + bestMatchFound.clear(); +} + +void midiEventHolder::printNotes(){ + printf("RECORDED MATRIX\n"); + for (int i = 0;i < recordedNoteOnMatrix.size();i++){ + printf("ticktime %i :: pitch %i @ millis %f\n", recordedNoteOnMatrix[i][0], recordedNoteOnMatrix[i][1], recordedEventTimes[i]); + } +} + + +double midiEventHolder::getEventTimeTicks(double millis){ + return (millis * pulsesPerQuarternote / period); +} + +double midiEventHolder::getEventTimeMillis(double ticks){ + return (period * ticks / (double) pulsesPerQuarternote); +} + +void midiEventHolder::newNoteOnEvent(int pitch, int velocity, double timePlayed){ +// tempoSpeedString = ""; + + //MOVE INTO BAYESSTRUCT?? XXX + //bayesStruct.copyPriorToPosterior(); + //why was this here?? + bayesStruct.prior.copyFromDynamicVector(bayesStruct.posterior);//try the otehr way + //bayesStruct.copyPriorToPosterior(); + //need to get new MAP position and set the offset of the arrays + //currently bestEstimate is the approx for the new MAP position + + + //add the new event to our played information matrix + IntVector v; + v.push_back(pitch); + v.push_back(velocity); + playedNoteOnMatrix.push_back(v); + + + //would update the arrays at this point to show where out current location (phase) and tempo is. +// double timeNow = ofGetElapsedTimeMillis() - startTime; + double timeNow = timePlayed;// - startTime; + recentNoteOnTime = timePlayed; + +// printf("Max time %f OF time %f \n", timePlayed, timeNow); + + playedEventTimes.push_back(timePlayed); + +// double timeDifference = ofGetElapsedTimeMillis() - bayesStruct.lastEventTime; + double timeDifference = timePlayed - bayesStruct.lastEventTime; + + + + //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); + + //addnoise to the tempo distribution + //bayesStruct.decaySpeedDistribution(timeDifference); + if (timeDifference > 50){ + bayesStruct.addGaussianNoiseToSpeedPosterior(timeDifference * 10 / 100.); +// bayesStruct.addTriangularNoiseToSpeedPosterior(timeDifference * 10 / 100.); + } + + bayesStruct.updateTmpBestEstimate(timeDifference);// debug - didnt work bayesStruct.bestEstimate = bayesStruct.tmpBestEstimate; + bayesStruct.updateBestEstimate(timeDifference); + bayesStruct.lastBestEstimateUpdateTime = getTimeNow(timePlayed); + +// double newMAPestimateTime = bayesStruct.posterior.getIndexInRealTerms(bayesStruct.posterior.MAPestimate); + //was offset + bayesStruct.posterior.MAPestimate; but this doesnt include scalar to convert to millis + + timeString = "Pitch:"+ofToString(pitch); + timeString += ", time now:"+ofToString(timeNow, 1); + timeString += " TD "+ofToString(timeDifference, 1); + timeString += " offset "+ofToString(bayesStruct.posterior.offset , 0); + timeString += " map Est: "+ofToString(bayesStruct.posterior.MAPestimate, 0); +// timeString += " Previous time" + ofToString(newMAPestimateTime,0); + timeString += " speedMap "+ofToString(bayesStruct.relativeSpeedPosterior.integratedEstimate, 2); + timeString += " :: "+ofToString(bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.integratedEstimate), 2); + +// newMAPestimateTime += (timeDifference * bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate)); +// timeString += " : Predicted MAP time" + ofToString(newMAPestimateTime,0); + + //then we recalculate the window start based on MAP being central + //then we do the matches on these and the likelihood on these. + + bayesStruct.setNewDistributionOffsets(max(0., bayesStruct.bestEstimate - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2))); +// bayesStruct.prior.offset = max(0.,newMAPestimateTime - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2)); + + timeString += " \n : new offset " + ofToString(bayesStruct.prior.offset , 0); + timeString += " \n best estimate "+ofToString(bayesStruct.bestEstimate, 1); + timeString += " error "+ofToString(minimumMatchError, 0); + timeString += " map "+ofToString(bayesStruct.relativeSpeedPosterior.integratedEstimate, 1); + timeString += " rel speed "+ofToString(bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.integratedEstimate), 2); + + + //be able to draw the prior in correct location relative to the midi notes + //this calculates the cross update of all possible speeds and all possible positions + bayesStruct.crossUpdateArrays(bayesStruct.posterior, bayesStruct.relativeSpeedPosterior, timeDifference); + + + timeString += " new OFF "+ofToString(bayesStruct.bestEstimate - (bayesStruct.prior.scalar*bayesStruct.prior.arraySize/2), 1); + timeString += " notearrayindex "+ofToString(noteArrayIndex, 0); + //when this is off teh screen there is a problem somehow XXX + + 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)); + + //trying to switch to prior + + + bayesStruct.lastEventTime = timePlayed;//bayesStruct.lastEventTime = ofGetElapsedTimeMillis(); + + //do the cross update to find current posterior for location +// totalConfidence= 0; + int numberOfMatchesFound = findLocalMatches(pitch); + setMatchLikelihoods(numberOfMatchesFound); + bayesStruct.calculatePosterior(); + + //having found matches we have matches for new note and matches for previous notes + if (!confidenceWeightingUsed) + findLocalTempoPairs(); + else + findLocalTempoPairsWeightedForConfidence(); + + //bayesStruct.addGaussianNoiseToSpeedPosterior(10); + +} + +double midiEventHolder::getTimeNow(double eventTime){ + double timeNow = eventTime; + if (runningInRealTime) + timeNow = ofGetElapsedTimeMillis(); + return timeNow; +} + +int midiEventHolder::findLocalMatches(int notePitch){ + + //here we find the matches to the new note within appropriate range + + matchString = ""; + + windowStartTime = max(0.0,(bayesStruct.bestEstimate - matchWindowWidth/2));//was playPositionInMillis +// cout << "best estimate is " << bayesStruct.bestEstimate << endl; + int numberOfMatches = findMatch(notePitch, windowStartTime, windowStartTime + matchWindowWidth); + + + //matchString += " pitch: "+ofToString(notePitch)+" matches "+ofToString(numberOfMatches)+" win start "+ofToString(windowStartTime); + + return numberOfMatches; + + +} + + +void midiEventHolder::setMatchLikelihoods(int numberOfMatches){ +//reset the offset to match the prior + bayesStruct.likelihood.offset = bayesStruct.prior.offset; + bayesStruct.likelihood.zero();//set to zero + + double quantity = likelihoodToNoiseRatio / numberOfMatches; + + for (int i = 0;i < numberOfMatches && matchesFound[i] >= 0 && matchesFound[i] < recordedEventTimes.size();i++){ + // printf("match times %i of %i::%f adding likelihood to %f\n", i, numberOfMatches, recordedEventTimes[matchesFound[i]], recordedEventTimes[matchesFound[i]] - bayesStruct.likelihood.offset); + //this is the vent time since start of file + if (recordedEventTimes[matchesFound[i]] - bayesStruct.likelihood.offset < bayesStruct.likelihood.arraySize){ + // double confidenceMeasure = 0; + // if (totalConfidence > 0) + // confidenceMeasure = bayesStruct.posterior.getValueAtMillis(recordedEventTimes[matchesFound[i]])/totalConfidence; + + bayesStruct.likelihood.addGaussianShape(recordedEventTimes[matchesFound[i]] - bayesStruct.likelihood.offset, likelihoodWidth, quantity);//* confidenceMeasure + }//end if + } + bayesStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesStruct.likelihood.length); +} + +int midiEventHolder::findMatch(const int& notePitch, const int& startTime, const int& endTime){ + + matchesFound.clear(); + int startIndex = 0; + + if (recordedEventTimes.size() > 0){ + + //get to the right range of events to check in + while (startIndex < recordedEventTimes.size() && recordedEventTimes[startIndex] < startTime) + startIndex++; + + } + + IntVector v; + DoubleVector d; + double tmpError = 100000.;//v high error + + double minimumConfidence = 0; + while (startIndex < recordedEventTimes.size() && recordedEventTimes[startIndex] < endTime){ + if (recordedNoteOnMatrix[startIndex][1] == notePitch){ + + matchesFound.push_back(startIndex); + v.push_back(startIndex); + //so startIndex is registered as a match + + double eventConfidence = bayesStruct.posterior.getValueAtMillis(recordedEventTimes[startIndex]); + if (eventConfidence > minimumConfidence){ + minimumConfidence = eventConfidence; + bestMatchIndex = startIndex; + } + d.push_back(eventConfidence); + + double confidence = eventConfidence;//bayesStruct.posterior.getValueAtMillis(mouseX); + // recordedEventTimes[startIndex]); + // matchString += "["+ofToString(startIndex)+"] = "+ofToString(confidence, 3)+" ."; + + if (abs(recordedEventTimes[startIndex] - bayesStruct.bestEstimate) < tmpError){ + //record the error between expected and observed times + tmpError = abs(recordedEventTimes[startIndex] - bayesStruct.bestEstimate); + minimumMatchError = tmpError;//recordedEventTimes[startIndex] - bayesStruct.bestEstimate; + } + + } + startIndex++; + } + + +// printf("%i MATCHES TO Note %i found\n", (int)matchesFound.size(), notePitch); + int size = matchesFound.size(); + if (size > 0) + noteOnMatches[bestMatchIndex] = true; + + v.insert(v.begin() , (int)size);//at beginning, we list how many matches there are that we have found + d.insert(d.begin() , (double)size); + + //v.push_back(size); + //d.push_back(size); + //for (int i = 0;i < matchesFound.size()+1;i++){ + // v.push_back(matchesFound[i]); + // printf("match %i,[%i] is %i\n", startIndex, i, v[i]); + //} + + + matchMatrix.push_back(v); + matchConfidence.push_back(d); + + //bringing in way to list only the best matches and use these in tempo process + bestMatchFound.push_back(bestMatchIndex); + +// 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); + + return size; +} + +bool midiEventHolder::checkIfMatchedNote(const int& tmpIndex){ + for (int i = 0;i < matchesFound.size();i++){ + if (matchesFound[i] == tmpIndex) + return true; + } + return false; +} + + + +void midiEventHolder::findLocalTempoPairs(){ + + int currentPlayedIndex = playedNoteOnMatrix.size()-1; + // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]); + // printMatchesFound(); + // printMatchMatrix(); + // printf("possible notes \n"); + bool needToUpdate = false; + bayesStruct.setLikelihoodToConstant(); + + + for (int i = 0;i < matchMatrix[currentPlayedIndex][0];i++){ + //iterate through the recently matched events - even dodgy matches included + //size, index of match0, index of match1, .... + + + int recordedCurrentIndex = matchMatrix[currentPlayedIndex][i+1]; + + int previousIndex = currentPlayedIndex-1; + + + while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) { + double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex]; + + for (int k = 0;k < matchMatrix[previousIndex][0];k++){ + + int recordedPreviousIndex = matchMatrix[previousIndex][k+1]; + + double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex]; + + + //we want the speed of the recording relative to that of the playing live + + double speedRatio = recordedTimeDifference / playedTimeDifference; + if (recordedTimeDifference > minimumTimeIntervalForTempoUpdate && + speedRatio <= maximumMatchSpeed && speedRatio >= minimumMatchSpeed){ + + //adding in a prior that prefers 1 + double priorWeighting = 1; + if (useTempoPrior) + priorWeighting = sin(speedRatio * PI/2); + + + + /* + printf("(%i)", matchMatrix[currentPlayedIndex][i+1]); + printf("[%i] :: ", recordedPreviousIndex); + printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference); + printf("update on speed ratio %f\n", speedRatio); + */ + // matchString += " speed: "+ofToString(speedRatio, 3); + // commented for debug + + //bayesStruct.updateTempoDistribution(speedRatio, 0.1);//second paramter is confidence in the match + double amount = (1-bayesStruct.speedLikelihoodNoise)/10; + amount *= priorWeighting; + bayesStruct.updateTempoLikelihood(speedRatio, amount); + // tempoSpeedString += ofToString(recordedPreviousIndex) + " "+ ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n"; + needToUpdate = true; + } + // printf("\n"); + } + + previousIndex--; + }//end while previousindex countdown + }//end for loop through possible current matches + + if (needToUpdate) + bayesStruct.updateTempoDistribution(); + + //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate)); +} + + +void midiEventHolder::findLocalTempoPairsWeightedForConfidence(){ + bool needToUpdate = false; + + DoubleVector speedIntervalsFound; + + //adapted this to just use the best match for each note + + int currentPlayedIndex = playedNoteOnMatrix.size()-1; + // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]); + // printMatchesFound(); + // printMatchMatrix(); + // printf("possible notes \n"); + + bayesStruct.setLikelihoodToConstant(); + + int recordedCurrentIndex = bestMatchFound[currentPlayedIndex]; + //we only look at intervals between the current best match and other recent best matched notes + //that is the difference in confidence method + + + //printf("BEST MATCH FOUND for index %i is %i ", currentPlayedIndex, bestMatchFound[currentPlayedIndex]); + + int previousIndex = currentPlayedIndex-1; + + //withing speedwindow i.e. 4 seconds + while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) { + double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex]; + + int recordedPreviousIndex = bestMatchFound[previousIndex]; + + double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex]; + + + //we want the speed of the recording relative to that of the playing live + + double speedRatio = recordedTimeDifference / playedTimeDifference; + if (recordedTimeDifference > minimumTimeIntervalForTempoUpdate + && speedRatio < maximumMatchSpeed && speedRatio > minimumMatchSpeed){ + + /* printf("(%i)", previousIndex); + printf("[%i] :: ", recordedPreviousIndex); + // printf(" conf %f & %f ", currentMatchConfidence, previousMatchConfidence); + printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference); + printf("update on speed ratio %f\n", speedRatio); + */ + // matchString += " speed: "+ofToString(speedRatio, 3); + // commented for debug + + + double priorWeighting = 1; + + if (useTempoPrior) + priorWeighting = sin(speedRatio * PI/2);//adding in a prior that prefers 1.0 speed + + + // double weighting = previousMatchConfidence * currentMatchConfidence ; + double amount = (1-bayesStruct.speedLikelihoodNoise)*priorWeighting/16;//was 9 + + speedIntervalsFound.push_back(speedRatio); + // bayesStruct.updateTempoLikelihood(speedRatio, amount);//second paramter is confidence in the match + + // tempoSpeedString += ofToString(recordedCurrentIndex) + " :: " + ofToString(recordedPreviousIndex); + // tempoSpeedString += " " + ofToString(recordedTimeDifference)+ " " + ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n"; + + needToUpdate = true; + } + // printf("\n"); + + + previousIndex--; + }//end while previousindex countdown + + if (speedIntervalsFound.size() > 0){ + double amount = (1 - bayesStruct.speedLikelihoodNoise) / speedIntervalsFound.size(); + for (int i = 0;i < speedIntervalsFound.size();i++) + bayesStruct.updateTempoLikelihood(speedIntervalsFound[i], amount); + } + + + if (needToUpdate) + bayesStruct.updateTempoDistribution(); + //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate)); +} + +/* +void midiEventHolder::findLocalTempoPairsWeightedForConfidence(){ + bool needToUpdate = false; + + //adapted this to just use the best match for each note + + int currentPlayedIndex = playedNoteOnMatrix.size()-1; + // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]); + // printMatchesFound(); + // printMatchMatrix(); + // printf("possible notes \n"); + + bayesStruct.setLikelihoodToConstant(); + + for (int i = 0;i < matchMatrix[currentPlayedIndex][0];i++){ + + //iterate through the recently matched events - even dodgy matches included + //size, index of match0, index of match1, .... + int recordedCurrentIndex = matchMatrix[currentPlayedIndex][i+1]; + + double currentMatchConfidence = matchConfidence[currentPlayedIndex][i+1];//new confidence + + int previousIndex = currentPlayedIndex-1; + + while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) { + double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex]; + + // for (int k = 0;k < matchMatrix[previousIndex][0];k++) + int recordedPreviousIndex = bestMatchFound[previousIndex];//matchMatrix[previousIndex][k+1]; + + //double previousMatchConfidence = matchConfidence[previousIndex][k+1]; + + + double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex]; + + + //we want the speed of the recording relative to that of the playing live + + double speedRatio = recordedTimeDifference / playedTimeDifference; + if (speedRatio <= maximumMatchSpeed && speedRatio >= minimumMatchSpeed){ + + printf("(%i)", matchMatrix[currentPlayedIndex][i+1]); + printf("[%i] :: ", recordedPreviousIndex); + // printf(" conf %f & %f ", currentMatchConfidence, previousMatchConfidence); + printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference); + printf("update on speed ratio %f\n", speedRatio); + + // matchString += " speed: "+ofToString(speedRatio, 3); + // commented for debug + + + double priorWeighting = 1; + + if (useTempoPrior) + priorWeighting = sin(speedRatio * PI/2);//adding in a prior that prefers 1.0 speed + + + // double weighting = previousMatchConfidence * currentMatchConfidence ; + double amount = (1-bayesStruct.speedLikelihoodNoise)*priorWeighting/10; + bayesStruct.updateTempoLikelihood(speedRatio, amount);//second paramter is confidence in the match + tempoSpeedString += ofToString(recordedPreviousIndex) + " " + ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n"; + + needToUpdate = true; + } + // printf("\n"); + + + previousIndex--; + }//end while previousindex countdown + }//end for loop through possible current matches + + if (needToUpdate) + bayesStruct.updateTempoDistribution(); + //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate)); +} +*/ + + +void midiEventHolder::updatePlayPosition(){ + + //in actual fact if we are changing the speed of the play position + //we will need to update this via the file + + //actually time since beginning of file i think + + double timeDifference = 0; + if (runningInRealTime) + timeDifference = ofGetElapsedTimeMillis() - lastPeriodUpdateTime;//elpased - lastperiodupdatetime + + //this is time diff in milliseconds + //then we have + double quarterNoteIntervals = (timeDifference / period); + tickLocation = quarterNoteIntervals * pulsesPerQuarternote; + + playPositionInMillis = timeDifference;//based on updating from when we change period + //this to be added + + if (runningInRealTime) + bayesStruct.updateBestEstimate(timeDifference); + +} + + +void midiEventHolder::drawMidiFile(){ + + //draws midi file on scrolling screen + int size = recordedNoteOnMatrix.size(); + if (size > 0){ + + numberOfScreensIn = floor(bayesStruct.bestEstimate / getEventTimeMillis(ticksPerScreen));//rpounds down on no screens in + + // numberOfScreensIn = tickLocation / ticksPerScreen;//rounds down + timeOffsetForScreen = getEventTimeMillis(numberOfScreensIn * ticksPerScreen); + + while (noteArrayIndex < recordedNoteOnMatrix.size()-1 && tickLocation > recordedNoteOnMatrix[noteArrayIndex][0] ) + noteArrayIndex++; + + + while (noteArrayIndex > 0 && noteArrayIndex < size && tickLocation < recordedNoteOnMatrix[noteArrayIndex][0]) + noteArrayIndex--; + + //need to start where we currently are in file + int maxNoteIndexToPrint = noteArrayIndex; + int minNoteIndexToPrint = min(size-1,noteArrayIndex);//not needed as changed above + + while (maxNoteIndexToPrint < recordedNoteOnMatrix.size() && recordedNoteOnMatrix[maxNoteIndexToPrint][0] < (numberOfScreensIn+1)*ticksPerScreen ) + maxNoteIndexToPrint++; + + while (minNoteIndexToPrint > 0 && recordedNoteOnMatrix[minNoteIndexToPrint][0] > numberOfScreensIn*ticksPerScreen)//&& minNoteIndexToPrint < size + minNoteIndexToPrint--; + + for (int tmpIndex = max(0,minNoteIndexToPrint);tmpIndex < min(maxNoteIndexToPrint, (int)recordedNoteOnMatrix.size());tmpIndex++){ + + ofSetColor(255,255,255); + if (checkIfMatchedNote(tmpIndex)) + ofSetColor(100,100,100);//0,0,255); + else if(noteOnMatches[tmpIndex]){ + ofSetColor(255,0,255);//dark grey + } + else{ + ofSetColor(255,255,255);//255,255,255); + } + + //ofSetColor(255,255,255); + if (tmpIndex == bestMatchIndex) + ofSetColor(255,0,0);//best recent match is in red + + // XXX replace ofgetwidth below + //if (tmpIndex >= 0 && tmpIndex < size) + int xLocation = (float)(recordedNoteOnMatrix[tmpIndex][0] - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen; + int duration = (float)(recordedNoteOnMatrix[tmpIndex][3]*(*screenWidth))/(float)ticksPerScreen; + + + int yLocation = (*screenHeight) - ((recordedNoteOnMatrix[tmpIndex][1] - noteMinimum )*(*screenHeight)/ (float)(noteMaximum - noteMinimum)); + ofRect(xLocation,yLocation, duration, noteHeight); + + } + + + int xLocation;// = getLocationFromTicks(tickLocation); + // ofLine(xLocation, 0, xLocation, (*screenHeight)); + + //orange line at best estimate + xLocation = getLocationFromMillis(bayesStruct.bestEstimate); + ofSetColor(80,80,80);//250,100,0); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + xLocation = getLocationFromMillis(bayesStruct.tmpBestEstimate); + ofSetColor(150,150,150);//250,100,0); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + + //lines where matching window start and end are + ofSetColor(0);//0,100,255); + xLocation = getLocationFromMillis(windowStartTime); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + xLocation = getLocationFromMillis(windowStartTime+matchWindowWidth); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + + int maxSize = recordedNoteOnMatrix[size-1][0]; + + // ofDrawBitmapString(tempoSpeedString, 20, 20); + /* string indexString = "num screens in "+ofToString(numberOfScreensIn)+"; min index to print "+ofToString(minNoteIndexToPrint)+", max index to print "+ofToString(maxNoteIndexToPrint); + indexString += " size "+ofToString(size)+" tick loc "+ofToString(tickLocation)+" max size "+ofToString(maxSize); + ofDrawBitmapString(indexString, 20, 40); + */ + } + + //ofDrawBitmapString(ofToString(timeOffsetForScreen, 1), 20,20); + + //ofDrawBitmapString(timeString, 20, 60); + + +} + + + +void midiEventHolder::drawMidiFile(IntMatrix& midiFileToDraw){ + + //draws midi file on scrolling screen + int size = midiFileToDraw.size(); + if (size > 0){ + + numberOfScreensIn = floor(bayesStruct.bestEstimate / getEventTimeMillis(ticksPerScreen));//rpounds down on no screens in + + // numberOfScreensIn = tickLocation / ticksPerScreen;//rounds down + timeOffsetForScreen = getEventTimeMillis(numberOfScreensIn * ticksPerScreen); + + while (noteArrayIndex < midiFileToDraw.size()-1 && tickLocation > midiFileToDraw[noteArrayIndex][0] ) + noteArrayIndex++; + + + while (noteArrayIndex > 0 && noteArrayIndex < size && tickLocation < midiFileToDraw[noteArrayIndex][0]) + noteArrayIndex--; + + //need to start where we currently are in file + int maxNoteIndexToPrint = noteArrayIndex; + int minNoteIndexToPrint = min(size-1,noteArrayIndex);//not needed as changed above + + while (maxNoteIndexToPrint < midiFileToDraw.size() && midiFileToDraw[maxNoteIndexToPrint][0] < (numberOfScreensIn+1)*ticksPerScreen ) + maxNoteIndexToPrint++; + + while (minNoteIndexToPrint > 0 && midiFileToDraw[minNoteIndexToPrint][0] > numberOfScreensIn*ticksPerScreen)//&& minNoteIndexToPrint < size + minNoteIndexToPrint--; + + for (int tmpIndex = max(0,minNoteIndexToPrint);tmpIndex < min(maxNoteIndexToPrint, (int)midiFileToDraw.size());tmpIndex++){ + + ofSetColor(255,0,255); + /* + if (checkIfMatchedNote(tmpIndex)) + ofSetColor(100,100,100);//0,0,255); + else if(noteOnMatches[tmpIndex]){ + ofSetColor(255,0,255);//dark grey + } + else{ + ofSetColor(255,255,255);//255,255,255); + } + */ + //ofSetColor(255,255,255); + if (tmpIndex == bestMatchIndex) + ofSetColor(255,0,0);//best recent match is in red + + // XXX replace ofgetwidth below + //if (tmpIndex >= 0 && tmpIndex < size) + int xLocation = (float)(midiFileToDraw[tmpIndex][0] - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen; + int duration = (float)(midiFileToDraw[tmpIndex][3]*(*screenWidth))/(float)ticksPerScreen; + + + int yLocation = (*screenHeight) - ((midiFileToDraw[tmpIndex][1] - noteMinimum )*(*screenHeight)/ (float)(noteMaximum - noteMinimum)); + ofRect(xLocation,yLocation, duration, noteHeight); + + } + + + + } + + + +} + + + +void midiEventHolder::drawFile(){ + drawMidiFile(); + + +// bayesStruct.drawArrays(); + +// ofSetColor(200,200,0); +// bayesStruct.prior.drawConstrainedVector(0, bayesStruct.prior.arraySize, 400, 800); + + //need to draw arrays within correct timescope + if (drawPhaseMode) + bayesStruct.drawArraysRelativeToTimeframe(timeOffsetForScreen, timeOffsetForScreen + getEventTimeMillis(ticksPerScreen)); + + if (drawTempoMode) + bayesStruct.drawTempoArrays(); + + + ofSetColor(0, 0, 0); + //ofDrawBitmapString(matchString, 20, ofGetHeight() - 20); + + double confidence = bayesStruct.posterior.getValueAtMillis(mouseX); +/* + string mouseString = "mouseX "+ofToString(confidence, 3)+" ."; + ofDrawBitmapString(mouseString, 20 , ofGetHeight() - 40); + + string mouseString = "updateCounter "+ofToString(bayesStruct.updateCounter); + ofDrawBitmapString(mouseString, 20 , ofGetHeight() - 40); + + string infostring = "speed "+ofToString(bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate), 3); + ofDrawBitmapString(infostring, 20 , ofGetHeight() - 60); +*/ + } + +int midiEventHolder::getLocationFromTicks(double tickPosition){ + return (int)((float)(tickPosition - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen); +} + +int midiEventHolder::getLocationFromMillis(double millisPosition){ + //(getEventTimeTicks(windowStartTime+matchWindowWidth) - numberOfScreensIn*ticksPerScreen)*(*screenWidth) / (double)ticksPerScreen + return (millisPosition - timeOffsetForScreen)*(*screenWidth)/getEventTimeMillis(ticksPerScreen); +} + + +void midiEventHolder::exampleCrossUpdate(){ + + bayesStruct.crossUpdateArrays(bayesStruct.posterior, bayesStruct.relativeSpeedPosterior, 200); + +} + + +void midiEventHolder::setStartPlayingTimes(){ + lastPeriodUpdateTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + startTime = lastPeriodUpdateTime; + +/* + bayesStruct.lastEventTime = 0;//ofGetElapsedTimeMillis(); + bayesStruct.bestEstimate = 0; + bayesStruct.resetArrays(); + bayesStruct.lastBestEstimateUpdateTime = ofGetElapsedTimeMillis(); +*/ + bayesStruct.setStartPlaying(); + matchString = ""; +} + + +void midiEventHolder::printMatchMatrix(){ + printf("match matrix:\n"); + for (int i = 0;i < matchMatrix.size();i++){ + for (int k = 0;k < matchMatrix[i].size();k++){ + printf("%i , ", matchMatrix[i][k]); + } + printf("\n"); + } + +} + + + +void midiEventHolder::printRecordedEvents(){ + printf("Recorded Events:\n"); + for (int i = 0;i < recordedNoteOnMatrix.size();i++){ + for (int k = 0;k < recordedNoteOnMatrix[i].size();k++){ + printf("[%i] = %i ,", i, recordedNoteOnMatrix[i][k]); + } + if (i < recordedEventTimes.size()) + printf("time %f \n", recordedEventTimes[i]); + else + printf("\n"); + } + +} + + + +void midiEventHolder::reorderMatrixFromNoteTimes(IntMatrix& noteOnMatrix){ + double currentTime = -19999.; + for (int i = 0;i < noteOnMatrix.size();i++){ + int nextIndex = getIndexOfMinimumAboveTime(currentTime, noteOnMatrix); + // cout << "index of min time " << currentTime << " is " << nextIndex << " at time " << noteOnMatrix[nextIndex][0] << endl; + + if (nextIndex >= 0 && nextIndex > i && noteOnMatrix[nextIndex][0] < noteOnMatrix[i][0] ){ + //which it should be + // cout << " index " << nextIndex << " at time " << noteOnMatrix[nextIndex][0] << " swaps with inex " << i << " at time " << noteOnMatrix[i][0] << endl; + noteOnMatrix[i].swap(noteOnMatrix[nextIndex]); + currentTime = noteOnMatrix[i][0]; + } + + } + + //printRecordedEvents(); + +} + + + + +void midiEventHolder::doublecheckOrder(IntMatrix& noteOnMatrix){ + + for (int i = 0;i < noteOnMatrix.size();i++){ + int nextIndex = getIndexOfMinimumAboveIndex(i, noteOnMatrix); + if (nextIndex > i){ + noteOnMatrix[i].swap(noteOnMatrix[nextIndex]); + } + } +} + +int midiEventHolder::getIndexOfMinimumAboveIndex(const int& index, IntMatrix& noteOnMatrix){ + int returnIndex = index; + int min = noteOnMatrix[index][0]; + for (int i = index;i < noteOnMatrix.size();i++){ + if (noteOnMatrix[i][0] < min){ + returnIndex = i; + min = noteOnMatrix[i][0]; + } + } + return returnIndex; +} + + +int midiEventHolder::getIndexOfMinimumAboveTime(const double& time, IntMatrix& noteOnMatrix){ + int index = 0; + double minimumTime = 100000000.; + int bestIndex = -1; + while (index < noteOnMatrix.size()){ + + if (noteOnMatrix[index][0] > time && noteOnMatrix[index][0] < minimumTime){ + bestIndex = index; + minimumTime = noteOnMatrix[index][0]; + } + index++; + } + return bestIndex; +} + + +void midiEventHolder::correctTiming(IntMatrix& noteOnMatrix){ + + if (noteOnMatrix.size() > 0 && noteOnMatrix[0][0] < 0) { + int offset = noteOnMatrix[0][0]; + for (int i = 0;i < noteOnMatrix.size();i++){ + noteOnMatrix[i][0] -= offset; + } + } + +}