view src/AudioEventMatcher.cpp @ 4:45b5cf9be377

checking through Bayesian update procedure - working without cross update method.
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Thu, 02 Feb 2012 12:13:44 +0000
parents 5e188c0035b6
children 5ef00d1dfe68
line wrap: on
line source
/*
 *  AudioEventMatcher.cpp
 *  MultipleAudioMathcher
 *
 *  Created by Andrew on 31/01/2012.
 *  Copyright 2012 QMUL. All rights reserved.
 *
 */

#include "AudioEventMatcher.h"


const int matchWindowWidth = 6000;

AudioEventMatcher::AudioEventMatcher(){
	bayesPositionWindow.setToRelativeSize(0, 0.4, 1, 0.2);
	bayesTempoWindow.setToRelativeSize(0, 0.8, 1, 0.2);
	bayesLikelihoodWindow.setToRelativeSize(0, 0.6, 1, 0.2);
	
	setArraySizes();
	
	usingRealTime = false;
	bayesianStruct.realTimeMode = &usingRealTime;
}


void AudioEventMatcher::setArraySizes(){
	bayesianStruct.resetSpeedSize(200);
	bayesianStruct.setRelativeSpeedScalar(0.01);
	bayesianStruct.setSpeedPrior(1.0);
	bayesianStruct.relativeSpeedPrior.getMaximum();
	
	bayesianStruct.resetSize(matchWindowWidth);
	bayesianStruct.setPositionDistributionScalar(1);
	
}

void  AudioEventMatcher::startPlaying(){
	bayesianStruct.setStartPlaying();
	//bayesianStruct.posterior.printArray();
}

void AudioEventMatcher::draw(){
	ofSetColor(20,200,200);
	bayesPositionWindow.drawOutline();
	bayesTempoWindow.drawOutline();
	
	recordedTracks.drawTracks();
	
	ofSetColor(255);
//	bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
	
	double screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
	
	bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
	
//	bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);

	
	bayesianStruct.likelihood.drawVector(bayesianStruct.likelihood.getRealTermsAsIndex(0), bayesianStruct.likelihood.getRealTermsAsIndex(screenWidthMillis), bayesLikelihoodWindow);
	
	bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
	
		
	string tmpStr = "zero is "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(0));
	tmpStr += " offsetis "+ofToString(bayesianStruct.posterior.offset);
	tmpStr += " screenWidth = "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis));
	ofDrawBitmapString(tmpStr, 20,140);
	tmpStr = "best est "+ofToString(bayesianStruct.bestEstimate);
	ofDrawBitmapString(tmpStr, 20, 180);
	
	ofDrawBitmapString("screenamp "+ofToString(screenWidthMillis), 20, 100);
	
	
}


void AudioEventMatcher::newPitchEvent(const double& pitchIn, const double& timeIn){
	liveInput.addPitchEvent(pitchIn, timeIn);
	
	//tmp print stuff
	printf("New pitch MAP post estimate now %i, ", bayesianStruct.posterior.MAPestimate);
	double tmp  = bayesianStruct.posterior.getMAPestimate();
	printf(" getting it %f and offset %f == %f ms\n", tmp,  bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(tmp));
	
	matchNewPitchEvent(0, pitchIn, timeIn);
}

void AudioEventMatcher::newKickEvent(const double& timeIn){
//	liveInput.addKickEvent(time);
	matchNewOnsetEvent(0, timeIn);
}


void AudioEventMatcher::newSnareEvent(const double& timeIn){
	matchNewOnsetEvent(0, timeIn);
}

//Needs just to set bounds for the matching process, not have TimeIn
void AudioEventMatcher::matchNewOnsetEvent(const int& channel, const double& timeIn){

	
	//start at beginning but OPTIMISE later
	double onsetLikelihoodToNoise = 0.5;
	
	double likelihoodWidth = 40;
	
	bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
	bayesianStruct.likelihood.zero();//set to zero
	
	double quantity = 1;//likelihoodToNoiseRatio / numberOfMatches;
	int numberOfMatchesFound = 0;

	
	double startTime = bayesianStruct.likelihood.offset;
	double endTime = bayesianStruct.likelihood.offset + matchWindowWidth;
	
	if (channel <= recordedTracks.numberOfAudioTracks){
		for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
			double millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
			if (millisTime >= startTime && millisTime <= endTime){
				bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, likelihoodWidth,  quantity);
				numberOfMatchesFound++;
				printf("Adding Gaussian for onset at time %f offset %f\n", millisTime, bayesianStruct.likelihood.offset);
				
			}
		}
	}
	
