view ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.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
/*
 *  PreciseOnsetDetectorOffline.cpp
 *  ofxPreciseOnsetDetectionOffline
 *
 *  Created by Andrew Robertson on 25/12/2013.
 *  Copyright 2013 QMUL. All rights reserved.
 *
 */


//add a retrigger threshold

#include "PreciseOnsetDetectorOffline.h"

const bool printingOn = false;//true

//can work just by calling initialise, then recursive call to processAudioFrame(float* frame, in numSamples)
//set the dfType - initialises to energy - good for bass and drums, not so good for harmonic instruments

PreciseOnsetDetectorOffline::PreciseOnsetDetectorOffline(){
	frameSize = 1024;
	hopSize = 512;
	preciseLocator.setup(frameSize);
	writeOutput = true;//write output to txt when loading file - stored at sample location as file
	
	dfType = 5;//see onset detection class
	//0 energy
	//5 is csd
	
	ringbuffer = new AudioRingBuffer(frameSize, hopSize);
	sampleCount = 0;
	frameCount = 0;
	
	initialised = false;
	
	//some params for peak processing
	peakProcess.cutoffForRepeatOnsetsFrames = 8;
	peakProcess.detectionTriggerRatio = 0.5f;//was 0.5
	peakProcess.bestSlopeMedian = 3;
	peakProcess.thresholdRelativeToMedian = 1.81;
	
	isBass = false;
}

PreciseOnsetDetectorOffline::~PreciseOnsetDetectorOffline(){
	
	if (initialised)
		delete detectionFunction;
	
	delete ringbuffer;
	
}

void PreciseOnsetDetectorOffline::initialise(){
	clearAll();
	sampleCount = 0;
	frameCount = 0; 
	// initialises with hopsize = 512, framesize = 1024, complex spectral difference DF and hanning window
	detectionFunction = new OnsetDetectionFunction(hopSize, frameSize, dfType, 1);
	
	setDfType(5);//for csd
	initialised = true;	
	
	peakProcess.reset();
}

void PreciseOnsetDetectorOffline::endProcessing(){
	delete detectionFunction;
	detectionFunction = NULL;
	initialised = false;
	
	printf("frames in seconds %f\n", secondsToFrameIndex(samples/44100.));
	printf("frames %i\n", (int) dfValues.size());

}

#pragma mark AudioProcessing
void PreciseOnsetDetectorOffline::processAudioFrame(float* frame, int n){
	//call initialise before starting here
	assert(initialised);
	
	sampleCount += n;
	
	if (ringbuffer->addToBuffer(frame, n)){
		//for (int i = 0; i < ringbuffer->buffersize; i++)
		//	printf("ring[%i] %.6f\n", i, ringbuffer->audiobuffer[i]);
	
		
		//needed to make sure df fn is same framesize as our ringbuffer
		double dfval = detectionFunction->getDFsample(ringbuffer->audiobuffer);	// compute detection function sample
		
		dfValues.push_back(dfval);
		
		if (printingOn)
			printf("det val %i: %f\n", frameCount, dfval);
		
		if (peakProcess.peakProcessing(dfval)){
			
			//onsetPositionFrames.push_back(frameCount);
			
			int precisesample = preciseLocator.findExactOnset(ringbuffer->audiobuffer);
			
			//printf("precise sample is %i\n", precisesample);
			//so exact sample is
			//int exactsample = (frameCount-1)*hopSize;//chunks in from beginning of frame
			//as we have just added hopsize samples in, the beginning of the frame is -1 from counter
			//exactsample += precisesample;
			
			//above was old way, but not as neat
			
			//number of samples in, but to beginning of frame, then precisesample in from there
			int alternativePreciseSample = sampleCount - frameSize + precisesample;

			//printf("PreciseSample %i, %i == %i\n", precisesample, exactsample, alternativePrecise);
			
			OnsetInfo newOnsetInfo;
			newOnsetInfo.onsetLocation = alternativePreciseSample/44100.;
			newOnsetInfo.positionSamples = alternativePreciseSample;
			newOnsetInfo.positionFrames = frameCount;
			newOnsetInfo.pitch = 0;
			newOnsetInfo.matchIndicator = 0;
			newOnsetInfo.onBeat = false;
			newOnsetInfo.expressiveTiming = 0;
//			onsetLocations.push_back(alternativePreciseSample/44100.);
//			onsetPositionSamples.push_back(alternativePreciseSample);
			onsetList.push_back(newOnsetInfo);
			
			
			if (printingOn)
				printf("BANG\n");
		}
		
		frameCount++;
		
	}

}


