view ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetectorOffline.cpp @ 8:184a7c232049 tip

changed files since updating computer
author Venetian
date Thu, 14 Aug 2014 17:53:57 +0100
parents b1c13e8bec26
children
line wrap: on
line source
/*
 *  PreciseBassOnsetDetectorOffline.cpp
 *  BasslinePrediction
 *
 *  Created by Andrew N Robertson on 11/04/2014.
 *  Copyright 2014 QMUL. All rights reserved.
 *
 */

#include "PreciseBassOnsetDetectorOffline.h"

bool printingOn = false;//true;

int PreciseBassOnsetDetectorOffline::processAudioFileForBeatTimes(std::string audiofile){
	printf("PBODO: Process Function BASS OFFLINE VERSION\n");

	//originally from BeatAnnotationViewer project
	
	initialise();// - needed but elsewhere
	isBass = true;
	
	// static double frame[FRAMESIZE]; // to hold a single frame
	double buffer[frameSize];
	double dfval;
	
	for (int i = 0;i < frameSize;i++)
	{
		buffer[i] = 0;
	}
	
	//detfun = new df(1,(FRAMESIZE*2),0);
	
	
	
    SNDFILE      *infile, *outfile ;	// define input and output sound files
	
    SF_INFO		sfinfo ;	// struct to hold info about sound file
    int			readcount ;	// counts number of samples read from sound file
    const char	*infilename =  audiofile.c_str();
	//"/Users/andrew/Music/Station To Station 2/3-03 Panic In Detroit (Live Nassau Coliseum '76).wav";//"ledzep.wav" ;	// input file name
	//   const char	*outfilename = "output.wav" ; // output file name
	
	
	// Open Input File
    if (! (infile = sf_open (infilename, SFM_READ, &sfinfo)))
    {   // Open failed
        printf ("Not able to open input file %s.\n", infilename) ;
        // Print the error message from libsndfile. 
        puts (sf_strerror (NULL)) ;
        return 1;
	} ;
	
	printf("opened '%s'\n", audiofile.c_str());
	loadedFilename = audiofile;
	//STEREO OKAY
	
	//HERE IS THE CLASSIC LOADING FILE CODE
	//DEALS WITH MORE THAN MONO
	int channels = sfinfo.channels;
	samples = sfinfo.frames;
	printf("Number of channels %i, samples %i\n", channels, samples);
	
	
	int blocksize = hopSize;//FRAMESIZE;	
	float buf [channels * blocksize] ;
	float frame[blocksize];
	
	int k, m;
	readcount = 1;
	
	
	//DoubleVector d;
	while ((readcount = sf_readf_float (infile, buf, blocksize)) > 0){
		for (k = 0 ; k < readcount ; k++){	
			//d.clear();
			frame[k] = 0;
			
			for (m = 0 ; m < channels ; m++){
				frame[k] += buf[k*channels + 0];//sum the channels together
				//d.push_back(buf [k * channels + m]);
			}
			
			frame[k] /= channels;//average of the channels
		}
		
		processAudioFrame(frame, blocksize);
		processYinAudio(frame, blocksize);
		//have now processed framecount samples
		processBassPitches();
		
		//add to our buffer for pocessing
		for (int i = 0; i< frameSize-hopSize;i++)
		{
			buffer[i] = buffer[i+hopSize];
			buffer[i+hopSize] = frame[i];
		}

		//printf("read %i samples\n", readcount);
		//was	sf_write_double(outfile, frame, readcount) ;
		
	}//end readcount
	//END STEREO OKAY
	
	// Close input file
    sf_close (infile);
	
	endProcessing();
	
	return 0;
	
}

void PreciseBassOnsetDetectorOffline::processYinAudio(float* frame, int blocksize){
	//continually update yin
	yule.processBuffer(frame, (int)blocksize);
}

