# HG changeset patch # User Venetian # Date 1408030072 -3600 # Node ID b1c13e8bec26eaf2194b1678e6245262aeb1d827 # Parent eb29c6b6dff87acd3e65b0e5a1c693dd2283543c adding new files diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/DetectionFunctions.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/DetectionFunctions.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,11 @@ +/* + * DetectionFunctions.cpp + * ofxPreciseOnsetDetectionOffline + * + * Created by Andrew on 04/03/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + +#include "DetectionFunctions.h" + diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/DetectionFunctions.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/DetectionFunctions.h Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,21 @@ +/* + * DetectionFunctions.h + * ofxPreciseOnsetDetectionOffline + * + * Created by Andrew Robertson on 04/03/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + + +#ifndef DETECTION_FUNCTIONS_H +#define DETECTION_FUNCTIONS_H + +class DetectionFunctions{ +public: + DetectionFunctions(); + ~DetectionFunctions(); + + +}; +#endif \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PointerOnsetVisualiser.cpp --- a/ofxPreciseOnsetDetectorOffline/PointerOnsetVisualiser.cpp Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PointerOnsetVisualiser.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -27,7 +27,7 @@ void PointerOnsetVisualiser::newFile(){ resetWindow(); // windowPress = 0; - + printf("loading sound %s\n", pod->loadedFilename.c_str()); soundPlay.loadSound(pod->loadedFilename, false); paused = true; soundPlay.play(); @@ -76,7 +76,8 @@ ofDrawBitmapString(ofToString(round(pod->secondsToFrameIndex((*windowEnd))), 1), window.x+window.width, window.y-10); ofSetColor(ofColor::blue); - plotter.drawBeatStripes(pod->onsetLocations, window, (*windowStart), (*windowEnd)); + plotter.drawBeatStripes(pod->onsetList, window, (*windowStart), (*windowEnd)); + //replaced above line with a plotter that can do our onset list //play position ofSetColor(ofColor::red); @@ -101,7 +102,7 @@ // while ((*windowEnd) < pod->samples/44100.) // (*windowEnd) *= 2; - (*windowEnd) = pod->samples/44100.; +// (*windowEnd) = pod->samples/44100.; printf("reset: start %.1f end %.1f\n", (*windowStart), (*windowEnd)); } diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PointerOnsetVisualiser.h --- a/ofxPreciseOnsetDetectorOffline/PointerOnsetVisualiser.h Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PointerOnsetVisualiser.h Thu Aug 14 16:27:52 2014 +0100 @@ -15,7 +15,7 @@ #include "PreciseOnsetDetectorOffline.h" #include "ofxWindowRegion.h" -#include "ofxPlotFunction.h" +#include "ofxPlotOnsetFunction.h" class PointerOnsetVisualiser{ public: @@ -54,7 +54,7 @@ // void drawOnsets(DoubleVector& onsetTimesSeconds, ofxWindowregion& window, double startTime, double endTime); ofxWindowRegion window; - ofxPlotFunction plotter; + ofxPlotOnsetFunction plotter; //this is the only change with pointer_onset_vis - it follows a NON-POINTER PreciseOnsetVisualiser double *windowStart; diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetector.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetector.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,123 @@ +/* + * PreciseBassOnsetDetector.cpp + * BasslinePrediction + * + * Created by Andrew N Robertson on 13/04/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + +#include "PreciseBassOnsetDetector.h" + + +PreciseBassOnsetDetector::PreciseBassOnsetDetector(){ + pov.window.setToRelativeSize(0.1, 0.1, 0.8, 0.3); + pov.pod = &pod;//set up pointer if we want to visualise the onsets in a window + + // std::string fileName; + // fileName = "/Users/andrewrobertson/Music/audiowavs/Islamey/BachBWV846-2.wav"; + // loadNewFile(fileName); +} + +PreciseBassOnsetDetector::~PreciseBassOnsetDetector(){ + +} + +void PreciseBassOnsetDetector::loadNewFile(std::string filename){ + + pod.initialise(); + pod.load(filename); + pod.printOnsetLocations(); + pov.newFile();//resets info in visualiser - could use pointer?? +} + +void PreciseBassOnsetDetector::update(){ + pod.update(); + pov.update(); +} + +void PreciseBassOnsetDetector::draw(){ + pov.draw(); +} + +bool PreciseBassOnsetDetector::getFilenameFromDialogBox(std::string* fileNameToSave){ + //this uses a pointer structure within the loader and returns true if the dialogue box was used successfully + // first, create a string that will hold the URL + string URL; + + ofFileDialogResult fileResult = ofSystemLoadDialog("Choose audio file to load"); + + if(fileResult.bSuccess){ + // now you can use the URL + *fileNameToSave = fileResult.filePath; + //printf("\n filename is %s \n", soundFileName.c_str()); + return true; + } + else { + // soundFileName = "OPEN canceled. "; + printf("\n open file cancelled \n"); + return false; + } +} + +void PreciseBassOnsetDetector::keyPressed(int key){ + std::string loadName; + switch (key) { + case 'r': + pov.resetWindow(); + break; + case 's': + pov.cropStart(); + pod.cropStartTo(pov.windowStart); + break; + case 'e': + pov.cropEnd(); + break; + case 'w': + printf("Exporting between %f and %f\n", pov.windowStart, pov.windowEnd); + pod.exportOnsetTimes(pov.windowStart, pov.windowEnd); + break; + case 'x': + printf("Exporting between %f and %f\n", 0., pov.lengthSeconds()); + pod.exportOnsetTimes(0, pov.lengthSeconds()); + break; + + case 'o': + if (getFilenameFromDialogBox(&loadName)){ + printf("loading %s\n", (loadName).c_str()); + loadNewFile(loadName); + }; + + //delete testName; + break; + case ' ': + pov.togglePlay(); + break; + case OF_KEY_RETURN: + pov.stop(); + + break; + + case OF_KEY_UP: case 'u': + pov.zoomIn(); + break; + case OF_KEY_DOWN: + pov.zoomOut(); + break; + case OF_KEY_RIGHT: + pov.scrollRight(); + break; + case OF_KEY_LEFT: + pov.scrollLeft(); + break; + + default: + break; + } +} + +void PreciseBassOnsetDetector::mousePressed(int x, int y, int button){ + + pov.mousePressed(x, y); + +} \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetector.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetector.h Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,45 @@ +/* + * PreciseBassOnsetDetector.h + * BasslinePrediction + * + * Created by Andrew N Robertson on 13/04/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + +#ifndef PRECISE_BASS_ONSET_DETECTOR +#define PRECISE_BASS_ONSET_DETECTOR + +//#include "PreciseOnsetDetector.h" +//#include "PreciseBassOnsetDetectorOffline.h" + +//#include "PreciseOnsetDetectorOffline.h" +#include "PreciseOnsetVisualiser.h" + +#include "ofxWindowRegion.h" +#include "ofxPlotFunction.h" + +#include "PreciseBassOnsetDetectorOffline.h" + + +class PreciseBassOnsetDetector{ +public: + + PreciseBassOnsetDetector(); + ~PreciseBassOnsetDetector(); + + void loadNewFile(std::string filename); + + void update(); + void draw(); + + void keyPressed(int key); + void mousePressed(int x, int y, int button); + + bool getFilenameFromDialogBox(std::string* fileNameToSave); + + PreciseBassOnsetDetectorOffline pod; + PreciseOnsetVisualiser pov; +}; + +#endif \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetectorOffline.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetectorOffline.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,692 @@ +/* + * 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 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 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 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 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]); +} + + diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetectorOffline.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetectorOffline.h Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,53 @@ +/* + * PreciseBassOnsetDetectorOffline.h + * BasslinePrediction + * + * Created by Andrew N Robertson on 11/04/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + +#ifndef PRECISE_BASS_DETECTOR_OFFLINE +#define PRECISE_BASS_DETECTOR_OFFLINE + +#include "PreciseOnsetDetectorOffline.h" +#include "YuleBrinner.h" + +//here qwe extend the usual precise onset detector to run different process functin that will log +//the pitches of bass onsets using pyin + +// +class PreciseBassOnsetDetectorOffline : public PreciseOnsetDetectorOffline{ +public: + //this version we will also try and detect pitch + int processAudioFileForBeatTimes(std::string audiofile); + + void processYinAudio(float* frame, int blocksize); + + + void processBassPitches(); + + void setMidiNoteString(int index, float midiPitch); + + void printOnsetLocations(); + void printPitchInfo(); + + //void categoriseOnsets(std::vector beatTimes); + //void doCorrection(int& beattype, int& beatPosition); + + void predictionProcess(); + int getOptimalLag(); + + double quantisedCorrelation(int lag); + int getOnsetAtBeat(int tmpIndex, int beatPosition, int beatType, int& midiPitch); + void checkPrediction(int lag); + void checkPredictionFirstOrderMarkovCorrect(int lag); + + void checkBars(); + + + + YuleBrinner yule; + +}; +#endif \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseOnsetDetector.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseOnsetDetector.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,125 @@ +/* + * PreciseOnsetDetector.cpp + * ofxPreciseOnsetDetectionOffline + * + * Created by Andrew N Robertson on 11/04/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + +#include "PreciseOnsetDetector.h" + + + +PreciseOnsetDetector::PreciseOnsetDetector(){ + pov.window.setToRelativeSize(0.1, 0.1, 0.8, 0.3); + pov.pod = &pod;//set up pointer if we want to visualise the onsets in a window + +// std::string fileName; +// fileName = "/Users/andrewrobertson/Music/audiowavs/Islamey/BachBWV846-2.wav"; +// loadNewFile(fileName); +} + +PreciseOnsetDetector::~PreciseOnsetDetector(){ + +} + +void PreciseOnsetDetector::loadNewFile(std::string filename){ + + pod.initialise(); + pod.load(filename); + pod.printOnsetLocations(); + + pov.newFile();//resets info in visualiser - could use pointer?? +} + +void PreciseOnsetDetector::update(){ + pod.update(); + pov.update(); +} + +void PreciseOnsetDetector::draw(){ + pov.draw(); +} + +bool PreciseOnsetDetector::getFilenameFromDialogBox(std::string* fileNameToSave){ + //this uses a pointer structure within the loader and returns true if the dialogue box was used successfully + // first, create a string that will hold the URL + string URL; + + ofFileDialogResult fileResult = ofSystemLoadDialog("Choose audio file to load"); + + if(fileResult.bSuccess){ + // now you can use the URL + *fileNameToSave = fileResult.filePath; + //printf("\n filename is %s \n", soundFileName.c_str()); + return true; + } + else { + // soundFileName = "OPEN canceled. "; + printf("\n open file cancelled \n"); + return false; + } +} + +void PreciseOnsetDetector::keyPressed(int key){ + std::string loadName; + switch (key) { + case 'r': + pov.resetWindow(); + break; + case 's': + pov.cropStart(); + pod.cropStartTo(pov.windowStart); + break; + case 'e': + pov.cropEnd(); + break; + case 'w': + printf("Exporting between %f and %f\n", pov.windowStart, pov.windowEnd); + pod.exportOnsetTimes(pov.windowStart, pov.windowEnd); + break; + case 'x': + printf("Exporting between %f and %f\n", 0., pov.lengthSeconds()); + pod.exportOnsetTimes(0, pov.lengthSeconds()); + break; + + case 'o': + if (getFilenameFromDialogBox(&loadName)){ + printf("loading %s\n", (loadName).c_str()); + loadNewFile(loadName); + }; + + //delete testName; + break; + case ' ': + pov.togglePlay(); + break; + case OF_KEY_RETURN: + pov.stop(); + + break; + + case OF_KEY_UP: case 'u': + pov.zoomIn(); + break; + case OF_KEY_DOWN: + pov.zoomOut(); + break; + case OF_KEY_RIGHT: + pov.scrollRight(); + break; + case OF_KEY_LEFT: + pov.scrollLeft(); + break; + + default: + break; + } +} + +void PreciseOnsetDetector::mousePressed(int x, int y, int button){ + + pov.mousePressed(x, y); + +} \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseOnsetDetector.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseOnsetDetector.h Thu Aug 14 16:27:52 2014 +0100 @@ -0,0 +1,44 @@ +/* + * PreciseOnsetDetector.h + * ofxPreciseOnsetDetectionOffline + * + * Created by Andrew N Robertson on 11/04/2014. + * Copyright 2014 QMUL. All rights reserved. + * + */ + +#ifndef PRECISE_ONSET_DETECTOR +#define PRECISE_ONSET_DETECTOR + + +#include "PreciseOnsetDetectorOffline.h" +#include "PreciseOnsetVisualiser.h" + +#include "ofxWindowRegion.h" +#include "ofxPlotFunction.h" + +#include "PreciseOnsetDetectorOffline.h" + +//this holds both the detection process and the visualiser for the result +//easy interfacing from OF + +class PreciseOnsetDetector{ +public: + PreciseOnsetDetector(); + ~PreciseOnsetDetector(); + + void loadNewFile(std::string filename); + + void update(); + void draw(); + + void keyPressed(int key); + void mousePressed(int x, int y, int button); + + bool getFilenameFromDialogBox(std::string* fileNameToSave); + + //vars + PreciseOnsetDetectorOffline pod; + PreciseOnsetVisualiser pov; +}; +#endif \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.cpp --- a/ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.cpp Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -12,36 +12,197 @@ #include "PreciseOnsetDetectorOffline.h" -const bool printingOn = false; +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(){ - //delete infile; + + 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' - return processAudioForBeatTimes(filename); + //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::processAudioForBeatTimes(std::string audiofile){ + +int PreciseOnsetDetectorOffline::processAudioFileForBeatTimes(std::string audiofile){ //originally from BeatAnnotationViewer project + printf("PODO: ProcessFunction %s\n", audiofile.c_str()); + + initialise();// - needed but elsewhere + - dfValues.clear(); - onsetLocations.clear(); - onsetPositionFrames.clear(); // static double frame[FRAMESIZE]; // to hold a single frame double buffer[frameSize]; @@ -54,8 +215,7 @@ //detfun = new df(1,(FRAMESIZE*2),0); - // initialises with hopsize = 512, framesize = 1024, complex spectral difference DF and hanning window - detectionFunction = new OnsetDetectionFunction(); + SNDFILE *infile, *outfile ; // define input and output sound files @@ -92,7 +252,7 @@ int k, m; readcount = 1; - int counter = 0; + //DoubleVector d; while ((readcount = sf_readf_float (infile, buf, blocksize)) > 0){ @@ -108,41 +268,48 @@ 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] %.5f\n", i, buffer[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 (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 + 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) ; @@ -152,10 +319,7 @@ // Close input file sf_close (infile); - delete detectionFunction; - detectionFunction = NULL; - - printf("Number of samples%i\n", samples); + endProcessing(); return 0; @@ -185,11 +349,11 @@ int index = 0; - while (index < onsetLocations.size() && onsetLocations[index] < startTime) + while (index < onsetList.size() && onsetList[index].onsetLocation < startTime) index++; - while (index < onsetLocations.size() && onsetLocations[index] <= endTime){ - writer.writeBeatTime(onsetLocations[index]); + while (index < onsetList.size() && onsetList[index].onsetLocation <= endTime){ + writer.writeBeatTime(onsetList[index].onsetLocation); index++; } @@ -207,8 +371,8 @@ } void PreciseOnsetDetectorOffline::printOnsetLocations(){ - for (int i = 0; i < (int)onsetLocations.size(); i++) - printf("Onset[%i]: %.3f\n", i, onsetLocations[i]); + 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){ @@ -216,11 +380,11 @@ double bestDiff = 99999; double bestVal = -1; - for (int testIndex = 0; testIndex < (int)onsetLocations.size(); testIndex++){ - double testDiff = (onsetLocations[testIndex] - targetVal); + for (int testIndex = 0; testIndex < (int)onsetList.size(); testIndex++){ + double testDiff = (onsetList[testIndex].onsetLocation - targetVal); if (fabs(testDiff) < bestDiff){ bestDiff = fabs(testDiff); - bestVal = onsetLocations[testIndex]; + bestVal = onsetList[testIndex].onsetLocation; bestIndex = testIndex; } } @@ -229,8 +393,202 @@ void PreciseOnsetDetectorOffline::loadOnsetLocations(DoubleVector& beats){ //replaces onset locations with new vector - onsetLocations.clear(); + onsetList.clear(); + + //onsetLocations.clear(); for (int i = 0; i < beats.size(); i++){ - onsetLocations.push_back(beats[i]); + //onsetLocations.push_back(beats[i]); + OnsetInfo newInfo; + newInfo.onsetLocation = beats[i]; + onsetList.push_back(newInfo); } -} \ No newline at end of file +} + +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 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 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; +} + diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.h --- a/ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.h Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseOnsetDetectorOffline.h Thu Aug 14 16:27:52 2014 +0100 @@ -8,8 +8,8 @@ */ -#ifndef PRECISE_ONSET_DETECTOR -#define PRECISE_ONSET_DETECTOR +#ifndef PRECISE_ONSET_DETECTOR_OFFLINE +#define PRECISE_ONSET_DETECTOR_OFFLINE #include "OnsetDetectionFunction.h" #include "vector.h" @@ -19,8 +19,37 @@ #include "PeakProcessor.h" #include "PreciseOnsetLocator.h" +#include "AudioRingBuffer.h" + #include "BeatWriter.h" +struct OnsetInfo{ + //double dfValue; + double onsetLocation;//of exact time in seconds + int positionFrames; + int positionSamples;//exact time in samples + //this for bass onsets + float pitch;//freq in Hz + float midiPitch;//in Midi as float + int roundedPitch;//rounded to nearest MIDI + int midiPrediction;//prediction according to naive ICMC 2014 method at optimal lag (determined here) + int markovMidiPrediction;//prediction according to first markov ICMC 2014 method at optimal lag (determined here) + + + int barPosition;//ignore + int beatPosition;//in beats + int onsetType;//0-11 where 0 is on beat, 6 is halfbeat etc + + float matchIndicator;//? + + std::string midiName;//eg C#3 + + bool onBeat;//is it near a beat - need to call testOnsetCloseToBeat and give a list of beat times in seconds + + float expressiveTiming; +}; + + class PreciseOnsetDetectorOffline{ public: PreciseOnsetDetectorOffline(); @@ -31,7 +60,16 @@ void update(); void draw(); - int processAudioForBeatTimes(std::string audiofile); + void initialise(); + void processAudioFrame(float* frame, int n); + + void setDfType(int t); + + void clearAll(); + virtual int processAudioFileForBeatTimes(std::string audiofile); + + void endProcessing(); + void exportOnsetTimes(); void exportOnsetTimes(double startTime, double endTime); @@ -42,8 +80,21 @@ double secondsToFrameIndex(const double& seconds); double closestOnset(double& targetVal); - void printOnsetLocations(); + virtual void printOnsetLocations(); + double onsetAtIndex(int index); + void cropStartTo(double startTime); + + void setMinimumThreshold(float fVal); + + double beatAtIndex(std::vector beatTimes, int beatIndex); + + void categoriseOnsets(std::vector beatTimes); + void doCorrection(int& beattype, int& beatPosition); + +// void testOnsetsCloseToBeats(std::vector beatTimes); + int onsetAtBeat(int testPosition); + //vars int frameSize; int hopSize; @@ -60,9 +111,24 @@ PreciseOnsetLocator preciseLocator; std::vector dfValues; + /* std::vector onsetLocations;//of exact time in seconds std::vector onsetPositionFrames; + std::vector onsetPositionSamples; + */ + std::vector onsetList; bool writeOutput;//write output to txt + + int dfType; + + AudioRingBuffer* ringbuffer; + + int sampleCount; + int frameCount; + bool isBass; +private: + bool initialised; + }; #endif \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseOnsetVisualiser.cpp --- a/ofxPreciseOnsetDetectorOffline/PreciseOnsetVisualiser.cpp Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseOnsetVisualiser.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -22,13 +22,14 @@ void PreciseOnsetVisualiser::newFile(){ resetWindow(); + + //problem example + //soundPlay.loadSound("/Users/andrew/Music/Logic/GreenOnionsEvaluationThree/GreenOnionsEvaluationMetronome3/Audio Files/SM57_Metronome#13.aif");//.c_str()); + windowPress = 0; - + printf("Precise onset, loading...'%s'\n", pod->loadedFilename.c_str()); soundPlay.loadSound(pod->loadedFilename, false); - stop(); -// paused = true; -// soundPlay.play(); -// soundPlay.setPaused(true); + stop();//sets it not playing at beginning } double PreciseOnsetVisualiser::windowWidth(){ @@ -48,6 +49,7 @@ void PreciseOnsetVisualiser::draw(){ + ofSetLineWidth(1); //if plotting use this, else comment out ofSetColor(ofColor::white); @@ -55,6 +57,8 @@ ofSetColor(ofColor::black); window.drawOutline(); + ofDrawBitmapString(title, window.x+60, window.y - 10); + //draw df function /* int startIndex = 0; @@ -68,12 +72,15 @@ ofSetColor(ofColor::tan); // plotter.drawVector(pod->dfValues, startIndex, endIndex, window); plotter.drawVector(pod->dfValues, round(pod->secondsToFrameIndex(windowStart)), round(pod->secondsToFrameIndex(windowEnd)), window); + ofSetColor(ofColor::black); ofDrawBitmapString(ofToString(round(pod->secondsToFrameIndex(windowStart)), 1), window.x, window.y-10); ofDrawBitmapString(ofToString(round(pod->secondsToFrameIndex(windowEnd)), 1), window.x+window.width, window.y-10); ofSetColor(ofColor::blue); - plotter.drawBeatStripes(pod->onsetLocations, window, windowStart, windowEnd); +// plotter.drawBeatStripes(pod->onsetLocations, window, windowStart, windowEnd); + plotter.drawBeatStripes(pod->onsetList, window, windowStart, windowEnd); + drawOnsetInfo(pod->onsetList, window, windowStart, windowEnd); //play position ofSetColor(ofColor::red); @@ -85,6 +92,58 @@ } +void PreciseOnsetVisualiser::drawOnsetInfo(std::vector onsetList, ofxWindowRegion& window, double startTime, double endTime){ + ofSetColor(ofColor::darkBlue); + if (endTime > startTime){ + int index = 0; + while (index < onsetList.size() && onsetList[index].onsetLocation < startTime) { + index++; + } + + int pos = max(0, window.x - 45); + ofDrawBitmapString("onsetLocation", pos, window.y+window.height+10); + ofDrawBitmapString("beatPosition", pos, window.y+window.height+30); + ofDrawBitmapString("onsetType", pos, window.y+window.height+50); + if (pod->isBass){ + ofDrawBitmapString("pitch", pos, window.y+window.height+70); + ofDrawBitmapString("midiPitch", pos, window.y+window.height+90); + ofDrawBitmapString("midiPrediction", pos, window.y+window.height+110); + ofDrawBitmapString("midiName", pos, window.y+window.height+130); + ofDrawBitmapString("exp timing", pos, window.y+window.height+150); + } + + while (index < onsetList.size() && onsetList[index].onsetLocation <= endTime){ + pos = window.width*((onsetList[index].onsetLocation - startTime)/(endTime - startTime)); + pos += window.x; + int lineWidth = 20; + ofDrawBitmapString(ofToString(onsetList[index].onsetLocation, 2), pos, window.y+window.height+10); + ofDrawBitmapString(ofToString(onsetList[index].beatPosition, 0), pos, window.y+window.height+30); + ofDrawBitmapString(ofToString(onsetList[index].onsetType, 0), pos, window.y+window.height+50); + if (pod->isBass){ + ofDrawBitmapString(ofToString(onsetList[index].pitch, 1), pos, window.y+window.height+70); + ofDrawBitmapString(ofToString(onsetList[index].midiPitch, 0), pos, window.y+window.height+90); + if (onsetList[index].midiPrediction == onsetList[index].roundedPitch) + ofSetColor(ofColor::darkGreen); + else if (abs(onsetList[index].midiPrediction - onsetList[index].roundedPitch) == 12) + ofSetColor(ofColor::orange); + else + ofSetColor(ofColor::darkRed); + + ofDrawBitmapString(ofToString(onsetList[index].midiPrediction, 0), pos, window.y+window.height+110); + ofSetColor(ofColor::darkBlue); + ofDrawBitmapString(onsetList[index].midiName, pos, window.y+window.height+130); + if (onsetList[index].expressiveTiming) + ofDrawBitmapString(ofToString(1000.0*onsetList[index].expressiveTiming, 1), pos, window.y+window.height+150); + } + +// ofLine(pos, window.y, pos, window.y+window.height); + index++; + } + } +} + + + double PreciseOnsetVisualiser::positionSeconds(){ return soundPlay.getPosition()*pod->samples/44100.; } @@ -98,7 +157,7 @@ // while (windowEnd < pod->samples/44100.) // windowEnd *= 2; - windowEnd = pod->samples/44100.; +// windowEnd = pod->samples/44100.; printf("reset: start %.1f end %.1f\n", windowStart, windowEnd); } @@ -145,12 +204,12 @@ if (!paused ){ soundPlay.setPaused(true); paused = true; - printf("was playing\n"); + //printf("was playing\n"); } else { soundPlay.setPaused(false);// paused = false; - printf("was not playing\n"); + //printf("was not playing\n"); } } @@ -254,3 +313,41 @@ double PreciseOnsetVisualiser::lengthSeconds(){ return pod->samples/44100.; } + + +void PreciseOnsetVisualiser::keyPressed(int key){ + switch (key) { + case 'r': + resetWindow(); + break; + case 's': + cropStart(); + break; + case 'e': + cropEnd(); + break; + case ' ': + togglePlay(); + break; + case OF_KEY_RETURN: + stop(); + + break; + + case OF_KEY_UP: case 'u': + zoomIn(); + break; + case OF_KEY_DOWN: + zoomOut(); + break; + case OF_KEY_RIGHT: + scrollRight(); + break; + case OF_KEY_LEFT: + scrollLeft(); + break; + + default: + break; + } +} diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/PreciseOnsetVisualiser.h --- a/ofxPreciseOnsetDetectorOffline/PreciseOnsetVisualiser.h Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/PreciseOnsetVisualiser.h Thu Aug 14 16:27:52 2014 +0100 @@ -13,7 +13,7 @@ #include "PreciseOnsetDetectorOffline.h" #include "ofxWindowRegion.h" -#include "ofxPlotFunction.h" +#include "ofxPlotOnsetFunction.h" class PreciseOnsetVisualiser{ public: @@ -23,6 +23,9 @@ void newFile(); void update(); void draw(); + + void drawOnsetInfo(std::vector onsetList, ofxWindowRegion& window, double startTime, double endTime); + double positionSeconds(); double windowWidth(); @@ -49,6 +52,8 @@ double lengthSeconds(); + void keyPressed(int key); + //vars PreciseOnsetDetectorOffline* pod; ofSoundPlayer soundPlay; @@ -56,10 +61,12 @@ // void drawOnsets(DoubleVector& onsetTimesSeconds, ofxWindowregion& window, double startTime, double endTime); ofxWindowRegion window; - ofxPlotFunction plotter; + ofxPlotOnsetFunction plotter; double windowStart, windowEnd, windowPress; double currentPlayPosition; + + std::string title; }; #endif \ No newline at end of file diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/testApp.cpp --- a/ofxPreciseOnsetDetectorOffline/testApp.cpp Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/testApp.cpp Thu Aug 14 16:27:52 2014 +0100 @@ -2,95 +2,34 @@ //possible to just omit the drawing (uses ofxWindowRegion for the screen and ofxPlotFunction to plot beat times) +//w: export window +//x:export whole file +//o: open file + //-------------------------------------------------------------- void testApp::setup(){ - pov.window.setToRelativeSize(0.1, 0.1, 0.8, 0.3); - pov.pod = &preciseOnsetDetect;//set up pointer if we want to visualise the onsets in a window - -// preciseOnsetDetect.load("/Users/andrew/Documents/work/programming/MadMax/AudioFiles/AdamBetts/AdamBetss_1_Swing_Kick.wav"); -// preciseOnsetDetect.load("/Users/andrew/Music/Logic/GreenOnionsChichester/GreenOnionsChichester/Bouncing/Snare_Sontronics#08edit.aif"); - - std::string fileName = "/Users/andrew/Music/Logic/GreenOnionsEvaluation/GreenOnionsEvaluationPlain/Audio Files/SongClickTrack#09.aif"; -// fileName = "/Users/andrew/Music/Logic/GreenOnionsChichester/GreenOnionsChichester/Audio Files/Ride_SM58#08.aif"; - loadNewFile(fileName); + } -void testApp::loadNewFile(std::string filename){ - preciseOnsetDetect.load(filename); - preciseOnsetDetect.printOnsetLocations(); - - pov.newFile(); -} - //-------------------------------------------------------------- void testApp::update(){ - preciseOnsetDetect.update(); - pov.update(); + + onsetDetector.update(); } //-------------------------------------------------------------- void testApp::draw(){ - pov.draw(); + onsetDetector.draw(); } //-------------------------------------------------------------- void testApp::keyPressed(int key){ - std::string loadName; - switch (key) { - case 'r': - pov.resetWindow(); - break; - case 's': - pov.cropStart(); - break; - case 'e': - pov.cropEnd(); - break; - case 'w': - printf("Exporting between %f and %f\n", pov.windowStart, pov.windowEnd); - preciseOnsetDetect.exportOnsetTimes(pov.windowStart, pov.windowEnd); - break; - case 'x': - printf("Exporting between %f and %f\n", 0., pov.lengthSeconds()); - preciseOnsetDetect.exportOnsetTimes(0, pov.lengthSeconds()); - break; - - case 'o': - if (getFilenameFromDialogBox(&loadName)){ - printf("loading %s\n", (loadName).c_str()); - loadNewFile(loadName); - }; - - //delete testName; - break; - case ' ': - pov.togglePlay(); - break; - case OF_KEY_RETURN: - pov.soundPlay.stop(); - break; - - case OF_KEY_UP: - pov.zoomIn(); - break; - case OF_KEY_DOWN: - pov.zoomOut(); - break; - case OF_KEY_RIGHT: - pov.scrollRight(); - break; - case OF_KEY_LEFT: - pov.scrollLeft(); - break; - - default: - break; - } + onsetDetector.keyPressed(key); } //-------------------------------------------------------------- @@ -110,13 +49,8 @@ //-------------------------------------------------------------- void testApp::mousePressed(int x, int y, int button){ -// if (window.tapped(x, y)){ -// windowPress = windowStart + (windowEnd-windowStart)*(x - window.x)/window.width; -// -// printf("window position is %f\n", windowPress); -// } - pov.mousePressed(x, y); + onsetDetector.mousePressed(x,y,button); } //-------------------------------------------------------------- @@ -141,40 +75,4 @@ -bool testApp::getFilenameFromDialogBox(std::string* fileNameToSave){ - //this uses a pointer structure within the loader and returns true if the dialogue box was used successfully - // first, create a string that will hold the URL - string URL; - - // // openFile(string& URL) returns 1 if a file was picked - // // returns 0 when something went wrong or the user pressed 'cancel' - // int response = ofxFileDialogOSX::openFile(URL); - // if(response){ - // // now you can use the URL - // *fileNameToSave = URL; - // //printf("\n filename is %s \n", soundFileName.c_str()); - // return true; - // } - // else { - // // soundFileName = "OPEN canceled. "; - // printf("\n open file cancelled \n"); - // return false; - // } - - // openFile(string& URL) returns 1 if a file was picked - // returns 0 when something went wrong or the user pressed 'cancel' - ofFileDialogResult fileResult = ofSystemLoadDialog("Choose audio file to load"); - - if(fileResult.bSuccess){ - // now you can use the URL - *fileNameToSave = fileResult.filePath; - //printf("\n filename is %s \n", soundFileName.c_str()); - return true; - } - else { - // soundFileName = "OPEN canceled. "; - printf("\n open file cancelled \n"); - return false; - } -} diff -r eb29c6b6dff8 -r b1c13e8bec26 ofxPreciseOnsetDetectorOffline/testApp.h --- a/ofxPreciseOnsetDetectorOffline/testApp.h Sun Jan 19 23:07:13 2014 +0000 +++ b/ofxPreciseOnsetDetectorOffline/testApp.h Thu Aug 14 16:27:52 2014 +0100 @@ -2,11 +2,11 @@ #include "ofMain.h" -#include "PreciseOnsetDetectorOffline.h" -#include "PreciseOnsetVisualiser.h" +#include "PreciseOnsetDetector.h" -#include "ofxWindowRegion.h" -#include "ofxPlotFunction.h" + +//#include "ofxWindowRegion.h" +//#include "ofxPlotFunction.h" class testApp : public ofBaseApp{ public: @@ -30,7 +30,8 @@ //vars - PreciseOnsetDetectorOffline preciseOnsetDetect; +// PreciseOnsetDetectorOffline preciseOnsetDetect; +// PreciseOnsetVisualiser pov; - PreciseOnsetVisualiser pov; + PreciseOnsetDetector onsetDetector; };