view src/TempoFollower.cpp @ 19:1a62561bd72d

Added in tempo follower class that models the tempo of played events
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Thu, 09 Feb 2012 18:09:34 +0000
parents
children 4f6006cac9de
line wrap: on
line source
/*
 *  TempoFollower.cpp
 *  MultipleAudioMathcher
 *
 *  Created by Andrew on 09/02/2012.
 *  Copyright 2012 QMUL. All rights reserved.
 *
 */

#include "TempoFollower.h"

TempoFollower::TempoFollower(){
	maximumTempoInterval = 600;
	minimumTempoInterval = 200;
	tempoArraySize = maximumTempoInterval - minimumTempoInterval;;
	
	tempoPrior.createVector(tempoArraySize);
	tempoPosterior.createVector(tempoArraySize);
	tempoLikelihood.createVector(tempoArraySize);
	
	tempoPosterior.addConstant(1);
	
	tempoPrior.offset = minimumTempoInterval;
	tempoPosterior.offset = minimumTempoInterval;
	tempoLikelihood.offset = minimumTempoInterval;
	
	tempoPrior.scalar = 1;
	tempoPosterior.scalar = 1;
	tempoLikelihood.scalar = 1;
}


void TempoFollower::reset(){

	tempoPrior.zero();
	tempoPosterior.addConstant(1);
}


void TempoFollower::setUpEventTimeMatrix(){
	for (int i = 0;i < NUMBER_OF_CHANNELS;i++){
		IntVector v;
		eventTimes.push_back(v);
		
		DoubleMatrix m;	
		tempoIntervals.push_back(m);
	}
	
	//[channel][index]
}



void TempoFollower::printEventTimes(){
	for (int i = 0;i < NUMBER_OF_CHANNELS;i++){
		printf("CHANNEL %i EVENT TIMES...\n", i);
		for (int j = 0;j < eventTimes[i].size();j++)
			printf("%i\n", eventTimes[i][j]);
	}
}


void TempoFollower::updateTempo(const int& channel, const int& timeIn){
	
	eventTimes[channel].push_back(timeIn);
	
	int interval = 0;
	int intervalLimit = 3600;
	
	int recentIndex = eventTimes[channel].size()-1;
	
	DoubleVector d;
	
	int recentEvent = eventTimes[channel][recentIndex];
	
	for (int i = 1;i < eventTimes[channel].size() && interval < intervalLimit;i++){
		interval = eventTimes[channel][recentIndex] - eventTimes[channel][recentIndex - i];
		
		if (testTempoInterval(channel, interval, d))
			printf("channel %i interval %i at division 1 == tempo update %i\n", channel, interval, interval);
		
		for (double divisionInEighthNotes = 2.0;divisionInEighthNotes < 9.0;divisionInEighthNotes+=2){
			
			double testInterval = interval / divisionInEighthNotes;
			if (testTempoInterval(channel, testInterval, d))
				printf("channel %i interval %i at division %.0f == tempo update %f\n", channel, interval, divisionInEighthNotes, testInterval);
			
		}
	}
	
	(tempoIntervals[channel]).push_back(d);
	updateTempoDistribution(d);
	
}


bool TempoFollower::testTempoInterval(const int& channel, const double& testInterval, DoubleVector& d){

	bool updated = false;
	if (testInterval >= minimumTempoInterval && testInterval <= maximumTempoInterval){
		d.push_back(testInterval);
		updated = true;
	}
	return updated;
}

void TempoFollower::updateTempoDistribution(const DoubleVector& d){
	double tempoLikelihoodToNoiseRatio = 0.8;
	tempoLikelihood.zero();
	double amount = tempoLikelihoodToNoiseRatio/d.size();
	for (int i = 0;i < d.size();i++){
		tempoLikelihood.addGaussianShapeFromRealTime(d[i], 10, amount);
	}
	tempoLikelihood.addConstant(0.1*(1-tempoLikelihoodToNoiseRatio));
	calculatePosterior();
	
	tempoPosterior.renormalise();
	playingTempo = tempoPosterior.getIndexInRealTerms(tempoPosterior.getMAPestimate());
}



void TempoFollower::calculatePosterior(){
	tempoPrior.copyFromDynamicVector(tempoPosterior);
	for (int i = 0;i < tempoArraySize;i++){
		tempoPosterior.array[i] = tempoLikelihood.array[i] * tempoPrior.array[i];
	}
}

void TempoFollower::drawTempoArray(ofxWindowRegion& window){
	ofSetColor(150,0,250);
	tempoPosterior.drawConstrainedVector(0, tempoArraySize, 0, ofGetWidth(), window);
	ofDrawBitmapString("tempo "+ofToString(playingTempo), window.x+ 20, window.y + 40);
}