Mercurial > hg > drum-timing-analyser
view DrumTimingLoader_OF/PerformanceAnalyserSrc/TimingAnalyser.cpp @ 3:303edbbcf1bd tip
updated ofxAubioOnsetDetection file
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Sun, 24 Nov 2013 08:15:17 +0000 |
parents | 50ba55abea8c |
children |
line wrap: on
line source
/* * TimingAnalyser.cpp * performanceTimingAnalyser * * Created by Andrew on 17/12/2011. * Copyright 2011 QMUL. All rights reserved. * */ #include "TimingAnalyser.h" //FROM TestApp.cpp //change these to your preferred filepaths for exporting the data //timingDataFilepath = "/Users/andrew/outputFile.txt";//here as decoded tempo and phase //change this to your filepath for storing the final path as output //can then load it back in easily without reanalysing using this... // importFileFromStoredData(fileToLoad); //processBeatTimesFilepath = "//Users/andrew/processedBeats";//here as a list of beat times //this latter will be a smoothed version of the original //RESTRICT PHASE HOP POSSIBLE //Correct or DELETE this BeatPosition variable - what is it? Why used? TimingAnalyser::TimingAnalyser(){ moveMatrixToOptimalCostPosition = true; //resolution in ms tempoScalar = 1.0; phaseScalar = 1.0; phaseCost = 1000.4;//a phase jump is 1.5 (y-x) where y now becomes the predicted position tempoCost = 2.8;//a tempo jump of z msec per period is charged at 2z units drawIOIintervals = false; drawExpressiveTimingData = true; printHistory = false; mozartTriplets = false; movementFactor = 0.5; drawBPM = true; //COST VARIABLES - Assuming that a simple error is costing linear (t-x) where x is predicted time blackAndWhite = true; setTempoLimits(400); //index between which we look at the variation tempoVariationStartIndex = 4; DoubleVector v; v.assign(phaseRange, 0.0); logProbabilityMatrix.assign(tempoRange, v); previousLogMatrix = logProbabilityMatrix; //[tempo][phase] phaseMinimumOffset = -1 * phaseScalar*(phaseRange - 1)/2;//offset in millis globalTimeOffset = 0;//global offset in millis lastBeatPosition = 0; numberOfPointsPerPage = 80; startPoint = 0; // initialiseRoutes(); recentPhaseShift = 0; recentTempoShift = 0; /* meanGlobalTempo = 440; tempoMinimumOffset = meanGlobalTempo - (tempoRange-1)/2; meanTempoIndex = (tempoRange - 1)/2; meanTempo = tempoMinimumOffset + meanTempoIndex; */ meanPhaseIndex = (phaseRange - 1)/2; currentBestTempo = meanTempoIndex; currentBestPhase = meanPhaseIndex; printf("Mean tempo initialised at %i and mean index %i\n", meanTempo, meanTempoIndex); int minDrawTempo = minimumTempo + minimumPhaseHop - 4; int maxDrawTempo = maximumTempo + maximumPhaseHop + 4; ofTrueTypeFont::setGlobalDpi(72); timesFont.loadFont("TimesNewRoman.ttf", 18, true, true); timesFont.setLineHeight(18.0f); timesFont.setLetterSpacing(1.037); } void TimingAnalyser::initialiseRoutes(){ } void TimingAnalyser::clearData(){ timingData.clear(); basicInterOnsetInterval.clear(); beatPosition.clear(); } void TimingAnalyser::setTempoLimits(const int& newGlobalTempo){ meanGlobalTempo = newGlobalTempo; tempoMinimumOffset = meanGlobalTempo - (tempoScalar*(tempoRange-1)/2); meanTempoIndex = (tempoRange - 1)/2; meanTempo = tempoMinimumOffset + meanTempoIndex*tempoScalar; printf("Setting tempo to %i units i.e. %f ms\n", newGlobalTempo, getTempo(meanTempoIndex)); } double TimingAnalyser::getPhaseIndexFromEventTime(const double& eventTime){ return eventTime / phaseScalar; } double TimingAnalyser::getTempoInUnits(const double& tempoInMs){ return tempoInMs / tempoScalar; } //NEW UPDATE METHOD double TimingAnalyser::getBestMinimumCost(const int& newTempoInUnits, const int& newPhaseInUnits, Route& r){ //new method to check all points in last matrix //new phase is the phase point from zero in units //including any offsets etc double cost = 0; double bestCost = INFINITY; Path* lastPath; int minPhase = 0; int minTempo = 0; r.tempoHop = 0; r.phaseHop = 0; //int tempoHop, phaseHop; no need if (pathHistory.size() > 0) lastPath = &pathHistory[pathHistory.size()-1]; else{ // printf("Initiating first path...\n"); } for (int tempo = 0;tempo < tempoRange;tempo++){ for (int phase = 0;phase < phaseRange;phase++){ //change tempo cost = 0; int phasePoint = 0; if (pathHistory.size() > 0){ cost = (*lastPath).currentRoute[tempo][phase].totalCost; cost += tempoCost * abs(newTempoInUnits - (tempo + (*lastPath).tempoOffset)); //our point then projects forwards at the new tempo phasePoint = phase + (*lastPath).phaseOffset + newTempoInUnits; //phase cost is cost += phaseHopCost(newPhaseInUnits - phasePoint);//i.e. phaseCost * abs(newPhaseInUnits - phasePoint); }//else it's the first point and all previous cost is zero if (cost < bestCost){ bestCost = cost; minPhase = phase; minTempo = tempo; if (pathHistory.size() > 0){ r.tempoHop = newTempoInUnits - (tempo + (*lastPath).tempoOffset); r.phaseHop = newPhaseInUnits - phasePoint; } else{ r.tempoHop = 0; r.phaseHop = 0; } r.previousTempoIndex = tempo; r.previousPhaseIndex = phase; }//end if mew minimum } } /* printf("best cost for tempo-phase pair %i, %i from tempo %i and phase %i, hop %i, %i is %.2f\n", newTempoInUnits, newPhaseInUnits, minTempo, minPhase, r.tempoHop, r.phaseHop, bestCost); */ return bestCost; } void TimingAnalyser::printCostMatrix(const RouteMatrix& m){ printf("\n"); for (int i = 0; i< m.size();i++){ printf("Tempo %.1f (index %i)\n", getTempo(i), i); for (int k = 0;k < m[i].size();k++){ printf("%.1f (new %.1f + prev %.1f (%i,%i)), ", m[i][k].totalCost, m[i][k].addedCost,m[i][k].previousCost, m[i][k].previousTempoIndex, m[i][k].previousPhaseIndex ); } printf("\n"); } } void TimingAnalyser::updatePlayIndex(const double& seconds){ double millis = seconds * 1000.0; while (playingIndex < timingData.size() && getNoteTime(playingIndex) < millis) playingIndex++; while (playingIndex > 0 && getNoteTime(playingIndex) > millis) playingIndex--; if (playingIndex > startPoint + numberOfPointsPerPage) startPoint += numberOfPointsPerPage;//scrolling with play } double TimingAnalyser::getNoteTime(const int& index){ return offsetToFirstPoint + timingData[index][2];// } void TimingAnalyser::updateCostToPoint(const int& eventTime, const int& eventBeatLocation){ // previousLogMatrix = logProbabilityMatrix;//store previous matrix - settings are in pathHistory vector printf("\nTiming Analyser: update cost to %i, \n", eventTime); //event beat location %i is 1 so don't bother printing // printf("old phase range is %.1f to %.1f\n", getPhase(0), getPhase(phaseRange-1)); // printf("old tempo range is %.1f to %.1f\n", getTempo(0), getTempo(tempoRange-1)); int interval = 1; //setting the new path offset points RouteMatrix r_mat; Path* lastPath; Path p;//the new path point p.tempoOffset = tempoMinimumOffset;//for now keep it the same if (pathHistory.size() > 0){ lastPath = &pathHistory[pathHistory.size()-1]; int ioi = (int) getTempoInUnits(eventTime - (*lastPath).eventTimeObserved); p.tempoOffset = ioi - ((tempoRange-1)/2); tempoMinimumOffset = p.tempoOffset; printf("New time %i, last time %i diff %i: IOI is %i\n", eventTime, (*lastPath).eventTimeObserved, (eventTime - (*lastPath).eventTimeObserved), ioi); } //and in tempo units! p.phaseOffset = round(getPhaseIndexFromEventTime(eventTime)) + phaseMinimumOffset;//in units //abandoned - now using the offset too - dont need - (phaseRange/2) as in the get phase fn printf("NEW PHASE and tempo offsets %i and %i, \n", p.phaseOffset, p.tempoOffset); //end set offset pts for (int tempoIndex = 0;tempoIndex < logProbabilityMatrix.size();tempoIndex++){ RouteVector r_vec; for (int phase = 0;phase < logProbabilityMatrix[tempoIndex].size();phase++){ Route r; double bestCost = getBestMinimumCost(p.tempoOffset + tempoIndex, phase + p.phaseOffset, r); double newCost = fabs(getPhaseIndexFromEventTime(eventTime) - (p.phaseOffset + phase)); //this is the phase position that would lead to this current tempo and phase iwthout any hopping //the bestPreviousCost calculates the best oposition including hopping //then we compare the past cost r.previousCost = bestCost; //this is the best past cost that can get us to current location //then we need new cost r.addedCost = newCost; //r.globalPhaseOffset = globalTimeOffset; logProbabilityMatrix[tempoIndex][phase] = bestCost + newCost; r.totalCost = bestCost + newCost; //just to check we get this right! r.tempoIndex = tempoIndex; r.phaseIndex = phase; r_vec.push_back(r); } r_mat.push_back(r_vec); } //history.push_back(r_mat); //printHistory(); - defunct as using pathHistory instead //printBestPath(); //printf("Global offset %i ph min off %i\n", globalTimeOffset, phaseMinimumOffset); //new method using Path class p.currentRoute = r_mat; globalTimeOffset = p.phaseOffset; //store the settings for the matrix at this time step //p.globalPhaseOffset = globalTimeOffset; // p.tempoOffset = tempoMinimumOffset; setBestTempoAndPhase(p); lastBeatPosition = eventBeatLocation; printf(" error %i ", (int)(eventTime - getPhase(currentBestPhase, globalTimeOffset))); p.phaseShiftApplied = recentPhaseShift;//this is what we did in the recent step p.tempoShiftApplied = recentTempoShift; //so is the shift by which current stored matrix and indices differ from the last path p.eventTimeObserved = eventTime; p.costMatrix = logProbabilityMatrix; if (pathHistory.size() > 0){ //then can calculate inter-onset interval int ioi = eventTime - pathHistory[pathHistory.size()-1].eventTimeObserved; basicInterOnsetInterval.push_back(ioi); printf("ioi for time %i is %i\n", eventTime, ioi); } printf("New PATH offset is %i\n", p.phaseOffset); pathHistory.push_back(p); //prints the complete cost matrix at every step //printCostMatrix(p.currentRoute); if (printHistory) printPathHistory(); // printf(", SHIFT (%i, %i)\n", p.phaseShiftApplied, p.tempoShiftApplied); //loaded the new prob matrix to the main log one //now looking to shift //change global offset to match new position // printLogMatrix(); previousLogMatrix = logProbabilityMatrix;//store previous matrix - settings are in pathHistory vector // printLogMatrix(); } void TimingAnalyser::printIOIdata(){ for (int i = 0;i < basicInterOnsetInterval.size();i++){ printf("IOI[%i] : %i\n", i, basicInterOnsetInterval[i]); } } void TimingAnalyser::checkShiftMatrix(){ if (moveMatrixToOptimalCostPosition){ int phaseShiftIndex = (int)(meanPhaseIndex + movementFactor*(currentBestPhase - meanPhaseIndex)); int tempoShiftIndex = (int)(meanTempoIndex + movementFactor*(currentBestTempo - meanTempoIndex)); printf("current best tempo %i, best phase %i, mean tempo %i, shift of %i\n", currentBestTempo, currentBestPhase, meanTempoIndex, tempoShiftIndex); shiftMatrixToMatchBestPhase(phaseShiftIndex); shiftMatrixToMatchBestTempo(tempoShiftIndex); } } void TimingAnalyser::shiftMatrixToMatchBestPhase(const int& bestPhaseIndex){ // int bestPhaseIndex = pathHistory[pathHistory.size()-1].bestPhase; printf("SHIFT:: BEST PHASE %i, ", bestPhaseIndex); int shift = bestPhaseIndex - meanPhaseIndex; DoubleMatrix tmpMat = logProbabilityMatrix; for (int t = 0;t < tempoRange;t++){ for (int p = 0;p < phaseRange;p++){ int switchIndex = p + shift; if (switchIndex < phaseRange && switchIndex >= 0){ logProbabilityMatrix[t][p] = tmpMat[t][switchIndex]; }else{ logProbabilityMatrix[t][p] = INFINITY; } } } globalTimeOffset += shift * phaseScalar; printf("new phase shift of %i and offset is %i \n", shift, globalTimeOffset); recentPhaseShift = shift; } void TimingAnalyser::shiftMatrixToMatchBestTempo(const int& bestTempoIndex){ // int bestPhaseIndex = pathHistory[pathHistory.size()-1].bestPhase; printf("SHIFT:: BEST TEMPO %i, ", bestTempoIndex); int shift = bestTempoIndex - meanTempoIndex; DoubleMatrix tmpMat = logProbabilityMatrix; for (int t = 0;t < tempoRange;t++){ for (int p = 0;p < phaseRange;p++){ int switchIndex = t + shift; if (switchIndex < tempoRange && switchIndex >= 0){ logProbabilityMatrix[t][p] = tmpMat[switchIndex][p]; }else{ logProbabilityMatrix[t][p] = INFINITY; } } } tempoMinimumOffset += shift * tempoScalar; printf("new tempo shift of %i and tempo offset is %i \n", shift, tempoMinimumOffset); recentTempoShift = shift; } bool TimingAnalyser::checkPhaseRange(const int& phaseToCheck){ if (phaseToCheck < phaseRange && phaseToCheck >= 0) return true; else return false; } bool TimingAnalyser::checkTempoRange(const int& tempoToCheck){ if (tempoToCheck < tempoRange && tempoToCheck >= 0) return true; else return false; } double TimingAnalyser::phaseHopCost(const int& phaseHop){ //printf("phase hop %i returns %f", phaseHop, phaseCost*abs(phaseHop)); return phaseCost*abs(phaseHop); } double TimingAnalyser::tempoHopCost(const int& tempoHop){ return tempoCost*abs(tempoHop); } double TimingAnalyser::getTempo(const int& tempoIndex){ return (tempoMinimumOffset + tempoIndex*tempoScalar); } double TimingAnalyser::getPhase(const int& phaseIndex){ return globalTimeOffset + (phaseIndex*phaseScalar); } double TimingAnalyser::getTempo(const int& tempoIndex, const int& tempoOffset){ return ((tempoOffset + tempoIndex)*tempoScalar); } double TimingAnalyser::getPhase(const int& phaseIndex, const int& phaseOffset){ //phase offset for middle of matrix return (phaseOffset + phaseIndex)*phaseScalar; } int TimingAnalyser::getTempoIndex(const double& tempo){ double index = tempo; index -= tempoMinimumOffset; index /= tempoScalar; return (int)round(index); } int TimingAnalyser::getPhaseIndex(const double& location){ double index = location; index -= globalTimeOffset; index -= phaseMinimumOffset; index /= phaseScalar; return (int)round(index); } double TimingAnalyser::getMinimumTransitionCost(const int& interval, const int& tempoIndex, const int& phaseIndex){ double minimumCost = 100000000.0;//very big //need to get the location of where we are now int endLocation = 0;//getBeatLocation() // int zeroLocation = getBeatLocation(); // getLocation(0, 0, beatPosition + interval); int tempo = 0; // for (int tempo = 0;tempo < logProbabilityMatrix.size();tempo++){ for (int phase = 0;phase < logProbabilityMatrix[tempo].size();phase++){ //double tempoTransition = getTempoTransitionCost(tempoIndex - tempo); //double phaseTransition = getPhaseTransitionCost(phaseindex - phase); } // } return minimumCost; } double TimingAnalyser::getMaximum(){ double maxVal = 1.0; for (int tempo = 0;tempo < logProbabilityMatrix.size();tempo++){ for (int phase = 0;phase < logProbabilityMatrix[tempo].size();phase++){ if (logProbabilityMatrix[tempo][phase] > maxVal && logProbabilityMatrix[tempo][phase] < INFINITY) maxVal = logProbabilityMatrix[tempo][phase]; } } return maxVal; } void TimingAnalyser::drawCostMatrix(){ double maximum = getMaximum(); double width = ofGetWidth() / phaseRange; double height = ofGetHeight() / tempoRange; for (int tempo = 0;tempo < logProbabilityMatrix.size();tempo++){ for (int phase = 0;phase < logProbabilityMatrix[tempo].size();phase++){ ofSetColor(0,0, 255.0*(maximum - logProbabilityMatrix[tempo][phase])/maximum ); ofRect(phase*width, tempo*height, width, height); ofSetColor(255,255,255); string infoStr = "T "+ofToString(getTempo(tempo, tempoMinimumOffset)); infoStr += "\nP "+ofToString(phase)+" ("+ofToString(getPhase(phase, globalTimeOffset))+")"; infoStr += "\n"+ofToString(logProbabilityMatrix[tempo][phase]); // ofDrawBitmapString(infoStr, phase*width + 10, tempo*height + 10); } } } void TimingAnalyser::setBestTempoAndPhase(Path& newPath){ double bestCost = newPath.currentRoute[currentBestTempo][currentBestPhase].totalCost;// INFINITY; for (int t = 0;t < tempoRange;t++){ for (int p = 0;p < phaseRange;p++){ if (newPath.currentRoute[t][p].totalCost < bestCost){ bestCost = newPath.currentRoute[t][p].totalCost; currentBestPhase = p; currentBestTempo = t; } } } newPath.bestTempo = currentBestTempo;// bestTempo; newPath.bestPhase = currentBestPhase;// bestPhase; printf("BEST TEMPO %i, PHASE %i :: ", currentBestTempo, currentBestPhase); string tString = "Tempo "+ofToString(getTempo(currentBestTempo, tempoMinimumOffset)); tString += ", Phase "+ofToString(getPhase(currentBestPhase, globalTimeOffset)); tString += ", BEST COST "+ofToString(bestCost); //tstring += ", error "+ofToString() printf("i.e. %s", tString.c_str()); } void TimingAnalyser::printPathHistory(){ /* printf("tempo %i (%3.0f), phase %i (%3.0f)\n", pathHistory[i].bestTempo, getTempo(pathHistory[i].bestTempo, pathHistory[i].tempoOffset), pathHistory[i].bestPhase, getPhase(pathHistory[i].bestPhase, pathHistory[i].globalPhaseOffset) ); */ int tempo, phase; RouteMatrix* tmpRoute; int pathIndex = pathHistory.size()-1; if ( pathHistory.size() > 0){ pathHistory[0].phaseShiftApplied = 0;//hack to fix non zero tempo = pathHistory[pathHistory.size()-1].bestTempo; phase = pathHistory[pathHistory.size()-1].bestPhase; printf("PATH BEST[%i] tempo %i (%3.0f), phase %i (%3.0f) at COST %3.3f preCOST %3.3f addCOST %3.3f\n", pathIndex, tempo, getTempo(tempo, pathHistory[pathHistory.size()-1].tempoOffset), phase, getPhase(phase, pathHistory[pathHistory.size()-1].phaseOffset), pathHistory[pathHistory.size()-1].currentRoute[tempo][phase].totalCost, pathHistory[pathHistory.size()-1].currentRoute[tempo][phase].previousCost, pathHistory[pathHistory.size()-1].currentRoute[tempo][phase].addedCost ); }//end if while (pathIndex >= 0 && pathIndex < pathHistory.size()){ // printf("index %i, history size %i\n", pathIndex, pathHistory.size()); tmpRoute = &(pathHistory[pathIndex].currentRoute); int previousTempo = (*tmpRoute)[tempo][phase].previousTempoIndex; int previousPhase = (*tmpRoute)[tempo][phase].previousPhaseIndex;// + pathHistory[pathIndex].phaseShiftApplied; //above was error on 24/12 that created wrong results - it is already included! printf("PTH[%i](%i,%i)\total{%3.2f}\t(prevCost %3.2f (%3.2f, %3.2f, %3.2f)\t:: adddedCost %3.2f\t: hop (%i, %i)\tprevious (%i, %i) applied (%i, %i){%i, %i}, ", pathIndex, tempo, phase, (*tmpRoute)[tempo][phase].totalCost, (*tmpRoute)[tempo][phase].previousCost, (*tmpRoute)[tempo][phase].preHopCost, (*tmpRoute)[tempo][phase].tempoHopCost, (*tmpRoute)[tempo][phase].phaseHopCost, (*tmpRoute)[tempo][phase].addedCost, (*tmpRoute)[tempo][phase].tempoHop, (*tmpRoute)[tempo][phase].phaseHop, previousTempo, previousPhase, pathHistory[pathIndex].tempoShiftApplied, pathHistory[pathIndex].phaseShiftApplied, pathHistory[pathIndex].tempoOffset, pathHistory[pathIndex].phaseOffset ); if (pathIndex > 0){ printf("Tempo %3.0f Phase %3.0f (PRED %3.0f) @event %i (%3.0f)\n", getTempo(previousTempo, pathHistory[pathIndex-1].tempoOffset), getPhase(previousPhase, pathHistory[pathIndex-1].phaseOffset), getPhase(previousPhase, pathHistory[pathIndex-1].phaseOffset) + getTempo(previousTempo, pathHistory[pathIndex-1].tempoOffset), pathHistory[pathIndex].eventTimeObserved, getPhase(phase, pathHistory[pathIndex].phaseOffset) ); } tempo = previousTempo; phase = previousPhase; pathIndex--; } } void TimingAnalyser::processPathHistory(){ printf("Process path history...\n"); timingData.clear(); maximumTempo = 0; minimumTempo = INFINITY; minimumPhaseHop = 0; maximumPhaseHop = 0; printf("\n"); int tempo, phase; RouteMatrix* tmpRoute; int pathIndex = pathHistory.size()-1; int observedPhasePoint; int observedTempo, observedEventTime; int storedTempoHop = 0; int storedTempo = 0; if ( pathHistory.size() > 0){ pathHistory[0].phaseShiftApplied = 0;//hack to fix non zero tempo = pathHistory[pathIndex].bestTempo; phase = pathHistory[pathIndex].bestPhase; observedPhasePoint = getPhase(phase, pathHistory[pathIndex].phaseOffset); } while (pathIndex >= 0){ tmpRoute = &(pathHistory[pathIndex].currentRoute); int previousTempo = (*tmpRoute)[tempo][phase].previousTempoIndex; int previousPhase = (*tmpRoute)[tempo][phase].previousPhaseIndex; IntVector v; observedTempo = storedTempo; observedPhasePoint = (int)round(getPhase(phase, pathHistory[pathIndex].phaseOffset)); observedEventTime = pathHistory[pathIndex].eventTimeObserved; if (observedTempo > maximumTempo) maximumTempo = observedTempo; if (observedTempo < minimumTempo && observedTempo > 0) minimumTempo = observedTempo; int phaseHop = (*tmpRoute)[tempo][phase].phaseHop; if (phaseHop < minimumPhaseHop) minimumPhaseHop = phaseHop; if (phaseHop > maximumPhaseHop) maximumPhaseHop = phaseHop; v.push_back(observedTempo);//timingdata[][0] v.push_back(observedPhasePoint);//timingdata[][1] v.push_back(observedEventTime);//timingdata[][2] v.push_back(storedTempoHop); v.push_back( (*tmpRoute)[tempo][phase].phaseHop);//timingData[][4] v.push_back(observedEventTime - observedPhasePoint);//timingdata[][5] timingData.push_back(v); printf("Index %i TEMPO %i, PHASE %i, EVENT %i HOP (%i, %i) error %i \n", (int) timingData.size(), observedTempo, observedPhasePoint, observedEventTime, storedTempoHop, (*tmpRoute)[tempo][phase].phaseHop, (observedEventTime - observedPhasePoint)); //checking this ned to align tempo, observed phase and observed events //N.B. MUST STORE THIS FROM FUTURE POINT storedTempo = (int)round(getTempo(tempo, pathHistory[pathIndex].tempoOffset)); storedTempoHop = (*tmpRoute)[tempo][phase].tempoHop; tempo = previousTempo; phase = previousPhase; pathIndex--; } reverse(timingData.begin(), timingData.end()); clearHistogram(); for (int i = 0;i < timingData.size();i++){ printf("Rev index %i tempo %i, phase hop %i, error %i, beat location %i\n", i, timingData[i][0], timingData[i][4], timingData[i][5], (int) beatPosition[i]); int error = (int) timingData[i][4] + timingData[i][5]; if (abs(error) < 30 && (int)beatPosition[i] - 1 < timingHistogram.size()) timingHistogram[(int)beatPosition[i] - 1].push_back(error); } printf("Maximum is %f and min %f hopMin %i hopMax %i\n", maximumTempo, minimumTempo, minimumPhaseHop, maximumPhaseHop); minDrawTempo = minimumTempo + minimumPhaseHop - 4; maxDrawTempo = maximumTempo + maximumPhaseHop + 4; getHistogramResults(); } void TimingAnalyser::clearHistogram(){ timingHistogram.clear(); IntVector v; for (int i = 0;i < 4;i++){ v.clear(); timingHistogram.push_back(v); } } void TimingAnalyser::getHistogramResults(){ DoubleVector results; for (int i = 0;i < 4;i++){ double total; for (int k = 0; k < timingHistogram[i].size();k++){ total += timingHistogram[i][k]; } total /= timingHistogram[i].size(); results.push_back(total); printf("Mean at beat %i is %.2f\n", i, results[i]); } } #pragma mark -drawTempoCurve void TimingAnalyser::drawTempoCurve(){ if (timingData.size() > 0){ screenWidth = ofGetWidth(); screenHeight = ofGetHeight(); double height = screenHeight; // int minDrawTempo = minimumTempo + minimumPhaseHop - 4; // int maxDrawTempo = maximumTempo + maximumPhaseHop + 4; if (maximumTempo > 0) height /= (maxDrawTempo - minDrawTempo); double bpmMaxTempo = msToBpm(minDrawTempo); double bpmMinTempo = msToBpm(maxDrawTempo); double bpmHeight = screenHeight / (bpmMaxTempo - bpmMinTempo); //int minHeightPixels = minHeight * height; ofBackground(255); double width = ofGetWidth()/numberOfPointsPerPage; int lastHeightPoint = getHeightPoint(height * (timingData[0][0] - minDrawTempo) ); int newHeightPoint; double error; int Xpoint; //horizonal lines for tempo ofSetColor(150); if(!drawBPM){ int horizontalTempo = minDrawTempo + 20 - minDrawTempo%20; // while (horizontalTempo > minDrawTempo) // horizontalTempo -= 20; while (horizontalTempo < maxDrawTempo){ ofSetColor(150); ofLine(0, getHeightPoint(height*(horizontalTempo - minDrawTempo)), screenWidth, getHeightPoint(height*(horizontalTempo - minDrawTempo))); ofSetColor(50); ofDrawBitmapString(ofToString(horizontalTempo), 20, getHeightPoint(height*(horizontalTempo - minDrawTempo))); // timesFont.drawString(ofToString(horizontalTempo), 20, getHeightPoint(height*(horizontalTempo - minDrawTempo)));//FONT horizontalTempo += 20; }//end while } else{ int resolution = 2; if (maxDrawTempo - minDrawTempo > 150) resolution = 4; if (maxDrawTempo - minDrawTempo > 400) resolution = 8; int horizontalTempo = (int)bpmMinTempo + resolution - ((int)bpmMinTempo % resolution);//mod ten above min while (horizontalTempo < bpmMaxTempo) { int tmp = getHeightPoint(bpmHeight * (horizontalTempo - bpmMinTempo)); ofSetColor(150); ofLine(0, tmp , screenWidth, tmp); ofSetColor(50); ofDrawBitmapString(ofToString(horizontalTempo), 20, tmp+2); std::string number = ofToString(horizontalTempo); // timesFont.drawString(number, 20, tmp+2);//FONT horizontalTempo += resolution; } } //vertical bars int solidBarEvery = 4;//normally every four beats int counter = 0; for (int i = 0;i < timingData.size();i++){ ofSetColor(200); bool drawLine = false; //this useful for drawing on ISMIR paper if (mozartTriplets){ //stronger lines on the bars solidBarEvery = 12;//- pollini triplets if ((i % solidBarEvery == 0)){// || beatPosition[i] == 0 ){ ofSetColor(80); drawLine = true; counter = 0; } //normal every three (eighth notes) if (i % 3 == 0 && mozartTriplets){ drawLine = true; } } int beatsPerBar = 4;//set 3 for some beatles tracks int mainBeat = 0; if ((int)i % beatsPerBar == mainBeat && !mozartTriplets){ //was BeatPosoiton[i] - dodgy drawLine = true; } /* if (i == tempoVariationEndIndex){ ofSetColor(200, 0,0); drawLine = true; } */ if (drawLine){ ofLine((i-startPoint) * width, 0, (i-startPoint) * width, ofGetHeight()); } } for (int i = startPoint+1;i < min((int)timingData.size(), startPoint + numberOfPointsPerPage);i++){ if (!drawIOIintervals){ ofSetColor(0,0,0); Xpoint = (i-startPoint) * width ; if (!drawBPM) newHeightPoint = getHeightPoint(height * (timingData[i][0] - minDrawTempo)); else newHeightPoint = getHeightPoint(bpmHeight * (msToBpm(timingData[i][0]) - bpmMinTempo)); ofLine((i-startPoint-1) * width, lastHeightPoint,Xpoint, newHeightPoint); lastHeightPoint = newHeightPoint; error = timingData[i][2] - timingData[i][1]; int phaseHop = timingData[i][4]; if (!blackAndWhite) ofSetColor(255,0,0); else ofSetColor(155); if (drawExpressiveTimingData) ofLine(Xpoint, newHeightPoint, Xpoint, newHeightPoint - (phaseHop * height)); //line up from tempo to where we observe point if (!blackAndWhite) ofSetColor(0,0, 155); else ofSetColor(60); if (drawExpressiveTimingData) ofCircle(Xpoint, newHeightPoint - ((phaseHop + error) * height), 2); // ofLine((i-startPoint) * width , screenHeight/2, (i-startPoint) * width , (screenHeight/2)*(1+(timingData[i][3]/60.0))); //if (!blackAndWhite) ofSetColor(0,0,255); ofLine(((playingIndex)-startPoint) * width, 0, ((playingIndex)-startPoint) * width, screenHeight); }//if not draw IOI if (drawIOIintervals){ ofSetColor(160,160,160); ofLine((i-startPoint-1) * width, getHeightPoint(height * (basicInterOnsetInterval[i-1]- minDrawTempo)), (i-startPoint) * width, getHeightPoint(height * (basicInterOnsetInterval[i]- minDrawTempo)) ); } // ofLine((i-startPoint) * width , screenHeight/2, (i-startPoint) * width , (screenHeight/2)*(1+(timingData[i][4]/60.0))); }//end for } } static const int moveAmount = 2; void TimingAnalyser::widenDrawWindow(){ maxDrawTempo += moveAmount; minDrawTempo -= moveAmount; } void TimingAnalyser::narrowDrawWindow(){ maxDrawTempo -= moveAmount; minDrawTempo += moveAmount; } void TimingAnalyser::moveDrawWindowUp(){ maxDrawTempo += moveAmount; minDrawTempo += moveAmount; } void TimingAnalyser::moveDrawWindowDown(){ maxDrawTempo -= moveAmount; minDrawTempo -= moveAmount; } int TimingAnalyser::getIndexAtMouseXposition(const int& Xpos){ int numberPointsIn = (int)round(Xpos * numberOfPointsPerPage / ofGetWidth()); printf("number pts %i size %i , start %i no. in from left %i\n", numberOfPointsPerPage, (int)timingData.size(), startPoint, numberPointsIn); numberPointsIn += startPoint; printf("MOUSE X IS %i == %i\n", Xpos, numberPointsIn); return min((int)timingData.size(), numberPointsIn); } int TimingAnalyser::getHeightPoint(float f){ return (int)round(screenHeight - f); } void TimingAnalyser::printMatrix(DoubleMatrix& logMatrix){ for (int t = 0;t < logMatrix.size();t++){ for (int p = 0;p < logMatrix[t].size();p++){ printf("(%i,%i)= %2.1f ", t, p, logMatrix[t][p]); } printf("\n"); } } void TimingAnalyser::printLogMatrix(){ printf("\n"); for (int p = 0;p < phaseRange;p++){ printf("LOG M [%i] %2.2f, ", p, logProbabilityMatrix[0][p]); } printf("offset %i\n", globalTimeOffset); } void TimingAnalyser::exportTimingData(){ ofstream ofs(timingDataFilepath.c_str()); // ofs << "output timng data size " << (int) timingData.size() << "IOI size " << (int)basicInterOnsetInterval.size() << endl; for (int i = 0;i < min((int)timingData.size(), (int) basicInterOnsetInterval.size());i++){ for (int k = 0; k < 5;k++){ ofs << timingData[i][k] << "\t"; } ofs << basicInterOnsetInterval[i] << "\t"; ofs << endl; } } void TimingAnalyser::importTimingData(std::string importFileName){ ifstream ifs(importFileName.c_str()); printf("IMPORT FILE %s\n", importFileName.c_str()); timingData.clear(); string value; int count = 0; //Notation n; IntVector v; while ( ifs.good() ) { getline ( ifs, value, '\n' ); // read a string until next enter command // cout << "disp " << string( value, 0, value.length()-1 ) << endl; // display value removing the first and the last character from it //printf("size of line is %i\n", (int)value.size()); v.clear(); if (value.size() > 0){ string::size_type start = value.find_first_not_of(" \t\v"); string part = value.substr(start, string::npos); string otherPart = value; printf("input line%i : '%s'\n", count, otherPart.c_str()); size_t found; found=part.find_first_of("\t\n"); size_t startC = 0; int r = 0; while (found!=string::npos){ string section = otherPart.substr(startC, found - startC); int my_int = atoi(section.c_str()); v.push_back(my_int); printf("timing[%i][%i] ", count, r, my_int); printf("section '%s'\n", section.c_str()); startC= otherPart.find_first_not_of("\t\n",found); found = otherPart.find_first_of("\t\n",found+1); r++; } timingData.push_back(v); }//end if > 0 count++; } basicInterOnsetInterval.clear(); maximumTempo = 0; minimumTempo = INFINITY; for (int i = 0;i < max(0, (int)timingData.size()-1);i++){ printf("TEMPO %i %i IOI %i\n", timingData[i][0], timingData[i][1], timingData[i][4]); basicInterOnsetInterval.push_back(timingData[i][5]); int observedTempo = timingData[i][0]; // printf("observed %i\n", observedTempo); if (observedTempo > maximumTempo) maximumTempo = observedTempo; if (observedTempo < minimumTempo && observedTempo > 0) minimumTempo = observedTempo; } printf("min t %f, Max t %f\n", minimumTempo, maximumTempo); } void TimingAnalyser::exportProcessedBeatTimes(const double& firstBeatTime){ ofstream ofs(processedBeatTimesFilepath.c_str()); for (int i = 0;i < min((int)timingData.size(), (int) basicInterOnsetInterval.size());i++){ double beatTime = (timingData[i][1] + firstBeatTime)/1000.0; printf("Beat %i : %f\n", i, beatTime); ofs << beatTime; ofs << endl; } } double TimingAnalyser::msToBpm(const double& ms){ return 60000./ms; } void TimingAnalyser::calculateTempoLimits(){ maximumTempo = 0; minimumTempo = INFINITY; for (int i = 0;i < max(0, (int)timingData.size()-1);i++){ int observedTempo = timingData[i][0]; if (observedTempo > maximumTempo) maximumTempo = observedTempo; if (observedTempo < minimumTempo && observedTempo > 0) minimumTempo = observedTempo; } tempoVariationStartIndex = min(4, (int)timingData.size()); tempoVariationEndIndex = max(0, (int)timingData.size() - 4); printf("VARIATION TO BE CALCULATED BETWEEN %i and %i\n", tempoVariationStartIndex, tempoVariationEndIndex); } double TimingAnalyser::calculateTempoVariation(){ if (timingData.size() > tempoVariationStartIndex && tempoVariationEndIndex + 1< timingData.size()){ double mean = 0; for (int i = tempoVariationStartIndex;i <= tempoVariationEndIndex;i++){ mean += timingData[i][0]; } mean /= (tempoVariationEndIndex - tempoVariationStartIndex); double variation = 0; double fluctuation = 0; int lastValue = timingData[tempoVariationStartIndex][0]; for (int i = tempoVariationStartIndex;i <= tempoVariationEndIndex;i++){ variation += (timingData[i][0] - mean)*(timingData[i][0] - mean); fluctuation += abs(timingData[i][0] - lastValue); lastValue = timingData[i][0]; } variation /= (tempoVariationEndIndex - tempoVariationStartIndex); variation = sqrt(variation); fluctuation /= (tempoVariationEndIndex - tempoVariationStartIndex); double slope = (double) timingData[tempoVariationStartIndex][0] / timingData[tempoVariationEndIndex][0]; printf("TEMPO STATS:\nMean: %.2f\nVariance %.2f \nSlope %.1f per cent\nAverage delta Tempo %.2f", mean, variation, (slope*100.0), fluctuation); } } void TimingAnalyser::zoomIn(){ numberOfPointsPerPage /= 2; } void TimingAnalyser::zoomOut(){ numberOfPointsPerPage *= 2; } /* double TimingAnalyser::getCost(const int& eventTime, const int& eventLocation, const int& tempoIndex, const int& phaseIndex){ double interval = eventLocation - lastBeatPosition; double newLocation = getPhase(phaseIndex) + interval*getTempo(tempoIndex); double phaseCost = fabs(newLocation - eventTime); return phaseCost; } */