void PreciseBassOnsetDetectorOffline::processBassPitches(){
	int tmpIndex = onsetList.size()-1;
	int hsize = 1024;
	while (tmpIndex >= 0 && onsetList[tmpIndex].positionSamples+8192+hsize>sampleCount){
	//	printf("testing onset %i at %i, samplecount now %i diff %i\n", tmpIndex, onsetList[tmpIndex].positionSamples, sampleCount, sampleCount-onsetList[tmpIndex].positionSamples);
		if (onsetList[tmpIndex].positionSamples+8192 <= sampleCount && !onsetList[tmpIndex].pitch){
			//do yin value for this onset
			onsetList[tmpIndex].pitch = yule.yinFrequency;
			
			float midiPitch = onsetList[tmpIndex].pitch/27.5;
			if (midiPitch > 0){
				midiPitch = 12.0*log(midiPitch)/log(2);
				midiPitch += 24+9;
			}
			
			onsetList[tmpIndex].midiPitch = midiPitch;
			onsetList[tmpIndex].roundedPitch = round(midiPitch);
			
			setMidiNoteString(tmpIndex, midiPitch);//sets string eg C#4
			
			if (printingOn)
				printf("PBODO: Yin Pitch %i, %f, %f, %s\n", tmpIndex, onsetList[tmpIndex].pitch, midiPitch, onsetList[tmpIndex].midiName.c_str());
		}
		
		tmpIndex--;
	}
}

void PreciseBassOnsetDetectorOffline::printOnsetLocations(){
	for (int i = 0; i < (int)onsetList.size(); i++)
		printf("PBODO: Onset[%i]: %.3f, samples %i pitch %f, %i\n", i, onsetList[i].onsetLocation, onsetList[i].positionSamples, onsetList[i].pitch, onsetList[i].roundedPitch);
}

void PreciseBassOnsetDetectorOffline::printPitchInfo(){
	for (int i = 0; i < (int)onsetList.size(); i++)
		printf("PBODO: Beat Position %i,%i: pitch %i\n", onsetList[i].beatPosition, onsetList[i].onsetType, onsetList[i].roundedPitch);
}

void PreciseBassOnsetDetectorOffline::setMidiNoteString(int index, float midiPitch){
	std:string midiName = "";
	if (midiPitch > 0){
		midiPitch = round(midiPitch);
		int midiLetter = (int)midiPitch%12;
		int midiOctave = midiPitch - (int)midiPitch%12;
		
		switch (midiLetter) {
			case 0:
				midiName = "C ";
				break;
			case 1:
				midiName = "C#";
				break;
			case 2:
				midiName = "D";
				break;
			case 3:
				midiName = "D#";
				break;
			case 4:
				midiName = "E";
				break;
			case 5:
				midiName = "F ";
				break;
			case 6:
				midiName = "F#";
				break;
			case 7:
				midiName = "G ";
				break;
			case 8:
				midiName = "G#";
				break;
			case 9:
				midiName = "A ";
				break;
			case 10:
				midiName = "A#";
				break;
			case 11:
				midiName = "B ";
				break;
			default:
				break;
		}
		
		midiName += ofToString(midiOctave/12);
	}
	if (index < onsetList.size())
		onsetList[index].midiName = midiName;
	
}


