Mercurial > hg > precise-onset-detection
view ofxPreciseOnsetDetectorOffline/PreciseBassOnsetDetectorOffline.cpp @ 7:b1c13e8bec26
adding new files
author | Venetian |
---|---|
date | Thu, 14 Aug 2014 16:27:52 +0100 |
parents | |
children |
line wrap: on
line source
/* * PreciseBassOnsetDetectorOffline.cpp * BasslinePrediction * * Created by Andrew N Robertson on 11/04/2014. * Copyright 2014 QMUL. All rights reserved. * */ #include "PreciseBassOnsetDetectorOffline.h" bool printingOn = false;//true; int PreciseBassOnsetDetectorOffline::processAudioFileForBeatTimes(std::string audiofile){ printf("PBODO: Process Function BASS OFFLINE VERSION\n"); //originally from BeatAnnotationViewer project initialise();// - needed but elsewhere isBass = true; // static double frame[FRAMESIZE]; // to hold a single frame double buffer[frameSize]; double dfval; for (int i = 0;i < frameSize;i++) { buffer[i] = 0; } //detfun = new df(1,(FRAMESIZE*2),0); SNDFILE *infile, *outfile ; // define input and output sound files SF_INFO sfinfo ; // struct to hold info about sound file int readcount ; // counts number of samples read from sound file const char *infilename = audiofile.c_str(); //"/Users/andrew/Music/Station To Station 2/3-03 Panic In Detroit (Live Nassau Coliseum '76).wav";//"ledzep.wav" ; // input file name // const char *outfilename = "output.wav" ; // output file name // Open Input File if (! (infile = sf_open (infilename, SFM_READ, &sfinfo))) { // Open failed printf ("Not able to open input file %s.\n", infilename) ; // Print the error message from libsndfile. puts (sf_strerror (NULL)) ; return 1; } ; printf("opened '%s'\n", audiofile.c_str()); loadedFilename = audiofile; //STEREO OKAY //HERE IS THE CLASSIC LOADING FILE CODE //DEALS WITH MORE THAN MONO int channels = sfinfo.channels; samples = sfinfo.frames; printf("Number of channels %i, samples %i\n", channels, samples); int blocksize = hopSize;//FRAMESIZE; float buf [channels * blocksize] ; float frame[blocksize]; int k, m; readcount = 1; //DoubleVector d; while ((readcount = sf_readf_float (infile, buf, blocksize)) > 0){ for (k = 0 ; k < readcount ; k++){ //d.clear(); frame[k] = 0; for (m = 0 ; m < channels ; m++){ frame[k] += buf[k*channels + 0];//sum the channels together //d.push_back(buf [k * channels + m]); } frame[k] /= channels;//average of the channels } processAudioFrame(frame, blocksize); processYinAudio(frame, blocksize); //have now processed framecount samples processBassPitches(); //add to our buffer for pocessing for (int i = 0; i< frameSize-hopSize;i++) { buffer[i] = buffer[i+hopSize]; buffer[i+hopSize] = frame[i]; } //printf("read %i samples\n", readcount); //was sf_write_double(outfile, frame, readcount) ; }//end readcount //END STEREO OKAY // Close input file sf_close (infile); endProcessing(); return 0; } void PreciseBassOnsetDetectorOffline::processYinAudio(float* frame, int blocksize){ //continually update yin yule.processBuffer(frame, (int)blocksize); } void PreciseBassOnsetDetectorOffline::processBassPitches(){ int tmpIndex = onsetList.size()-1; int hsize = 1024; while (tmpIndex >= 0 && onsetList[tmpIndex].positionSamples+8192+hsize>sampleCount){ // printf("testing onset %i at %i, samplecount now %i diff %i\n", tmpIndex, onsetList[tmpIndex].positionSamples, sampleCount, sampleCount-onsetList[tmpIndex].positionSamples); if (onsetList[tmpIndex].positionSamples+8192 <= sampleCount && !onsetList[tmpIndex].pitch){ //do yin value for this onset onsetList[tmpIndex].pitch = yule.yinFrequency; float midiPitch = onsetList[tmpIndex].pitch/27.5; if (midiPitch > 0){ midiPitch = 12.0*log(midiPitch)/log(2); midiPitch += 24+9; } onsetList[tmpIndex].midiPitch = midiPitch; onsetList[tmpIndex].roundedPitch = round(midiPitch); setMidiNoteString(tmpIndex, midiPitch);//sets string eg C#4 if (printingOn) printf("PBODO: Yin Pitch %i, %f, %f, %s\n", tmpIndex, onsetList[tmpIndex].pitch, midiPitch, onsetList[tmpIndex].midiName.c_str()); } tmpIndex--; } } void PreciseBassOnsetDetectorOffline::printOnsetLocations(){ for (int i = 0; i < (int)onsetList.size(); i++) printf("PBODO: Onset[%i]: %.3f, samples %i pitch %f, %i\n", i, onsetList[i].onsetLocation, onsetList[i].positionSamples, onsetList[i].pitch, onsetList[i].roundedPitch); } void PreciseBassOnsetDetectorOffline::printPitchInfo(){ for (int i = 0; i < (int)onsetList.size(); i++) printf("PBODO: Beat Position %i,%i: pitch %i\n", onsetList[i].beatPosition, onsetList[i].onsetType, onsetList[i].roundedPitch); } void PreciseBassOnsetDetectorOffline::setMidiNoteString(int index, float midiPitch){ std:string midiName = ""; if (midiPitch > 0){ midiPitch = round(midiPitch); int midiLetter = (int)midiPitch%12; int midiOctave = midiPitch - (int)midiPitch%12; switch (midiLetter) { case 0: midiName = "C "; break; case 1: midiName = "C#"; break; case 2: midiName = "D"; break; case 3: midiName = "D#"; break; case 4: midiName = "E"; break; case 5: midiName = "F "; break; case 6: midiName = "F#"; break; case 7: midiName = "G "; break; case 8: midiName = "G#"; break; case 9: midiName = "A "; break; case 10: midiName = "A#"; break; case 11: midiName = "B "; break; default: break; } midiName += ofToString(midiOctave/12); } if (index < onsetList.size()) onsetList[index].midiName = midiName; } /* #pragma mark Quantisation void PreciseBassOnsetDetectorOffline::categoriseOnsets(std::vector<double> beatTimes){ double onsetTime; for (int i = 0; i < onsetList.size(); i++){ onsetTime = onsetList[i].onsetLocation; int beatIndex = 0; while(beatIndex < beatTimes.size() && beatTimes[beatIndex] < onsetTime){ beatIndex++; } while(beatIndex > 0 && beatTimes[beatIndex] > onsetTime){ beatIndex--; } //beatIndex now either side of onset, or onset before first beat printf("beat %.2f, beat+1 %.2f, onset %.2f freq %.2f, ", beatAtIndex(beatTimes, beatIndex), beatAtIndex(beatTimes, beatIndex+1), onsetTime, onsetList[i].pitch); double beatTime = beatAtIndex(beatTimes, beatIndex);//vampBeats.vampBeatAtIndex(beatIndex); double diff = onsetTime - beatTime; double nextBeat = beatAtIndex(beatTimes, beatIndex+1); double period; if (nextBeat){ period = nextBeat - beatAtIndex(beatTimes, beatIndex); } else { period = beatAtIndex(beatTimes, beatIndex) - beatAtIndex(beatTimes, beatIndex-1); } if (period > 0){ while (diff < 0){ diff += period; //i.e. add the beat period to bring it in range } //now can look which point it is nearest double ratio = diff/period; ratio *= 12; int beattype = round(ratio); if (beattype == 12){ beattype = 0; beatIndex++;//added need to test } doCorrection(beattype, beatIndex); // Onset newOnset; // newOnset.time = onsetTime; // newOnset.onsetType = beattype; int position = beatIndex%4; // if (onsetTime > beatTime + (period/2.)) // pos++; // newOnset.position = pos%4; onsetList[i].beatPosition = beatIndex; onsetList[i].onsetType = beattype; printf("Pitch %i, Position %i,%i\n", onsetList[i].roundedPitch, onsetList[i].beatPosition, beattype); } } } */ #pragma mark PredictionProcess void PreciseBassOnsetDetectorOffline::predictionProcess(){ //NOW do do precition int optimalLag = getOptimalLag(); checkPrediction(optimalLag); checkPredictionFirstOrderMarkovCorrect(optimalLag); //checkBars(); - not quite sure what the idea here was /* beatInfo.clear(); int tmpIndex = 0; double nearestOnset = preciseDetector.pod.onsetAtIndex(0);//start with first for (int beatIndex = 0; beatIndex < beatTimes.size(); beatIndex++){ double newBeatTime = beatTimes[beatIndex]; printf("BEAT %i: %f, ", beatIndex, newBeatTime); double diff = pod.onsetAtIndex(tmpIndex) - newBeatTime; while (fabs(pod.onsetAtIndex(tmpIndex+1) - newBeatTime) < fabs(diff)){ tmpIndex++; nearestOnset = pod.onsetAtIndex(tmpIndex); diff = pod.onsetAtIndex(tmpIndex) - newBeatTime; printf("testing %f, ", nearestOnset); } Beat newBeat(false); newBeat.onsetFound = false; if (fabs(nearestOnset - newBeatTime) < thresholdForBeat){ newBeat.onsetFound = true; newBeat.onsetDifference = nearestOnset - newBeatTime; printf("nearest onset %f\n", nearestOnset); } else printf("no onset %f\n", nearestOnset); //within 80 msec we say the onset is attributable to the beat location newBeat.onsetTime = nearestOnset; newBeat.time = newBeatTime; newBeat.errorFound = false; newBeat.period = nan(0); //find error int secondBeat = beatIndex-4; if (beatIndex%4 != 0) secondBeat = beatIndex - (beatIndex%4); int firstBeat = secondBeat - 4; //for the one use previous bar kicks //otherwise recent kicks on the one if (firstBeat >= 0 && secondBeat < beatInfo.size()){ if (beatInfo[firstBeat].onsetFound && beatInfo[secondBeat].onsetFound){ //can project double beatPeriod = beatInfo[secondBeat].onsetTime - beatInfo[firstBeat].onsetTime; beatPeriod /= 4.; printf("%i beat period %f, ", beatIndex, beatPeriod); double error = nearestOnset - (beatInfo[secondBeat].onsetTime + (beatIndex - secondBeat)*beatPeriod); printf("error %.1f, beat period %.1f\n", error*1000., beatPeriod*1000.); newBeat.errorFound = true; newBeat.error = error; newBeat.period = beatPeriod; } }//end if error beatInfo.push_back(newBeat); } */ //dtwProcess(); } int PreciseBassOnsetDetectorOffline::getOptimalLag(){ std::vector<double> correlationScores; int maxCorr = 35; for (int i = 0; i < maxCorr; i++){ correlationScores.push_back(quantisedCorrelation(i)); printf("Lag[%i] %f\n", i, correlationScores[i]); } int optimalLag = 8; BeatWriter newWriter; newWriter.closeFile(); newWriter.openFile("/Users/andrewrobertson/corr_scores.txt"); //make rayligh std::vector<double> wv; float rayparam = 16; for (int n = 0; n < maxCorr;n++){ wv.push_back(((float) n / pow(rayparam,2)) * exp((-1*pow((float)-n,2)) / (2*pow(rayparam,2)))); //wv.push_back(1.0 - fabs(n - 12.0)/64.0); } double maxScore = 0; for (int i = 2; i < maxCorr; i++){ printf("Lag[%i] %f, rayleigh %f, weighted vec %f\n", i, correlationScores[i], wv[i], correlationScores[i]*wv[i]); if (correlationScores[i]*wv[i] > maxScore){ maxScore = correlationScores[i]*wv[i]; optimalLag = i; } newWriter.outputFile << i << "\t" << correlationScores[i] << std::endl; } newWriter.closeFile(); printf("Optimal lag %i\n", optimalLag); return optimalLag; } /* void PreciseBassOnsetDetectorOffline::doCorrection(int& beattype, int& beatPosition){ switch (beattype) { case 1: beattype = 0;//on beat break; case 2: beattype = 3;//16th break; case 5: case 7: beattype = 6;//8th note break; case 10: beattype = 9; break; case 11: beattype = 0;//on the beat beatPosition++; break; default: break; } } */ double PreciseBassOnsetDetectorOffline::quantisedCorrelation(int lag){ double meanCorrelation = 0; int count = 0; bool printResult = false; for (int index = 0; index < onsetList.size(); index++){ int midiPitch = 0; if (printResult) printf("testing %i,%i (%i): ", onsetList[index].beatPosition, onsetList[index].onsetType, onsetList[index].roundedPitch); getOnsetAtBeat(index, onsetList[index].beatPosition-lag, onsetList[index].onsetType, midiPitch); if (midiPitch){ count++; if (midiPitch == onsetList[index].roundedPitch){ meanCorrelation++; } if (printResult){ printf("MIDI pitch found %i at lag %i\n", midiPitch, lag); } else if (printResult){ printf("none\n"); } } } if (count > 0) meanCorrelation /= count; return meanCorrelation; } void PreciseBassOnsetDetectorOffline::checkPrediction(int lag){ //NAIVE METHOD - JUST LOOKING BACK FOR A BASS NOTE IN SAME POSITION AT SAME LAG int matches = 0; int octaveErrors = 0; int mismatches = 0; bool printResult = false;//true; for (int index = 0; index < onsetList.size(); index++){ onsetList[index].midiPrediction = 0; int midiPitch = 0; if (printResult) printf("prediction %i,%i (%i): ", onsetList[index].beatPosition, onsetList[index].onsetType, onsetList[index].roundedPitch); int k = 1; while (onsetList[index].beatPosition - k *lag >= 0){ int tmp = getOnsetAtBeat(index, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, midiPitch); // if (tmp == 0 && printResult) // printf("k %i, pos %i,%i : midi pred %i\n", k, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, tmp); if (midiPitch)//i.e. non zero prediction made looking back k*lag beats break; k++;//keep looking back } if (midiPitch){ onsetList[index].midiPrediction = midiPitch; if (printResult) printf(", pred %i, (lag %i) ", midiPitch, k); if (midiPitch == onsetList[index].roundedPitch){ onsetList[index].matchIndicator = 1; matches++; if (printResult) printf(", MATCH"); } if (midiPitch && onsetList[index].roundedPitch && abs(midiPitch - onsetList[index].roundedPitch) == 12){ octaveErrors++; if (printResult) printf(", OCTAVE %i", onsetList[index].roundedPitch-midiPitch); } else if (midiPitch && onsetList[index].roundedPitch && midiPitch != onsetList[index].roundedPitch){ mismatches++; if (printResult) printf(", NOT"); } } if (printResult) printf("\n"); } double sum = matches+octaveErrors+mismatches; printf("\nResult: naive method\noptimal lag %i, matches %.3f, octaves %.3f, wrong %.3f\n", lag, matches/sum, octaveErrors/sum, mismatches/sum); } int PreciseBassOnsetDetectorOffline::getOnsetAtBeat(int tmpIndex, int beatPosition, int beatType, int& midiPitch){ bool onsetFound = false; while (tmpIndex >= 0 && onsetList[tmpIndex].beatPosition >= beatPosition){ if (onsetList[tmpIndex].beatPosition == beatPosition && onsetList[tmpIndex].onsetType == beatType){ midiPitch = onsetList[tmpIndex].roundedPitch; //printf("beat pos %i,%i: pitch %i\n", beatPosition, beatType, midiPitch); onsetFound = true; break; } tmpIndex--; } return onsetFound; } void PreciseBassOnsetDetectorOffline::checkPredictionFirstOrderMarkovCorrect(int lag){ //LOOKING BACK, NOW REQUIRE ANOTHER CORRECT BASS NOTE RECENT BEAT POSITION //hard to find printf("First Order check\n"); int matches = 0; int octaveErrors = 0; int mismatches = 0; bool printResult = false;//rue; if (printResult) printPitchInfo(); for (int index = 0; index < onsetList.size(); index++){ onsetList[index].midiPrediction = 0; int midiPitch = 0; int previousMidiPitch = 0; if (printResult) printf("\nBeat Position %i,%i:\n", onsetList[index].beatPosition, onsetList[index].onsetType); int k = 1; int previousOffset = 1;//the note before while (index - previousOffset >= 0 && (onsetList[index-previousOffset].onsetType != 0 || !onsetList[index-previousOffset].roundedPitch)){ //printf("rejected: index %i offset %i onset type %i pitch %i\n", index, previousOffset, onsetList[index-previousOffset].onsetType, onsetList[index-previousOffset].roundedPitch); previousOffset++; } if (printResult){ printf("\nBeat Position %i,%i (midi %i, prev %i,%i: %i):\n", onsetList[index].beatPosition, onsetList[index].onsetType, onsetList[index].roundedPitch, onsetList[index-previousOffset].beatPosition, onsetList[index-previousOffset].onsetType, onsetList[index-previousOffset].roundedPitch); printf("Previous position is %i,%i offset %i, previous pitch %i\n", onsetList[index-previousOffset].beatPosition, onsetList[index-previousOffset].onsetType, previousOffset, onsetList[index-previousOffset].roundedPitch); } bool printit = false; //first try our new markov condition while (onsetList[index].beatPosition - k *lag >= 0){ if(getOnsetAtBeat(index, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, midiPitch) && getOnsetAtBeat(index - previousOffset, onsetList[index - previousOffset].beatPosition - (k*lag), onsetList[index-previousOffset].onsetType, previousMidiPitch)) { if (printit) printf("k %i Predicted pitch %i Lag previous pitch %i \n", k, midiPitch, previousMidiPitch); //midi picth is what we would do precition as, but require that the previous note also match our last observed note //onset found at beat pos - k*lag //then is the same bool goodPrediction = false; if (previousMidiPitch == onsetList[index-previousOffset].roundedPitch){ goodPrediction = true; if (printit) printf("Previous Found! Lag %i Lag previous pitch %i\nPrediction %i", k, onsetList[index-previousOffset].roundedPitch, previousMidiPitch, midiPitch); } if (midiPitch && goodPrediction)//i.e. non zero prediction made looking back k*lag beats { if (printit) printf("MIDI PREDICTION IS %i\n", midiPitch); break; } } k++;//keep looking back } if (!midiPitch){ k = 1; while (onsetList[index].beatPosition - k *lag >= 0){ int tmp = getOnsetAtBeat(index, onsetList[index].beatPosition-(k*lag), onsetList[index].onsetType, midiPitch); if (midiPitch)//i.e. non zero prediction made looking back k*lag beats break; k++;//keep looking back } } if (midiPitch){//only test non zero onsetList[index].midiPrediction = midiPitch; if (printResult) printf(", pred %i, (lag %i) ", midiPitch, k); if (midiPitch == onsetList[index].roundedPitch){ onsetList[index].matchIndicator = 1; matches++; if (printResult) printf(", MATCH"); } if (midiPitch && onsetList[index].roundedPitch && abs(midiPitch - onsetList[index].roundedPitch) == 12){ octaveErrors++; if (printResult) printf(", OCTAVE %i", onsetList[index].roundedPitch-midiPitch); } else if (midiPitch && onsetList[index].roundedPitch && midiPitch != onsetList[index].roundedPitch){ mismatches++; if (printResult) printf(", NOT"); } } if (printResult) printf("\n"); } double sum = matches+octaveErrors+mismatches; printf("Results : First Oreder Markov Correct\nOptimal lag %i, matches %.3f, octaves %.3f, wrong %.3f\n", lag, matches/sum, octaveErrors/sum, mismatches/sum); } void PreciseBassOnsetDetectorOffline::checkBars(){ int bar = 0; double barScore = 0; double barCount; std::vector<double> scorePerBar; for (int index = 0; index < onsetList.size(); index++){ while (bar*4 < onsetList[index].beatPosition){ if (barCount > 0) barScore /= barCount; scorePerBar.push_back(barScore); //now going to next bar so reinitialise bar++; barScore = 0; barCount = 0; } barScore += onsetList[index].matchIndicator; barCount++; } for (int i = 0; i < scorePerBar.size(); i++) printf("Score bar [%i] %f\n", i, scorePerBar[i]); }