//	bayesianStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesianStruct.likelihood.length);
	bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(onsetLikelihoodToNoise*bayesianStruct.likelihood.length));

	bayesianStruct.likelihood.renormalise();
	
		
}



void AudioEventMatcher::matchNewPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
	//start at beginning but OPTIMISE later
	
	
	updateBayesianDistributions(timeIn);
	
	///set offsets
//	bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
	double pitchLikelihoodToNoise = 0.5;
	int numberOfMatches = 0;
	bayesianStruct.likelihood.zero();//set to zero
	
	double quantity = 0;
	if (channel <= recordedTracks.numberOfAudioTracks){
		for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
			
			if (checkMatch(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn)) {
				quantity = getPitchDistance(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn, 40);
				bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, 30, quantity);
				recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = true;
				numberOfMatches++;
			} 
			else{
				recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = false;
			}
			
		}
	}
	
	bayesianStruct.likelihood.addConstant(numberOfMatches*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));
	
	recordedTracks.recentPitch = pitchIn;
	

	//tmp set likelihood constant and calculate using that
	bayesianStruct.likelihood.zero();
	bayesianStruct.likelihood.addConstant(1);
	
	bayesianStruct.calculatePosterior();
	
	//tmp print stuff
	printf("After CALC");
	printPostOffset();	
	
}

double AudioEventMatcher::getPitchDistance(const double& pitchOne, const double& pitchTwo, const double& scale){
	
	double distance = abs(pitchOne - pitchTwo);
	if (distance < scale)
		distance = 1 - (distance/scale);
	else
		distance = 0;
	
//	printf("[pitch distance %f vs %f = %f\n", pitchOne, pitchTwo, distance);
	return distance;

}


bool AudioEventMatcher::checkMatch(const double& recordedPitch, const double& livePitch){
	if (abs(recordedPitch - livePitch) < 40)
		return true;
	else
		return false;
}



void AudioEventMatcher::windowResized(const int& w, const int& h){
	recordedTracks.windowResized(w,h);
	bayesTempoWindow.resized(w,h);
	bayesPositionWindow.resized(w,h);
}



void  AudioEventMatcher::updateBayesianDistributions(const double& newEventTime){
	//MOVE INTO bayesianStruct?? XX
	
	//NEED TO CHECK HERE THAT THEY HAVE THE SAME OFFSETS
	bayesianStruct.prior.copyFromDynamicVector(bayesianStruct.posterior);//try the otehr way

	//bayesianStruct.copyPriorToPosterior();
	//need to get new MAP position and set the offset of the arrays
	//currently bestEstimate is the approx for the new MAP position
	int tmpMap = bayesianStruct.posterior.getMAPestimate();
	
	double timeDifference = newEventTime - bayesianStruct.lastEventTime; 
	printf("updating distributions at time %f diff %f offset %f tmpmap est %i\n", newEventTime, timeDifference, bayesianStruct.posterior.offset, tmpMap);
	
	//addnoise to the tempo distribution
	//bayesianStruct.decaySpeedDistribution(timeDifference);

	if (timeDifference > 50){
		bayesianStruct.addGaussianNoiseToSpeedPosterior(timeDifference * 10.0 / 100.);
	}
	
	bayesianStruct.updateBestEstimate(timeDifference);
	bayesianStruct.lastBestEstimateUpdateTime = newEventTime;//getTimeNow(timePlayed);
		
//	printf("set new distrb max 0 or %f\n", bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2));
	
	//tmp print stuff
	
	printf("HALFWAY BEST ");
	printPostOffset();
	
	bayesianStruct.setNewDistributionOffsets(max(0., bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2)));
	bayesianStruct.crossUpdateArrays(bayesianStruct.posterior, bayesianStruct.relativeSpeedPosterior, timeDifference);
	
	//i.e. using the same offset as prior
	bayesianStruct.posterior.offset = bayesianStruct.prior.offset;// 

//	float tmpPrior = max(0., bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2));//	bayesianStruct.prior.offset = max(0., bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2));
//	printf("Using prior offset of %f not %f\n", tmpPrior, bayesianStruct.prior.offset);
	
	bayesianStruct.lastEventTime = newEventTime;//bayesianStruct.lastEventTime = ofGetElapsedTimeMillis();


}

void AudioEventMatcher::printPostOffset(){
	double tmp = bayesianStruct.posterior.getMAPestimate();
	printf(" MAP index %i post offset %f == %f ms\n", bayesianStruct.posterior.MAPestimate, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate));
}