/*
#pragma mark Quantisation
void PreciseBassOnsetDetectorOffline::categoriseOnsets(std::vector<double> beatTimes){
	double onsetTime;
	for (int i = 0; i < onsetList.size(); i++){
		
		onsetTime = onsetList[i].onsetLocation;
		
		int beatIndex = 0;
		while(beatIndex < beatTimes.size() && beatTimes[beatIndex] < onsetTime){
			beatIndex++;
		}
		while(beatIndex > 0 && beatTimes[beatIndex] > onsetTime){
			beatIndex--;
		}
		//beatIndex now either side of onset, or onset before first beat
		
		printf("beat %.2f, beat+1 %.2f, onset %.2f freq %.2f, ", beatAtIndex(beatTimes, beatIndex),
			   beatAtIndex(beatTimes, beatIndex+1), onsetTime, onsetList[i].pitch);
		
		double beatTime =  beatAtIndex(beatTimes, beatIndex);//vampBeats.vampBeatAtIndex(beatIndex);
		
		
		double diff = onsetTime - beatTime;
		double nextBeat = beatAtIndex(beatTimes, beatIndex+1);
		double period;
		if (nextBeat){
			period = nextBeat - beatAtIndex(beatTimes, beatIndex);
		} else {
			period = beatAtIndex(beatTimes, beatIndex) - beatAtIndex(beatTimes, beatIndex-1);
		}
		if (period > 0){
			while (diff < 0){
				diff += period;
				//i.e. add the beat period to bring it in range
			}
			//now can look which point it is nearest
			double ratio = diff/period;
			ratio *= 12;
			int beattype = round(ratio);
			if (beattype == 12){
				beattype = 0;
				beatIndex++;//added need to test
			}
			
			doCorrection(beattype, beatIndex);
			
			
			
			//	Onset newOnset;
			//	newOnset.time = onsetTime;
			//	newOnset.onsetType = beattype;
			int position = beatIndex%4;
			
			//		if (onsetTime > beatTime + (period/2.))
			//			pos++;
			//		newOnset.position = pos%4;
			
			onsetList[i].beatPosition = beatIndex;
			onsetList[i].onsetType = beattype;
			
			printf("Pitch %i, Position %i,%i\n", onsetList[i].roundedPitch, onsetList[i].beatPosition, beattype);
		}
		
	}
}
 */

#pragma mark PredictionProcess
void PreciseBassOnsetDetectorOffline::predictionProcess(){


	
	//NOW do do precition
	int optimalLag = getOptimalLag();
	checkPrediction(optimalLag);
	
	checkPredictionFirstOrderMarkovCorrect(optimalLag);
	
	//checkBars(); - not quite sure what the idea here was
	
	/*
	 beatInfo.clear();
	 
	 int tmpIndex = 0;
	 double nearestOnset = preciseDetector.pod.onsetAtIndex(0);//start with first
	 for (int beatIndex = 0; beatIndex < beatTimes.size(); beatIndex++){
	 double newBeatTime =  beatTimes[beatIndex];
	 printf("BEAT %i: %f, ", beatIndex, newBeatTime);
	 double diff = pod.onsetAtIndex(tmpIndex) - newBeatTime;
	 while (fabs(pod.onsetAtIndex(tmpIndex+1) - newBeatTime) < fabs(diff)){
	 tmpIndex++;
	 nearestOnset = pod.onsetAtIndex(tmpIndex);
	 diff = pod.onsetAtIndex(tmpIndex) - newBeatTime;
	 printf("testing %f, ", nearestOnset);
	 }
	 
	 
	 Beat newBeat(false);
	 newBeat.onsetFound = false;
	 if (fabs(nearestOnset - newBeatTime) < thresholdForBeat){
	 newBeat.onsetFound = true;
	 newBeat.onsetDifference = nearestOnset - newBeatTime;
	 printf("nearest onset %f\n", nearestOnset);
	 } else 
	 printf("no onset %f\n", nearestOnset);
	 //within 80 msec we say the onset is attributable to the beat location
	 newBeat.onsetTime = nearestOnset;
	 newBeat.time = newBeatTime;
	 newBeat.errorFound = false;
	 newBeat.period = nan(0);
	 //find error
	 int secondBeat = beatIndex-4;
	 if (beatIndex%4 != 0)
	 secondBeat = beatIndex - (beatIndex%4);
	 int firstBeat = secondBeat - 4;
	 
	 //for the one use previous bar kicks
	 //otherwise recent kicks on the one
	 
	 if (firstBeat >= 0 && secondBeat < beatInfo.size()){
	 if (beatInfo[firstBeat].onsetFound && beatInfo[secondBeat].onsetFound){
	 //can project
	 double beatPeriod = beatInfo[secondBeat].onsetTime - beatInfo[firstBeat].onsetTime;
	 beatPeriod /= 4.;
	 printf("%i beat period %f, ", beatIndex, beatPeriod);
	 double error = nearestOnset - (beatInfo[secondBeat].onsetTime + (beatIndex - secondBeat)*beatPeriod);
	 printf("error %.1f, beat period %.1f\n", error*1000., beatPeriod*1000.);
	 newBeat.errorFound = true;				
	 newBeat.error = error;
	 newBeat.period = beatPeriod;
	 }
	 
	 }//end if  error
	 
	 beatInfo.push_back(newBeat);
	 
	 
	 }
	 */
	
	//dtwProcess();
}

