Mercurial > hg > bayesian-drum-tracker
view newOFsrc/BayesDrumTracker.cpp @ 10:d880f7f29fbe
better output of data
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Wed, 07 Mar 2012 16:47:10 +0000 |
parents | fc095148e2a8 |
children | 23ff520d28ff |
line wrap: on
line source
/* * BayesDrumTracker.cpp * bayesianTempoInitialiser5 * * Created by Andrew on 14/07/2011. * Copyright 2011 QMUL. All rights reserved. * */ #include "BayesDrumTracker.h" #define OUTPORT 12346 #define HOST "localhost" //beatCorrection process indicates how the phase is changing from max BayesDrumTracker::BayesDrumTracker(){ initialiseTracker(); sender.setup( HOST, OUTPORT ); } BayesDrumTracker::~BayesDrumTracker(){} void BayesDrumTracker::initialiseTracker(){ beatDistribution.initialiseArray(); tempoDistribution.initialiseArray(); beatTimes.lastBeatTime = 0; correctionFactor = 0.5; tempoDistribution.likelihoodStdDev = arraySize / 32; // tempoDistribution.likelihoodNoise = 0.96; tempoDistribution.likelihoodNoise = 0.7; tempoDistribution.setGaussianPrior(arraySize/2, arraySize/1);//wide beatDistribution.likelihoodStdDev = arraySize / 32; beatDistribution.likelihoodNoise = 0.56; beatDistribution.setGaussianPrior(arraySize/2, arraySize/1); tempoMinimum = 180; tempoMaximum = 400; posteriorMaximum = 0.1; adaptiveStandardDeviationMode = false; setDistributionOnStartTempo = true; setBeatToNowTime = ofGetElapsedTimeMillis(); recentClickTime = ofGetElapsedTimeMillis(); resetParameters(); //check what we can delete above SINCE RESET CALLED } void BayesDrumTracker::resetParameters(){ beatTimes.startIndex = 0; beatTimes.lastBeatTime = 0; maxPhase = 0; posteriorMaximum = 0.1; accompanimentStarted = false; tempoDistribution.likelihoodNoise = 0.8; tempoDistribution.setGaussianPrior(arraySize/2, arraySize/2);//wide beatDistribution.initialiseArray(); tempoDistribution.initialiseArray(); tempoDistribution.calculateStandardDeviation(); beatDistribution.calculateStandardDeviation(); tempoStdDev = tempoDistribution.standardDeviation; beatTimes.resetBeatTimeArray(); } void BayesDrumTracker::decayDistributions(){ if (accompanimentStarted){ tempoDistribution.decayPosteriorWithGaussianNoise (); beatDistribution.decayPosteriorWithGaussianNoise(); } else{ if (tempoStdDev < 0.8 && beatDistribution.standardDeviation < 5) accompanimentStarted = true; } } void BayesDrumTracker::setBeatDistribution(int beatPosition){ switch (beatPosition){ //beat position is in twelfth divisions of a 'beat' so position 6 is an eighth note out etc //early sixteenth is that the beat is a sixteenth earlier case 0: case 1: case 11: //i.e. these zones are interpreted as "on the beat" beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0; beatDistribution.lateSixteenthNoteProportion = 0; break; //10 and 2 were here case 2: beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0.25;//was 0.3 in Bayesian8 //i.e. a 25% chance it is early sixteenth - 75% that the beat actually lies here beatDistribution.lateSixteenthNoteProportion = 0; break; case 3: beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0.3;//was 0.4 in Bayesian8 //half chance it is early beatDistribution.lateSixteenthNoteProportion = 0; break; case 5: case 6: case 7: beatDistribution.eighthNoteProportion = 0.3;//i.e. nearly half a chance we are on the 8th note beatDistribution.earlySixteenthNoteProportion = 0; beatDistribution.lateSixteenthNoteProportion = 0; break; case 4: beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0.25;//was 0.3 in Bayesian8 beatDistribution.lateSixteenthNoteProportion = 0.05;//was 0.2 in Bayesian8 //chsanged to 0.2 and 0.1 then back break; case 8: beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0.05;//was 0.2 in Bayesian8 beatDistribution.lateSixteenthNoteProportion = 0.25;//was 0.3 in Bayesian8 break; case 9: beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0; beatDistribution.lateSixteenthNoteProportion = 0.35;//was 0.4 in Bayesian8 break; case 10: beatDistribution.eighthNoteProportion = 0; beatDistribution.earlySixteenthNoteProportion = 0; beatDistribution.lateSixteenthNoteProportion = 0.25;//was 0.2 in Bayesian8 break; } } void BayesDrumTracker::newKickError(const float& error, const double& cpuEventTime, const string& onsetTypeString){ onsetType = onsetTypeString; cpuBeatTime = cpuEventTime; kickError = error; //printf("beat errror %f time %f\n", kickError, cpuBeatTime); while (kickError > 0.5){ kickError -= 1; } if (paused != true){ updateTempoProcess(cpuBeatTime, onsetType); //this also cross updates the distributions beatTimes.beatMapTimeDifferences[beatTimes.beatSegment] = kickError*beatTimes.tatum; }//end if paused if (onsetType == "kick"){ if (accompanimentStarted) beatDistribution.likelihoodNoise = 0.5; else beatDistribution.likelihoodNoise = 0.5; // printf("kick %f ", cpuBeatTime); } else{ //snare if (accompanimentStarted) beatDistribution.likelihoodNoise = 0.7; else beatDistribution.likelihoodNoise = 0.85; // printf("snare %f ", cpuBeatTime); } setBeatDistribution(beatTimes.beatSegment%12); if (kickError <= 0.5 && kickError >= -0.5) { float beatStandardDeviation; if (adaptiveStandardDeviationMode) beatStandardDeviation = min((double)beatDistribution.likelihoodStdDev, beatDistribution.standardDeviation); else beatStandardDeviation = beatDistribution.likelihoodStdDev; beatDistribution.resetPrior();//prior is old posterior beatDistribution.setGaussianLikelihoodForBeats((arraySize/2)+(kickError*arraySize), beatStandardDeviation); beatDistribution.calculatePosterior(); beatDistribution.renormalisePosterior(); sendMaxPhase(); beatDistribution.calculateStandardDeviation(); }//end if error < 0.5 if (beatTimes.beatSegment % 12 == 6){ kickString = "Kick "; kickString += ofToString(kickError); kickString += " ERROR "; kickString += ofToString(kickError, 2); kickString += " at time diff "; kickString += ofToString(cpuBeatTime - beatTimes.lastClickTime, 2); kickString += " index "; kickString += ofToString(beatTimes.lastClickIndex, 2); kickString += " TYPE "; kickString += ofToString(beatTimes.beatSegment%12); kickString += " Time diff "; kickString += ofToString(beatTimes.timeDifference, 2); } } void BayesDrumTracker::startTatum(const float& startTatum){ beatTimes.tatum = startTatum; if (setDistributionOnStartTempo){ beatDistribution.setGaussianPosterior(arraySize/2, 8); tempoDistribution.setGaussianPosterior(arraySize/2, 12); float tmpIndex; tmpIndex = ( (beatTimes.tatum - ((tempoMinimum+tempoMaximum)/2) ) * arraySize)/(tempoMaximum - tempoMinimum); tempoDistribution.translateDistribution(tmpIndex); sendMaxTempo(); } } void BayesDrumTracker::setUniformTempo(){ for (int i = 0;i < arraySize;i++) tempoDistribution.posterior[i] = (float)1/arraySize; } void BayesDrumTracker::setUniformPhase(){ for (int i = 0;i < arraySize;i++) beatDistribution.posterior[i] = (float)1/arraySize; } void BayesDrumTracker::setBeatNow(const double& beatTime){ for (int i = 0;i < arraySize;i++) beatDistribution.prior[i] = (float)1/arraySize; setBeatToNowTime = ofGetElapsedTimeMillis(); double difference = (setBeatToNowTime - recentClickTime); printf("SET BEAT TO NOW %f vs %f :: diff %f tatum %f :: ", setBeatToNowTime, recentClickTime, difference, beatTimes.tatum ); double beatTimeToUse = 0; if (difference < beatTimes.tatum)//tatum is the eighth note time beatTimeToUse = difference/ (2*beatTimes.tatum); else beatTimeToUse = -1*(2*beatTimes.tatum - difference) / (2*beatTimes.tatum); printf("sending %f \n", beatTimeToUse); beatDistribution.setGaussianLikelihoodForBeats((arraySize/2)+(beatTimeToUse*arraySize), 2); beatDistribution.calculatePosterior(); beatDistribution.renormalisePosterior(); sendMaxPhase(); } void BayesDrumTracker::newBeat(int& beatIndex){ ofxOscMessage m; m.setAddress( "/beatInfo" ); m.addFloatArg(beatTimes.tatum); m.addFloatArg(maxPhase); beatTimes.tatum = maxTempo; // printf("BEAT INFO %f, %f\n", beatTimes.tatum, maxPhase); sender.sendMessage( m ); } void BayesDrumTracker::sendMaxTempo(){ ofxOscMessage m; m.setAddress( "/tempo" ); //maxTempo = tempoDistribution.maximumIndex * (tempoMaximum - tempoMinimum) / arraySize; //would be introduced new in bayesian8 maxTempo = tempoDistribution.getIntegratedEstimateIndex() * (tempoMaximum - tempoMinimum) / arraySize; maxTempo += tempoMinimum; beatTimes.tatum = maxTempo; // printf("SEND TATUM %f\n", beatTimes.tatum); m.addFloatArg( maxTempo ); sender.sendMessage( m ); //printf("max tempo %f\n", maxTempo); } void BayesDrumTracker::sendMaxPhase(){ // maxPhase = (beatDistribution.maximumIndex - (arraySize/2)) / arraySize; maxPhase = (beatDistribution.getIntegratedEstimateIndex() - (arraySize/2)) / arraySize; // printf("\nphase index %f :: %f\n", (float) beatDistribution.integratedEstimate , maxPhase); ofxOscMessage m; m.setAddress( "/phase" ); m.addFloatArg( maxPhase ); sender.sendMessage( m ); //beatCorrection = maxPhase * beatTimes.tatum / 4; } void BayesDrumTracker::setNewClickIndex(const int& clickIndex, const float& clickTime){ beatTimes.lastClickIndex = clickIndex; beatTimes.lastClickTime = clickTime; int clickIndexToUse = clickIndex % 16; beatTimes.clickIndex = clickIndex; beatTimes.clickNumber[clickIndexToUse] = clickIndex; beatTimes.clickTimes[clickIndexToUse] = clickTime; recentClickTime = ofGetElapsedTimeMillis(); } void BayesDrumTracker::doBeatCorrection(const float& beatCorrFloat){ beatCorrection = beatCorrFloat; correctBeatBy = round(correctionFactor * beatCorrection * arraySize / (2 * beatTimes.tatum)); beatDistribution.translateDistribution(-1 * correctBeatBy); } bool BayesDrumTracker::filterBeatTime(double newBeatTime){ bool newBeatFound = false; if ((newBeatTime - beatTimes.lastBeatTime) > 20 || beatTimes.lastBeatTime == 0){ crossUpdateArrays((float)(newBeatTime - beatTimes.lastBeatTime)); beatTimes.lastBeatTime = newBeatTime; newBeatFound = true; } return newBeatFound; } void BayesDrumTracker::crossUpdateArrays(float timeInterval){ int finalBeatIndex, tmpTempoIndex, startBeatIndex; //finalBeat has contribution from BEAT[finalBeat + INT.k] * TEMPO[Max_tempo + k] where INT = INTERVAL float interval; interval = timeInterval / maxTempo;//beatTimes.tatum; tempoDistribution.resetMaximumPosterior(); beatDistribution.resetMaximumPosterior(); int tmpBeatIndex; //&& interval > 0.8 idea? if (timeInterval > 0 && timeInterval < 12000 ){//need between 0 and 12 seconds only to update for (tmpBeatIndex = 0;tmpBeatIndex < arraySize;tmpBeatIndex++){ tmpArray[tmpBeatIndex] = 0; float minusMsecToMakeUp = beatIndexToMsec(tmpBeatIndex) / interval; float plusMsecToMakeUp = beatIndexToMsec(arraySize - tmpBeatIndex) / interval; float convertMsecToTempoIndex = arraySize / (tempoMaximum - tempoMinimum) ; int minTempoIndex = -1 * (int)(minusMsecToMakeUp * convertMsecToTempoIndex); int maxTempoIndex = (int)(plusMsecToMakeUp * convertMsecToTempoIndex); if (tmpBeatIndex == beatDistribution.maximumIndex){ // minTmpDebug = tempoDistribution.maximumIndex + minTempoIndex; // maxTmpDebug = tempoDistribution.maximumIndex + maxTempoIndex; debugArray[0] = beatDistribution.maximumIndex;// debugArray[1] = timeInterval; debugArray[2] = interval;//beatDistribution.maximumIndex; debugArray[3] = tempoDistribution.maximumIndex; } for (tmpTempoIndex = minTempoIndex;tmpTempoIndex <= maxTempoIndex;tmpTempoIndex++){ if ((tempoDistribution.maximumIndex + tmpTempoIndex) >= 0 && (tempoDistribution.maximumIndex + tmpTempoIndex) < arraySize && (tmpBeatIndex - (int)(interval*tmpTempoIndex)) >= 0 && (tmpBeatIndex - (int)(interval*tmpTempoIndex))< arraySize){ tmpArray[tmpBeatIndex] += beatDistribution.posterior[tmpBeatIndex - (int)(interval*tmpTempoIndex)] * tempoDistribution.posterior[(int)tempoDistribution.maximumIndex + tmpTempoIndex]; } }//end for tmpTmepo } float tmpFloat; for (tmpBeatIndex = 0;tmpBeatIndex < arraySize;tmpBeatIndex++){ //debug - dont actually update:: tmpFloat = beatDistribution.posterior[tmpBeatIndex]; beatDistribution.posterior[tmpBeatIndex] = tmpArray[tmpBeatIndex]; tmpArray[tmpBeatIndex] = tmpFloat; } beatDistribution.renormaliseArray(&beatDistribution.posterior[0], arraySize); } //end if } void BayesDrumTracker::updateTempoProcess(const double& cpuTime, const string& onsetDescription){ if (filterBeatTime(cpuTime) == true){ //checks for no repeat if (onsetDescription == "kick") beatTimes.addBeatTime(cpuTime, 1); else beatTimes.addBeatTime(cpuTime, 2); //recalculate the distribution int altIndex = 0; tempoDataString = "Tatum :"; tempoDataString += ofToString(beatTimes.tatum, 2); tempoDataString += " BPM "; tempoDataString += ofToString((double)30000/beatTimes.tatum, 2); timeString = "Last BEAT "; timeString += ofToString(beatTimes.lastBeatTime); timeString += " CLICK "; timeString += ofToString(beatTimes.lastClickTime); timeString += " DIFDF "; timeString += ofToString(beatTimes.timeDifference); timeString += " segment "; timeString += ofToString(beatTimes.beatSegment); for (altIndex = 0;altIndex< 16;altIndex++){ tempoInterval = beatTimes.intervalDifferences[beatTimes.index][altIndex]; integerMultipleOfTatum = beatTimes.relativeIntervals[altIndex][1]; ///NEW VERSION tempoUpdateStrings[altIndex] = ""; double timeInterval = beatTimes.beatTimes[beatTimes.index] - beatTimes.beatTimes[altIndex]; //raw time difference beatTimes.intervalDifferences[beatTimes.index][altIndex] = 0; beatTimes.intervalUsed[beatTimes.index][altIndex] = false; if (onsetType == "kick") beatTimes.OnsetIsKick[beatTimes.index] = true; else beatTimes.OnsetIsKick[beatTimes.index] = false; if (!accompanimentStarted){ //if we need to find tempo and start use this method //we have 'started' once std dev is sufficiently low updateTempoIfWithinRange(timeInterval);//taken as being the tatum interval for (int i = 1;i <= 4;i++){ //we test the main beats and the two bar (16 tatum intervals) double testInterval = timeInterval / 2*i;//pow(2, i);//pow(2.0, i); if (updateTempoIfWithinRange(testInterval)){ //printf("test time %f, beats %i\n", testInterval, i); beatTimes.intervalUsed[beatTimes.index][altIndex] = true; beatTimes.intervalDifferences[beatTimes.index][altIndex] = testInterval; //xx what if two within range here? tempoUpdateStrings[altIndex] = "Tempo Updates ("; tempoUpdateStrings[altIndex] += ofToString(beatTimes.index, 0); tempoUpdateStrings[altIndex] += ") : ["; tempoUpdateStrings[altIndex] += ofToString(altIndex); tempoUpdateStrings[altIndex] += "]] : "; tempoUpdateStrings[altIndex] += ofToString(timeInterval); tempoUpdateStrings[altIndex] += ", ioi:"; tempoUpdateStrings[altIndex] += ofToString(i); //tempoUpdateStrings[altIndex] += ""; } } double testInterval = timeInterval / 16;//pow(2, i);//pow(2.0, i); if (updateTempoIfWithinRange(testInterval)){ beatTimes.intervalUsed[beatTimes.index][altIndex] = true; beatTimes.intervalDifferences[beatTimes.index][altIndex] = testInterval; } }else{ //OLD VERSON //THIS USES THE CURRENT TEMPO ESTIMATE TO DECIDE WHAT THE BEST INTERVAL IS //&& integerMultipleOfTatum % 2 == 0 removed below XXX put back if (altIndex != beatTimes.index && integerMultipleOfTatum < 17 && integerMultipleOfTatum > 0 && beatTimes.startIndex > 8//beattimes.index > 8 - the start && integerMultipleOfTatum%2 == 0){//mod 2 - i.e. proper beat intervals only double testInterval = timeInterval / integerMultipleOfTatum; if (updateTempoIfWithinRange(testInterval)){ beatTimes.intervalUsed[beatTimes.index][altIndex] = true; beatTimes.intervalDifferences[beatTimes.index][altIndex] = testInterval; if (paused == false){ tempoUpdateStrings[altIndex] = "Tempo Updates : ("; tempoUpdateStrings[altIndex] += ofToString(beatTimes.index, 0); tempoUpdateStrings[altIndex] += ") : ["; tempoUpdateStrings[altIndex] += ofToString(altIndex, 0); tempoUpdateStrings[altIndex] += "] :: "; tempoUpdateStrings[altIndex] += ofToString(integerMultipleOfTatum); tempoUpdateStrings[altIndex] += " intervals :: "; tempoUpdateStrings[altIndex] += ofToString(tempoInterval); tempoUpdateStrings[altIndex] += " ms."; // tempoUpdateStrings[altIndex] += ", ioi:"; // tempoUpdateStrings[altIndex] += ofToString(integerMultipleOfTatum); }//end if not paused }//end if good interval to update }//end if not same index etc } }//end for all intervals sendMaxTempo(); }//end if new beat time double tempoEstimate = tempoDistribution.getIntegratedEstimateIndex(); tempoDistribution.calculateStandardDeviation(); tempoStdDev = tempoDistribution.standardDeviation; } bool BayesDrumTracker::updateTempoIfWithinRange(double timeInterval){ bool updated = false; if (timeInterval > tempoMinimum && timeInterval < tempoMaximum ){ calculateTempoUpdate(timeInterval); updated = true; } return updated; } void BayesDrumTracker::calculateTempoUpdate(double tempoInterval){ tempoDistribution.resetPrior(); //need to relook at likelihood for the tempo distribution - not the same as.... tempoDistribution.setGaussianLikelihood(arraySize * (tempoInterval-tempoMinimum)/(tempoMaximum - tempoMinimum), tempoDistribution.likelihoodStdDev); tempoDistribution.calculatePosterior(); tempoDistribution.renormalisePosterior(); //did take pic of screen here - see initialiser4 } float BayesDrumTracker::tempoIndexToMsec(const int& index){ float msec; msec = index * (tempoMaximum - tempoMinimum) / arraySize; msec += tempoMinimum; return msec; } float BayesDrumTracker::beatIndexToMsec(const int& index){ float msec; msec = index * maxTempo / arraySize; msec += tempoMinimum; return msec; }