view src/AudioEventMatcher.cpp @ 6:746a5af43c02

windowed bayesian distributions - drawn within constrained portion of the screen
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Thu, 02 Feb 2012 17:52:08 +0000
parents 5ef00d1dfe68
children 33dedfe32893
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(){
	//draw some outlines in blue
	ofSetColor(20,200,200);
	bayesPositionWindow.drawOutline();
	bayesTempoWindow.drawOutline();
	
	//draw the scrolling audio tracks
	recordedTracks.drawTracks();
	
	ofSetColor(255);
//	bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
	
	drawBayesianDistributions();
	//	bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
	
	//bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
	
	//	bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
	
}
	
void AudioEventMatcher::drawBayesianDistributions(){
	
	
	double screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
	

	double screenStartTimeMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.drawParams.windowStartFrame);
	double screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
	int startIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenStartTimeMillis);
	int endIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenEndTimeMillis);
	
	bayesianStruct.posterior.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesPositionWindow);
	
	string tmpString = "start "+ofToString(screenStartTimeMillis)+" (index "+ofToString(startIndex)+"), end "+ofToString(screenEndTimeMillis);
	ofDrawBitmapString(tmpString, bayesPositionWindow.x+20, bayesPositionWindow.y+20);

	
	
	bayesianStruct.likelihood.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesLikelihoodWindow);
	
	
	//bayesianStruct.likelihood.drawVector(bayesianStruct.likelihood.getRealTermsAsIndex(0), bayesianStruct.likelihood.getRealTermsAsIndex(screenWidthMillis), bayesLikelihoodWindow);

	bayesianStruct.relativeSpeedPosterior.drawConstrainedVector(0, bayesianStruct.relativeSpeedPosterior.arraySize, 0, ofGetWidth(), 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 int& channel, 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(channel, pitchIn, timeIn);
}

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

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


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

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

	
	bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
	
	//start at beginning but OPTIMISE later
	double onsetLikelihoodToNoise = 0.3;
	
	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
	
	
	bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
	
	///set offsets
//	bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
	double pitchLikelihoodToNoise = 0.2;//more noise
	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, 20);
				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;
			}
			
		}
	}

	if (numberOfMatches > 0){//no point updating unless there is a match
		bayesianStruct.likelihood.addConstant(numberOfMatches*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));

	//tmp set likelihood constant and calculate using that
	//bayesianStruct.likelihood.zero();
	//bayesianStruct.likelihood.addConstant(1);
	
		bayesianStruct.calculatePosterior();
	}
	
}

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);
}