annotate src/AudioEventMatcher.cpp @ 37:9806a4f22fd0

Fixed bugs in the likelohoods that caused some to zero when no events found.
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Tue, 24 Apr 2012 14:16:01 +0100
parents eb43b2a007ea
children f5de07b4d733
rev   line source
andrew@0 1 /*
andrew@0 2 * AudioEventMatcher.cpp
andrew@0 3 * MultipleAudioMathcher
andrew@0 4 *
andrew@0 5 * Created by Andrew on 31/01/2012.
andrew@0 6 * Copyright 2012 QMUL. All rights reserved.
andrew@0 7 *
andrew@0 8 */
andrew@0 9
andrew@0 10 #include "AudioEventMatcher.h"
andrew@0 11
andrew@0 12
andrew@36 13 const int matchWindowWidth = 8000;
andrew@32 14 const float pitchCutOff = 16;//within which pitches are even considered
andrew@0 15
andrew@0 16 AudioEventMatcher::AudioEventMatcher(){
andrew@7 17
andrew@35 18 useChromaDotProduct = false;
andrew@15 19
andrew@37 20 printingData = false;
andrew@37 21
andrew@23 22 pitchLikelihoodToNoise = 0.6;//more noise
andrew@32 23 chromaLikelihoodToNoise = 0.5;//lower => more noise, higher more weight for events
andrew@32 24 chromaLikelihoodWidth = 50;//ms round onset event
andrew@16 25
andrew@36 26 onsetLikelihoodToNoise = 0.1;
andrew@17 27 onsetLikelihoodWidth = 10;//in ms
andrew@15 28
andrew@0 29 setArraySizes();
andrew@3 30
andrew@3 31 usingRealTime = false;
andrew@3 32 bayesianStruct.realTimeMode = &usingRealTime;
andrew@7 33 recentPitch = 0;
andrew@8 34 currentAlignmentPosition = 0;
andrew@14 35
andrew@9 36 followingLiveInput = true;
andrew@15 37 startedPlaying = false;
andrew@20 38 recordedTempoIndex = 0;
andrew@20 39 // temporal.setUpEventTimeMatrix();
andrew@20 40 // recordedTempoData.setUpEventTimeMatrix();
andrew@0 41 }
andrew@0 42
andrew@14 43
andrew@19 44
andrew@19 45
andrew@7 46 void AudioEventMatcher::setWindowDimensions(){
andrew@7 47 double startHeight = recordedTracks.numberOfAudioTracks * recordedTracks.trackScreenHeight;
andrew@7 48 double heightAvailable = 1 - startHeight;
andrew@32 49 heightAvailable /= numberOfChannels;
andrew@7 50
andrew@7 51 bayesPositionWindow.setToRelativeSize(0, startHeight, 1, heightAvailable);
andrew@7 52 bayesLikelihoodWindow.setToRelativeSize(0, startHeight + 1*heightAvailable, 1, heightAvailable);
andrew@7 53 bayesTempoWindow.setToRelativeSize(0, startHeight + 2*heightAvailable, 1, heightAvailable);
andrew@7 54
andrew@7 55
andrew@7 56 }
andrew@0 57
andrew@0 58 void AudioEventMatcher::setArraySizes(){
andrew@0 59 bayesianStruct.resetSpeedSize(200);
andrew@0 60 bayesianStruct.setRelativeSpeedScalar(0.01);
andrew@0 61 bayesianStruct.setSpeedPrior(1.0);
andrew@0 62 bayesianStruct.relativeSpeedPrior.getMaximum();
andrew@0 63
andrew@36 64 float scalarForBayesianDistribution = 2;
andrew@36 65
andrew@36 66 bayesianStruct.resetSize(matchWindowWidth / scalarForBayesianDistribution);
andrew@36 67 bayesianStruct.setPositionDistributionScalar(2);
andrew@0 68
andrew@0 69 }
andrew@0 70
andrew@16 71 void AudioEventMatcher::loadAudioFiles(){
andrew@16 72 recordedTracks.loadTestAudio();
andrew@16 73 synchroniser.fileLengthSamples = recordedTracks.loadedAudioFiles[0].fileLoader.totalNumberOfSamples;
andrew@16 74 printf("synchroniser has %f samples\n", synchroniser.fileLengthSamples);
andrew@20 75
andrew@20 76 calculateRecordedTempoData();
andrew@20 77 printf("\n\nFIRST PASS: FINAL recorded tempo is %f\n", recordedTempoData.playingTempo);
andrew@20 78 setTempoPrior(recordedTempoData.playingTempo);
andrew@20 79 calculateRecordedTempoData();//now calculate again using better prior
andrew@20 80
andrew@20 81 printf("\n\nSECOND PASS: FINAL recorded tempo is %f\n", recordedTempoData.playingTempo);
andrew@20 82 printf("GLOBAL TEMPO of RECORDED FILES\n");
andrew@20 83 recordedTempoData.printTempoTimes();
andrew@20 84 }
andrew@20 85
andrew@20 86 void AudioEventMatcher::setTempoPrior(double tempo){
andrew@20 87 recordedTempoData.zero();
andrew@20 88 recordedTempoData.tempoPosterior.zero();
andrew@20 89 recordedTempoData.tempoPosterior.addGaussianShapeFromRealTime(tempo, 3, 1);
andrew@20 90
andrew@20 91 }
andrew@20 92
andrew@20 93 void AudioEventMatcher::calculateRecordedTempoData(){
andrew@20 94 int indexForOnsets[3];
andrew@20 95 indexForOnsets[0] = 0;
andrew@20 96 indexForOnsets[1] = 0;
andrew@20 97 indexForOnsets[2] = 0;
andrew@20 98 int kickTime, snareTime;
andrew@20 99 while (indexForOnsets[0] < recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.chromaOnsets.size() ||
andrew@20 100 indexForOnsets[2] < recordedTracks.loadedAudioFiles[2].fileLoader.onsetDetect.chromaOnsets.size()) {
andrew@20 101
andrew@20 102 setNextOnsetTime(0, kickTime, &indexForOnsets[0]);
andrew@20 103 setNextOnsetTime(2, snareTime, &indexForOnsets[0]);
andrew@20 104
andrew@20 105 if (kickTime < snareTime){
andrew@20 106 printf("update kick at %i\n", kickTime);
andrew@20 107 recordedTempoData.updateTempo(0, kickTime);
andrew@20 108 printf("recorded tempo is %f\n", recordedTempoData.playingTempo);
andrew@20 109 indexForOnsets[0]++;
andrew@20 110 }else {
andrew@20 111 printf("update snare at %i\n", snareTime);
andrew@20 112 recordedTempoData.updateTempo(2, snareTime);
andrew@20 113 printf("recorded tempo is %f\n", recordedTempoData.playingTempo);
andrew@20 114 indexForOnsets[2]++;
andrew@20 115 }
andrew@20 116 }//end while
andrew@20 117
andrew@20 118
andrew@20 119 }
andrew@20 120
andrew@20 121 void AudioEventMatcher::setNextOnsetTime(const int& channel, int& time, int* indexForOnsets){
andrew@20 122 if (indexForOnsets[channel] < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size()){
andrew@20 123 time = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[indexForOnsets[channel]].millisTime;
andrew@20 124 }
andrew@20 125 else {
andrew@20 126 time = 2147483647;//infinity
andrew@20 127 }
andrew@16 128 }
andrew@16 129
andrew@9 130 void AudioEventMatcher::startPlaying(){
andrew@3 131 bayesianStruct.setStartPlaying();
andrew@8 132 currentAlignmentPosition = 0;
andrew@8 133 startTime = ofGetElapsedTimeMillis();
andrew@11 134
andrew@11 135 projectedPrior = bayesianStruct.prior;
andrew@15 136 startedPlaying = true;
andrew@17 137 synchroniser.reset();
andrew@19 138 temporal.reset();
andrew@17 139
andrew@20 140 recordedTempoIndex = 0;
andrew@20 141 recordedTempo = recordedTempoData.globalTempo[recordedTempoIndex];
andrew@20 142
andrew@20 143 currentSpeedRatio = 1;
andrew@20 144
andrew@21 145 temporal.tempoPosterior.zero();
andrew@36 146 temporal.tempoPosterior.addGaussianShapeFromRealTime(recordedTempo, 2000, 1);
andrew@21 147
andrew@20 148 //SET TEMPO PRIOR for Speed Ratio
andrew@20 149 //the update this
andrew@20 150 setSpeedRatioDistribution(currentSpeedRatio);
andrew@37 151
andrew@37 152 euclideanMaximumDistance = 0;
andrew@37 153
andrew@3 154 //bayesianStruct.posterior.printArray();
andrew@3 155 }
andrew@3 156
andrew@9 157
andrew@20 158 void AudioEventMatcher::setSpeedRatioDistribution(const double& speedRatio){
andrew@20 159 bayesianStruct.relativeSpeedPosterior.zero();
andrew@20 160 bayesianStruct.relativeSpeedPosterior.addToIndex(bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(speedRatio), 1);
andrew@22 161 bayesianStruct.relativeSpeedPosterior.addGaussianShapeFromRealTime(1, 0.06, 0.8);
andrew@20 162 }
andrew@20 163
andrew@15 164 void AudioEventMatcher::stopPlaying(){
andrew@15 165 startedPlaying = false;
andrew@37 166 //temporal.printEventTimes();
andrew@15 167 }
andrew@15 168
andrew@22 169 void AudioEventMatcher::rescue(){
andrew@22 170 bayesianStruct.posterior.zero();
andrew@22 171 bayesianStruct.posterior.addConstant(1);
andrew@22 172 bayesianStruct.prior.zero();
andrew@22 173 bayesianStruct.prior.addConstant(1);
andrew@22 174 }
andrew@22 175
andrew@9 176 void AudioEventMatcher::updatePosition(){
andrew@19 177
andrew@19 178 if (startedPlaying){
andrew@9 179 if (!followingLiveInput)
andrew@9 180 recordedTracks.updatePosition();
andrew@19 181 else
andrew@9 182 recordedTracks.updatePositionToMillis(currentAlignmentPosition);
andrew@9 183
andrew@20 184 updateBestAlignmentPosition();
andrew@19 185 }
andrew@19 186
andrew@20 187 updateRecordedTempo();
andrew@20 188
andrew@19 189 temporal.tempoPosterior.addGaussianShape(temporal.tempoPosterior.MAPestimate, temporal.tempoArraySize / 4, 0.5 );
andrew@9 190 }
andrew@9 191
andrew@20 192 void AudioEventMatcher::updateRecordedTempo(){
andrew@20 193 //tempo of equivalent recorded position is updated
andrew@37 194 if (recordedTempoIndex < recordedTempoData.globalTempoTimes.size()){//if for debug
andrew@20 195 while(currentAlignmentPosition > recordedTempoData.globalTempoTimes[recordedTempoIndex]){
andrew@20 196 recordedTempoIndex++;
andrew@20 197 }
andrew@20 198 recordedTempo = recordedTempoData.globalTempo[recordedTempoIndex];
andrew@20 199 double tmpRatio = currentSpeedRatio;
andrew@20 200 currentSpeedRatio = temporal.playingTempo / recordedTempo;
andrew@20 201 if (currentSpeedRatio != tmpRatio)
andrew@20 202 setSpeedRatioDistribution(currentSpeedRatio);
andrew@37 203
andrew@37 204 }//end if to prevent debug crash
andrew@20 205 }
andrew@20 206
andrew@8 207 void AudioEventMatcher::updateBestAlignmentPosition(){
andrew@10 208 //THIS DEALS WITH WHERE WE ARE NOW! ON THE SCREEN
andrew@10 209 //DIFFERENT TO WHEN EVENTS COME IN AS THEY ARE TIMESTAMPED - SO EG A PITCH EVENT MAY ARRIVE 16 CHROMA FRAMES LATER - BIG DIFFERENCE
andrew@10 210
andrew@10 211 int newTime = ofGetElapsedTimeMillis() - startTime;
andrew@10 212 // double tmp = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);;
andrew@10 213 // double timetmp = (newTime - lastAlignmentTime);
andrew@10 214 // double speedtmp = bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
andrew@11 215 // currentAlignmentTime = newTime;
andrew@9 216 currentAlignmentPosition = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);
andrew@10 217 currentAlignmentPosition += (newTime - lastAlignmentTime) * bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
andrew@10 218
andrew@16 219
andrew@17 220 synchroniser.updateRecordedPosition(currentAlignmentPosition, newTime);
andrew@16 221
andrew@16 222 synchroniser.updateOutputSpeed();
andrew@16 223
andrew@11 224 bayesianStruct.projectDistribution(newTime, currentAlignmentPosition, projectedPrior);//prior gets updated to where we are now
andrew@32 225
andrew@32 226 // printf("updateBestAlignment:: alignment %i:: %i\n", newTime, (int) currentAlignmentPosition);
andrew@11 227
andrew@10 228 // printf("ALIGN pos %f time diff %f (now %f , last %f)speed %f :: ALIGN BEST %f\n", tmp, timetmp, (double)ofGetElapsedTimeMillis(), lastAlignmentTime, speedtmp, currentAlignmentPosition);
andrew@8 229 }
andrew@8 230
andrew@0 231 void AudioEventMatcher::draw(){
andrew@32 232
andrew@32 233 //MAIN DRAW FUNCTION FOR ALL
andrew@32 234
andrew@6 235 //draw some outlines in blue
andrew@3 236 ofSetColor(20,200,200);
andrew@3 237 bayesPositionWindow.drawOutline();
andrew@3 238 bayesTempoWindow.drawOutline();
andrew@0 239
andrew@6 240 //draw the scrolling audio tracks
andrew@1 241 recordedTracks.drawTracks();
andrew@7 242
andrew@2 243 ofSetColor(255);
andrew@2 244 // bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
andrew@9 245
andrew@9 246 setScreenDisplayTimes();
andrew@6 247 drawBayesianDistributions();
andrew@8 248
andrew@11 249 //bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
andrew@6 250 //bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
andrew@11 251 //bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
andrew@9 252
andrew@20 253 temporal.drawTempoArray(bayesLikelihoodWindow);
andrew@20 254
andrew@20 255 drawRecordedTempo();
andrew@20 256 drawPlayingTempo();
andrew@20 257
andrew@20 258
andrew@6 259 }
andrew@20 260
andrew@20 261 void AudioEventMatcher::drawRecordedTempo(){
andrew@6 262
andrew@21 263 int xTempoIndex = ofGetWidth() * (double)(recordedTempo - recordedTempoData.minimumTempoInterval)/(double)(recordedTempoData.maximumTempoInterval - recordedTempoData.minimumTempoInterval);
andrew@20 264 ofSetColor(0, 200, 0);
andrew@20 265 ofLine(xTempoIndex, bayesLikelihoodWindow.y, xTempoIndex, bayesLikelihoodWindow.y + bayesLikelihoodWindow.height);
andrew@20 266 ofDrawBitmapString(ofToString(recordedTempo), xTempoIndex, bayesLikelihoodWindow.y + 10);
andrew@20 267 }
andrew@20 268
andrew@20 269 void AudioEventMatcher::drawPlayingTempo(){
andrew@21 270 //purple line for MAP estimate of new intervals
andrew@21 271 int xTempoIndex = (double)(ofGetWidth() * (temporal.playingTempo - temporal.minimumTempoInterval))/(double)(temporal.maximumTempoInterval - temporal.minimumTempoInterval);
andrew@20 272 ofSetColor(200, 0, 200);
andrew@20 273 ofLine(xTempoIndex, bayesLikelihoodWindow.y, xTempoIndex, bayesLikelihoodWindow.y + bayesLikelihoodWindow.height);
andrew@21 274 ofDrawBitmapString(ofToString(temporal.playingTempo), xTempoIndex, bayesLikelihoodWindow.y + 10);
andrew@20 275
andrew@21 276 //red line where the ratio is between playing tempo and recorded one
andrew@20 277 int xSpeedRatioIndex = (double)(temporal.tempoPosterior.getIndexInRealTerms(currentSpeedRatio)*ofGetWidth())/(double)temporal.tempoPosterior.arraySize;
andrew@20 278 ofSetColor(200,0,0);
andrew@20 279 ofLine(xSpeedRatioIndex, bayesTempoWindow.y, xSpeedRatioIndex, bayesTempoWindow.y + bayesTempoWindow.height);
andrew@21 280 string tmpString = "playing "+ofToString(temporal.playingTempo);
andrew@21 281 tmpString += ", recorded "+ofToString(recordedTempo);
andrew@21 282 tmpString += " ratio "+ofToString(currentSpeedRatio);
andrew@21 283 ofSetColor(155,155,155);
andrew@21 284 ofDrawBitmapString(tmpString, 20, bayesTempoWindow.y+10);
andrew@20 285
andrew@20 286 }
andrew@20 287
andrew@20 288
andrew@9 289 void AudioEventMatcher::setScreenDisplayTimes(){
andrew@9 290 screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
andrew@9 291 // if (!followingLiveInput){
andrew@9 292
andrew@9 293 screenStartTimeMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.drawParams.windowStartFrame);
andrew@9 294 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
andrew@9 295
andrew@9 296 //need PRECISION in this alignment
andrew@9 297
andrew@9 298
andrew@9 299 /*}else{
andrew@9 300
andrew@9 301 screenStartTimeMillis = (int)(currentAlignmentPosition/screenWidthMillis) * screenWidthMillis;
andrew@9 302 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
andrew@9 303 }*/
andrew@9 304 }
andrew@9 305
andrew@6 306 void AudioEventMatcher::drawBayesianDistributions(){
andrew@6 307
andrew@32 308
andrew@32 309 drawPositionWindow();
andrew@4 310
andrew@8 311 // bayesianStruct.likelihood.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesLikelihoodWindow);
andrew@2 312
andrew@6 313 bayesianStruct.relativeSpeedPosterior.drawConstrainedVector(0, bayesianStruct.relativeSpeedPosterior.arraySize, 0, ofGetWidth(), bayesTempoWindow);
andrew@32 314
andrew@6 315
andrew@32 316 drawTrackLikelihoods();
andrew@32 317
andrew@32 318 // int priorStartIndex = bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis);
andrew@32 319 // int priorEndIndex = bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis);
andrew@32 320 // ofSetColor(0,200,200);//recent prior
andrew@32 321 // recentPrior.drawConstrainedVector(priorStartIndex, priorEndIndex, 0, ofGetWidth(), bayesPositionWindow);
andrew@32 322
andrew@32 323 drawInfo();
andrew@32 324
andrew@3 325
andrew@32 326 }
andrew@32 327
andrew@32 328 void AudioEventMatcher::drawPositionWindow(){
andrew@32 329 int startIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenStartTimeMillis);
andrew@32 330 int endIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenEndTimeMillis);
andrew@32 331 string tmpString = "start "+ofToString(screenStartTimeMillis)+" (index "+ofToString(startIndex)+"), end "+ofToString(screenEndTimeMillis);
andrew@32 332 ofDrawBitmapString(tmpString, bayesPositionWindow.x+20, bayesPositionWindow.y+20);
andrew@32 333
andrew@32 334 //draw posterior in the bayes position window
andrew@32 335 ofSetColor(255,0,255);
andrew@32 336 bayesianStruct.posterior.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesPositionWindow);
andrew@3 337
andrew@9 338 //green line at current best estimate
andrew@13 339 ofSetColor(0,255,0);//green scrolling line best position
andrew@8 340 double currentEstimateIndex = (currentAlignmentPosition - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
andrew@8 341 ofLine(currentEstimateIndex, bayesPositionWindow.y, currentEstimateIndex, bayesPositionWindow.y + bayesPositionWindow.height);
andrew@7 342
andrew@32 343
andrew@16 344 ofSetColor(0,255,255);//synchroniser position
andrew@16 345 currentEstimateIndex = (synchroniser.playingPositionMillis - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
andrew@16 346 ofLine(currentEstimateIndex, bayesLikelihoodWindow.y, currentEstimateIndex, bayesLikelihoodWindow.y + bayesPositionWindow.height);
andrew@32 347
andrew@32 348 ofSetColor(255,0,100);//purple prior
andrew@32 349 bayesianStruct.prior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesPositionWindow);
andrew@16 350
andrew@32 351 ofSetColor(255,0,0);//projected prior in red
andrew@32 352 projectedPrior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesPositionWindow);
andrew@16 353
andrew@37 354 //draw pitch
andrew@37 355 ofSetColor(0,100,255);
andrew@37 356 int index = getScreenWidthIndexOfEventTime(recentPitchEventTime);
andrew@37 357 //this window would be used (recordedTracks.loadedAudioFiles[1].fileLoader.onsetDetect.window);
andrew@16 358
andrew@32 359
andrew@32 360 }
andrew@32 361
andrew@37 362 int AudioEventMatcher::getScreenWidthIndexOfEventTime(const double& time){
andrew@37 363 return (time - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
andrew@37 364 }
andrew@37 365
andrew@32 366 void AudioEventMatcher::drawTrackLikelihoods(){
andrew@7 367 //draw track by track likelihoods
andrew@7 368 for (int i = 0; i <recordedTracks.numberOfAudioTracks;i++){
andrew@13 369 ofSetColor(200,255,50);//channel likelihoods in yellow
andrew@8 370 likelihoodVisualisation[i].drawConstrainedVector(likelihoodVisualisation[i].getRealTermsAsIndex(screenStartTimeMillis), likelihoodVisualisation[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
andrew@11 371
andrew@13 372 ofSetColor(0,255,150);//channel priors
andrew@11 373 recentPriors[i].drawConstrainedVector(recentPriors[i].getRealTermsAsIndex(screenStartTimeMillis), recentPriors[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
andrew@11 374
andrew@11 375
andrew@8 376 ofSetColor(255);
andrew@8 377 ofDrawBitmapString("recent event "+ofToString(recentEventTime[i]), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window.x + 20, recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window.y + recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window.height - 10);
andrew@7 378 }
andrew@32 379 }
andrew@8 380
andrew@8 381
andrew@32 382 void AudioEventMatcher::drawInfo(){
andrew@32 383 string tmpStr = "zero is "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(0));
andrew@32 384 tmpStr += " offsetis "+ofToString(bayesianStruct.posterior.offset);
andrew@32 385 tmpStr += " screenWidth = "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis));
andrew@32 386 ofDrawBitmapString(tmpStr, 20,140);
andrew@32 387 tmpStr = "best est "+ofToString(bayesianStruct.bestEstimate);
andrew@32 388 ofDrawBitmapString(tmpStr, 20, 180);
andrew@32 389 ofDrawBitmapString("screenwidth "+ofToString(screenWidthMillis), 20, 800);
andrew@11 390
andrew@32 391 ofSetColor(255);
andrew@32 392 tmpStr = "pitch "+ofToString(recentPitch, 2);
andrew@32 393 tmpStr += " Nearest "+ofToString(pitchOfNearestMatch,2);
andrew@32 394 tmpStr += " dist "+ofToString(distanceOfNearestMatch, 2);
andrew@37 395 tmpStr += ", Time "+ofToString(recentPitchEventTime, 0);
andrew@32 396 ofDrawBitmapString(tmpStr, 20, 20);
andrew@7 397
andrew@32 398 string alignString = " align "+ofToString(currentAlignmentPosition, 2);
andrew@32 399 alignString += " playing "+ofToString(synchroniser.playingPositionRatio, 5);
andrew@32 400 alignString += " pos "+ofToString(synchroniser.playingPositionMillis,0)+" ms";
andrew@32 401 alignString += " rec pos "+ofToString(synchroniser.recordedPositionMillis,0)+" ms";
andrew@32 402 ofDrawBitmapString(alignString, 20, 50);
andrew@20 403
andrew@32 404 ofDrawBitmapString("pos "+ofToString(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.playPosition), 200,600);
andrew@19 405
andrew@1 406 }
andrew@1 407
andrew@6 408 void AudioEventMatcher::newPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
andrew@7 409 if (pitchIn > 0){
andrew@1 410 liveInput.addPitchEvent(pitchIn, timeIn);
andrew@4 411
andrew@10 412 //printPosteriorMAPinfo();
andrew@11 413
andrew@7 414 matchNewPitchEvent(channel, pitchIn, timeIn);//main pitch matching fn
andrew@7 415
andrew@7 416 likelihoodVisualisation[1] = bayesianStruct.likelihood;
andrew@7 417
andrew@7 418 recentPitch = pitchIn;//for drawing
andrew@37 419 recentPitchEventTime = timeIn;
andrew@7 420 }
andrew@32 421 }
andrew@32 422
andrew@32 423
andrew@32 424 void AudioEventMatcher::newChromaEvent(const int& channel, float* chromaIn, const double& timeIn){
andrew@32 425
andrew@32 426 // could add event to the liveInput list? as in pitch event
andrew@37 427 if (printingData){
andrew@37 428 printf("match chroma channel %i\n", channel);
andrew@37 429 for (int i = 0;i < 12;i++){
andrew@34 430 printf("chroma in[%i] = %f\n", i, chromaIn[i]);
andrew@37 431 }
andrew@34 432 }
andrew@34 433
andrew@32 434 matchNewChromaEvent(channel, chromaIn, timeIn);//main pitch matching fn
andrew@32 435
andrew@32 436 likelihoodVisualisation[channel] = bayesianStruct.likelihood;
andrew@32 437
andrew@8 438
andrew@2 439 }
andrew@2 440
andrew@32 441
andrew@6 442 void AudioEventMatcher::newKickEvent(const double& timeIn){
andrew@6 443 // liveInput.addKickEvent(timeIn);
andrew@2 444 matchNewOnsetEvent(0, timeIn);
andrew@7 445 likelihoodVisualisation[0] = bayesianStruct.likelihood;
andrew@2 446 }
andrew@2 447
andrew@6 448 void AudioEventMatcher::newKickEvent(const int& channel, const double& timeIn){
andrew@6 449 // liveInput.addKickEvent(timeIn);
andrew@6 450 matchNewOnsetEvent(channel, timeIn);
andrew@7 451 likelihoodVisualisation[0] = bayesianStruct.likelihood;
andrew@6 452 }
andrew@6 453
andrew@2 454
andrew@2 455 void AudioEventMatcher::newSnareEvent(const double& timeIn){
andrew@6 456 matchNewOnsetEvent(2, timeIn);
andrew@7 457 likelihoodVisualisation[2] = bayesianStruct.likelihood;
andrew@7 458 }
andrew@7 459
andrew@7 460
andrew@7 461 void AudioEventMatcher::newSnareEvent(const int& channel, const double& timeIn){
andrew@7 462 matchNewOnsetEvent(channel, timeIn);
andrew@7 463 likelihoodVisualisation[2] = bayesianStruct.likelihood;
andrew@2 464 }
andrew@2 465
andrew@2 466 //Needs just to set bounds for the matching process, not have TimeIn
andrew@2 467 void AudioEventMatcher::matchNewOnsetEvent(const int& channel, const double& timeIn){
andrew@3 468
andrew@6 469 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@10 470
andrew@2 471 //start at beginning but OPTIMISE later
andrew@2 472 bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
andrew@2 473 bayesianStruct.likelihood.zero();//set to zero
andrew@36 474 //double quantity = 1;//
andrew@36 475 double quantity = 1*onsetLikelihoodToNoise;//BETTER CHANGE THIS BACK TOO..see below//likelihoodToNoiseRatio / numberOfMatches;
andrew@2 476 int numberOfMatchesFound = 0;
andrew@2 477
andrew@10 478 double startMatchingTime = bayesianStruct.likelihood.offset;
andrew@10 479 double endMatchingTime = bayesianStruct.likelihood.offset + matchWindowWidth;
andrew@32 480 double millisTime = -1*INFINITY;//or 0 is fine
andrew@32 481 int checkIndex = 0;
andrew@36 482 if (channel <= recordedTracks.numberOfAudioTracks && checkIndex < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size()){
andrew@32 483 while (millisTime < startMatchingTime) {
andrew@32 484 millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[checkIndex].millisTime;
andrew@32 485 checkIndex++;
andrew@32 486 }
andrew@32 487 for (int i = checkIndex;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size() && millisTime <= endMatchingTime;i++){
andrew@32 488 millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
andrew@10 489 if (millisTime >= startMatchingTime && millisTime <= endMatchingTime){
andrew@14 490 bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, onsetLikelihoodWidth, quantity);
andrew@2 491 numberOfMatchesFound++;
andrew@6 492 // printf("Adding Gaussian for onset at time %f offset %f\n", millisTime, bayesianStruct.likelihood.offset);
andrew@2 493
andrew@32 494 }//end if within limits (changed so it now is 4 sure)
andrew@2 495 }
andrew@2 496 }
andrew@2 497
andrew@11 498 if (numberOfMatchesFound > 0){
andrew@3 499 // bayesianStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesianStruct.likelihood.length);
andrew@36 500 // bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(onsetLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@36 501 bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(bayesianStruct.likelihood.length));//BETTER CHANGE THIS BACK...
andrew@2 502 bayesianStruct.likelihood.renormalise();
andrew@2 503
andrew@8 504 bayesianStruct.calculatePosterior();
andrew@10 505 lastAlignmentTime = timeIn;//use TIMESTAMP
andrew@10 506 recentEventTime[channel] = timeIn;//ofGetElapsedTimeMillis() - startTime;
andrew@11 507
andrew@11 508 recentPriors[channel] = bayesianStruct.prior;
andrew@13 509 projectedPrior = bayesianStruct.prior;
andrew@19 510
andrew@19 511
andrew@19 512 temporal.updateTempo(channel, timeIn);
andrew@11 513 }
andrew@11 514
andrew@3 515 }
andrew@3 516
andrew@3 517
andrew@3 518
andrew@3 519 void AudioEventMatcher::matchNewPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
andrew@3 520 //start at beginning but OPTIMISE later
andrew@10 521 /*printf("TIME %i\n", ofGetElapsedTimeMillis());
andrew@10 522 //tmp debug
andrew@10 523 updateBestAlignmentPosition();
andrew@10 524 printf("current alignment best estimate %f\n", currentAlignmentPosition);
andrew@10 525 */
andrew@6 526 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@8 527
andrew@7 528 //set the lielihoods by matching the pitched note
andrew@7 529
andrew@15 530
andrew@3 531 int numberOfMatches = 0;
andrew@3 532 bayesianStruct.likelihood.zero();//set to zero
andrew@18 533 double newOnsetTime;
andrew@18 534 double closestDistance = INFINITY;
andrew@3 535
andrew@3 536 double quantity = 0;
andrew@32 537 double totalLikelihoodAdded = 0;
andrew@3 538 if (channel <= recordedTracks.numberOfAudioTracks){
andrew@3 539 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
andrew@3 540
andrew@3 541 if (checkMatch(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn)) {
andrew@32 542 quantity = getPitchDistance(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn, 12);
andrew@18 543
andrew@3 544 bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, 30, quantity);
andrew@3 545 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = true;
andrew@3 546 numberOfMatches++;
andrew@32 547 totalLikelihoodAdded += quantity;
andrew@3 548 }
andrew@3 549 else{
andrew@3 550 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = false;
andrew@3 551 }
andrew@18 552 //checking nearest pitch
andrew@18 553 newOnsetTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
andrew@18 554 if (abs(newOnsetTime - currentAlignmentPosition) < closestDistance){
andrew@18 555 closestDistance = abs(newOnsetTime - currentAlignmentPosition);
andrew@18 556 pitchOfNearestMatch = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch;
andrew@18 557 distanceOfNearestMatch = quantity;
andrew@18 558 }
andrew@3 559
andrew@3 560 }
andrew@3 561 }
andrew@6 562
andrew@8 563
andrew@8 564
andrew@37 565 if (numberOfMatches > 0 && totalLikelihoodAdded > 0){//no point updating unless there is a match
andrew@32 566 //replacing numberOfMatches with totalLike below...
andrew@37 567 //bug here was that if totaladded = 0, we add then zero likelihood
andrew@37 568 bayesianStruct.likelihood.addConstant(totalLikelihoodAdded*(1-pitchLikelihoodToNoise)/(bayesianStruct.likelihood.length));
andrew@37 569 // bayesianStruct.likelihood.addConstant(totalLikelihoodAdded*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@4 570
andrew@4 571 //tmp set likelihood constant and calculate using that
andrew@6 572 //bayesianStruct.likelihood.zero();
andrew@6 573 //bayesianStruct.likelihood.addConstant(1);
andrew@7 574
andrew@6 575 bayesianStruct.calculatePosterior();
andrew@11 576 lastAlignmentTime = timeIn;//has to use the STAMPED time
andrew@11 577 recentEventTime[channel] = timeIn;
andrew@11 578
andrew@11 579 recentPriors[channel] = bayesianStruct.prior;
andrew@13 580 projectedPrior = bayesianStruct.prior;
andrew@19 581
andrew@19 582 temporal.eventTimes[channel].push_back(timeIn);
andrew@6 583 }
andrew@4 584
andrew@11 585
andrew@1 586 }
andrew@1 587
andrew@3 588 double AudioEventMatcher::getPitchDistance(const double& pitchOne, const double& pitchTwo, const double& scale){
andrew@3 589
andrew@18 590 double scaleFactor = scale * pitchOne / 110.0;
andrew@16 591
andrew@18 592 int multiplicationFactor = 1;
andrew@18 593 if (pitchTwo > 0){
andrew@32 594 multiplicationFactor = round(pitchOne/pitchTwo);
andrew@18 595 }
andrew@16 596
andrew@18 597 double distance = abs(pitchOne - pitchTwo*multiplicationFactor);
andrew@16 598 if (distance < scaleFactor)
andrew@16 599 distance = 1 - (distance/scaleFactor);
andrew@3 600 else
andrew@3 601 distance = 0;
andrew@3 602
andrew@32 603 //printf("[pitch distance %f vs %f, factor %i = %f\n", pitchOne, pitchTwo, multiplicationFactor, distance);
andrew@3 604 return distance;
andrew@3 605
andrew@3 606 }
andrew@3 607
andrew@3 608
andrew@3 609 bool AudioEventMatcher::checkMatch(const double& recordedPitch, const double& livePitch){
andrew@18 610
andrew@18 611 if (livePitch > 0){
andrew@18 612 int multiplicationFactor = (int)(round(recordedPitch/livePitch));
andrew@18 613
andrew@32 614 if (abs(recordedPitch - livePitch * multiplicationFactor) < pitchCutOff)
andrew@3 615 return true;
andrew@3 616 else
andrew@3 617 return false;
andrew@18 618 }else {
andrew@18 619 return false;
andrew@18 620 }
andrew@18 621
andrew@3 622 }
andrew@3 623
andrew@3 624
andrew@32 625 void AudioEventMatcher::matchNewChromaEvent(const int& channel, float* chromaIn, const double& timeIn){
andrew@32 626 //start at beginning but OPTIMISE later
andrew@32 627
andrew@32 628 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@32 629
andrew@32 630 //set the likelihoods by matching the pitched note
andrew@32 631
andrew@32 632 int numberOfMatches = 0;
andrew@32 633 bayesianStruct.likelihood.zero();//set to zero
andrew@32 634 double newOnsetTime;
andrew@32 635 double closestDistance = INFINITY;
andrew@32 636
andrew@32 637 double quantity = 1;
andrew@32 638 double totalLikelihoodAdded = 0;
andrew@32 639
andrew@32 640 double startMatchingTime = bayesianStruct.likelihood.offset;
andrew@32 641 double endMatchingTime = bayesianStruct.likelihood.offset + matchWindowWidth;
andrew@32 642 double millisTime = -1*INFINITY;//or 0 is fine
andrew@32 643
andrew@32 644 int checkIndex = 0;
andrew@37 645 if (channel <= recordedTracks.numberOfAudioTracks && checkIndex < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size()){
andrew@37 646
andrew@32 647 while (millisTime < startMatchingTime) {
andrew@32 648 millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[checkIndex].millisTime;
andrew@32 649 checkIndex++;
andrew@32 650 }//go up to where we need to check from fast
andrew@32 651
andrew@32 652 for (int i = checkIndex;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size() && millisTime <= endMatchingTime;i++){
andrew@32 653 millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
andrew@32 654
andrew@32 655 if (millisTime >= startMatchingTime && millisTime <= endMatchingTime){
andrew@35 656
andrew@35 657 if (useChromaDotProduct)
andrew@35 658 quantity = getChromaDotProductDistance(chromaIn, &recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].chromaValues[0]);
andrew@35 659 else
andrew@35 660 quantity = getChromaEuclideanDistance(chromaIn, &recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].chromaValues[0]);
andrew@35 661
andrew@35 662
andrew@32 663 bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, chromaLikelihoodWidth, quantity);
andrew@32 664
andrew@32 665 // bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, onsetLikelihoodWidth, quantity);
andrew@32 666 numberOfMatches++;
andrew@32 667 totalLikelihoodAdded += quantity;
andrew@37 668
andrew@37 669 //printf("Adding CHROMA Gaussian for onset at time %.1f dist %.3f\n", millisTime, quantity);
andrew@32 670
andrew@32 671 }//end if within limits (changed so it now is 4 sure)
andrew@32 672 }
andrew@32 673 }
andrew@32 674
andrew@32 675
andrew@37 676 if (numberOfMatches > 0 && totalLikelihoodAdded > 0){//no point updating unless there is a match
andrew@32 677 //replacing numberOfMatches with totalLike below...
andrew@32 678
andrew@32 679 printf("CHROMA HAS %i MATCHES\n", numberOfMatches);
andrew@32 680
andrew@37 681 bayesianStruct.likelihood.addConstant(totalLikelihoodAdded*(1-chromaLikelihoodToNoise)/(bayesianStruct.likelihood.length));
andrew@37 682 //previous way
andrew@37 683 // bayesianStruct.likelihood.addConstant(totalLikelihoodAdded*(1-chromaLikelihoodToNoise)/(chromaLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@32 684
andrew@32 685 bayesianStruct.calculatePosterior();
andrew@32 686 lastAlignmentTime = timeIn;//has to use the STAMPED time
andrew@32 687 recentEventTime[channel] = timeIn;
andrew@32 688
andrew@32 689 recentPriors[channel] = bayesianStruct.prior;
andrew@32 690 projectedPrior = bayesianStruct.prior;
andrew@32 691
andrew@32 692 temporal.eventTimes[channel].push_back(timeIn);
andrew@32 693 }
andrew@32 694
andrew@32 695 }
andrew@32 696
andrew@32 697
andrew@35 698 double AudioEventMatcher::getChromaDotProductDistance(float* chromaOne, float* chromaTwo){
andrew@32 699 double distance = 0;
andrew@32 700 double total = 0;
andrew@32 701 for (int i = 0;i < 12;i++){
andrew@32 702 distance += chromaOne[i]*chromaTwo[i];
andrew@32 703 total += chromaOne[i]*chromaOne[i] + (chromaTwo[i]*chromaTwo[i]);
andrew@32 704 }
andrew@32 705
andrew@35 706 if (total > 0)
andrew@35 707 distance /= sqrt(total);
andrew@35 708
andrew@35 709 return distance;
andrew@35 710 }
andrew@35 711
andrew@35 712 double AudioEventMatcher::getChromaEuclideanDistance(float* chromaOne, float* chromaTwo){
andrew@35 713 double distance = 0;
andrew@35 714 double total = 0;
andrew@37 715
andrew@35 716 // printf("\n");
andrew@35 717 for (int i = 0;i < 12;i++){
andrew@35 718 total += (chromaOne[i] - chromaTwo[i])*(chromaOne[i] - chromaTwo[i]);
andrew@35 719 // printf("chroma1: %.2f; chroma2: %.2f\n", chromaOne[i], chromaTwo[i]);
andrew@35 720 // total += chromaOne[i]*chromaOne[i] + (chromaTwo[i]*chromaTwo[i]);
andrew@35 721 }
andrew@35 722
andrew@37 723 if (total > euclideanMaximumDistance)
andrew@37 724 euclideanMaximumDistance = total;
andrew@37 725
andrew@37 726 distance = ((euclideanMaximumDistance - total)/ euclideanMaximumDistance);//i.e. 1 is
andrew@37 727
andrew@37 728 // if (total > 0)
andrew@37 729
andrew@37 730
andrew@37 731 // distance = 1.0/sqrt(total);
andrew@35 732 // printf("DISTANCE : %.3f\n", distance);
andrew@32 733 return distance;
andrew@32 734 }
andrew@1 735
andrew@1 736 void AudioEventMatcher::windowResized(const int& w, const int& h){
andrew@1 737 recordedTracks.windowResized(w,h);
andrew@3 738 bayesTempoWindow.resized(w,h);
andrew@3 739 bayesPositionWindow.resized(w,h);
andrew@3 740 }
andrew@3 741
andrew@10 742 /*
andrew@10 743
andrew@10 744 void printPosteriorMAPinfo(){ //tmp print stuff
andrew@10 745 printf("New pitch MAP post estimate now %i, ", bayesianStruct.posterior.MAPestimate);
andrew@10 746 double tmp = bayesianStruct.posterior.getMAPestimate();
andrew@10 747 printf(" getting it %f and offset %f == %f ms\n", tmp, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(tmp));
andrew@10 748
andrew@10 749 }
andrew@10 750 */
andrew@3 751