void PreciseOnsetDetectorOffline::setDfType(int t){
	if (t >= 0 && t <= 9)
		dfType = t;
	
	/*
	 switch (df_type){
	 case 0:
	 df_sample = energy_envelope();	// calculate energy envelope detection function sample
	 break;	
	 case 1:
	 df_sample = energy_difference();	// calculate half-wave rectified energy difference detection function sample
	 break;
	 case 2:
	 df_sample = spectral_difference();	// calculate spectral difference detection function sample
	 break;
	 case 3:
	 df_sample = spectral_difference_hwr(); // calculate spectral difference detection function sample (half wave rectified)
	 break;
	 case 4:
	 df_sample = phase_deviation();		// calculate phase deviation detection function sample (half wave rectified)
	 break;
	 case 5:
	 df_sample = complex_spectral_difference(); // calcualte complex spectral difference detection function sample
	 break;
	 case 6:
	 df_sample = complex_spectral_difference_hwr();  // calcualte complex spectral difference detection function sample (half-wave rectified)
	 break;
	 case 7:
	 df_sample = high_frequency_content(); // calculate high frequency content detection function sample
	 break;
	 case 8:
	 df_sample = high_frequency_spectral_difference(); // calculate high frequency spectral difference detection function sample
	 break;
	 case 9:
	 df_sample = high_frequency_spectral_difference_hwr(); // calculate high frequency spectral difference detection function (half-wave rectified)
	 break;
	 */
}

int PreciseOnsetDetectorOffline::load(std::string filename){
	
	//name for output file is same dir and filename as aif/wav but with _preciseOnsets.txt added
	//eg 'song.wav' is 'song.wav_preciseOnsets.txt'
	
	//printf("PODO: load '%s'\n", filename.c_str());
	return processAudioFileForBeatTimes(filename);
}


void PreciseOnsetDetectorOffline::clearAll(){
	
	dfValues.clear();
	onsetList.clear();
//	onsetLocations.clear();
//	onsetPositionFrames.clear();
//	onsetPositionSamples.clear();
}


int PreciseOnsetDetectorOffline::processAudioFileForBeatTimes(std::string audiofile){
	//originally from BeatAnnotationViewer project
	printf("PODO: ProcessFunction %s\n", audiofile.c_str());
	
	initialise();// - needed but elsewhere
	

	
	// 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);
		
		//add to our buffer for pocessing
		for (int i = 0; i< frameSize-hopSize;i++)
		{
			buffer[i] = buffer[i+hopSize];
			buffer[i+hopSize] = frame[i];
		}
		/*
		for (int i = 0; i < frameSize; i++)
			printf("buffer[%i] %.6f\n", i, buffer[i]);
		
		dfval = detectionFunction->getDFsample(buffer);	// compute detection function sample
		
		dfValues.push_back(dfval);
		
	//	if (printingOn)
	//		printf("det val %i: %f\n", counter, dfval);
		
		if (peakProcess.peakProcessing(dfval)){
			
			onsetPositionFrames.push_back(counter);
			int precisesample = preciseLocator.findExactOnset(&buffer[0]);
			printf("precise sample is %i\n", precisesample);
			//so exact sample is
			int exactsample = (counter-1)*hopSize;//chunks in from beginning of frame
			//as we have just added hopsize samples in, the beginning of the frame is -1 from counter
			exactsample += precisesample;
			//printf("PreciseSample %i, %i\n", precisesample, exactsample);
				
			
			onsetLocations.push_back(exactsample/44100.);
			onsetPositionSamples.push_back(exactsample);

			
			
			if (printingOn)
				printf("BANG\n");
		}
		
		counter++;
		*/ 
		//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;
	
}

double PreciseOnsetDetectorOffline::frameIndexToSeconds(const int& frameIndex){
	return ((double)(frameIndex*hopSize) /44100.);//- (detectionFunction.framesize/2)?;
}

double PreciseOnsetDetectorOffline::secondsToFrameIndex(const double& seconds){
	return (seconds*44100./(double)hopSize );//- (detectionFunction.framesize/2)?;
}


void PreciseOnsetDetectorOffline::exportOnsetTimes(){
	exportOnsetTimes(0.0, samples/44100.0);//i.e. the whole file
}


void PreciseOnsetDetectorOffline::exportOnsetTimes(double startTime, double endTime){
	//for writing output
	BeatWriter writer;
	std::string outputFilename = loadedFilename+"_preciseOnsets.txt";
	
	if (writeOutput)
		writer.openFile(outputFilename);
	
	
	int index = 0;
	while (index < onsetList.size() && onsetList[index].onsetLocation < startTime)
		index++;
	
	while (index < onsetList.size() && onsetList[index].onsetLocation <= endTime){
		writer.writeBeatTime(onsetList[index].onsetLocation);
		index++;
	}
	
	if (writeOutput)	
		writer.closeFile();
}

void PreciseOnsetDetectorOffline::update(){

}


void PreciseOnsetDetectorOffline::draw(){
	//do vizualisation in a specialised class
}

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

