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