int PreciseBassOnsetDetectorOffline::getOptimalLag(){
	std::vector<double> correlationScores;
	int maxCorr = 35;
	
	for (int i = 0; i < maxCorr; i++){
		correlationScores.push_back(quantisedCorrelation(i));
		printf("Lag[%i] %f\n", i, correlationScores[i]);
	}
	
	int optimalLag = 8;
	
	BeatWriter newWriter;
	newWriter.closeFile();
	newWriter.openFile("/Users/andrewrobertson/corr_scores.txt");
	
	
	//make rayligh
	std::vector<double> wv;
	float rayparam = 16;
	for (int n = 0; n < maxCorr;n++){
		wv.push_back(((float) n / pow(rayparam,2)) * exp((-1*pow((float)-n,2)) / (2*pow(rayparam,2))));
		//wv.push_back(1.0 - fabs(n - 12.0)/64.0);
		
	}
	
	double maxScore = 0;
	for (int i = 2; i < maxCorr; i++){
		printf("Lag[%i] %f, rayleigh %f, weighted vec %f\n", i, correlationScores[i], wv[i], correlationScores[i]*wv[i]);
		if (correlationScores[i]*wv[i] > maxScore){
			maxScore = correlationScores[i]*wv[i];
			optimalLag = i;
		}
		
		newWriter.outputFile << i << "\t" << correlationScores[i] << std::endl;
	}
	
	newWriter.closeFile();
	
	printf("Optimal lag %i\n", optimalLag);
	
	return optimalLag;
}


/*
void PreciseBassOnsetDetectorOffline::doCorrection(int& beattype, int& beatPosition){
	switch (beattype) {
		case 1:
			beattype = 0;//on beat
			break;
		case 2:
			beattype = 3;//16th
			break;
		case 5: case 7:
			beattype = 6;//8th note
			break;
		case 10:
			beattype = 9;
			break;
		case 11:
			beattype = 0;//on the beat
			beatPosition++;
			break;
		default:
			break;
	}
	
}
*/



double PreciseBassOnsetDetectorOffline::quantisedCorrelation(int lag){
	
	double meanCorrelation = 0;
	int count = 0;
	
	bool printResult = false;
	
	for (int index = 0; index < onsetList.size(); index++){
		int midiPitch = 0;
		if (printResult)
			printf("testing %i,%i (%i): ", onsetList[index].beatPosition, onsetList[index].onsetType, onsetList[index].roundedPitch);
		
		getOnsetAtBeat(index, onsetList[index].beatPosition-lag, onsetList[index].onsetType, midiPitch);
		
		if (midiPitch){
			count++;
			if (midiPitch == onsetList[index].roundedPitch){
				meanCorrelation++;
			}
			if (printResult){
				printf("MIDI pitch found %i at lag %i\n", midiPitch, lag);
			} else if (printResult){
				printf("none\n");
			}
		}
	}
	
	if (count > 0)
		meanCorrelation /= count;
	
	return meanCorrelation;
}