double PreciseOnsetDetectorOffline::closestOnset(double& targetVal){
	int bestIndex = 0;
	double bestDiff = 99999;
	double bestVal = -1;
	
	for (int testIndex = 0; testIndex < (int)onsetList.size(); testIndex++){
		double testDiff = (onsetList[testIndex].onsetLocation - targetVal);
		if (fabs(testDiff) < bestDiff){
			bestDiff = fabs(testDiff);
			bestVal = onsetList[testIndex].onsetLocation;
			bestIndex = testIndex;
		}
	}
	return bestVal;
}

void PreciseOnsetDetectorOffline::loadOnsetLocations(DoubleVector& beats){
	//replaces onset locations with new vector
	onsetList.clear();
	
	//onsetLocations.clear();
	for (int i = 0; i < beats.size(); i++){
		//onsetLocations.push_back(beats[i]);
		OnsetInfo newInfo;
		newInfo.onsetLocation = beats[i];
		onsetList.push_back(newInfo);
	}
}

double PreciseOnsetDetectorOffline::onsetAtIndex(int index){
	if (index < onsetList.size())
		return onsetList[index].onsetLocation;
	else 
		return 0;
	/*
	if (index < onsetLocations.size())
		return onsetLocations[index];
	else 
		return 0;
	 */
}

void PreciseOnsetDetectorOffline::cropStartTo(double startTime){
	int index = 0;
	while (index < onsetList.size() && onsetList[index].onsetLocation < startTime){
		onsetList.erase(onsetList.begin());
//		onsetLocations.erase(onsetLocations.begin());
//		onsetPositionSamples.erase(onsetPositionSamples.begin());
//		onsetPositionFrames.erase(onsetPositionFrames.begin());
	}
/*	
	int index = 0;
	while (index < onsetLocations.size() && onsetLocations[index] < startTime){
		onsetLocations.erase(onsetLocations.begin());
		onsetPositionSamples.erase(onsetPositionSamples.begin());
		onsetPositionFrames.erase(onsetPositionFrames.begin());
	}
 */
}

void PreciseOnsetDetectorOffline::setMinimumThreshold(float fVal){
	peakProcess.minimumThreshold = fVal;
}



double PreciseOnsetDetectorOffline::beatAtIndex(std::vector<double> beatTimes, int beatIndex){
	if (beatIndex >= 0 && beatIndex < beatTimes.size()){
		return beatTimes[beatIndex];
	} else {
		printf("OUT OF RANGE\n");
		return 0;
	}
}

	
	
#pragma mark Quantisation
void PreciseOnsetDetectorOffline::categoriseOnsets(std::vector<double> beatTimes){
	double onsetTime;
	double cutoff = 0.06;//seconds - is it within this range of given beat times
	
	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\n", beatAtIndex(beatTimes, beatIndex),
			   beatAtIndex(beatTimes, beatIndex+1), onsetTime);
		
		double beatTime =  beatAtIndex(beatTimes, beatIndex);
		double nextBeatTime =  beatAtIndex(beatTimes, beatIndex+1);
		
		//new cutoff part
		onsetList[i].onBeat = false;

		
		
		if (fabs(onsetTime-beatTime) < cutoff || fabs(onsetTime-nextBeatTime) < cutoff){
			onsetList[i].onBeat = true;
		}
		//end cutoff part
		
		double diff = onsetTime - beatTime;
		
		double period;
		if (nextBeatTime){
			period = nextBeatTime - beatTime;
		} else {
			period = beatAtIndex(beatTimes, beatIndex) - beatAtIndex(beatTimes, beatIndex-1);
		}
		
		while (onsetTime < beatTime){
			//bug if onset is before th first beat
			beatTime -= period;
			nextBeatTime -= period;
			beatIndex--;
			printf("FIXING: beat index %i time %f onsest %f\n", beatIndex, beatTime, onsetTime);
		}
		
		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("Position %i,%i\n", onsetList[i].beatPosition, beattype);
		}
		
	}

}


void PreciseOnsetDetectorOffline::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;
	}
	
}




int PreciseOnsetDetectorOffline::onsetAtBeat(int testPosition){
	//find onset bear at particular beat
	int index = 0;
	int foundIndex = 0;
	bool found = false;
	double tmpDiff = 10000;
	while (index < onsetList.size() && onsetList[index].beatPosition <= testPosition){
		
		if (onsetList[index].beatPosition == testPosition && onsetList[index].onsetType == 0){
			found = true;
			foundIndex = index;
			//tmpDiff = fabs(onsetList[index].onsetLocation - 
			//break; - could break here - would find the first onset
			//commented out finds the last one - really we'd just want the closest one to beat time
		}
		index++;
	}
	if (found)
		return foundIndex;
	else
		return -1;
}