Mercurial > hg > precise-onset-detection
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; }