Mercurial > hg > midi-score-follower
changeset 33:fa527df85c2c
Added the JNMR code which now features chopping after loading all events, easier that way
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Mon, 12 Dec 2011 12:46:17 +0000 |
parents | f91b7b019350 |
children | 9d2a651a87b2 |
files | jnmr/BayesianArrayStructure.cpp jnmr/BayesianArrayStructure.h jnmr/CannamMidiFileLoader.cpp jnmr/CannamMidiFileLoader.h jnmr/DynamicVector.cpp jnmr/DynamicVector.h jnmr/MidiInputStream.cpp jnmr/MidiInputStream.h jnmr/drawMidiNotes.cpp jnmr/drawMidiNotes.h jnmr/hackday_notes.txt jnmr/main.cpp jnmr/midiEventHolder.cpp jnmr/midiEventHolder.h jnmr/testApp.cpp jnmr/testApp.h |
diffstat | 16 files changed, 3941 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/BayesianArrayStructure.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,592 @@ +/* + * BayesianArrayStructure.cpp + * midiCannamReader + * + * Created by Andrew on 17/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +//look at reset speed to one - what does this do? - get rid of? + + +#include "BayesianArrayStructure.h" + +BayesianArrayStructure::BayesianArrayStructure(){ + printf("Bayesian structure: DeFault constructor called"); + usingIntegratedTempoEstimate = true;// use max index + + relativeSpeedLikelihoodStdDev = 5.0; + + prior.createVector(1); + likelihood.createVector(1); + posterior.createVector(1); + + + speedPriorValue = 1.0; + speedEstimate = speedPriorValue; + + lastEventTime = 0;//ofGetElapsedTimeMillis(); + + tmpBestEstimate = 0; + crossUpdateTimeThreshold = 60; + priorWidth = 50; + +} + +BayesianArrayStructure::BayesianArrayStructure(int length){ + printf("BAYESIAN STURTUCRE CREATED LENGTH: %i\n", length); + //this constructor isnt called it seems + prior.createVector(length); + likelihood.createVector(length); + posterior.createVector(length); + + lastEventTime = 0; + + +} + + + +void BayesianArrayStructure::resetSize(int length){ + printf("BAYESIAN STRUCTURE size is : %i\n", length); + + prior.createVector(length); + likelihood.createVector(length); + posterior.createVector(length); + + acceleration.createVector(length); + +} + + + +void BayesianArrayStructure::resetSpeedToOne(){ + relativeSpeedPrior.zero(); + relativeSpeedPosterior.zero(); + relativeSpeedLikelihood.zero(); + + + relativeSpeedPosterior.addGaussianShape(100, 20, 0.8); + relativeSpeedPosterior.renormalise(); + relativeSpeedPosterior.getMaximum(); + + setSpeedPrior(speedPriorValue); + speedEstimate = speedPriorValue; + + prior.zero(); + posterior.zero(); + + posterior.addToIndex(0, 1); + posterior.renormalise(); + + + //acceleration.addGaussianShape(2000, 20, 0.8); + +} + +void BayesianArrayStructure::setSpeedPrior(double f){ + speedPriorValue = f; + int index = relativeSpeedPosterior.getRealTermsAsIndex(speedPriorValue); + relativeSpeedPosterior.zero(); + relativeSpeedPosterior.addGaussianShape(index, priorWidth, 0.8); + relativeSpeedPosterior.renormalise(); + relativeSpeedPosterior.getMaximum(); + relativeSpeedPrior.copyFromDynamicVector(relativeSpeedPosterior); + printf("BAYES STRUCTU ' SPEED PRIOR %f . index %i\n", speedPriorValue, index); + +} + +void BayesianArrayStructure::resetSpeedSize(int length){ + printf("BAYESIAN SPEED size is : %i\n", length); + + relativeSpeedPrior.createVector(length); + relativeSpeedLikelihood.createVector(length); + relativeSpeedPosterior.createVector(length); + tmpPosteriorForStorage.createVector(length); + + + +} + +void BayesianArrayStructure::setRelativeSpeedScalar(double f){ + relativeSpeedPrior.scalar = f; + relativeSpeedPosterior.scalar = f; + relativeSpeedLikelihood.scalar = f; +} + + +void BayesianArrayStructure::setPositionDistributionScalar(double f){ + if (f > 0){ + prior.scalar = f; + posterior.scalar = f; + likelihood.scalar = f; + } +} + +void BayesianArrayStructure::simpleExample(){ + relativeSpeedPosterior.getMaximum(); + relativeSpeedPrior.copyFromDynamicVector(relativeSpeedPosterior); +} + +void BayesianArrayStructure::copyPriorToPosterior(){ + + for (int i = 0;i < prior.arraySize;i++){ + posterior.array[i] = prior.array[i]; + } +} + +void BayesianArrayStructure::setStartPlaying(){ + + lastEventTime = 0; + bestEstimate = 0; + lastBestEstimateUpdateTime = 0; + if (*realTimeMode) + lastBestEstimateUpdateTime = ofGetElapsedTimeMillis(); + //cannot just be zero - offline bug + printf("start playing - best estimate %f\n", lastBestEstimateUpdateTime); + + resetArrays(); +} + +void BayesianArrayStructure::resetArrays(){ + //called when we start playing + + prior.zero(); + likelihood.zero(); + posterior.zero(); + + updateCounter = 0; + + posterior.offset = -1000; + setNewDistributionOffsets(0); + + int zeroIndex = posterior.getRealTermsAsIndex(0); + + posterior.addGaussianShape(zeroIndex, 500, 1); + // posterior.addToIndex(0, 1); + likelihood.addConstant(1); + + updateCounter = 0; + + + printf("bayes reset arrays - best estimate %f\n", lastBestEstimateUpdateTime); + + setSpeedPrior(speedPriorValue); +} + + +void BayesianArrayStructure::zeroArrays(){ + prior.zero(); + likelihood.zero(); + posterior.zero(); + + relativeSpeedPrior.zero(); + relativeSpeedPosterior.zero(); + relativeSpeedLikelihood.zero(); + +} + + +void BayesianArrayStructure::updateTmpBestEstimate(const double& timeDifference){ + //input is the time since the start of playing + +// double timeDiff = ofGetElapsedTimeMillis() - lastEventTime;//lastBestEstimateUpdateTime; + + tmpBestEstimate = posterior.getIndexInRealTerms(posterior.MAPestimate) + timeDifference*relativeSpeedPosterior.getIndexInRealTerms(relativeSpeedPosterior.integratedEstimate); + // + //printf("tmp best %f and best %f time diff %f posterior MAP %f at speed %f\n", tmpBestEstimate, bestEstimate, timeDifference, posterior.getIndexInRealTerms(posterior.MAPestimate), relativeSpeedPosterior.getIndexInRealTerms(relativeSpeedPosterior.integratedEstimate)); + //lastBestEstimateUpdateTime = ofGetElapsedTimeMillis(); +} + +void BayesianArrayStructure::updateBestEstimate(const double& timeDifference){ +// double timeDiff = ofGetElapsedTimeMillis() - lastEventTime;// + + double timeDiff = timeDifference; + + //Using timedifferencfe here will make it go wrong. Is time since beginning of playing + + if (*realTimeMode) + timeDiff = ofGetElapsedTimeMillis() - lastBestEstimateUpdateTime; + + double speedEstimate; + if (usingIntegratedTempoEstimate) + speedEstimate = relativeSpeedPosterior.getIntegratedEstimate(); + else + speedEstimate = relativeSpeedPosterior.MAPestimate; + + + speedEstimate = relativeSpeedPosterior.getIndexInRealTerms(speedEstimate); + //relativeSpeedPosterior.getIndexInRealTerms(relativeSpeedPosterior.MAPestimate) + bestEstimate = posterior.getIndexInRealTerms(posterior.MAPestimate) + timeDiff*speedEstimate; + +// bestEstimate = tmpBestEstimate; +} + +void BayesianArrayStructure::calculatePosterior(){ + //posterior.doProduct(prior, likelihood); + + int i; + for (i = 0;i < posterior.length;i++){ + posterior.array[i] = likelihood.array[i] * prior.array[i]; + } + + + posterior.renormalise(); + + + + +} + + + + +void BayesianArrayStructure::setNewDistributionOffsets(const double& newOffset){ + prior.offset = newOffset; + likelihood.offset = newOffset; + //posterior.offset = newOffset; +} + + +void BayesianArrayStructure::crossUpdateArrays(DynamicVector& position, DynamicVector& speed, double timeDifference){ + //set the cutoff for offset of position first! XXX + +// printf("time difference %f, ", timeDifference); + + double timeDifferenceInPositionVectorUnits = timeDifference / prior.scalar; + + + prior.zero();//kill prior + calculateNewPriorOffset(timeDifference);//set new prior offset here + + if (timeDifferenceInPositionVectorUnits > crossUpdateTimeThreshold) + complexCrossUpdate(timeDifferenceInPositionVectorUnits); + else + translateByMaximumSpeed(timeDifferenceInPositionVectorUnits); + + + updateCounter++; + prior.renormalise(); + +} + +void BayesianArrayStructure::complexCrossUpdate(const double& timeDifferenceInPositionVectorUnits){ + int distanceMoved, newPriorIndex; + + double speedValue = relativeSpeedPosterior.offset; + + for (int i = 0;i < relativeSpeedPosterior.arraySize;i++){ + + // double speedValue = relativeSpeedPosterior.getIndexInRealTerms(i);//so for scalar 0.01, 50 -> speed value of 0.5 + + //so we have moved + distanceMoved = round(timeDifferenceInPositionVectorUnits * speedValue);//round the value + + if (relativeSpeedPosterior.array[i] != 0){ + double speedContribution = relativeSpeedPosterior.array[i]; + // printf("speed [%i] gives %f moved %i in %f units \n", i, speedValue, distanceMoved, timeDifferenceInPositionVectorUnits); + + newPriorIndex = posterior.offset - prior.offset + distanceMoved; + + for (int postIndex = 0;postIndex < posterior.arraySize;postIndex++){ + //old posterior contributing to new prior + // newPriorIndex = postIndex + posterior.offset - prior.offset + distanceMoved; + + if (newPriorIndex >= 0 && newPriorIndex < prior.arraySize){ + prior.addToIndex(newPriorIndex, posterior.array[postIndex]*speedContribution); + } + + newPriorIndex++;//optimised code - the commented line above explains how this works + }//end for + + + }//if not zero + speedValue += relativeSpeedPosterior.scalar; + //optimised line + //as we wanted: + // double speedValue = relativeSpeedPosterior.getIndexInRealTerms(i);//so for scalar 0.01, 50 -> speed value of 0.5 + }//end speed +} + + + +void BayesianArrayStructure::translateByMaximumSpeed(const double& timeDifferenceInPositionVectorUnits){ + + int distanceMoved, newPriorIndex; + + double speedValue = relativeSpeedPosterior.getIndexInRealTerms(relativeSpeedPosterior.integratedEstimate); + //so for scalar 0.01, 50 -> speed value of 0.5 + double speedContribution = relativeSpeedPosterior.array[relativeSpeedPosterior.integratedEstimate]; + //so we have moved + distanceMoved = round(timeDifferenceInPositionVectorUnits * speedValue);//round the value + // printf("speed [%i] gives %f moved %i in %f units \n", i, speedValue, distanceMoved, timeDifferenceInPositionVectorUnits); + + for (int postIndex = 0;postIndex < posterior.arraySize;postIndex++){ + //old posterior contributing to new prior + newPriorIndex = postIndex + posterior.offset - prior.offset + distanceMoved; + if (newPriorIndex >= 0 && newPriorIndex < prior.arraySize){ + prior.addToIndex(newPriorIndex, posterior.array[postIndex]*speedContribution); + } + + } + +} + +void BayesianArrayStructure::addGaussianNoiseToSpeedPosterior(const double& std_dev){ + tmpPosteriorForStorage.copyFromDynamicVector(relativeSpeedPosterior); + + for (int i = 0;i < relativeSpeedPosterior.length;i++){ + tmpPosteriorForStorage.addGaussianShape(i, std_dev, relativeSpeedPosterior.array[i]); + } + + tmpPosteriorForStorage.renormalise(); + + relativeSpeedPosterior.copyFromDynamicVector(tmpPosteriorForStorage); +} + + +void BayesianArrayStructure::addTriangularNoiseToSpeedPosterior(const double& std_dev){ + tmpPosteriorForStorage.copyFromDynamicVector(relativeSpeedPosterior); + + for (int i = 0;i < relativeSpeedPosterior.length;i++){ + //adding a linear amount depending on distance + tmpPosteriorForStorage.addTriangularShape(i, std_dev*2.0, relativeSpeedPosterior.array[i]); + } + + tmpPosteriorForStorage.renormalise(); + + relativeSpeedPosterior.copyFromDynamicVector(tmpPosteriorForStorage); +} + +void BayesianArrayStructure::calculateNewPriorOffset(const double& timeDifference){ + + double maxSpeed = relativeSpeedPosterior.getIndexInRealTerms(relativeSpeedPosterior.integratedEstimate); + // printf("Maxspeed is %f\n", maxSpeed); + + double priorMax = posterior.getMaximum(); + double distanceTravelled = maxSpeed * (timeDifference / prior.scalar); + double newMaxLocation = posterior.MAPestimate + distanceTravelled; + // printf("MAP: %i, tim df %f, distance %f, new location %f\n", posterior.MAPestimate, timeDifference, distanceTravelled, newMaxLocation); + +} + + +void BayesianArrayStructure::decaySpeedDistribution(double timeDifference){ + + // commented for the moment + double relativeAmount = max(1.0, timeDifference/1000.); +// printf("decay %f around %i \n", timeDifference, relativeSpeedPosterior.MAPestimate); + relativeAmount *= speedDecayAmount; + relativeSpeedPosterior.renormalise(); + relativeSpeedPosterior.addGaussianShape(relativeSpeedPosterior.MAPestimate, speedDecayWidth, relativeAmount); + + relativeSpeedPosterior.renormalise(); + double newMax = relativeSpeedPosterior.getMaximum(); + + //old code +// relativeSpeedPosterior.addGaussianShape(relativeSpeedPosterior.MAPestimate, speedDecayWidth, 10); + //relativeSpeedPosterior.addConstant(1); + + /* + relativeSpeedPrior.copyFromDynamicVector(relativeSpeedPosterior); + relativeSpeedLikelihood.zero(); + relativeSpeedLikelihood.addConstant(0.2); + relativeSpeedLikelihood.addGaussianShape(relativeSpeedPosterior.maximumValue, speedDecayWidth, relativeAmount); + relativeSpeedPosterior.doProduct(relativeSpeedPrior, relativeSpeedLikelihood); + relativeSpeedPosterior.renormalise(); + */ + + + +} + +void BayesianArrayStructure::setLikelihoodToConstant(){ + //set new likelihood + relativeSpeedLikelihood.zero(); + relativeSpeedLikelihood.addConstant(speedLikelihoodNoise); +} + + +void BayesianArrayStructure::updateTempoLikelihood(const double& speedRatio, const double& matchFactor){ + + //speedratio is speed of played relative to the recording + + double index = relativeSpeedLikelihood.getRealTermsAsIndex(speedRatio); + // printf("index of likelihood would be %f for ratio %f\n", index, speedRatio); + if (index >= 0 && index < relativeSpeedPrior.length){ + relativeSpeedLikelihood.addGaussianShape(index , relativeSpeedLikelihoodStdDev, matchFactor); + } +} + + + +void BayesianArrayStructure::updateTempoDistribution(){ + + //copy posterior to prior + relativeSpeedPrior.copyFromDynamicVector(relativeSpeedPosterior); + + //update + relativeSpeedPosterior.doProduct(relativeSpeedPrior, relativeSpeedLikelihood); + + //normalise + relativeSpeedPosterior.renormalise(); + + relativeSpeedPosterior.getMaximum(); + +// relativeSpeedPosterior.updateIntegratedEstimate(); + relativeSpeedPosterior.updateLimitedIntegratedEstimate(); + + speedEstimate = relativeSpeedPosterior.getIndexInRealTerms(relativeSpeedPosterior.integratedEstimate); +} + + +void BayesianArrayStructure::calculateTempoUpdate(){ + //copy posterior to prior + relativeSpeedPrior.copyFromDynamicVector(relativeSpeedPosterior); + + //update + relativeSpeedPosterior.doProduct(relativeSpeedPrior, relativeSpeedLikelihood); + + //normalise + relativeSpeedPosterior.renormalise(); + + relativeSpeedPosterior.getMaximum(); + +} + + +void BayesianArrayStructure::drawArrays(){ + + //bayesArray.drawFloatArray(&bayesArray.prior[0], 0, 200); + //bayesArray.drawFloatArray(&bayesArray.prior[0], 0, 200); + + int displaySize = prior.arraySize; + ofSetColor(0,0,255); + prior.drawVector(0, displaySize); + ofSetColor(0,255,0); + likelihood.drawVector(0, displaySize); + ofSetColor(255,0,255); + posterior.drawVector(0, displaySize); + + + +} + + +void BayesianArrayStructure::drawTempoArrays(){ + ofSetColor(0,0,255); +// relativeSpeedPrior.drawVector(0, relativeSpeedPrior.arraySize); + + ofSetColor(0,150,255); + relativeSpeedLikelihood.drawVector(0, relativeSpeedLikelihood.arraySize); + +// relativeSpeedLikelihood.drawConstrainedVector(0, 199, 0, 1000);// relativeSpeedLikelihood.arraySize); + ofSetColor(255,0,0); + relativeSpeedPosterior.drawVector(0, relativeSpeedPosterior.arraySize); + +// ofSetColor(0,0,255); +// tmpPosteriorForStorage.drawVector(0, tmpPosteriorForStorage.arraySize); + + ofSetColor(255,255, 255); + ofLine(screenWidth/2, 0, screenWidth/2, ofGetHeight());//middle of screen + + ofSetColor(25, 0, 250); + double fractionOfScreen = ((double)relativeSpeedPosterior.integratedEstimate / relativeSpeedPosterior.length); + ofLine(screenWidth * fractionOfScreen, 0, screenWidth * fractionOfScreen, ofGetHeight()); + + ofSetColor(255, 0, 20); + fractionOfScreen = ((double)relativeSpeedPosterior.MAPestimate / relativeSpeedPosterior.length); + ofLine(screenWidth * fractionOfScreen, 0, screenWidth * fractionOfScreen, ofGetHeight()); + + +} + + +void BayesianArrayStructure::drawArraysRelativeToTimeframe(const double& startTimeMillis, const double& endTimeMillis){ + + screenWidth = ofGetWidth(); + + int startArrayIndex = 0; + + if (prior.getIndexInRealTerms(prior.arraySize-1) > startTimeMillis){ + //i.e. the array is on the page + + while (prior.getIndexInRealTerms(startArrayIndex) < startTimeMillis){ + startArrayIndex++; + } + int endArrayIndex = prior.arraySize-1; + //could find constraints here + if (prior.getIndexInRealTerms(prior.arraySize-1) > endTimeMillis) + endArrayIndex = (floor)((endTimeMillis - prior.offset)/prior.scalar); + + //so we need to figure where start and end array are on screen + int startScreenPosition, endScreenPosition; + double screenWidthMillis = endTimeMillis - startTimeMillis; + + startScreenPosition = (prior.getIndexInRealTerms(startArrayIndex) - startTimeMillis)*screenWidth/screenWidthMillis; + endScreenPosition = (double)(prior.getIndexInRealTerms(endArrayIndex) - startTimeMillis)*screenWidth/screenWidthMillis; + + ofSetColor(0,0,100); + string relativeString = " offset "+ofToString(prior.offset, 1);//starttimes("+ofToString(startTimeMillis)+", "+ofToString(endTimeMillis); + relativeString += ": index "+ofToString(startArrayIndex)+" , "+ofToString(endArrayIndex)+" ["; +// relativeString += ofToString(prior.getIndexInRealTerms(endArrayIndex), 3)+"] (sc-width:"+ofToString(screenWidthMillis, 1)+") "; + relativeString += " mapped to screen "+ofToString(startScreenPosition)+" , "+ofToString(endScreenPosition); +// ofDrawBitmapString(relativeString, 100, 180); + + + + ofSetColor(100,100,100);//255, 255, 0); + likelihood.drawConstrainedVector(startArrayIndex, endArrayIndex, startScreenPosition, endScreenPosition); + +// ofSetColor(0,0,200); + ofSetColor(170,170,170);//00,200); + prior.drawConstrainedVector(startArrayIndex, endArrayIndex, startScreenPosition, endScreenPosition); + + ofSetColor(0,0,150); +// ofSetColor(200, 0, 0); + posterior.drawConstrainedVector(startArrayIndex, endArrayIndex, startScreenPosition, endScreenPosition); + + +// ofSetColor(0, 200, 255); +// acceleration.drawConstrainedVector(startArrayIndex, endArrayIndex, startScreenPosition, endScreenPosition); + + + } + +} + + +/* + + void BayesianArrayStructure::updateTempoDistribution(const double& speedRatio, const double& matchFactor){ + //speedratio is speed of played relative to the recording + + double index = relativeSpeedLikelihood.getRealTermsAsIndex(speedRatio); + // printf("\nindex of likelihood would be %f\n", index); + if (index >= 0 && index < relativeSpeedPrior.length){ + //then we can do update + + //set new likelihood + relativeSpeedLikelihood.zero(); + relativeSpeedLikelihood.addConstant(speedLikelihoodNoise); + + relativeSpeedLikelihood.addGaussianShape(index , 5, 0.5*matchFactor); + + + //copy posterior to prior + relativeSpeedPrior.copyFromDynamicVector(relativeSpeedPosterior); + + //update + relativeSpeedPosterior.doProduct(relativeSpeedPrior, relativeSpeedLikelihood); + + //normalise + relativeSpeedPosterior.renormalise(); + + relativeSpeedPosterior.getMaximum(); + }//end if within range + + + } + + */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/BayesianArrayStructure.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,99 @@ +/* + * BayesianArrayStructure.h + * midiCannamReader + * + * Created by Andrew on 17/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ +#ifndef BAYESIAN_ARRAY_STRUCTURE +#define BAYESIAN_ARRAY_STRUCTURE + + +#include "ofMain.h" +//#include "DynamicBayesianArray.h" +#include "DynamicVector.h" + +class BayesianArrayStructure { + +public: +// BayesianArrayStructure(); + BayesianArrayStructure(); + BayesianArrayStructure(int length); + + void calculatePosterior(); + void drawArrays(); + void drawArraysRelativeToTimeframe(const double& startTimeMillis, const double& endTimeMillis); + + void drawTempoArrays(); + + void resetSize(int length); + void resetArrays(); + void simpleExample(); + void setStartPlaying(); + void zeroArrays(); + + double screenWidth; + + void copyPriorToPosterior(); +// DynamicBayesianArray bayesArray; + + double lastEventTime; + double likelihoodNoise; + double speedLikelihoodNoise; + + //DynamicVector tmpPrior; + DynamicVector tmpPosteriorForStorage; + DynamicVector prior; + DynamicVector posterior; + DynamicVector likelihood; + + DynamicVector relativeSpeedPrior; + DynamicVector relativeSpeedLikelihood; + DynamicVector relativeSpeedPosterior; + DynamicVector acceleration; + + double tmpBestEstimate; + void updateTmpBestEstimate(const double& timeDifference); + + int updateCounter; + + void setPositionDistributionScalar(double f); + + void resetSpeedToOne(); + void addGaussianNoiseToSpeedPosterior(const double& std_dev); + void addTriangularNoiseToSpeedPosterior(const double& std_dev); + + double bestEstimate; + void updateBestEstimate(const double& timeDifference); + double lastBestEstimateUpdateTime; + double speedEstimate; + + double speedDecayWidth, speedDecayAmount; + void decaySpeedDistribution(double timeDifference); + + void resetSpeedSize(int length); + void setRelativeSpeedScalar(double f); + void setSpeedPrior(double f); + void calculateNewPriorOffset(const double& timeDifference); + void setNewDistributionOffsets(const double& newOffset); + + void setLikelihoodToConstant(); + void updateTempoLikelihood(const double& speedRatio, const double& matchFactor); + void updateTempoDistribution(); + + void calculateTempoUpdate(); + + void crossUpdateArrays(DynamicVector& position, DynamicVector& speed, double timeDifference); + void complexCrossUpdate(const double& timeDifferenceInPositionVectorUnits); + void translateByMaximumSpeed(const double& timeDifferenceInPositionVectorUnits); + double crossUpdateTimeThreshold;//time after which we do complex update of multiple speeds + + double speedPriorValue; + int priorWidth; + bool* realTimeMode; + bool usingIntegratedTempoEstimate; + double relativeSpeedLikelihoodStdDev; + +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/CannamMidiFileLoader.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,397 @@ +/* + * CannamMidiFileLoader.cpp + * midi-score-follower + * + * Created by Andrew on 19/08/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +#include "MIDIFileReader.h" +#include "CannamMidiFileLoader.h" + +CannamMidiFileLoader::CannamMidiFileLoader(){ + chopBeginning = true; + firstTickTime = 0; +} + +int CannamMidiFileLoader::loadFile(std::string& filename, midiEventHolder& myMidiEvents){ + + noteOnIndex = 0; + firstTickTime = 0; + myMidiEvents.clearAllEvents(); + + + setTempoFromMidiValue(500000, myMidiEvents);//default is 120bpm + myMidiEvents.pulsesPerQuarternote = 240;//default + //myMidiEvents.measureVector.push_back(0); + //int main(int argc, char **argv) + //{ + // if (argc != 2) { + // cerr << "Usage: midifile <file.mid>" << endl; + // return 1; + // } + + // std::string filename = midiFileName;//argv[1]; + + MIDIFileReader fr(filename); + + if (!fr.isOK()) { + std::cerr << "Error: " << fr.getError().c_str() << std::endl; + return 1; + } + + MIDIComposition c = fr.load(); + + switch (fr.getFormat()) { + case MIDI_SINGLE_TRACK_FILE: cout << "Format: MIDI Single Track File" << endl; break; + case MIDI_SIMULTANEOUS_TRACK_FILE: cout << "Format: MIDI Simultaneous Track File" << endl; break; + case MIDI_SEQUENTIAL_TRACK_FILE: cout << "Format: MIDI Sequential Track File" << endl; break; + default: cout << "Format: Unknown MIDI file format?" << endl; break; + } + + cout << "Tracks: " << c.size() << endl; + + int td = fr.getTimingDivision(); + if (td < 32768) { + if (printMidiInfo) + cout << "Timing division: " << fr.getTimingDivision() << " ppq" << endl; + myMidiEvents.pulsesPerQuarternote = fr.getTimingDivision(); + ticksPerMeasure = myMidiEvents.pulsesPerQuarternote * 4;//default setting + + } else { + int frames = 256 - (td >> 8); + int subframes = td & 0xff; + if (printMidiInfo) + cout << "SMPTE timing: " << frames << " fps, " << subframes << " subframes" << endl; + } + + for (MIDIComposition::const_iterator i = c.begin(); i != c.end(); ++i) { + if (printMidiInfo) + cout << "Start of track: " << i->first+1 << endl; + + for (MIDITrack::const_iterator j = i->second.begin(); j != i->second.end(); ++j) { + + unsigned int t = j->getTime(); + int ch = j->getChannelNumber(); + + if (j->isMeta()) { + int code = j->getMetaEventCode(); + string name; + bool printable = true; + switch (code) { + + case MIDI_END_OF_TRACK: + cout << t << ": End of track" << endl; + break; + + case MIDI_TEXT_EVENT: name = "Text"; break; + case MIDI_COPYRIGHT_NOTICE: name = "Copyright"; break; + case MIDI_TRACK_NAME: name = "Track name"; break; + case MIDI_INSTRUMENT_NAME: name = "Instrument name"; break; + case MIDI_LYRIC: name = "Lyric"; break; + case MIDI_TEXT_MARKER: name = "Text marker"; break; + case MIDI_SEQUENCE_NUMBER: name = "Sequence number"; printable = false; break; + case MIDI_CHANNEL_PREFIX_OR_PORT: name = "Channel prefix or port"; printable = false; break; + case MIDI_CUE_POINT: name = "Cue point"; break; + case MIDI_CHANNEL_PREFIX: name = "Channel prefix"; printable = false; break; + case MIDI_SEQUENCER_SPECIFIC: name = "Sequencer specific"; printable = false; break; + case MIDI_SMPTE_OFFSET: name = "SMPTE offset"; printable = false; break; + + case MIDI_SET_TEMPO: + { + int m0 = j->getMetaMessage()[0]; + int m1 = j->getMetaMessage()[1]; + int m2 = j->getMetaMessage()[2]; + long tempo = (((m0 << 8) + m1) << 8) + m2; + //if (printMidiInfo) + cout << t << ": Tempo: " << 60000000.0 / double(tempo) << endl; + setTempoFromMidiValue(tempo, myMidiEvents); + DoubleVector tmp; + + + double lastTickInMillis = 0; + double millisTimeNow = lastTickInMillis; + int tickInterval = 0; + if (myMidiEvents.periodValues.size() > 0){ + lastTickInMillis = myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][2]; + tickInterval = (t - firstTickTime) - myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][0]; + millisTimeNow = lastTickInMillis + (myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][1]*tickInterval); + + } + + if (!chopBeginning) + tmp.push_back(t); + else + tmp.push_back(t - firstTickTime); + + tmp.push_back(60000000.0 / double(tempo)); + tmp.push_back(millisTimeNow); + myMidiEvents.periodValues.push_back(tmp); + + printf("tick[%i]: TEMPO period %f : time now %f\n", t, 60000000.0 / double(tempo), millisTimeNow); + //printf("period double is %f\n", myMidiEvents.period); + + } + break; + + case MIDI_TIME_SIGNATURE: + { + int numerator = j->getMetaMessage()[0]; + int denominator = 1 << (int)j->getMetaMessage()[1]; + + newTimeSignature(t, numerator, denominator, myMidiEvents); + + //if (printMidiInfo) + cout << t << ": Time signature: " << numerator << "/" << denominator << endl; + printf(" ticks %i Time signature: %i by %i \n", t, numerator , denominator ); + } + + case MIDI_KEY_SIGNATURE: + { + int accidentals = j->getMetaMessage()[0]; + int isMinor = j->getMetaMessage()[1]; + bool isSharp = accidentals < 0 ? false : true; + accidentals = accidentals < 0 ? -accidentals : accidentals; + if (printMidiInfo) + cout << t << ": Key signature: " << accidentals << " " + << (isSharp ? + (accidentals > 1 ? "sharps" : "sharp") : + (accidentals > 1 ? "flats" : "flat")) + << (isMinor ? ", minor" : ", major") << endl; + } + + } + + + if (name != "") { + if (printable) { + cout << t << ": File meta event: code " << code + << ": " << name << ": \"" << j->getMetaMessage() + << "\"" << endl; + } else { + cout << t << ": File meta event: code " << code + << ": " << name << ": "; + for (int k = 0; k < j->getMetaMessage().length(); ++k) { + cout << (int)j->getMetaMessage()[k] << " "; + } + } + } + continue; + } + + switch (j->getMessageType()) { + + case MIDI_NOTE_ON: + if (printMidiInfo) + cout << t << ": Note: channel " << ch + << " duration " << j->getDuration() + << " pitch " << j->getPitch() + << " velocity " << j->getVelocity() + << "event time " << myMidiEvents.getEventTimeMillis(t) << endl; + + + if (noteOnIndex == 0 || t < firstTickTime){ + //easier just to pick the minimum + firstTickTime = t; + printf("FIRST TICK TIME %i\n", firstTickTime); + } + + noteOnIndex++; + + v.clear(); + + // printf("note on at %i\n", t); + + if (!chopBeginning) + v.push_back(t); + else + v.push_back(t - firstTickTime); + + v.push_back(j->getPitch()); + v.push_back(j->getVelocity()); + v.push_back(j->getDuration()); + myMidiEvents.recordedNoteOnMatrix.push_back(v); + + myMidiEvents.noteOnMatches.push_back(false); + + break; + + case MIDI_POLY_AFTERTOUCH: + if (printMidiInfo) + cout << t << ": Polyphonic aftertouch: channel " << ch + << " pitch " << j->getPitch() + << " pressure " << j->getData2() << endl; + break; + + case MIDI_CTRL_CHANGE: + { + int controller = j->getData1(); + string name; + switch (controller) { + case MIDI_CONTROLLER_BANK_MSB: name = "Bank select MSB"; break; + case MIDI_CONTROLLER_VOLUME: name = "Volume"; break; + case MIDI_CONTROLLER_BANK_LSB: name = "Bank select LSB"; break; + case MIDI_CONTROLLER_MODULATION: name = "Modulation wheel"; break; + case MIDI_CONTROLLER_PAN: name = "Pan"; break; + case MIDI_CONTROLLER_SUSTAIN: name = "Sustain"; break; + case MIDI_CONTROLLER_RESONANCE: name = "Resonance"; break; + case MIDI_CONTROLLER_RELEASE: name = "Release"; break; + case MIDI_CONTROLLER_ATTACK: name = "Attack"; break; + case MIDI_CONTROLLER_FILTER: name = "Filter"; break; + case MIDI_CONTROLLER_REVERB: name = "Reverb"; break; + case MIDI_CONTROLLER_CHORUS: name = "Chorus"; break; + case MIDI_CONTROLLER_NRPN_1: name = "NRPN 1"; break; + case MIDI_CONTROLLER_NRPN_2: name = "NRPN 2"; break; + case MIDI_CONTROLLER_RPN_1: name = "RPN 1"; break; + case MIDI_CONTROLLER_RPN_2: name = "RPN 2"; break; + case MIDI_CONTROLLER_SOUNDS_OFF: name = "All sounds off"; break; + case MIDI_CONTROLLER_RESET: name = "Reset"; break; + case MIDI_CONTROLLER_LOCAL: name = "Local"; break; + case MIDI_CONTROLLER_ALL_NOTES_OFF: name = "All notes off"; break; + } + if (printMidiInfo) + cout << t << ": Controller change: channel " << ch + << " controller " << j->getData1(); + if (name != "") cout << " (" << name << ")"; + cout << " value " << j->getData2() << endl; + } + break; + + case MIDI_PROG_CHANGE: + if (printMidiInfo) + cout << t << ": Program change: channel " << ch + << " program " << j->getData1() << endl; + break; + + case MIDI_CHNL_AFTERTOUCH: + if (printMidiInfo) + cout << t << ": Channel aftertouch: channel " << ch + << " pressure " << j->getData1() << endl; + break; + + case MIDI_PITCH_BEND: + if (printMidiInfo) + cout << t << ": Pitch bend: channel " << ch + << " value " << (int)j->getData2() * 128 + (int)j->getData1() << endl; + break; + + case MIDI_SYSTEM_EXCLUSIVE: + if (printMidiInfo) + cout << t << ": System exclusive: code " + << (int)j->getMessageType() << " message length " << + j->getMetaMessage().length() << endl; + break; + + + } + + + } + + + } + if (printMidiInfo) + myMidiEvents.printRecordedEvents(); + + //printMeasuresSoFar(myMidiEvents); + + if (myMidiEvents.recordedNoteOnMatrix.size() > 0){ + + printf("END FILE MEASURE UPDATE\n"); + updateMeasureToTickPosition(myMidiEvents.recordedNoteOnMatrix[myMidiEvents.recordedNoteOnMatrix.size()-1][0], myMidiEvents); + // printMeasuresSoFar(myMidiEvents); + } + +// printf("|||||||||||||||||||||| \n\n\n\n\n\n\n"); + myMidiEvents.reorderMatrixFromNoteTimes(myMidiEvents.recordedNoteOnMatrix); + myMidiEvents.correctTiming(myMidiEvents.recordedNoteOnMatrix); + myMidiEvents.doublecheckOrder(myMidiEvents.recordedNoteOnMatrix); + + createEventTiming(myMidiEvents); + + printf("BEFORE DOING MEASURE UPDATE\n first tick pos is %i\n", firstTickTime); +// printMeasuresSoFar(myMidiEvents); + if (chopBeginning) + correctMeasuresTiming(myMidiEvents); + + if (printMidiInfo) + myMidiEvents.printRecordedEvents(); + + printf("Duration of MIDI file is %f \n", myMidiEvents.recordedEventTimes[myMidiEvents.recordedEventTimes.size()-1]); + + //printMeasuresSoFar(myMidiEvents); + +}//end cannam midi main + + +void CannamMidiFileLoader::createEventTiming( midiEventHolder& myMidiEvents){ + + long t; + t = myMidiEvents.recordedNoteOnMatrix[0][0]; + firstNoteTime = myMidiEvents.getEventTimeMillis(t); + + for (int i = 0; i < myMidiEvents.recordedNoteOnMatrix.size();i++){ + t = myMidiEvents.recordedNoteOnMatrix[i][0]; + + if (!chopBeginning) + myMidiEvents.recordedEventTimes.push_back(myMidiEvents.getEventTimeMillis(t)); + else { + myMidiEvents.recordedEventTimes.push_back(myMidiEvents.getEventTimeMillis(t) - firstNoteTime); + + } + } + +} + +void CannamMidiFileLoader::setTempoFromMidiValue(long tempo, midiEventHolder& myMidiEvents){ + myMidiEvents.tempo = 60000000.0 / double(tempo); + myMidiEvents.period = double(tempo)/1000.0; + myMidiEvents.ticksFactor = myMidiEvents.pulsesPerQuarternote / myMidiEvents.period; +} + + +void CannamMidiFileLoader::newTimeSignature(int ticks, int numerator, int denominator, midiEventHolder& myMidiEvents){ + + updateMeasureToTickPosition(ticks, myMidiEvents); + + ticksPerMeasure = myMidiEvents.pulsesPerQuarternote * 4 * numerator / denominator; + +} + +void CannamMidiFileLoader::updateMeasureToTickPosition(int ticks, midiEventHolder& myMidiEvents){ + printf("update measure at tick pos %i at tpm %i\n", ticks, ticksPerMeasure); + + int measureVectorSize = myMidiEvents.measureVector.size(); + int lastMeasurePosition = 0; +// if (chopBeginning) +// lastMeasurePosition = -1*firstTickTime; + + if (measureVectorSize > 0) + lastMeasurePosition = myMidiEvents.measureVector[measureVectorSize-1]; + + +while (lastMeasurePosition < ticks){ + //update + lastMeasurePosition += ticksPerMeasure; + myMidiEvents.measureVector.push_back(lastMeasurePosition); +// cout << "MEASURE " << myMidiEvents.measureVector.size()-1 << " is " << lastMeasurePosition << endl; +// printf("MEASURE %i is %i \n", (int)myMidiEvents.measureVector.size()-1 , lastMeasurePosition); + } +} + + +void CannamMidiFileLoader::correctMeasuresTiming(midiEventHolder& myMidiEvents){ + //correct measures + for (int i = 0;i <myMidiEvents.measureVector.size();i++){ + myMidiEvents.measureVector[i] -= firstTickTime; + } + printf("AFTER DOING MEASURE UPDATE\n"); + printMeasuresSoFar(myMidiEvents); +} + +void CannamMidiFileLoader::printMeasuresSoFar(midiEventHolder& myMidiEvents){ + for (int i = 0;i < myMidiEvents.measureVector.size();i++){ + printf("measure [%i] at %i\n", i, myMidiEvents.measureVector[i]); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/CannamMidiFileLoader.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,45 @@ +/* + * CannamMidiFileLoader.h + * midi-score-follower + * + * Created by Chris Cannam on 19/08/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +#ifndef CANNAM_MIDI_FILE_LOADER +#define CANNAM_MIDI_FILE_LOADER + +#include "MIDIFileReader.h" +//#include "MIDIEvent.h" +#include "midiEventHolder.h" +using namespace MIDIConstants; + +class CannamMidiFileLoader{ + +public: + CannamMidiFileLoader(); + + typedef std::vector<double> DoubleVector; + + int loadFile(std::string& filename, midiEventHolder& myMidiEvents); + + void createEventTiming( midiEventHolder& myMidiEvents); + void setTempoFromMidiValue(long tempo, midiEventHolder& myMidiEvents); + double firstNoteTime; + int firstTickTime; + bool chopBeginning; + + typedef std::vector<int> IntVector; + IntVector v; + int noteOnIndex; + + int ticksPerMeasure; + void newTimeSignature(int ticks, int numerator, int denominator, midiEventHolder& myMidiEvents); + void updateMeasureToTickPosition(int ticks, midiEventHolder& myMidiEvents); + bool printMidiInfo; + void printMeasuresSoFar(midiEventHolder& myMidiEvents); + void correctMeasuresTiming(midiEventHolder& myMidiEvents); + +}; +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/DynamicVector.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,293 @@ +/* + * DynamicVector.cpp + * midiCannamReader + * + * Created by Andrew on 18/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + + +#include "DynamicVector.h" + +DynamicVector::DynamicVector(){ + length = 0; + arraySize = 0; + maximumValue = 0; + MAPestimate = 0; + offset = 0; + scalar = 1; + integratedEstimate = length/2; + + gaussianLookupMean = (double) GAUSSIAN_LOOKUP_LENGTH/2; + gaussianLookupStdDev = (double)(GAUSSIAN_LOOKUP_LENGTH/16); + double factor = 1.0;//(1.0 / (gaussianLookupStdDev*sqrt(2*PI)) );//1.0;//-1.0/(2*PI*sqrt(gaussianLookupStdDev)); + for (int i = 0;i < GAUSSIAN_LOOKUP_LENGTH;i++){ + gaussianLookupTable[i] = factor*exp(-1.0*(i-gaussianLookupMean)*(i-gaussianLookupMean)/(2.0*gaussianLookupStdDev*gaussianLookupStdDev)); + } + +} + +void DynamicVector::copyFromDynamicVector(const DynamicVector& dynamicVec){ + if (dynamicVec.length == length){ + for (int i = 0;i < length;i++) + array[i] = dynamicVec.array[i]; + } + else{ + printf("CANNOT COPY VECTORS OF NON SAME LENGTH!!\n"); + } +} + +void DynamicVector::createVector(int len){ + array.clear(); + for (int i = 0; i < len;i++){ + array.push_back(0); + } + length = len; + arraySize = array.size(); + integratedEstimate = length/2; +} + + +double DynamicVector::getMaximum(){ + int i; + double max = 0; + for (i=0;i < length;i++){ + if (array[i] > max){ + max = array[i]; + MAPestimate = i; + } + } + maximumValue = max; + return max; +} + +double DynamicVector::getIntegratedEstimate(){ + //returns the index of the integrated average - where the probability distribution is centred + integratedEstimate = 0; + double integratedTotal = 0; + for (int i = 0;i < length;i++){ + integratedEstimate += array[i]*i; + integratedTotal += array[i]; + } + if (integratedTotal > 0){ + integratedEstimate /= integratedTotal; + } + return integratedEstimate; +} + +void DynamicVector::updateIntegratedEstimate(){ + //returns the index of the integrated average - where the probability distribution is centred + integratedEstimate = 0; + double integratedTotal = 0; + for (int i = 0;i < length;i++){ + integratedEstimate += array[i]*i; + integratedTotal += array[i]; + } + if (integratedTotal > 0){ + integratedEstimate /= integratedTotal; + } + +} + +void DynamicVector::updateLimitedIntegratedEstimate(){ + //returns the index of the integrated average - where the probability distribution is centred + //but limited round the MAP estimate + double tmp = getMaximum(); + int limit = min(MAPestimate, length - MAPestimate); + int start = max(0, MAPestimate - limit); + int end = min(MAPestimate + limit, length-1); + + integratedEstimate = 0; + double integratedTotal = 0; + for (int i = start;i <= end;i++){ + integratedEstimate += array[i]*i; + integratedTotal += array[i]; + } + if (integratedTotal > 0){ + integratedEstimate /= integratedTotal; + } + +} + + +void DynamicVector::zero(){ + for (int i = 0;i < array.size();i++) + array[i] = 0; +} + +void DynamicVector::renormalise(){ + double tmpMax = getMaximum(); + if (tmpMax > 0){ +// printf("renormalise : max is %f and size is %i\n", tmpMax, arraySize); + for (int i = 0;i < array.size();i++) + array[i] /= tmpMax; + + } + //printArray(); +} + +void DynamicVector::doProduct(DynamicVector& arrayOne, DynamicVector& arrayTwo){ + + for (int i = 0;i < arrayOne.length;i++) + array[i] = arrayOne.array[i] * arrayTwo.array[i]; +} + + +void DynamicVector::printArray(){ + for (int i = 0;i < arraySize;i++){ + printf("[%i] = %f\n", i, array[i]); + } +} + +void DynamicVector::translateDistribution(int translationIndex){ + int tmpIndex; + DoubleVector tmpArray; + int i; + + for (i=0;i < arraySize;i++){ + tmpArray.push_back(array[i]); + } + //translate values + for (i=0;i < arraySize;i++){ + tmpIndex = (i + translationIndex + arraySize)%arraySize; + array[tmpIndex] = tmpArray[i]; + } + tmpArray.clear(); + //now delete tmp array +} + +void DynamicVector::addGaussianShape(const double& mean, const double& StdDev, double factor){ + + int i; + double std_dev_factor = (2*StdDev*StdDev); + factor *= (1/(StdDev*sqrt(2*PI))); + int maxVal = min((int) array.size(), (int)(mean + 4.8*StdDev)); + int minVal = max(0, (int)(mean - 4.8*StdDev)); + + for (i=minVal;i < maxVal;i++){ + array[i] += factor*exp(-1*(i-mean)*(i-mean)/(std_dev_factor)); + } + +// addGaussianShapeByLookupTable(mean, StdDev, factor); +} + +void DynamicVector::addGaussianShapeByLookupTable(double& mean, double& StdDev, double factor){ + int i; + int lookupIndex ; + factor *= (1/(StdDev*sqrt(2*PI))); + for (i=0;i<array.size()-1;i++){ + lookupIndex = round(getLookupIndex(i, mean, StdDev)); + array[i] += factor*gaussianLookupTable[lookupIndex]; + } + //printf("ADDED GAUSSIAN SHAPE %i\n", (int)array.size()); +} + +double DynamicVector::getLookupIndex(const int& i, const double& mean, const double& StdDev){ + + double Z = ((double)i - mean)/StdDev; + double lookupIndex = Z*gaussianLookupStdDev + gaussianLookupMean; + + if (lookupIndex < 0) + lookupIndex = 0; + + if (lookupIndex >= GAUSSIAN_LOOKUP_LENGTH) + lookupIndex = GAUSSIAN_LOOKUP_LENGTH-1; + +// (i - mean)*(i-mean)*(GAUSSIAN_LOOKUP_LENGTH*GAUSSIAN_LOOKUP_LENGTH/16.0)/(StdDev*StdDev); + return lookupIndex; +} + +void DynamicVector::addTriangularShape(double mean, double width, double factor){ + int i; + + for (i= max(0., (double)(mean - width));i < min((mean+width), (double)array.size());i++){ + array[i] += factor * abs(i - mean) / mean; + } + +} + +void DynamicVector::addConstant(const double& value){ + for (int i=0;i<array.size();i++){ + array[i] += value; + } +} + + +void DynamicVector::addToIndex(const int& index, const double& constant){ + array[index] += constant; +} + + +double DynamicVector::getIndexInRealTerms(const int& index){ + if (index < arraySize) + return (offset + scalar*index); + else + return 0; +} + +double DynamicVector::getRealTermsAsIndex(double value){ + value -= offset; + value /= scalar; + + return value; + +} + +double DynamicVector::getValueAtMillis(const double& millis){ + + int index = round(getRealTermsAsIndex(millis)); + if (index >= 0 && index < length) + return array[index]; + else + return 0; +} + +void DynamicVector::drawVector(const int& minIndex, const int& maxIndex){ + + + double stepSize = ofGetWidth() / (double)(maxIndex - minIndex); + double screenHeight = (double) ofGetHeight(); + double maxVal = getMaximum(); + + int startInt = max(1,minIndex+1); + int endInt = min(maxIndex, (int)array.size()); + double heightConstant = screenHeight / maxVal; + int lastHeightPixel = heightConstant * (maxVal - array[startInt-1]); + int newHeightPixel; + for (int i = startInt;i < endInt;i++){ + newHeightPixel = (int) heightConstant * (maxVal - array[i]); + ofLine (stepSize*(i-1), lastHeightPixel, stepSize*i, newHeightPixel); + lastHeightPixel = newHeightPixel; + } + +} + + +void DynamicVector::drawConstrainedVector(const int& minIndex, const int& maxIndex, const int& minScreenIndex, const int& maxScreenIndex){ + //constrain the height and width + + double stepSize = (maxScreenIndex - minScreenIndex) / (double)(maxIndex - minIndex);//step size in pixels per array bin + double screenHeight = ofGetHeight(); + double maxVal = getMaximum(); + + //OPTIMIZE!! XXX could just add stepsize each time + //not add minindex each time + int i = max(1,minIndex+1); +// ofDrawBitmapString("i = "+ofToString(i)+" :: screen min: "+ofToString(minScreenIndex + stepSize*(i-minIndex-1)), 20, 640); + + while ((minScreenIndex + stepSize*(i-minIndex)) < 0) + i++;//only draw what is on the screen + + for ( ; i < min(maxIndex+1, (int)array.size());i++){ + ofLine (minScreenIndex + (stepSize*(i-minIndex-1)), screenHeight * (1 - array[i-1] / maxVal), + minScreenIndex + (stepSize*(i-minIndex)), screenHeight * (1 - array[i] / maxVal) ); + + } + + ofLine(minScreenIndex, screenHeight, minScreenIndex, screenHeight/2); + ofLine(maxScreenIndex, screenHeight, maxScreenIndex, screenHeight/2); + +// ofDrawBitmapString(ofToString(stepSize, 2)+" "+ofToString(maxScreenIndex - minScreenIndex, 0), 20, 600); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/DynamicVector.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,68 @@ +/* + * DynamicVector.h + * midiCannamReader + * + * Created by Andrew on 18/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +//OPTIMIZE CONSTRAINED VECTOR + +#include "stdlib.h" +#include "ofMain.h" + +#ifndef _DYNAMIC_VECTOR +#define _DYNAMIC_VECTOR +#define GAUSSIAN_LOOKUP_LENGTH 1000000 + +class DynamicVector{ +public: + DynamicVector(); + + void createVector(int len); + void renormalise(); + void translateDistribution(int translationIndex); + typedef std::vector<double> DoubleVector; + DoubleVector array; + double getMaximum(); + double getIntegratedEstimate(); + double getLookupIndex(const int& i, const double& mean, const double& StdDev); + void addGaussianShapeByLookupTable(double& mean, double& StdDev, double factor); + double gaussianLookupTable[GAUSSIAN_LOOKUP_LENGTH]; + double gaussianLookupMean, gaussianLookupStdDev; + double integratedEstimate; + void updateIntegratedEstimate(); + void updateLimitedIntegratedEstimate(); + + void drawVector(const int& minIndex, const int& maxIndex); + void drawConstrainedVector(const int& minIndex, const int& maxIndex, const int& minScreenIndex, const int& maxScreenIndex); + + void addConstant(const double& value); + void addGaussianShape(const double& mean, const double& stddev, double factor); + void addTriangularShape(double mean, double width, double factor); + void addToIndex(const int& index, const double& constant); + + void doProduct(DynamicVector& arrayOne, DynamicVector& arrayTwo); + + double getIndexInRealTerms(const int& index); + double getRealTermsAsIndex(double value); + double getValueAtMillis(const double& millis); + + void printArray(); + void zero(); + + void copyFromDynamicVector(const DynamicVector& dynamicVec); + + //variables + int length, arraySize; + double maximumValue; + int MAPestimate; + + double offset; + double scalar;//each array point is this much of the quantity + //i.e. array[index] contributes to (offset + scalar*index) in real terms + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/MidiInputStream.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,108 @@ +/* + * MidiInputStream.cpp + * MuseScoreMidiFollower + * + * Created by Andrew on 03/12/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +#include "MidiInputStream.h" + + +MidiInputStream::MidiInputStream(){ + reset(); +} + + +void MidiInputStream::reset(){ + + midiInputEvents.clear(); + midiInputTimes.clear(); + + eventTimesForNote.clear(); + eventTimesForNote.assign (127, 0.0); + + + totalNotesRecievedByPitch.clear(); + totalNotesRecievedByPitch.assign (127, 0); + +} + +bool MidiInputStream::noteInReceived(ofxMidiEventArgs &args){ +// printf("midi input received port %i, channel %i status %i, byteOne %i, byteTwo %i\n", args.port,args.channel, args.status, newPitch, args.byteTwo); + bool noteOn = false; + int newPitch; + switch (args.status) { + + case 144: + newPitch = (int)args.byteOne; + newPitch += (*transposeVal); + + // printf("note on %i", args.byteOne); + if (args.byteTwo){ + // printf("volume is %i\n", args.byteTwo); + double time = ofGetElapsedTimeMillis(); + eventTimesForNote[newPitch] = time; + noteOn = true; + newNoteCounted(newPitch); + + }else{ + double time = ofGetElapsedTimeMillis(); + time -= eventTimesForNote[newPitch]; + // printf(", NOTE OFF after %f millis %f ticks\n", time, time * (*factor)); + + int index = endNote(newPitch); + //correct the length of note time + if (midiInputEvents[index].size() > 2) + midiInputEvents[index][3] = (time * (*factor)); + } + + break; + default: + break; + } + + return noteOn; + //cout << "MIDI message [port: " << args.port << ", channel: " << args.channel << ", status: " << args.status << ", byteOne: " << newPitch << ", byteTwo: " << args.byteTwo << ", timestamp: " << args.timestamp << "]" << endl; +} + +void MidiInputStream::newNoteCounted(const int& pitch){ + totalNotesRecievedByPitch[pitch] += 1; +} + +int MidiInputStream::endNote(int notePitch){ + int index = midiInputEvents.size()-1; + while (index > 0 && midiInputEvents[index][1] != notePitch){ + index--; + } +// printf("found index %i\n", index); + return index; +} + +double MidiInputStream::calculateTotalScore(midiEventHolder& midiEvents){ + double gameScore = 1.0; + double totalNotes = 1.0; + if (totalNotesRecievedByPitch.size() > 100){ + for (int i = 30;i < 90;i++){ + gameScore += abs(totalNotesRecievedByPitch[i] - midiEvents.recordedTotalNoteCounterByPitch[i]); + totalNotes += midiEvents.recordedTotalNoteCounterByPitch[i]; + } + } + return 1.0 - (gameScore / totalNotes); + +} + +void MidiInputStream::printNotes(){ + printf("live input \n"); + for (int i = 0;i < midiInputEvents.size();i++){ + printf("ticktime %i :: pitch %i @ millis %f\n", midiInputEvents[i][0], midiInputEvents[i][1], midiInputTimes[i]); + } +} + +void MidiInputStream::printTotalCount(){ + for (int i = 0;i < totalNotesRecievedByPitch.size();i++){ + printf("LIVE PLAYED TOTAL[%i] := %i\n", i, totalNotesRecievedByPitch[i]); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/MidiInputStream.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,52 @@ +/* + * MidiInputStream.h + * MuseScoreMidiFollower + * + * Created by Andrew on 03/12/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +#ifndef MIDI_INPUT_STREAM +#define MIDI_INPUT_STREAM + +#include "ofMain.h" +#include "ofxMidi.h" +#include "midiEventHolder.h" +//WOULD BE GOOD TO USE STD CONSTANTS here +//#include "MIDIFileReader.h" +//using namespace MIDIConstants; + +class MidiInputStream{ + +public: + MidiInputStream(); + bool noteInReceived(ofxMidiEventArgs &args); + void reset(); + void printNotes(); + int endNote(int notePitch); + + typedef std::vector<int> IntVector; + typedef std::vector<IntVector> IntMatrix; + + typedef std::vector<double> DoubleVector; + DoubleVector eventTimesForNote; + + double calculateTotalScore(midiEventHolder& midiEvents); + + IntMatrix midiInputEvents; + DoubleVector midiInputTimes; + IntVector v; + IntVector totalNotesRecievedByPitch; + void newNoteCounted(const int& pitch); + void printTotalCount(); + + double* startTime; + midiEventHolder* eventHolder; + + int* transposeVal; + + double* factor; + +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/drawMidiNotes.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,87 @@ +/* + * drawMidiNotes.cpp + * midiCannamReader + * + * Created by Andrew on 17/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + +#include "drawMidiNotes.h" + +drawMidiNotes::drawMidiNotes(){ + + + ticksPerScreen = 8000; + tickLocation = 0; + pulsesPerQuarternote = 240; + noteArrayIndex = 0; + noteMinimum = 30; + noteMaximum = 96; + screenWidth = ofGetWidth(); + screenHeight = ofGetHeight(); + noteHeight = screenHeight / (float)(noteMaximum - noteMinimum); +} + +void drawMidiNotes::reset(){ + noteArrayIndex = 0; + tickLocation = 0; + lastPeriodUpdateTime = ofGetElapsedTimeMillis(); + +} + +void drawMidiNotes::updatePlayPosition(){ + double timeDifference = ofGetElapsedTimeMillis() - lastPeriodUpdateTime; + //this is time diff in milliseconds + //then we have + double quarterNoteIntervals = (timeDifference / period); + tickLocation = quarterNoteIntervals * pulsesPerQuarternote; + +} + +void drawMidiNotes::drawFile(const IntMatrix& noteOnMatrix){ + int size = noteOnMatrix.size(); + if (size > 0){ + + int numberOfScreensIn = tickLocation / ticksPerScreen; + + while (noteArrayIndex < noteOnMatrix.size() && tickLocation > noteOnMatrix[noteArrayIndex][0] ) + noteArrayIndex++; + + while (noteArrayIndex > 0 && noteArrayIndex < size && tickLocation < noteOnMatrix[noteArrayIndex][0]) + noteArrayIndex--; + + //need to start where we currently are in file + int maxNoteIndexToPrint = noteArrayIndex; + int minNoteIndexToPrint = noteArrayIndex; + + + while (maxNoteIndexToPrint < noteOnMatrix.size() && noteOnMatrix[maxNoteIndexToPrint][0] < (numberOfScreensIn+1)*ticksPerScreen ) + maxNoteIndexToPrint++; + + while (minNoteIndexToPrint > 0 && minNoteIndexToPrint < size && noteOnMatrix[minNoteIndexToPrint][0] > numberOfScreensIn*ticksPerScreen) + minNoteIndexToPrint--; + + + for (int tmpIndex = minNoteIndexToPrint;tmpIndex < maxNoteIndexToPrint;tmpIndex++){ + int xLocation = (float)(noteOnMatrix[tmpIndex][0] - numberOfScreensIn*ticksPerScreen)*screenWidth/(float)ticksPerScreen; + int duration = (float)(noteOnMatrix[tmpIndex][3]*screenWidth)/(float)ticksPerScreen; + + + int yLocation = screenHeight - ((noteOnMatrix[tmpIndex][1] - noteMinimum )*screenHeight/ (float)(noteMaximum - noteMinimum)); + ofRect(xLocation,yLocation, duration, noteHeight); + + } + + int xLocation = (float)(tickLocation - numberOfScreensIn*ticksPerScreen)*screenWidth/(float)ticksPerScreen; + ofLine(xLocation, 0, xLocation, screenHeight); + + // if (noteArrayIndex < size ) + // printf("tick %i :: note array :%i: %i\n", tickLocation, noteArrayIndex, noteOnMatrix[noteArrayIndex][0]); + // else + // printf("end of file\n"); + + + } + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/drawMidiNotes.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,42 @@ +/* + * drawMidiNotes.h + * midiCannamReader + * + * Created by Andrew on 17/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ + + +#include "ofMain.h" + +class drawMidiNotes{ +public: + drawMidiNotes(); + void updatePlayPosition(); + + typedef std::vector<double> DoubleVector; + typedef std::vector<DoubleVector> DoubleMatrix; + + DoubleMatrix beatPeriodMatrix; + + typedef std::vector<int> IntVector; + typedef std::vector<IntVector> IntMatrix; + + void drawFile(const IntMatrix& noteOnMatrix); + void reset(); + + int ticksPerScreen; + int tickLocation; + int noteArrayIndex; + + int noteMinimum, noteMaximum; + int screenWidth, screenHeight; + float noteHeight; + float tempo; + double period; + int pulsesPerQuarternote; + double lastPeriodUpdateTime; + + +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/hackday_notes.txt Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,36 @@ +hackney wiki - london hacks link for full list + +the matrix + +ctrl + +beat tracking + +musochackday app - spotifyle system + +dylanjones.info - Facebook music taste game + +drivetime - code on github + +scotd.herokuapp.com + +mmsm.herokuapp.com - yves' monster mashup + +ethicsgirls.com/pitchforkeffect + +github for "guess the intro" app in spiffy + +owl octave! + + +gig out iphone app: share test flight - foursquare venue data - gigs around you - at export?? on twitter + +chordify spotify app and pretentiometer + +alarm clock app + + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/main.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,25 @@ +#include "ofMain.h" +#include "testApp.h" +#include "ofAppGlutWindow.h" +#include "ofxArgs.h" + +//======================================================================== +int main(int argc, char *argv[]){ + ofxArgs* args = new ofxArgs(argc, argv); + + if (argc > 0){ + cout << "MIDI FOLLOW (" << argc << ") arg[1] is " << argv[1] << endl; + } + + ofAppGlutWindow window; + ofSetupOpenGL(&window, 1024,768, OF_WINDOW); // <-------- setup the GL context + + // this kicks off the running of my app + // can be OF_WINDOW or OF_FULLSCREEN + // pass in width and height too: + + ofRunApp(new testApp(args)); + delete args; + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/midiEventHolder.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,1216 @@ +/* + * 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" + +midiEventHolder::midiEventHolder(){ +// recordedNoteOnIndex = 0; + + useTempoPrior = false;//puts sine wave round tempo + confidenceWeightingUsed = true; + newOptimalMethod = true; + + matchWindowWidth = 8000;//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; + + + //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 + speedPriorValue = 1.0; + 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; + lastPeriodUpdateTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + bayesStruct.lastEventTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + numberOfScreensIn = 0; +// recordedNoteOnIndex = 0; + bayesStruct.setNewDistributionOffsets(0); + bayesStruct.posterior.offset = 0; + + playedEventTimes.clear(); + playedNoteOnMatrix.clear(); + matchMatrix.clear(); + bestMatchIndex = 0; + + recordedTotalNoteCounterByPitch.clear(); + recordedTotalNoteCounterByPitch.assign(127,0); + totalNoteCounterIndex = 0; + + interNoteIntervals.clear(); + + bayesStruct.resetSpeedToOne(); + bayesStruct.setSpeedPrior(speedPriorValue); + setMatchedNotesBackToFalse(); + +} + +void midiEventHolder::setMatchedNotesBackToFalse(){ + for (int i = 0;i < noteOnMatches.size();i++) + noteOnMatches[i] = false; +} + +void midiEventHolder::clearAllEvents(){ + recordedNoteOnMatrix.clear(); + matchesFound.clear(); + noteOnMatches.clear(); + recordedEventTimes.clear(); + measureVector.clear(); + //played events: + playedEventTimes.clear(); + playedNoteOnMatrix.clear(); + matchMatrix.clear(); + bestMatchFound.clear(); + periodValues.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 (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); + +// 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(); + } + +} + +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::findLocalTempoPairsWeightedForConfidence(){ + bool needToUpdate = false; + + //adapted this to just use the best match for each note + + int currentPlayedIndex = playedNoteOnMatrix.size()-1; + // printf("played %i : %i, vel %i\n", currentPlayedIndex, playedNoteOnMatrix[currentPlayedIndex][0], playedNoteOnMatrix[currentPlayedIndex][1]); + // printMatchesFound(); + // printMatchMatrix(); + // printf("possible notes \n"); + + bayesStruct.setLikelihoodToConstant(); + + for (int i = 0;i < matchMatrix[currentPlayedIndex][0];i++){ + + //iterate through the recently matched events - even dodgy matches included + //size, index of match0, index of match1, .... + int recordedCurrentIndex = matchMatrix[currentPlayedIndex][i+1]; + + double currentMatchConfidence = matchConfidence[currentPlayedIndex][i+1];//new confidence + + int previousIndex = currentPlayedIndex-1; + + while (previousIndex >= 0 && playedEventTimes[previousIndex] + speedWindowWidthMillis > playedEventTimes[currentPlayedIndex]) { + double playedTimeDifference = playedEventTimes[currentPlayedIndex] - playedEventTimes[previousIndex]; + + // for (int k = 0;k < matchMatrix[previousIndex][0];k++) + int recordedPreviousIndex = bestMatchFound[previousIndex];//matchMatrix[previousIndex][k+1]; + + //double previousMatchConfidence = matchConfidence[previousIndex][k+1]; + + + double recordedTimeDifference = recordedEventTimes[recordedCurrentIndex] - recordedEventTimes[recordedPreviousIndex]; + + + //we want the speed of the recording relative to that of the playing live + + double speedRatio = recordedTimeDifference / playedTimeDifference; + if (speedRatio <= maximumMatchSpeed && speedRatio >= minimumMatchSpeed){ + + printf("(%i)", matchMatrix[currentPlayedIndex][i+1]); + printf("[%i] :: ", recordedPreviousIndex); + // printf(" conf %f & %f ", currentMatchConfidence, previousMatchConfidence); + printf(" rec{%f} vs play(%f) ", recordedTimeDifference, playedTimeDifference); + printf("update on speed ratio %f\n", speedRatio); + + // matchString += " speed: "+ofToString(speedRatio, 3); + // commented for debug + + + double priorWeighting = 1; + + if (useTempoPrior) + priorWeighting = sin(speedRatio * PI/2);//adding in a prior that prefers 1.0 speed + + + // double weighting = previousMatchConfidence * currentMatchConfidence ; + double amount = (1-bayesStruct.speedLikelihoodNoise)*priorWeighting/10; + bayesStruct.updateTempoLikelihood(speedRatio, amount);//second paramter is confidence in the match + tempoSpeedString += ofToString(recordedPreviousIndex) + " " + ofToString(speedRatio, 2) + " "+ofToString(amount, 2) += " \n"; + + needToUpdate = true; + } + // printf("\n"); + + + previousIndex--; + }//end while previousindex countdown + }//end for loop through possible current matches + + if (needToUpdate) + bayesStruct.updateTempoDistribution(); + //printf("current speed is %f\n", bayesStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesStruct.relativeSpeedPosterior.MAPestimate)); +} +*/ + + +void midiEventHolder::updatePlayPosition(){ + + //in actual fact if we are changing the speed of the play position + //we will need to update this via the file + + //actually time since beginning of file i think + + double timeDifference = 0; + if (runningInRealTime) + timeDifference = ofGetElapsedTimeMillis() - lastPeriodUpdateTime;//elpased - lastperiodupdatetime + + //this is time diff in milliseconds + //then we have + double quarterNoteIntervals = (timeDifference / period); + tickLocation = quarterNoteIntervals * pulsesPerQuarternote; + + playPositionInMillis = timeDifference;//based on updating from when we change period + //this to be added + + if (runningInRealTime) + bayesStruct.updateBestEstimate(timeDifference); + + updateNoteCounter(); + +} + +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(80,80,80);//250,100,0); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + xLocation = getLocationFromMillis(bayesStruct.tmpBestEstimate); + ofSetColor(150,150,150);//250,100,0); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + + //lines where matching window start and end are + ofSetColor(0);//0,100,255); + xLocation = getLocationFromMillis(windowStartTime); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + xLocation = getLocationFromMillis(windowStartTime+matchWindowWidth); + ofLine(xLocation, 0, xLocation, (*screenHeight)); + + + int maxSize = recordedNoteOnMatrix[size-1][0]; + + 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(); + + +// 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 (int)((float)(tickPosition - numberOfScreensIn*ticksPerScreen)*(*screenWidth)/(float)ticksPerScreen); +} + +int midiEventHolder::getLocationFromMillis(double millisPosition){ + //(getEventTimeTicks(windowStartTime+matchWindowWidth) - numberOfScreensIn*ticksPerScreen)*(*screenWidth) / (double)ticksPerScreen + return (millisPosition - timeOffsetForScreen)*(*screenWidth)/getEventTimeMillis(ticksPerScreen); +} + + +void midiEventHolder::exampleCrossUpdate(){ + + bayesStruct.crossUpdateArrays(bayesStruct.posterior, bayesStruct.relativeSpeedPosterior, 200); + +} + + +void midiEventHolder::setStartPlayingTimes(){ + lastPeriodUpdateTime = getTimeNow(0);//ofGetElapsedTimeMillis(); + startTime = lastPeriodUpdateTime; + +/* + bayesStruct.lastEventTime = 0;//ofGetElapsedTimeMillis(); + bayesStruct.bestEstimate = 0; + bayesStruct.resetArrays(); + bayesStruct.lastBestEstimateUpdateTime = ofGetElapsedTimeMillis(); +*/ + bayesStruct.setStartPlaying(); + matchString = ""; +} + + +void midiEventHolder::printMatchMatrix(){ + printf("match matrix:\n"); + for (int i = 0;i < matchMatrix.size();i++){ + for (int k = 0;k < matchMatrix[i].size();k++){ + printf("%i , ", matchMatrix[i][k]); + } + printf("\n"); + } + +} + + + +void midiEventHolder::printRecordedEvents(){ + printf("Recorded Events:\n"); + for (int i = 0;i < recordedNoteOnMatrix.size();i++){ + for (int k = 0;k < recordedNoteOnMatrix[i].size();k++){ + printf("[%i] = %i ,", i, recordedNoteOnMatrix[i][k]); + } + if (i < recordedEventTimes.size()) + printf("time %f \n", recordedEventTimes[i]); + else + printf("\n"); + } + +} + + + +void midiEventHolder::reorderMatrixFromNoteTimes(IntMatrix& noteOnMatrix){ + double currentTime = -19999.; + for (int i = 0;i < noteOnMatrix.size();i++){ + int nextIndex = getIndexOfMinimumAboveTime(currentTime, noteOnMatrix); + // cout << "index of min time " << currentTime << " is " << nextIndex << " at time " << noteOnMatrix[nextIndex][0] << endl; + + if (nextIndex >= 0 && nextIndex > i && noteOnMatrix[nextIndex][0] < noteOnMatrix[i][0] ){ + //which it should be + // cout << " index " << nextIndex << " at time " << noteOnMatrix[nextIndex][0] << " swaps with inex " << i << " at time " << noteOnMatrix[i][0] << endl; + noteOnMatrix[i].swap(noteOnMatrix[nextIndex]); + currentTime = noteOnMatrix[i][0]; + } + + } + //printRecordedEvents(); + +} + + + + +void midiEventHolder::doublecheckOrder(IntMatrix& noteOnMatrix){ + + for (int i = 0;i < noteOnMatrix.size();i++){ + int nextIndex = getIndexOfMinimumAboveIndex(i, noteOnMatrix); + if (nextIndex > i){ + noteOnMatrix[i].swap(noteOnMatrix[nextIndex]); + } + } +} + +int midiEventHolder::getIndexOfMinimumAboveIndex(const int& index, IntMatrix& noteOnMatrix){ + int returnIndex = index; + int min = noteOnMatrix[index][0]; + for (int i = index;i < noteOnMatrix.size();i++){ + if (noteOnMatrix[i][0] < min){ + returnIndex = i; + min = noteOnMatrix[i][0]; + } + } + return returnIndex; +} + + +int midiEventHolder::getIndexOfMinimumAboveTime(const double& time, IntMatrix& noteOnMatrix){ + int index = 0; + double minimumTime = 100000000.; + int bestIndex = -1; + while (index < noteOnMatrix.size()){ + + if (noteOnMatrix[index][0] > time && noteOnMatrix[index][0] < minimumTime){ + bestIndex = index; + minimumTime = noteOnMatrix[index][0]; + } + index++; + } + return bestIndex; +} + + + + +void midiEventHolder::correctTiming(IntMatrix& noteOnMatrix){ + + if (noteOnMatrix.size() > 0 && noteOnMatrix[0][0] < 0) { + int offset = noteOnMatrix[0][0]; + for (int i = 0;i < noteOnMatrix.size();i++){ + noteOnMatrix[i][0] -= offset; + } + } + +} + + +void midiEventHolder::printNoteCounter(){ + for (int i = 0;i < recordedTotalNoteCounterByPitch.size();i++){ + printf("RECORDED TOTAL[%i] := %i", i, recordedTotalNoteCounterByPitch[i]); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/midiEventHolder.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,169 @@ +/* + * midiEventHolder.h + * midiCannamReader3 + * + * Created by Andrew on 19/07/2011. + * Copyright 2011 QMUL. All rights reserved. + * + */ +#ifndef MIDI_EVENT_HOLDER +#define MIDI_EVENT_HOLDER + +#include "ofMain.h" +#include "BayesianArrayStructure.h" + +class midiEventHolder{ + +public: + + midiEventHolder(); + void printNotes(); + + typedef std::vector<int> IntVector; + typedef std::vector<IntVector> IntMatrix; + + typedef std::vector<bool> BoolVector; + + typedef std::vector<double> DoubleVector; + typedef std::vector<DoubleVector> DoubleMatrix; + + //the rehearsal version + IntMatrix recordedNoteOnMatrix;//note, velocity, duration + DoubleVector recordedEventTimes; + + IntVector matchesFound; + BoolVector noteOnMatches; + + void drawMidiFile(IntMatrix& midiFileToDraw); + +// int recordedNoteOnIndex; + + + IntMatrix playedNoteOnMatrix; + DoubleVector playedEventTimes; + int playedNoteIndex; + IntMatrix matchMatrix; + IntVector bestMatchFound; + IntVector measureVector; + + IntVector recordedTotalNoteCounterByPitch; + int totalNoteCounterIndex; + + DoubleMatrix matchConfidence; + double totalConfidence; + + double mouseX; + + void clearAllEvents(); + bool drawTempoMode, drawPhaseMode; + + double minimumMatchSpeed , maximumMatchSpeed; + + double period, pulsesPerQuarternote; + double getEventTimeMillis(double ticks); + double getEventTimeTicks(double millis); + + int getLocationFromTicks(double tickPosition); + int getLocationFromMillis(double millisPosition); + + double getTimeNow(double eventTime); + bool runningInRealTime; + + double windowStartTime; + + //functions for finding match to incoming note + void newNoteOnEvent(int pitch, int velocity, double timePlayed); + int findLocalMatches(int notePitch); + bool checkIfMatchedNote(const int& tmpIndex); + int findMatch(const int& notePitch, const int& startTime, const int& endTime); + + void updateTempo(); + void findLocalTempoPairs(); + void findLocalTempoPairsWeightedForConfidence(); + void findOptimumTempoPairsToCurrentBestMatch(); + double getBestSpeedEstimate(const int& currentPlayedIndex, const int& equivalentRecordedIndex); + + + void calcuateNewInterNoteIntervals(); + + double likelihoodWidth; + double likelihoodToNoiseRatio; + + void printMatchMatrix(); + void printRecordedEvents(); + void printNoteCounter(); + void updateNoteCounter(); + + void setMatchLikelihoods(int numberOfMatches); + + void setStartPlayingTimes(); + void setSpeedPrior(double speedPriorValue); + + int width, height; + ///// + string matchString; + void updatePlayPosition(); + + DoubleMatrix beatPeriodMatrix; + + void drawFile(); + void drawMidiFile(); + void reset(); + void setMatchedNotesBackToFalse(); + + int ticksPerScreen; + int tickLocation; + int numberOfScreensIn; + int noteArrayIndex; + + int matchWindowWidth; + + int noteMinimum, noteMaximum; + int* screenWidth; + int* screenHeight; + float noteHeight; + float tempo; + double lastPeriodUpdateTime; + int lastPlayedPitch; + + double playPositionInMillis; + + double timeOffsetForScreen; + + double recentNoteOnTime; + + void exampleCrossUpdate(); + BayesianArrayStructure bayesStruct; + + double speedPriorValue; + int bestMatchIndex; + string timeString; + double startTime; + int speedWindowWidthMillis; + + bool confidenceWeightingUsed; + + double minimumMatchError;//recent best error between observed note and aligned midi file + + void reorderMatrixFromNoteTimes(IntMatrix& noteOnMatrix); + int getIndexOfMinimumAboveTime(const double& time, IntMatrix& noteOnMatrix); + void correctTiming(IntMatrix& noteOnMatrix); + void doublecheckOrder(IntMatrix& noteOnMatrix); + int getIndexOfMinimumAboveIndex(const int& index, IntMatrix& noteOnMatrix); + bool useTempoPrior; + string tempoSpeedString; + int minimumTimeIntervalForTempoUpdate; + + double ticksFactor; + + bool newOptimalMethod; + DoubleMatrix interNoteIntervals; + IntVector intervalsToCheck; + void checkForCorrectInterval(const double& playedTimeDifference, DoubleVector* v); + void drawInterNoteIntervals(); + void printInterNoteIntervals(); + int interNoteRange; + DoubleMatrix periodValues; + +}; +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/testApp.cpp Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,584 @@ +#include "testApp.h" + + +testApp::testApp(ofxArgs* args){ + this->args = args; +} + +//-------------------------------------------------------------- +void testApp::setup(){ + + + this->args->printArgs(); + this->args->printOpts(); + midiFileName = "../../../data/frerejacques.mid"; + + if (this->args->getCount() > 0){ + museScoreFilename = this->args->getString(1); + //printf("MUSESCORE FILENAME IS %s\n", museScoreFilename); + cout << "running!! " << museScoreFilename << endl; + midiFileName = museScoreFilename; + } + + int retVal = cannamMainFunction(); + + ofSetVerticalSync(true); + //ofBackground(255,255,255); + midiPort = 2; + midiIn.listPorts(); + midiIn.openPort(midiPort); + midiPortName = ""; + //midiPortName = midiIn.portNames(midiPort); + + transpose = 0; + noteInStream.transposeVal = &transpose; + + noteInStream.startTime = &midiEvents.startTime;//point start time of note in stream to the same time in MIDI events + + noteInStream.factor = &midiEvents.ticksFactor; + printf("TICKS FACTOR %f \n", midiEvents.ticksFactor);//noteInStream->factor) + +// portName = "hello";//midiIn.portNames[2]; +// printf("MIDI PORT %c\n", portName); + cout << "MIDI PORT " << endl;//portName << endl; + + + //midiIn.addListener(this); + ofAddListener(midiIn.newMessageEvent, this, &testApp::newMessage); + + + verdana30.loadFont("verdana.ttf", 50, true, true); + verdana30.setLineHeight(48.0f); + verdana30.setLetterSpacing(1.035); + + playing = false; + readyToStart = true; + + receiver.setup( PORT ); + + sender.setup( HOST, SEND_PORT ); + + screenWidth = ofGetWidth(); + screenHeight = ofGetHeight(); + midiEvents.screenWidth = &screenWidth; + midiEvents.screenHeight = &screenHeight; + midiEvents.drawTempoMode = true; + ofSetFrameRate(30); + + midiEvents.ticksPerScreen += 4000; + lastScoreIndexSent = 0; + performanceRating = 0.0; + + liveInputPlaying = false; + lastScoreIndexSent = 0; + midiEvents.bestMatchIndex = 0; + +} + +//-------------------------------------------------------------- +void testApp::update(){ + if (playing){ + midiEvents.updatePlayPosition();//this fn calls midiEvents.bayesStruct.updateBestEstimate(); + } +// drawer.tickLocation+=20; + + // check for waiting messages + while( receiver.hasWaitingMessages() ) + { + ofxOscMessage m; + receiver.getNextMessage( &m ); + + if ( m.getAddress() == "/midinoteon" ) + { + int newMidiOnPitch = m.getArgAsInt32(0) + transpose; + int velocity = m.getArgAsInt32(1); + double time = m.getArgAsFloat(2); + + if (velocity != 0){ + if (readyToStart){ + startPlaying(); + printf("starting to PLAY!!!"); + } + printf("MIDI NOTE %i \n", newMidiOnPitch); + midiEvents.newNoteOnEvent(newMidiOnPitch, velocity, time); + noteInStream.newNoteCounted(newMidiOnPitch); + } + + } + + if ( m.getAddress() == "/setSpeedPrior" ) + { + float speedPrior = m.getArgAsFloat(0); + printf("speed prior set to %f\n", speedPrior); + midiEvents.speedPriorValue = speedPrior; + midiEvents.bayesStruct.speedPriorValue = speedPrior; + } + + if ( m.getAddress() == "/startplaying" ) + { + prepareToStartOnNextNote(); + } + + if ( m.getAddress() == "/stopplaying" ) + { + stopPlaying(); + } + + + if ( m.getAddress() == "/integratedEstimate" ) + { + midiEvents.bayesStruct.usingIntegratedTempoEstimate = true; + } + + if ( m.getAddress() == "/MAPestimate" ) + { + midiEvents.bayesStruct.usingIntegratedTempoEstimate = false; + } + + + if ( m.getAddress() == "/realtime" ) + { + midiEvents.runningInRealTime = true; + } + + + if ( m.getAddress() == "/offline" ) + { + midiEvents.runningInRealTime = false; + } + + if ( m.getAddress() == "/minimumSpeedRatio" ) + { + + float minSpeed = m.getArgAsFloat(0); + //printf("minimum speed received is %f and max is %f\n", minSpeed, midiEvents.bayesStruct.relativeSpeedLikelihood.getIndexInRealTerms(midiEvents.bayesStruct.relativeSpeedLikelihood.length-1)); + if (minSpeed > 0 && minSpeed < midiEvents.bayesStruct.relativeSpeedLikelihood.getIndexInRealTerms(midiEvents.bayesStruct.relativeSpeedLikelihood.length-1)){ + printf("minimum speed accepted is %f\n", minSpeed); + midiEvents.minimumMatchSpeed = minSpeed; + } + } + + if ( m.getAddress() == "/maximumSpeedRatio" ) + { + + float maxSpeed = m.getArgAsFloat(0); + //printf("minimum speed received is %f and max is %f\n", minSpeed, midiEvents.bayesStruct.relativeSpeedLikelihood.getIndexInRealTerms(midiEvents.bayesStruct.relativeSpeedLikelihood.length-1)); + if (maxSpeed > midiEvents.minimumMatchSpeed && maxSpeed <= midiEvents.bayesStruct.relativeSpeedLikelihood.getIndexInRealTerms(midiEvents.bayesStruct.relativeSpeedLikelihood.length-1)){ + printf("maximum speed accepted is %f\n", maxSpeed); + midiEvents.maximumMatchSpeed = maxSpeed; + } + } + + if ( m.getAddress() == "/likelihoodToNoiseRatio" ) + { + + float ratio = m.getArgAsFloat(0); + + if (ratio > 0.001 && ratio < 0.6){ + midiEvents.likelihoodToNoiseRatio = ratio; + printf("likelihood for events relative to noise uses ratio %f\n", ratio); + } + + } + + }//end while osc + if (midiEvents.recordedEventTimes.size() > 0) + checkNewScoreNote(); + +} + + +void testApp::checkNewScoreNote(){ + if (lastScoreIndexSent != midiEvents.bestMatchIndex){ + //then we send out new note + sendNoteToMuseScore(); + lastScoreIndexSent = midiEvents.bestMatchIndex; + findMeasure(); + } +} + +void testApp::findMeasure(){ + int ticks = midiEvents.recordedNoteOnMatrix[midiEvents.bestMatchIndex][0]; + int tmpMeasure = lastMeasureSent; + + while (lastMeasureSent > 0 && midiEvents.measureVector[lastMeasureSent] > ticks) { + lastMeasureSent--; + } + + while (lastMeasureSent < midiEvents.measureVector.size() && midiEvents.measureVector[lastMeasureSent] < ticks) { + lastMeasureSent++; + } + if (lastMeasureSent != tmpMeasure){ + //sendMeasureToMuseScore(); + performanceRating = noteInStream.calculateTotalScore(midiEvents); + } + + +} + +void testApp::sendBlackNotes(){ + ofxOscMessage m; + m.setAddress( "/plugin" ); + string noteString; + noteString = "blacknotes.js"; + m.addStringArg( noteString); + sender.sendMessage( m ); +} + +void testApp::sendNoteToMuseScore(){ + if (midiEvents.recordedNoteOnMatrix.size() > 0){ + int ticks = midiEvents.recordedNoteOnMatrix[midiEvents.bestMatchIndex][0]; + int pitch = midiEvents.recordedNoteOnMatrix[midiEvents.bestMatchIndex][1]; +// printf("sending to muse score %i, %i \n", ticks, pitch); + sendNoteDataByOsc(pitch, ticks); + } + /* + ofxOscMessage m; + m.setAddress( "/plugin" ); + string noteString; + noteString = "blackNotes.js"; + m.addStringArg( noteString); + sender.sendMessage( m ); + */ + + +// /color-note 60,3440 + + //crappy javascript message + /* + ofxOscMessage m; + m.setAddress( "/plugin" ); + string noteString; + noteString = "coloronenote.js"; + noteString += ",myTick,"+ofToString(ticks)+",myPitch,"+ofToString(pitch); + // printf("%s\n", noteString); + m.addStringArg( noteString); + sender.sendMessage( m ); */ + + + // /plugin coloronenote.js mytick 100 mypitch 56; +} + +void testApp::sendNoteDataByOsc(const int& pitch, const int& ticks){ + ofxOscMessage m; + m.setAddress( "/color-note" ); + string noteString; + noteString = ofToString(ticks); + noteString += ","+ofToString(pitch); + m.addStringArg( noteString); + sender.sendMessage( m ); +} + +void testApp::sendMeasureToMuseScore(){ + + printf("sending measure to muse score %i \n", lastMeasureSent); + + ofxOscMessage m; + m.setAddress( "/select-measure" ); + m.addIntArg(lastMeasureSent); + sender.sendMessage( m ); + + // /select-measure 6 + // /plugin coloronenote.js mytick 100 mypitch 56; +} + + +void testApp::newMessage(ofxMidiEventArgs &args){ + + int pitch; + if (noteInStream.noteInReceived(args)){ + double timeNow = ofGetElapsedTimeMillis(); + + if (!liveInputPlaying){ + firstNoteTime = timeNow; + liveInputPlaying = true; + startPlaying(); + printf("FIRST LIVE NOTE IS NOW AT TIME %f\n", timeNow); + } + + pitch = args.byteOne + transpose; + + midiEvents.newNoteOnEvent(pitch, args.byteTwo, timeNow - firstNoteTime); + + + int tickTime = midiEvents.getEventTimeTicks(timeNow-firstNoteTime); + IntVector v; + v.push_back(tickTime); + v.push_back(pitch); + v.push_back(args.byteTwo); + v.push_back(200);//tmp time til note off happens + noteInStream.midiInputEvents.push_back(v); + noteInStream.midiInputTimes.push_back(timeNow - firstNoteTime); + //printf("NOTE %i at time %f at tick time %i\n", pitch, (timeNow - firstNoteTime), tickTime); + } + +// cout << "MIDI message [port: " << args.port << ", channel: " << args.channel << ", status: " << args.status << ", byteOne: " << pitch << ", byteTwo: " << args.byteTwo << ", timestamp: " << args.timestamp << "]" << endl; +} + +//-------------------------------------------------------------- +void testApp::draw(){ + + midiEvents.drawFile(); + + string info = "Measure "; + info += ofToString(lastMeasureSent); + info += " Last note "; + info += ofToString(lastScoreIndexSent); + + ofSetHexColor(0xFF0000); +// ofDrawBitmapString(info, 20, 20); + midiEvents.drawMidiFile(noteInStream.midiInputEvents); + +// ofDrawBitmapString("Rating "+ofToString(performanceRating*100), 60, 50); + //ofDrawBitmapString("filename "+museScoreFilename, 20, 80); + string ratingString = ofToString(performanceRating*100,0)+"%"; + if (performanceRating > 0.84) + ratingString += "!* *"; + string extraText = ""; + if (performanceRating > 0.9){ + extraText += " pretty good, huh?"; + } + if (performanceRating > 0.95) + extraText = " blimey! "; + if (performanceRating > 0.97) + extraText = " maestro!"; + + ratingString += extraText; + verdana30.drawString(ratingString, 20, 60); + + ofSetHexColor(0x000000); + ofDrawBitmapString(midiPortName, 20, ofGetHeight() - 20); + +} + +//-------------------------------------------------------------- +void testApp::keyPressed(int key){ + + if (key == '.'){ + midiPort++; + midiIn.openPort(midiPort); + } + + if (key == ',' && midiPort > 0){ + midiPort--; + midiIn.openPort(midiPort); + } + + if (key == '-') + transpose -= 12; + + if (key == '=') + transpose += 12; + + if (key == 'c'){ + double timenow = ofGetElapsedTimeMillis(); + midiEvents.exampleCrossUpdate(); + timenow *= -1; + timenow += ofGetElapsedTimeMillis(); + printf("CROSS UPDATE TOOK %f", timenow); + } + + if (key == 'x') + sendNoteDataByOsc(60, 0); + + if (key == OF_KEY_LEFT){ + + } + + if (key == OF_KEY_RIGHT) + + if (key == OF_KEY_RETURN) + stopPlaying(); + + if (key == OF_KEY_UP){ + if (midiEvents.ticksPerScreen >= 4000) + midiEvents.ticksPerScreen += 2000; + else + midiEvents.ticksPerScreen += 500; + } + + if (key == 'm'){ +// midiEvents.findMatch(84, 0, 10000); + } + + if (key == 'b'){ + sendBlackNotes(); + } + + + if (key == 'n'){ + midiEvents.printInterNoteIntervals(); + } + + if (key == OF_KEY_DOWN){ + if (midiEvents.ticksPerScreen >= 4000) + midiEvents.ticksPerScreen -= 2000; + else if (midiEvents.ticksPerScreen > 500) + midiEvents.ticksPerScreen -= 500; + } + + if (key == 'w') + midiEvents.printMatchMatrix(); + + if (key == 'k'){ + noteInStream.printNotes(); + } + + if (key == 'p'){ + midiEvents.printNotes(); + } + + if (key == 'l') + + + //midiEvents.bayesStruct.decaySpeedDistribution(100); + + if (key == 't') + midiEvents.drawTempoMode = !midiEvents.drawTempoMode; + + if (key == 'y') + midiEvents.drawPhaseMode = !midiEvents.drawPhaseMode; + + if (key == 'r'){ + noteInStream.reset(); + liveInputPlaying = false; + stopPlaying(); + lastMeasureSent = 0; + sendMeasureToMuseScore(); + sendBlackNotes(); + lastScoreIndexSent = 0; + performanceRating = 0; + + } + + if (key == 'o' || key == 'O'){ + loadRecordedMidiFile(); + } + + + +} + +void testApp::loadRecordedMidiFile(){ + //open audio file + string *filePtr; + filePtr = &midiFileName; + + if (getFilenameFromDialogBox(filePtr)){ + printf("Midifile: Loaded name okay :\n'%s' \n", midiFileName.c_str()); + cannamMainFunction(); + } +} + +//-------------------------------------------------------------- +void testApp::keyReleased(int key){ + +} + +//-------------------------------------------------------------- +void testApp::mouseMoved(int x, int y ){ + midiEvents.mouseX = midiEvents.getEventTimeMillis((x * midiEvents.ticksPerScreen)/ screenWidth); +} + +//-------------------------------------------------------------- +void testApp::mouseDragged(int x, int y, int button){ + +} + +//-------------------------------------------------------------- +void testApp::mousePressed(int x, int y, int button){ + +} + +//-------------------------------------------------------------- +void testApp::mouseReleased(int x, int y, int button){ + +} + +//-------------------------------------------------------------- +void testApp::windowResized(int w, int h){ + screenWidth = w; + screenHeight = h; + midiEvents.noteHeight = screenHeight / (float)(midiEvents.noteMaximum - midiEvents.noteMinimum); + +} + +void testApp::prepareToStartOnNextNote(){ + readyToStart = true; +} + + +void testApp::startPlaying(){ + playing = !playing; + midiEvents.reset(); + noteInStream.reset(); + midiEvents.setStartPlayingTimes(); + sendBlackNotes(); + readyToStart = false; + //this is where we stop and start playing +} + +void testApp::stopPlaying(){ + //midiEvents.printNoteCounter(); + //noteInStream.printTotalCount(); + + noteInStream.calculateTotalScore(midiEvents); + + + playing = false; + liveInputPlaying = false; + lastScoreIndexSent = 0; + midiEvents.bestMatchIndex = 0; + sendNoteToMuseScore(); + +} + +bool testApp::getFilenameFromDialogBox(string* fileNameToSave){ + //this uses a pointer structure within the loader and returns true if the dialogue box was used successfully + // first, create a string that will hold the URL + string URL; + + // openFile(string& URL) returns 1 if a file was picked + // returns 0 when something went wrong or the user pressed 'cancel' + int response = ofxFileDialogOSX::openFile(URL); + if(response){ + // now you can use the URL + *fileNameToSave = URL; + //printf("\n filename is %s \n", soundFileName.c_str()); + return true; + } + else { + // soundFileName = "OPEN canceled. "; + printf("\n open file cancelled \n"); + return false; + } + + + +} + + + + +int testApp::cannamMainFunction(){ + + + midiEvents.clearAllEvents(); + + //int main(int argc, char **argv) + //{ + // if (argc != 2) { + // cerr << "Usage: midifile <file.mid>" << endl; + // return 1; + // } + + std::string filename = midiFileName;//argv[1]; + +// fileLoader.chopBeginning = true; + fileLoader.loadFile(filename, midiEvents); + +}//new end of load function + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jnmr/testApp.h Mon Dec 12 12:46:17 2011 +0000 @@ -0,0 +1,128 @@ +#ifndef _TEST_APP +#define _TEST_APP + + +//midieventholder - newMidiEvents() sent there +//create cannamMidiLoader for cannamMainfunction + + +//check new prior offset function - how is used? + + + +//check the widening function - adding decay noise +//ticksperscreen could be better as millis per screen + + + +//uses ftmMidiPlay in max5 via osc to communicate notes in + +#include "ofMain.h" + +#include "MIDIFileReader.h" +#include "ofxFileDialogOSX.h" +#include "drawMidiNotes.h" +#include "DynamicBayesianArray.h" +#include "CannamMidiFileLoader.h" +#include <iostream> +#include "midiEventHolder.h" +#include "ofxMidiIn.h" +#include "ofxOsc.h" +#include "MidiInputStream.h" +#include "ofxArgs.h" + +#define PORT 12121 +#define SEND_PORT 5282 +#define HOST "localhost" + + +using namespace std; +using namespace MIDIConstants; + +class testApp : public ofBaseApp{ +private: + ofxArgs* args; + string option1, option2; + bool flag1; + ofxOscReceiver receiver; + ofxOscSender sender; + +public: + testApp(ofxArgs* args); + + void setup(); + void update(); + void draw(); + + void keyPressed (int key); + void keyReleased(int key); + void mouseMoved(int x, int y ); + void mouseDragged(int x, int y, int button); + void mousePressed(int x, int y, int button); + void mouseReleased(int x, int y, int button); + void windowResized(int w, int h); + + void startPlaying(); + void stopPlaying(); + bool getFilenameFromDialogBox(string* fileNameToSave); + + typedef std::vector<int> IntVector; + typedef std::vector<double> DoubleVector; +// typedef std::vector<IntVector> IntMatrix; + IntVector v; + + midiEventHolder midiEvents; + + int cannamMainFunction(); + string midiFileName; + std::string museScoreFilename; + + bool playing; + //drawMidiNotes drawer; + +// BayesianArrayStructure bayesStruct; + + int screenWidth, screenHeight; + CannamMidiFileLoader fileLoader; + + //MIDI INPUT + // vars + int port; + int id; + int value; + double timestamp; + char msg[255]; + string portName; + + void checkNewScoreNote(); + void sendNoteToMuseScore(); + void sendMeasureToMuseScore(); + void sendBlackNotes(); + void findMeasure(); + + // midi addon + ofxMidiIn midiIn; + // this is your listener function + void newMessage(ofxMidiEventArgs &args); + + MidiInputStream noteInStream; + + double firstNoteTime; + bool liveInputPlaying; + double timePlayed; + int transpose; + int lastScoreIndexSent; + int lastMeasureSent; + double performanceRating; + void sendNoteDataByOsc(const int& pitch, const int& ticks); + + ofTrueTypeFont verdana30; + int midiPort; + std::string midiPortName; + void loadRecordedMidiFile(); + + bool readyToStart; + void prepareToStartOnNextNote(); +}; + +#endif