Mercurial > hg > drum-timing-analyser
diff DrumTimingLoader_OF/src/RecordedMultipleAudio.cpp @ 0:82352cfc0b23
Added files from ISMIR groove drum timing work
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Mon, 01 Oct 2012 22:24:32 +0100 |
parents | |
children | 50ba55abea8c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DrumTimingLoader_OF/src/RecordedMultipleAudio.cpp Mon Oct 01 22:24:32 2012 +0100 @@ -0,0 +1,525 @@ +/* + * RecordedMultipleAudio.cpp + * MultipleAudioMathcher + * + * Created by Andrew on 31/01/2012. + * Copyright 2012 QMUL. All rights reserved. + * + */ + +#include "RecordedMultipleAudio.h" + +RecordedMultipleAudio::RecordedMultipleAudio(){ + + infoFilepath = "../../../data/errorData.txt"; + //infoFilepath = "/Users/andrew/errorData.txt"; + + timingOffset = 0; +} + +void RecordedMultipleAudio::loadTestAudio(){ + + + numberOfAudioTracks = 2; + + printf("loaded max val is %f\n", loadedAudioFiles[0].fileLoader.onsetDetect.onsetDetector.maximumDetectionValue); + + int multitrackToLoad = 5; + setDifferentMultitracks(multitrackToLoad);//command to load this set of audio files - see below + + drumTimingAnalyser.phaseCost = 1000;//v high - i.e. dont do phase + + drawWindow = 1; + trackScreenHeight = 0.25; + + + +// printf("AFTER LOADING: \n"); +// printInfo(); + +} +#pragma mark -loadingPrerecordedTracks +void RecordedMultipleAudio::setDifferentMultitracks(const int& setToLoad){ + const char *kickfilename ;//= "../../../data/sound/LiveDues/kick_liveDues.wav"; + const char *roomfilename ;//"../../../data/sound/LiveDues/bass_upsideLive.wav"; + const char *snarefilename ; + std::string sonicVizBeatsFilename ; + + switch (setToLoad) { + case 0: + kickfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/kickFuture.wav"; + roomfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/roomFuture.wav"; + snarefilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/snareFuture.wav"; + sonicVizBeatsFilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/FutureHidesBeats.txt"; + break; + + + case 1: + roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/Mixdown/PennyArcade_StudioMixdown.wav"; + kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/kick.wav"; + snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/snare.wav"; + sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/Mixdown/PennyArcade_StudioMixdown_beats.txt"; + break; + + case 2: + roomfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/colesL_bip.wav"; + kickfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/Kick_bip.wav"; + snarefilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/Snare_bip.wav"; + sonicVizBeatsFilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/IngramGreenSectionBeats.txt"; + break; + + case 3: + roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/coles_bip.wav"; + kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/kick d112_bip.wav"; + snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/snare bottom_bip.wav"; + sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/TakeOneBeats.txt"; + break; + + case 4: + roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/neuamnn_bip.wav"; + kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/kick_bip.wav"; + snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/snare_bip.wav"; + sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/PennyArcade_take4_beats.txt"; + break; + + case 5: + roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/neuamnn_bip.wav"; + kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/kick_bip.wav"; + snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/snare_bip.wav"; + sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/PennyArcade_take5_beats.txt"; + break; + + + + } + if (kickfilename != NULL){ + + loadAudioTrack(kickfilename, 0); + } + + if (roomfilename != NULL){ + printf("roomfilename: %s\n", roomfilename); + loadAudioTrack(roomfilename, 1); + } + + if (snarefilename != NULL) + loadAudioTrack(snarefilename, 2); + + if (sonicVizBeatsFilename.c_str() != NULL){ + readInBeatsFile(sonicVizBeatsFilename); + printBeatTimes(); + checkFileErrors(0); + checkFileErrors(2); + findBeatOnsets(); + } +} + +void RecordedMultipleAudio::loadAudioTrack(std::string name, const int& channel){ + //kick - track type 0 + //bass - type 1 + //snare type 2 + //guitar type 3 + if (channel >= 0 && channel <= numberOfAudioTracks){ + loadedAudioPtr = new LoadedAudioHolder; + //set tracktype before we do analysis + //so we dont do unnecessary chroma and pitch calculations + if (channel == 0 || channel == 2){ + loadedAudioPtr->setTrackType(channel); + } + else{ + loadedAudioPtr->setTrackType(0); + } + loadedAudioPtr->loadAudioFile(name); + + loadedAudioFiles[channel] = *loadedAudioPtr; + loadedAudioFiles[channel].fileLoader.onsetDetect.window.setToRelativeSize(0, trackScreenHeight*channel, 1, trackScreenHeight); + //loadedAudioFiles[channel].setTrackType(channel); + } +} + + + +void RecordedMultipleAudio::readInBeatsFile(std::string& pathName){ + + // "/Users/andrew/Documents/work/MuseScore/RWC/ANNOTATION/RM-C002_annotation+WavPos.csv" + beatTimes.clear(); + + printf("- - - - \n\nREAD FILE %s\n", pathName.c_str()); + ifstream file ( pathName.c_str()); + string value, tmpLine; + stringstream iss; + int count = 0; + + while ( file.good() ) + { + getline(file, tmpLine); + iss << tmpLine; + int lineCount = 0; + // printf("tmp line %s\n", tmpLine.c_str()); + while(getline ( iss, value, '\t' )){ // read a string until next comma: http://www.cplusplus.com/reference/string/getline/ + // cout << string( value, 1, value.length()-2 ); // display value removing the first and the last character from it + // printf("line:%s\n", value.c_str()); + string::size_type start = value.find_first_not_of(" ,\t\v\n"); + + string part = value.substr(start, string::npos); + + //printf("%s\n", firstpart.c_str()); + if (lineCount == 0){ + //printf("First part of line found '%s'\n", part.c_str()); + double newBeatTime = atof(part.c_str()); + beatTimes.push_back(newBeatTime); + } + lineCount++; + + }//end while reading line + iss.clear(); + + + }//end while + + // printBeatTimes(); + printf("There are %i BEAT annotations\n", (int)beatTimes.size()); + +} + +void RecordedMultipleAudio::printBeatTimes(){ + for (int i = 0;i < beatTimes.size();i++){ + printf("Beat[%i] = %f\n", i, beatTimes[i]); + } +} + + +void RecordedMultipleAudio::checkFileErrors(int channel){ + int beatIndex = 0; + int cutoff = 50;//ms width to check + for (int i = 0;i < loadedAudioFiles[channel].onsetTimesMillis.size();i++){ + while (beatIndex < beatTimes.size() && 1000.0*beatTimes[beatIndex] < loadedAudioFiles[channel].onsetTimesMillis[i] - cutoff) { + beatIndex++; + } + double error = (1000.0*beatTimes[beatIndex] - loadedAudioFiles[channel].onsetTimesMillis[i]); + if (fabs(error) < cutoff){ + if (channel == 0) + printf("Pos: %i Beat Time %f Kick Time %f Error %f\n", beatIndex%4, 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error); + else + printf("Pos: %i Beat Time %f Snare Time %f Error %f\n", beatIndex%4, 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error); + + }else{ + if (channel == 0) + printf("Out of Beat: Kick Time %f beat error %f\n", 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error); + else + printf("Out of Beat: %f Snare Time %f best error %f\n", 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error); + + } + } +} + + +#pragma mark -labelExactOnsets + +void RecordedMultipleAudio::findBeatOnsets(){ +//tries to find kicks on 1, 3; snares on 2,4 + int beatIndex = 0; + int kickIndex = 0; + int snareIndex = 0; + double kickTime, snareTime; + int cutoff = 50;//ms width to check + + onsetInfo.clear(); + +// kickErrors.clear(); +// snareErrors.clear(); + + bool beatFound; + for (int k = 0;k < beatTimes.size();k++){ + beatFound = false; + double newBeatTime = beatTimes[k]*1000.0; + int beatPosition = k % 4; + OnsetInformation information; + switch (beatPosition) { + case 0: case 2://check for kick when it is on the `one' or 'three' (0 or 2 in our metrical position) + // printf("check %i kindex %i\n", beatPosition, kickIndex); + while (kickIndex < loadedAudioFiles[0].onsetTimesMillis.size() && loadedAudioFiles[0].onsetTimesMillis[kickIndex] < newBeatTime - cutoff){ + kickIndex++; + kickTime = loadedAudioFiles[0].onsetTimesMillis[kickIndex]; + // printf("checking beat[%i] %f kick %f error %f\n", k, beatTimes[k]*1000.0, kickTime, (kickTime - beatTimes[k]*1000.0)); + if (fabs(kickTime - beatTimes[k]*1000.0) < cutoff){ + beatFound = true; + printf("beat[%i] %f kick %f error %f\n", k, beatTimes[k]*1000.0, kickTime, (kickTime - beatTimes[k]*1000.0)); + + information.error = (kickTime - beatTimes[k]*1000.0);//FOR NOW ONLY + information.metricalPosition = beatPosition; + information.type = 0; + information.exactOnsetTime = kickTime; + // exactBeatPositions.push_back(kickTime); + } + } + + break; + case 1: case 3://snare + while (snareIndex < loadedAudioFiles[1].onsetTimesMillis.size() && loadedAudioFiles[1].onsetTimesMillis[snareIndex] < newBeatTime - cutoff ){ + snareIndex++; + snareTime = loadedAudioFiles[1].onsetTimesMillis[snareIndex]; + if (fabs(snareTime - beatTimes[k]*1000.0) < cutoff){ + beatFound = true; + // snareErrors.push_back((beatTimes[k]*1000.0 - snareTime)); + information.error = (snareTime - beatTimes[k]*1000.0); + information.metricalPosition = beatPosition;//.push_back(beatPosition); + information.type = 1; + information.exactOnsetTime = snareTime; + printf("beat[%i] %f snare %f error %f\n", k, beatTimes[k]*1000.0, snareTime, (snareTime - beatTimes[k]*1000.0)); + // exactBeatPositions.push_back(snareTime); + } + } + + break; + } + if (!beatFound){ + information.type = -1;//not a kick or snare + information.exactOnsetTime = beatTimes[k]*1000.0; + information.metricalPosition = beatPosition;//.push_ + // exactBeatPositions.push_back(beatTimes[k]*1000.0);//have to go with the annotated beat instead (no matching kick or snare) + + printf("beat[%i] %f NOT FOUND, kicktime %f snaretime %f\n", k, beatTimes[k]*1000.0, kickTime, snareTime ); + } + + onsetInfo.push_back(information); + + }//end for all beat annotations + + correctExactBeatTiming();//get rid of the first onset time + + calculateTimingAnalysis(); + +} + +#pragma mark -doTimingAnalysis +void RecordedMultipleAudio::correctExactBeatTiming(){ + //get rid of firtst onset time + if (onsetInfo.size() > 0){ + timingOffset = onsetInfo[0].exactOnsetTime;//s[0]; + double tmpPosn; + for (int i = 0;i < onsetInfo.size();i++){ + onsetInfo[i].beatTimeToProcess = onsetInfo[i].exactOnsetTime - timingOffset; + printf("exact [%i] type %i %f, corrected %f\n", i, onsetInfo[i].type, onsetInfo[i].exactOnsetTime, onsetInfo[i].beatTimeToProcess ); + } + } +} + +void RecordedMultipleAudio::calculateTimingAnalysis(){ + + for (int i = 0;i < onsetInfo.size();i++){ + drumTimingAnalyser.updateCostToPoint(onsetInfo[i].beatTimeToProcess, i); + //updatecounter is the beat position for this note event - can be used to do other kinds of durations than just simple beats + + drumTimingAnalyser.beatPosition.push_back(onsetInfo[i].exactOnsetTime); + } + drumTimingAnalyser.processPathHistory(); + drumTimingAnalyser.calculateTempoLimits(); + + getErrorTimesFromAnalysis(); + + alternativeKickRelativeAnalysis(); + + exportErrorInformation(); + + displayKickRelativeMedianErrors();//NB messes ther order of these + + //this was how we did it in multimatch program +// timer.processPathHistory(); +// timer.calculateTempoLimits(); +// timer.exportTimingData(); +// timer.exportProcessedBeatTimes(firstNoteTime); +} + +void RecordedMultipleAudio::getErrorTimesFromAnalysis(){ + printf("\nDrumTimingLoader: get error times from analysis!!!\n"); + setUpErrorsByMetricalPosition(); + //gets the errors from the drum timing analyser (i.e. ISMIR paper code) and attributes these to the onsets + for (int i = 0;i < drumTimingAnalyser.timingData.size();i++){ + onsetInfo[i].error = drumTimingAnalyser.timingData[i][5]; + onsetInfo[i].clickTime = drumTimingAnalyser.timingData[i][1] + timingOffset;//this is where teh timing analyser placed the click times + errorsByMetricalPosition[onsetInfo[i].metricalPosition].push_back(onsetInfo[i].error); + printf("beat %i metrical posn %i exact beat time %f error %i\n", i, onsetInfo[i].metricalPosition, onsetInfo[i].exactOnsetTime, (int)onsetInfo[i].error); + } + displayMedianErrors(); +} + + +void RecordedMultipleAudio::alternativeKickRelativeAnalysis(){ + printf("\n\nAnalysis Relative to the Kick Drum on the ONE\n"); + //this sees kicks as ON the beat, looks at relative error of the three other beats if we chop the bar evenly + + double recentBeatTime, currentTempo; + kickRelativeErrors.clear(); + kickRelativeClickTimes.clear(); + + for (int i = 0;i < drumTimingAnalyser.timingData.size();i++){ + int beatPosition = i%4; + if (beatPosition == 0){ + recentBeatTime = onsetInfo[i].exactOnsetTime; + if (i+4 < onsetInfo.size()) + currentTempo = (onsetInfo[i+4].exactOnsetTime - onsetInfo[i].exactOnsetTime)/4.0; + printf("new beat time %f tempo %f\n", recentBeatTime, currentTempo); + } + double error = onsetInfo[i].exactOnsetTime - (recentBeatTime + beatPosition*currentTempo); + printf("Beat %i KR Predicted Beat %f Actual exact %f KRerror %f DTerror %i\n", beatPosition, (recentBeatTime + beatPosition*currentTempo), onsetInfo[i].exactOnsetTime, error, (int)onsetInfo[i].error); + kickRelativeErrors.push_back(error); + kickRelativeClickTimes.push_back(recentBeatTime + beatPosition*currentTempo); + } +} + +#pragma label -exportInfo +void RecordedMultipleAudio::exportErrorInformation(){ + printf("Export final timing information\n"); + + ofstream ofs(infoFilepath.c_str()); + for (int i = 0;i < onsetInfo.size() && kickRelativeErrors.size();i++){// drumTimingAnalyser.timingData.size() + ofs << i << "," << (i%4) << "," << onsetInfo[i].exactOnsetTime << ","; + ofs << onsetInfo[i].clickTime << "," << onsetInfo[i].error << ",";//the error for the ISMIR timing analyser + ofs << kickRelativeClickTimes[i] << "," << kickRelativeErrors[i];//the click and error for beats evenly between kicks + ofs << endl; + } + +} + +void RecordedMultipleAudio::printKickRelativeErrors(){ + for (int i = 0;i < kickRelativeErrors.size();i++){ + printf("KR error [%i] : %.1f\n", i, kickRelativeErrors[i]); + } +} + +void RecordedMultipleAudio::setUpErrorsByMetricalPosition(){ + //clear this matrix + errorsByMetricalPosition.clear(); + for (int i = 0;i < 4;i++){ + DoubleVector v; + errorsByMetricalPosition.push_back(v); + } + +} + +void RecordedMultipleAudio::displayMedianErrors(){ + printf("Medians of the Decoded Tempo variations\n"); + for (int i = 0;i < 4;i++){ + //printErrorsForMetricalPosition(i); + std::sort(errorsByMetricalPosition[i].begin(), errorsByMetricalPosition[i].end());//sort vector + double median = errorsByMetricalPosition[i][(int)(errorsByMetricalPosition[i].size()/2)]; + printf("median for metrical position %i is %f\n", i, median); + } +} + + +void RecordedMultipleAudio::displayKickRelativeMedianErrors(){ + printf("Medians of the KR variations\n"); + + DoubleVector tmpKRErrors; + + + for (int i = 0;i < 4;i++){ + + tmpKRErrors.clear(); + int index = 0; + + while (index+i < kickRelativeErrors.size()) { + tmpKRErrors.push_back(kickRelativeErrors[index + i]); + index += 4; + } + +// for (int k = 0;k < tmpKRErrors.size();k++) +// printf("kr %i [%i] = %f\n", i, k, tmpKRErrors[k]); + + //printErrorsForMetricalPosition(i); + std::sort(tmpKRErrors.begin(), tmpKRErrors.end());//sort vector + +// for (int k = 0;k < tmpKRErrors.size();k++) +// printf("sorted kr %i [%i] = %f\n", i, k, tmpKRErrors[k]); + + + double median = tmpKRErrors[(int)(tmpKRErrors.size()/2)]; + printf("median for metrical position %i is %f\n", i, median); + } +} + +void RecordedMultipleAudio::printErrorsForMetricalPosition(const int& i){ + for (int k = 0;k < errorsByMetricalPosition[i].size();k++){ + printf("metrical posn %i, [%i] = %f\n", i, k, errorsByMetricalPosition[i][k]); + } +} + +#pragma mark -drawTracks + +void RecordedMultipleAudio::drawTracks(){ + if (drawWindow == 0){ + for (int i = 0;i < numberOfAudioTracks;i++){ + loadedAudioFiles[i].draw(); + } + } else { + drumTimingAnalyser.drawTempoCurve(); + } +} + +#pragma mark -update +void RecordedMultipleAudio::updatePosition(){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].updateToPlayPosition(); +} + +void RecordedMultipleAudio::updatePositionToMillis(const double& millis){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].updateToMillisPosition(millis); +} + +void RecordedMultipleAudio::updatePlaybackPositionToMillis(const double& millis){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].updatePlaybackPositionToMillis(millis); +} + +void RecordedMultipleAudio::switchScreens(){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].switchScreens(); +} + + +void RecordedMultipleAudio::togglePlay(){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].togglePlay(); +} + +void RecordedMultipleAudio::stop(){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].stop(); +} + + +void RecordedMultipleAudio::printInfo(){ + loadedAudioFiles[0].fileLoader.onsetDetect.printChromaInfo(); + loadedAudioFiles[0].printEvents(); +} + +void RecordedMultipleAudio::windowResized(const int& w, const int& h){ + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].windowResized(w, h); +} + +void RecordedMultipleAudio::zoomIn(){ + if (drawWindow == 0){ + printf("zoom in\n"); + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].fileLoader.zoomIn(); + } + + if (drawWindow == 1) + drumTimingAnalyser.zoomIn();//numberOfPointsPerPage /= 2; +} + +void RecordedMultipleAudio::zoomOut(){ + printf("zoom out\n"); + for (int i = 0;i < numberOfAudioTracks;i++) + loadedAudioFiles[i].fileLoader.zoomOut(); + + if (drawWindow == 1) + drumTimingAnalyser.zoomOut();//numberOfPointsPerPage *= 2; + +} + +