andrew@19: /* andrew@19: * TempoFollower.cpp andrew@19: * MultipleAudioMathcher andrew@19: * andrew@19: * Created by Andrew on 09/02/2012. andrew@19: * Copyright 2012 QMUL. All rights reserved. andrew@19: * andrew@19: */ andrew@19: andrew@19: #include "TempoFollower.h" andrew@19: andrew@19: TempoFollower::TempoFollower(){ andrew@20: andrew@20: andrew@20: andrew@20: andrew@19: maximumTempoInterval = 600; andrew@19: minimumTempoInterval = 200; andrew@19: tempoArraySize = maximumTempoInterval - minimumTempoInterval;; andrew@19: andrew@19: tempoPrior.createVector(tempoArraySize); andrew@19: tempoPosterior.createVector(tempoArraySize); andrew@19: tempoLikelihood.createVector(tempoArraySize); andrew@19: andrew@19: tempoPosterior.addConstant(1); andrew@19: andrew@19: tempoPrior.offset = minimumTempoInterval; andrew@19: tempoPosterior.offset = minimumTempoInterval; andrew@19: tempoLikelihood.offset = minimumTempoInterval; andrew@19: andrew@19: tempoPrior.scalar = 1; andrew@19: tempoPosterior.scalar = 1; andrew@19: tempoLikelihood.scalar = 1; andrew@20: andrew@20: intervalsToTest[0] = 1; andrew@20: intervalsToTest[1] = 2; andrew@20: intervalsToTest[2] = 4; andrew@20: intervalsToTest[3] = 8; andrew@20: intervalsToTest[4] = 16; andrew@20: andrew@20: setUpEventTimeMatrix(); andrew@19: } andrew@19: andrew@20: void TempoFollower::zero(){ andrew@20: eventTimes.clear(); andrew@20: tempoIntervals.clear(); andrew@20: divisions.clear(); andrew@20: tempo.clear(); andrew@20: globalTempo.clear(); andrew@20: globalTempoTimes.clear(); andrew@20: setUpEventTimeMatrix(); andrew@20: } andrew@19: andrew@19: void TempoFollower::reset(){ andrew@55: printf("reset temporal follower\n"); andrew@19: tempoPrior.zero(); andrew@19: tempoPosterior.addConstant(1); andrew@20: andrew@19: } andrew@19: andrew@19: andrew@19: void TempoFollower::setUpEventTimeMatrix(){ andrew@19: for (int i = 0;i < NUMBER_OF_CHANNELS;i++){ andrew@19: IntVector v; andrew@19: eventTimes.push_back(v); andrew@19: andrew@19: DoubleMatrix m; andrew@19: tempoIntervals.push_back(m); andrew@20: andrew@20: IntMatrix h; andrew@20: divisions.push_back(h); andrew@20: andrew@20: DoubleVector d; andrew@20: tempo.push_back(d); andrew@20: andrew@19: } andrew@19: andrew@19: //[channel][index] andrew@19: } andrew@19: andrew@19: andrew@19: andrew@19: void TempoFollower::printEventTimes(){ andrew@19: for (int i = 0;i < NUMBER_OF_CHANNELS;i++){ andrew@19: printf("CHANNEL %i EVENT TIMES...\n", i); andrew@19: for (int j = 0;j < eventTimes[i].size();j++) andrew@19: printf("%i\n", eventTimes[i][j]); andrew@19: } andrew@19: } andrew@19: andrew@19: andrew@20: void TempoFollower::printTempoTimes(){ andrew@20: andrew@20: for (int i = 0;i < globalTempo.size();i++){ andrew@20: printf("Time %i : tempo %f\n", globalTempoTimes[i], globalTempo[i]); andrew@20: } andrew@20: } andrew@20: andrew@19: void TempoFollower::updateTempo(const int& channel, const int& timeIn){ andrew@19: andrew@19: eventTimes[channel].push_back(timeIn); andrew@19: andrew@19: int interval = 0; andrew@19: int intervalLimit = 3600; andrew@19: andrew@19: int recentIndex = eventTimes[channel].size()-1; andrew@19: andrew@19: DoubleVector d; andrew@20: IntVector div; andrew@19: int recentEvent = eventTimes[channel][recentIndex]; andrew@19: andrew@19: for (int i = 1;i < eventTimes[channel].size() && interval < intervalLimit;i++){ andrew@19: interval = eventTimes[channel][recentIndex] - eventTimes[channel][recentIndex - i]; andrew@19: andrew@20: for (int k = 0;k < 3;k++){ andrew@19: andrew@20: double divisionInEighthNotes = intervalsToTest[k]; andrew@19: double testInterval = interval / divisionInEighthNotes; andrew@20: if (testTempoInterval(channel, testInterval, d)){ andrew@55: printf("channel %i interval %i at division %.0f == tempo update %f\n", channel, interval, divisionInEighthNotes, testInterval); andrew@20: div.push_back((int)divisionInEighthNotes); andrew@20: } andrew@19: } andrew@19: } andrew@19: andrew@19: (tempoIntervals[channel]).push_back(d); andrew@20: (divisions[channel]).push_back(div); andrew@19: updateTempoDistribution(d); andrew@19: andrew@20: tempo[channel].push_back(playingTempo); andrew@20: andrew@20: globalTempo.push_back(playingTempo); andrew@20: globalTempoTimes.push_back(timeIn); andrew@20: andrew@20: andrew@19: } andrew@19: andrew@19: andrew@19: bool TempoFollower::testTempoInterval(const int& channel, const double& testInterval, DoubleVector& d){ andrew@19: andrew@19: bool updated = false; andrew@19: if (testInterval >= minimumTempoInterval && testInterval <= maximumTempoInterval){ andrew@19: d.push_back(testInterval); andrew@19: updated = true; andrew@19: } andrew@19: return updated; andrew@19: } andrew@19: andrew@19: void TempoFollower::updateTempoDistribution(const DoubleVector& d){ andrew@19: double tempoLikelihoodToNoiseRatio = 0.8; andrew@20: tempoLikelihoodStdDev = 4; andrew@20: andrew@19: tempoLikelihood.zero(); andrew@19: double amount = tempoLikelihoodToNoiseRatio/d.size(); andrew@19: for (int i = 0;i < d.size();i++){ andrew@20: tempoLikelihood.addGaussianShapeFromRealTime(d[i], tempoLikelihoodStdDev, amount); andrew@19: } andrew@19: tempoLikelihood.addConstant(0.1*(1-tempoLikelihoodToNoiseRatio)); andrew@19: calculatePosterior(); andrew@19: tempoPosterior.renormalise(); andrew@19: playingTempo = tempoPosterior.getIndexInRealTerms(tempoPosterior.getMAPestimate()); andrew@20: andrew@19: } andrew@19: andrew@19: andrew@19: andrew@19: void TempoFollower::calculatePosterior(){ andrew@19: tempoPrior.copyFromDynamicVector(tempoPosterior); andrew@19: for (int i = 0;i < tempoArraySize;i++){ andrew@19: tempoPosterior.array[i] = tempoLikelihood.array[i] * tempoPrior.array[i]; andrew@19: } andrew@19: } andrew@19: andrew@19: void TempoFollower::drawTempoArray(ofxWindowRegion& window){ andrew@19: ofSetColor(150,0,250); andrew@19: tempoPosterior.drawConstrainedVector(0, tempoArraySize, 0, ofGetWidth(), window); andrew@20: andrew@20: ofSetColor(150,150,150); andrew@20: tempoLikelihood.drawConstrainedVector(0, tempoArraySize, 0, ofGetWidth(), window); andrew@20: andrew@50: if (printOutput) andrew@19: ofDrawBitmapString("tempo "+ofToString(playingTempo), window.x+ 20, window.y + 40); andrew@20: andrew@20: andrew@20: for (int channel = 0;channel < 3;channel+=2){ andrew@20: int index = tempoIntervals[channel].size()-1; andrew@20: andrew@20: for (int i = 0;i < 9 && index - i>=0 ;i++){ andrew@20: for (int j = 0;j < tempoIntervals[channel][index - i].size();j++){ andrew@20: ofSetColor(channel*125, 0, 250); andrew@20: ofCircle(window.x+((tempoIntervals[channel][index - i][j] - minimumTempoInterval)*window.width)/tempoArraySize, window.y+6+(window.height*i/9.0), 4); andrew@20: ofSetColor(255,255,255); andrew@20: ofDrawBitmapString(ofToString(divisions[channel][index-i][j]), (window.x+((tempoIntervals[channel][index - i][j] - minimumTempoInterval)*window.width)/tempoArraySize)-3, window.y+8+(window.height*i/9.0)); andrew@20: } andrew@20: } andrew@20: } andrew@19: } andrew@20: