Mercurial > hg > multitrack-audio-matcher
view bayesianArraySrc/midiEventHolder.cpp @ 56:4394c9490716 tip
minor changes
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Mon, 24 Dec 2012 18:58:39 +0000 |
parents | 45b5cf9be377 |
children |
line wrap: on
line source
/* * 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 //why was this here?? bayesStruct.prior.copyFromDynamicVector(bayesStruct.posterior);//try the otehr way //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]); } }