void PreciseBassOnsetDetectorOffline::checkPrediction(int lag){
	
	//NAIVE METHOD - JUST LOOKING BACK FOR A BASS NOTE IN SAME POSITION AT SAME LAG
	
	int matches = 0;
	int octaveErrors = 0;
	int mismatches = 0;
	
	bool printResult = false;//true;
	
	for (int index = 0; index < onsetList.size(); index++){
		onsetList[index].midiPrediction = 0;
		
		int midiPitch = 0;
		if (printResult)
			printf("prediction %i,%i (%i): ", onsetList[index].beatPosition, onsetList[index].onsetType, onsetList[index].roundedPitch);
		
		int k = 1;
		while (onsetList[index].beatPosition - k *lag >= 0){
				
			int tmp = getOnsetAtBeat(index, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, midiPitch);
			
		//	if (tmp == 0 && printResult)
		//		printf("k %i, pos %i,%i : midi pred %i\n", k, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, tmp);
			
			if (midiPitch)//i.e. non zero prediction made looking back k*lag beats
				break;
			
			k++;//keep looking back
		}
		
		if (midiPitch){
			onsetList[index].midiPrediction = midiPitch;
		
			if (printResult)
				printf(", pred %i, (lag %i) ", midiPitch, k);
			
			if (midiPitch == onsetList[index].roundedPitch){
				onsetList[index].matchIndicator = 1;
				matches++;
				if (printResult)	
					printf(", MATCH");
				
			}
			
			if (midiPitch && onsetList[index].roundedPitch && abs(midiPitch - onsetList[index].roundedPitch) == 12){
				octaveErrors++;
				if (printResult)
					printf(", OCTAVE %i", onsetList[index].roundedPitch-midiPitch);
			}
			else if (midiPitch && onsetList[index].roundedPitch && midiPitch != onsetList[index].roundedPitch){
				mismatches++;
				if (printResult)
					printf(", NOT");
			}
		}
		
		if (printResult)
			printf("\n");
		
	}
	double sum = matches+octaveErrors+mismatches;
	printf("\nResult: naive method\noptimal lag %i, matches %.3f, octaves %.3f, wrong %.3f\n", lag, matches/sum, octaveErrors/sum, mismatches/sum);
}


int PreciseBassOnsetDetectorOffline::getOnsetAtBeat(int tmpIndex, int beatPosition, int beatType, int& midiPitch){
	bool onsetFound = false;
	while (tmpIndex >= 0 && onsetList[tmpIndex].beatPosition >= beatPosition){
		if (onsetList[tmpIndex].beatPosition == beatPosition && onsetList[tmpIndex].onsetType == beatType){
			midiPitch = onsetList[tmpIndex].roundedPitch;
			//printf("beat pos %i,%i: pitch %i\n", beatPosition, beatType, midiPitch);
			onsetFound = true;
			break;
		}
		tmpIndex--;
	}
	return onsetFound;
	
}




