Mercurial > hg > multitrack-audio-matcher
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bayesianArraySrc/midiEventHolder.cpp Tue Jan 31 13:54:17 2012 +0000 @@ -0,0 +1,1292 @@ +/* + * midiEventHolder.cpp + * midiCannamReader3 + * + * Created by Andrew on 19/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + + +//Main file to look at here is newNoteEvent() - this calls everything else to update the Bayesian array + +#include "midiEventHolder.h" + +#include <iostream> +#include <fstream> +#include <assert.h> + +midiEventHolder::midiEventHolder(){ + + + +// recordedNoteOnIndex = 0; + alignmentPosition = 0; + + useTempoPrior = false;//puts sine wave round tempo + confidenceWeightingUsed = true; + newOptimalMethod = true; + + matchWindowWidth = 16000;//window size for matching in ms + interNoteRange = 1600;//preferred duration + //so max here is really four + + + likelihoodWidth = 100;//using 100 is good + likelihoodToNoiseRatio = 0.20;//was 0.02 on 18/11/11, changing to give more weight to observations + //was 0.08 on 11/12/11 but need more for tempo varn in rwc database + + bayesStruct.speedLikelihoodNoise = 0.1;//was 0.05 + bayesStruct.speedDecayWidth = 40; + bayesStruct.speedDecayAmount = 10; + + drawTempoMode = false; + //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; + + + + + + speedPriorValue = 1.0; + + + bayesStruct.resetSize(matchWindowWidth); + bayesStruct.setPositionDistributionScalar(1); + + bayesStruct.resetSpeedSize(200); + bayesStruct.setRelativeSpeedScalar(0.01); + bayesStruct.relativeSpeedPrior.getMaximum(); + //bayesStruct.simpleExample(); + + + speedWindowWidthMillis = 1600;//4000 + + noteHeight = (*screenHeight) / (float)(noteMaximum - noteMinimum); + + + intervalsToCheck.push_back(1); + intervalsToCheck.push_back(2); + //intervalsToCheck.push_back(3); + intervalsToCheck.push_back(4); + intervalsToCheck.push_back(6); + intervalsToCheck.push_back(8); + intervalsToCheck.push_back(16); + + + 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; + startPlayingTime = 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; + + recordedTotalNoteCounterByPitch.clear(); + recordedTotalNoteCounterByPitch.assign(127,0); + totalNoteCounterIndex = 0; + + interNoteIntervals.clear(); + + smoothIndex = 0; + smoothPlayPosition = 0.0; +// relativeSpeedForSmooth = 1.0; +// storedSmoothPlayPosition = smoothPlayPosition; +// lastSmoothUpdateTime = getTimeNow(0); + + printf("reset speed prior is %f\n", speedPriorValue); + bayesStruct.resetSpeedToOne(); + bayesStruct.setSpeedPrior(speedPriorValue); + setMatchedNotesBackToFalse(); + + periodCounter = 0; + for (int i = 0;i < periodValues.size();i++){ + // printf("period at %f is %f\n", periodValues[i][2], periodValues[i][1]); + } +/* if (periodValues.size() > 0){ + updatePeriodValue(0);// periodValues[0][2]; + printf("Resetting period to %f , size is %i\n", period, (int)periodValues.size()); + } +*/ + //period = 500.0; +} + +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(); + measureVector.clear(); + //played events: + playedEventTimes.clear(); + playedNoteOnMatrix.clear(); + matchMatrix.clear(); + bestMatchFound.clear(); + periodValues.clear(); + + beatPositions.clear(); + + recordedTotalNoteCounterByPitch.clear(); + recordedTotalNoteCounterByPitch.assign(127, 0); + totalNoteCounterIndex = 0; +} + +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 0.0; + //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 + + lastPlayedPitch = pitch; + //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.0 / 100.); +// bayesStruct.addTriangularNoiseToSpeedPosterior(timeDifference * 10 / 100.); + } + +// bayesStruct.updateTmpBestEstimate(timeDifference);// debug - didnt work bayesStruct.bestEstimate = bayesStruct.tmpBestEstimate; + bayesStruct.updateBestEstimate(timeDifference); + + bayesStruct.lastBestEstimateUpdateTime = getTimeNow(timePlayed); + //updatePeriodValue(bayesStruct.lastBestEstimateUpdateTime); + +// 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(); + + if (recordedEventTimes.size() > 0){ + updateTempo(); + //calcuateNewInterNoteIntervals(); + } + + //storedSmoothPlayPosition = smoothPlayPosition; + +} + +void midiEventHolder::updateTempo(){ + //having found matches we have matches for new note and matches for previous notes + if (newOptimalMethod) + findOptimumTempoPairsToCurrentBestMatch(); + else 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)); +} + +double midiEventHolder::getBestSpeedEstimate(const int& currentPlayedIndex, const int& equivalentRecordedIndex){ + double estimate = 1.0; + if (bestMatchIndex > 0){ + double accordingToFileLengthEstimate = recordedEventTimes[equivalentRecordedIndex] - recordedEventTimes[0]; + double playedEquivalent = (playedEventTimes[currentPlayedIndex] - playedEventTimes[0]); + if (accordingToFileLengthEstimate > 0 && playedEquivalent > 0) + accordingToFileLengthEstimate /= playedEquivalent; + estimate = accordingToFileLengthEstimate; + } + return estimate; +} + +void midiEventHolder::findOptimumTempoPairsToCurrentBestMatch(){ + bool needToUpdate = false; + + DoubleVector speedIntervalsFound; + + double currentSpeedEstimate = bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate); + + int currentPlayedIndex = playedNoteOnMatrix.size()-1; + + bayesStruct.setLikelihoodToConstant(); + + int recordedCurrentIndex = bestMatchFound[currentPlayedIndex]; + //we only look at intervals between the current best match and other recent best matched notes + + //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; + + //now check if this can be closer the observed value + int checkRecordedCurrentIndex = recordedCurrentIndex; + int checkRecordedPreviousIndex ;//= recordedCurrentIndex; + int currentPlayedPitch = playedNoteOnMatrix[currentPlayedIndex][1]; + int previousPlayedPitch = playedNoteOnMatrix[previousIndex][1]; + + double recordedTimeOfBestMatch = recordedEventTimes[recordedCurrentIndex]; + + //change this so we start first in window and go to end + + while (checkRecordedCurrentIndex >= 0 && recordedEventTimes[checkRecordedCurrentIndex] > recordedTimeOfBestMatch - matchWindowWidth){ + + checkRecordedCurrentIndex--; + } + + double bestSpeedEstimate = getBestSpeedEstimate(currentPlayedIndex, bestMatchIndex); + + while (checkRecordedCurrentIndex < recordedEventTimes.size() && recordedEventTimes[checkRecordedCurrentIndex] < recordedTimeOfBestMatch + matchWindowWidth ){ + if (recordedNoteOnMatrix[checkRecordedCurrentIndex][1] == currentPlayedPitch ){ + checkRecordedPreviousIndex = checkRecordedCurrentIndex; + double recordedTimeCurrent = recordedEventTimes[checkRecordedCurrentIndex] ; + while (checkRecordedPreviousIndex >= 0 && recordedEventTimes[checkRecordedPreviousIndex] + maximumMatchSpeed*playedTimeDifference > recordedTimeCurrent ) { + if (recordedNoteOnMatrix[checkRecordedPreviousIndex][1] == previousPlayedPitch){ + //we have a candidate + double speedToTest = recordedEventTimes[checkRecordedCurrentIndex] - recordedEventTimes[checkRecordedPreviousIndex]; + speedToTest /= playedTimeDifference; + if (abs(speedToTest-currentSpeedEstimate) < abs(speedRatio - currentSpeedEstimate) ){ + speedRatio = speedToTest; + } + } + checkRecordedPreviousIndex--; + } + } + checkRecordedCurrentIndex++; + } + + + 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::calcuateNewInterNoteIntervals(){ + DoubleVector v; + + int currentPlayedIndex = playedNoteOnMatrix.size()-1; +// int recordedCurrentIndex = bestMatchFound[currentPlayedIndex]; + int previousIndex = currentPlayedIndex-1; + + //withing speedwindow i.e. 4 seconds + while (previousIndex >= 0 && playedEventTimes[previousIndex] + interNoteRange > playedEventTimes[currentPlayedIndex]) { + + double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex]; + + checkForCorrectInterval(playedTimeDifference, &v); + + previousIndex--; + } + + if (v.size() > 0) + interNoteIntervals.push_back(v); + //printf("\n"); +} + +void midiEventHolder::checkForCorrectInterval(const double& playedTimeDifference, DoubleVector* v){ + double intervalDuration = 0.0; + + for (int intervalIndex = 0;intervalIndex < 3;intervalIndex++){ + //on;y check 1,2 and 4 + double possibleDuration = playedTimeDifference / intervalsToCheck[intervalIndex]; + if (possibleDuration >= 200 && possibleDuration < 400){ + v->push_back(possibleDuration); + // printf("int %f / %i :: %f ", playedTimeDifference, intervalsToCheck[intervalIndex], possibleDuration); + } + } +} + + +void midiEventHolder::updatePlayPosition(){ + //timeDifference = ofGetElapsedTimeMillis() - startPlayingTime;//elpased + + //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){ + + bayesStruct.updateBestEstimate(timeDifference); + // bayesStruct.updateTmpBestEstimate(timeDifference); + } + + + + if (smoothPlayPosition < bayesStruct.bestEstimate){ + updateSmoothPositionTo(bayesStruct.bestEstimate); + //smoothPlayPosition = bayesStruct.bestEstimate; + } + +// playPositionInMillis = timeDifference;//based on updating from when we change period + //this to be added + + + //this is time diff in milliseconds + //then we have + double quarterNoteIntervals = (timeDifference / period); + tickLocation = quarterNoteIntervals * pulsesPerQuarternote; + + updateNoteCounter(); + +} + +void midiEventHolder::updateSmoothPositionTo(const double& newPosition){ + //smooth play position was where we last outputted notes from. + //checking index is there to make sense. + while (smoothIndex > 0 && recordedEventTimes[smoothIndex] > smoothPlayPosition){ + smoothIndex--; + //printf("going backewaers on smooth, "); + } + while (smoothIndex < recordedEventTimes.size()-1 && recordedEventTimes[smoothIndex] < smoothPlayPosition){ + smoothIndex++; + //printf("outputting smooth\n "); + } + + + double playingTime = ofGetElapsedTimeMillis(); + playingTime -= startPlayingTime; + //now at the last one + + float smoothLocation = beatPositions[smoothIndex]; + int currentNote = recordedNoteOnMatrix[smoothIndex][1]; + + while (smoothIndex < recordedEventTimes.size() && recordedEventTimes[smoothIndex] < newPosition){ + float annotationTime = 0; + float annotationLocation = 0; + int annotationTick = 0; + int annotationNote = 0; + float difference = 10000;//very big + float currentLocationDifference = 1.0; + float range = 1.0; +/* if (smoothIndex < myNotation.rwcAnnotations.size()){ + //add in test here to find closest matching note + + + annotationTime = myNotation.rwcAnnotations[smoothIndex].eventTime; + annotationNote = myNotation.rwcAnnotations[smoothIndex].midiNote; + annotationLocation = myNotation.rwcAnnotations[smoothIndex].beatLocation; + annotationTick = round(annotationLocation*480.0); + }else{ + printf("No annotaion size %i\n", (int)myNotation.rwcAnnotations.size()); + } + */ + difference = playingTime - (annotationTime*1000.0); + /* + if ((*fileOutput).is_open()){ + (*fileOutput) << fixed << beatPositions[smoothIndex] <<",\t" << recordedNoteOnMatrix[smoothIndex][1] << ",\t"; + (*fileOutput) << playingTime ; + + if ( recordedNoteOnMatrix[smoothIndex][1] == annotationNote){ + (*fileOutput) << " corresponds to " << annotationTime; + } + (*fileOutput) << " \n"; + + } + */ + + 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", + recordedNoteOnMatrix[smoothIndex][0], annotationTick, recordedNoteOnMatrix[smoothIndex][1], + beatPositions[smoothIndex], playingTime, + annotationNote, annotationLocation, annotationTime, difference); + +// assert(annotationNote == recordedNoteOnMatrix[smoothIndex][1]); + assert(annotationTick == recordedNoteOnMatrix[smoothIndex][0]); + + /* + if ((*differenceOutput).is_open()){ + (*differenceOutput) << beatPositions[smoothIndex] << "," << difference << "\n"; + // printf("midi %i beat pos %f playing time now at %f :: annotaion %i loc % f time %f diff \t%f ms\n", + // recordedNoteOnMatrix[smoothIndex][1], + // beatPositions[smoothIndex], playingTime, + // annotationNote, annotationLocation, annotationTime, difference); + }else{ + printf("file not open\n"); + } + */ + smoothIndex++; + } + + + smoothPlayPosition = newPosition; + +} + + +void midiEventHolder::updatePeriodValue(const double& millis){ + + double tmp = period; +/* + while (periodCounter >= 0 && periodCounter < periodValues.size()-1 && periodValues[periodCounter][2] < millis){ + periodCounter++; + } + while (periodCounter > 0 && periodValues[periodCounter][2] > millis){ + periodCounter--; + } + */ + //period = periodValues[periodCounter][1]; + + if (period != tmp){ + printf("new period at %f of %f\n", millis, period); + } +} + +void midiEventHolder::updateNoteCounter(){ + while (totalNoteCounterIndex < bestMatchIndex){ + int tmpPitch = recordedNoteOnMatrix[totalNoteCounterIndex][1]; + recordedTotalNoteCounterByPitch[tmpPitch] += 1; + totalNoteCounterIndex++; + } +} + + +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(250,250,20);//250,100,0); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + xLocation = getLocationFromMillis(smoothPlayPosition);//bayesStruct.tmpBestEstimate + ofSetColor(0,250,0);//250,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]; + + int tmpIndex = 0; + while (tmpIndex < measureVector.size() && measureVector[tmpIndex] < (numberOfScreensIn+1)*ticksPerScreen){ + int measureLocation = measureVector[tmpIndex]; + int xLocation = (float)(measureLocation - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen; + ofSetColor(155,155,0); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + tmpIndex++; + } + + + // 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); + +//last played piutch + ofSetColor(0,200,0,50); + int yLocation = (*screenHeight) - ((lastPlayedPitch - noteMinimum )*(*screenHeight)/ (float)(noteMaximum - noteMinimum)); + ofRect(0,yLocation, 100, noteHeight); + + + +} + + + +void midiEventHolder::drawMidiFile(IntMatrix& midiFileToDraw){ + + //using this to draw the live input + + //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(0,0,255, 200); + + 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(); + + ofSetColor(0,0,255); + ofDrawBitmapString("period"+ofToString(period, 2), ofGetWidth() - 180, 20); + +// 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); +*/ + + //drawInterNoteIntervals(); + + } + +void midiEventHolder::drawInterNoteIntervals(){ + + ofSetColor(0,0,150); + int size = interNoteIntervals.size(); + int numberToShow = min(100, size); + double x ; + for (int y = 1;y < numberToShow;y++){ + for (int point = 0;point < interNoteIntervals[y].size();point++){ + double interval = interNoteIntervals[size - y][point]; + x = interval - 200; + x *= (*screenWidth) / 200.0; + } + double h = (double)(y * (*screenHeight)) / numberToShow; + ofCircle(x, h, 5); + } + +} + + +void midiEventHolder::printInterNoteIntervals(){ + + int size = interNoteIntervals.size(); + int numberToShow = 20; + double x ; + for (int y = max(0, size - numberToShow);y < interNoteIntervals.size();y++){ + for (int point = 0;point < interNoteIntervals[y].size();point++){ + printf("[%i][%i] : %f", y, point, interNoteIntervals[y][point]); + } + printf("\n"); + } + +} + +int midiEventHolder::getLocationFromTicks(double tickPosition){ + return 0; +// return (int)((float)(tickPosition - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen); +//not used +} + +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(){ + startPlayingTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + //startTime = startPlayingTime; + printf("starting playing at time %f\n", startPlayingTime); +/* + 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]); + // double tmp = beatPositions[i]; + // beatPositions[i] = beatPositions[nextIndex]; + // = tmp; + + swap (beatPositions[i], beatPositions[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]); + swap (beatPositions[i], beatPositions[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; + } + } + +} + + +void midiEventHolder::printNoteCounter(){ + for (int i = 0;i < recordedTotalNoteCounterByPitch.size();i++){ + printf("RECORDED TOTAL[%i] := %i", i, recordedTotalNoteCounterByPitch[i]); + } +}