void PreciseBassOnsetDetectorOffline::checkPredictionFirstOrderMarkovCorrect(int lag){
	
	//LOOKING BACK, NOW REQUIRE ANOTHER CORRECT BASS NOTE RECENT BEAT POSITION
	//hard to find 
	printf("First Order check\n");
	
	
	int matches = 0;
	int octaveErrors = 0;
	int mismatches = 0;
	
	bool printResult = false;//rue;
	
	if (printResult)
		printPitchInfo();
	
	for (int index = 0; index < onsetList.size(); index++){
		onsetList[index].midiPrediction = 0;
		
		int midiPitch = 0;
		int previousMidiPitch = 0;
		
		if (printResult)
			printf("\nBeat Position %i,%i:\n", onsetList[index].beatPosition, onsetList[index].onsetType);
		
		int k = 1;
		
		int previousOffset = 1;//the note before
		while (index - previousOffset >= 0 && (onsetList[index-previousOffset].onsetType != 0 || !onsetList[index-previousOffset].roundedPitch)){
			//printf("rejected: index %i offset %i onset type %i pitch %i\n", index, previousOffset, onsetList[index-previousOffset].onsetType, onsetList[index-previousOffset].roundedPitch);
			previousOffset++;
		}
		
		
		if (printResult){
			printf("\nBeat Position %i,%i (midi %i, prev %i,%i: %i):\n", onsetList[index].beatPosition, onsetList[index].onsetType, onsetList[index].roundedPitch, onsetList[index-previousOffset].beatPosition, onsetList[index-previousOffset].onsetType, onsetList[index-previousOffset].roundedPitch);
			printf("Previous position is %i,%i offset %i, previous pitch %i\n", onsetList[index-previousOffset].beatPosition, onsetList[index-previousOffset].onsetType, previousOffset, onsetList[index-previousOffset].roundedPitch);
		}
		
		bool printit = false;
		//first try our new markov condition
		while (onsetList[index].beatPosition - k *lag >= 0){
			
			if(getOnsetAtBeat(index, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, midiPitch)
			   && getOnsetAtBeat(index - previousOffset, onsetList[index - previousOffset].beatPosition - (k*lag), onsetList[index-previousOffset].onsetType, previousMidiPitch))
			{
				if (printit)
					printf("k %i Predicted pitch %i Lag previous pitch %i \n", k, midiPitch, previousMidiPitch);
				//midi picth is what we would do precition as, but require that the previous note also match our last observed note
				
				//onset found at beat pos - k*lag
				//then is the same
		
				bool goodPrediction = false;
				if (previousMidiPitch == onsetList[index-previousOffset].roundedPitch){
					goodPrediction = true;
					if (printit)
							printf("Previous Found! Lag %i Lag previous pitch %i\nPrediction %i", k, onsetList[index-previousOffset].roundedPitch, previousMidiPitch, midiPitch); 
				}
					
				
				
				if (midiPitch && goodPrediction)//i.e. non zero prediction made looking back k*lag beats
				{
					if (printit)
						printf("MIDI PREDICTION IS %i\n", midiPitch);
					break;
				}
			}
			k++;//keep looking back
		}
		
		
		if (!midiPitch){
			k = 1;
			while (onsetList[index].beatPosition - k *lag >= 0){
				
				int tmp = getOnsetAtBeat(index, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, midiPitch);
				
				if (midiPitch)//i.e. non zero prediction made looking back k*lag beats
					break;
				
				k++;//keep looking back
			}
		}
		
		if (midiPitch){//only test non zero
			onsetList[index].midiPrediction = midiPitch;
			
			if (printResult)
				printf(", pred %i, (lag %i) ", midiPitch, k);
			
			if (midiPitch == onsetList[index].roundedPitch){
				onsetList[index].matchIndicator = 1;
				matches++;
				if (printResult)	
					printf(", MATCH");
				
			}
			
			if (midiPitch && onsetList[index].roundedPitch && abs(midiPitch - onsetList[index].roundedPitch) == 12){
				octaveErrors++;
				if (printResult)
					printf(", OCTAVE %i", onsetList[index].roundedPitch-midiPitch);
			}
			else if (midiPitch && onsetList[index].roundedPitch && midiPitch != onsetList[index].roundedPitch){
				mismatches++;
				if (printResult)
					printf(", NOT");
			}
		}
		
		if (printResult)
			printf("\n");
		
	}
	double sum = matches+octaveErrors+mismatches;
	printf("Results : First Oreder Markov Correct\nOptimal lag %i, matches %.3f, octaves %.3f, wrong %.3f\n", lag, matches/sum, octaveErrors/sum, mismatches/sum);
}



void PreciseBassOnsetDetectorOffline::checkBars(){
	int bar = 0;
	double barScore = 0;
	double barCount;
	std::vector<double> scorePerBar;
	for (int index = 0; index < onsetList.size(); index++){
		while (bar*4 < onsetList[index].beatPosition){
			if (barCount > 0)
				barScore /= barCount;
			scorePerBar.push_back(barScore);
			//now going to next bar so reinitialise
			bar++;
			barScore = 0;
			barCount = 0;
		}
		barScore += onsetList[index].matchIndicator;
		barCount++;
		
	}
	
	for (int i = 0; i < scorePerBar.size(); i++)
		printf("Score bar [%i] %f\n", i, scorePerBar[i]);
}