annotate src/AudioEventMatcher.cpp @ 12:446121327276

adding max patches
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Sun, 05 Feb 2012 00:01:46 +0000
parents 9a2b008c4706
children 66783ace7506
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@2 13 const int matchWindowWidth = 6000;
andrew@0 14
andrew@0 15 AudioEventMatcher::AudioEventMatcher(){
andrew@7 16
andrew@0 17 setArraySizes();
andrew@3 18
andrew@3 19 usingRealTime = false;
andrew@3 20 bayesianStruct.realTimeMode = &usingRealTime;
andrew@7 21 recentPitch = 0;
andrew@8 22 currentAlignmentPosition = 0;
andrew@9 23
andrew@9 24 followingLiveInput = true;
andrew@0 25 }
andrew@0 26
andrew@7 27 void AudioEventMatcher::setWindowDimensions(){
andrew@7 28 double startHeight = recordedTracks.numberOfAudioTracks * recordedTracks.trackScreenHeight;
andrew@7 29 double heightAvailable = 1 - startHeight;
andrew@7 30 heightAvailable /= 3.0;
andrew@7 31
andrew@7 32 bayesPositionWindow.setToRelativeSize(0, startHeight, 1, heightAvailable);
andrew@7 33 bayesLikelihoodWindow.setToRelativeSize(0, startHeight + 1*heightAvailable, 1, heightAvailable);
andrew@7 34 bayesTempoWindow.setToRelativeSize(0, startHeight + 2*heightAvailable, 1, heightAvailable);
andrew@7 35
andrew@7 36
andrew@7 37 }
andrew@0 38
andrew@0 39 void AudioEventMatcher::setArraySizes(){
andrew@0 40 bayesianStruct.resetSpeedSize(200);
andrew@0 41 bayesianStruct.setRelativeSpeedScalar(0.01);
andrew@0 42 bayesianStruct.setSpeedPrior(1.0);
andrew@0 43 bayesianStruct.relativeSpeedPrior.getMaximum();
andrew@0 44
andrew@0 45 bayesianStruct.resetSize(matchWindowWidth);
andrew@0 46 bayesianStruct.setPositionDistributionScalar(1);
andrew@0 47
andrew@0 48 }
andrew@0 49
andrew@9 50 void AudioEventMatcher::startPlaying(){
andrew@3 51 bayesianStruct.setStartPlaying();
andrew@8 52 currentAlignmentPosition = 0;
andrew@8 53 startTime = ofGetElapsedTimeMillis();
andrew@11 54
andrew@11 55 projectedPrior = bayesianStruct.prior;
andrew@3 56 //bayesianStruct.posterior.printArray();
andrew@3 57 }
andrew@3 58
andrew@9 59
andrew@9 60 void AudioEventMatcher::updatePosition(){
andrew@9 61 if (!followingLiveInput)
andrew@9 62 recordedTracks.updatePosition();
andrew@9 63 else
andrew@9 64 recordedTracks.updatePositionToMillis(currentAlignmentPosition);
andrew@9 65
andrew@9 66 updateBestAlignmentPosition();
andrew@9 67 }
andrew@9 68
andrew@8 69 void AudioEventMatcher::updateBestAlignmentPosition(){
andrew@10 70 //THIS DEALS WITH WHERE WE ARE NOW! ON THE SCREEN
andrew@10 71 //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 72
andrew@10 73 int newTime = ofGetElapsedTimeMillis() - startTime;
andrew@10 74 // double tmp = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);;
andrew@10 75 // double timetmp = (newTime - lastAlignmentTime);
andrew@10 76 // double speedtmp = bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
andrew@11 77 // currentAlignmentTime = newTime;
andrew@9 78 currentAlignmentPosition = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);
andrew@10 79 currentAlignmentPosition += (newTime - lastAlignmentTime) * bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
andrew@10 80
andrew@11 81 bayesianStruct.projectDistribution(newTime, currentAlignmentPosition, projectedPrior);//prior gets updated to where we are now
andrew@11 82
andrew@10 83 // 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 84 }
andrew@8 85
andrew@0 86 void AudioEventMatcher::draw(){
andrew@6 87 //draw some outlines in blue
andrew@3 88 ofSetColor(20,200,200);
andrew@3 89 bayesPositionWindow.drawOutline();
andrew@3 90 bayesTempoWindow.drawOutline();
andrew@0 91
andrew@6 92 //draw the scrolling audio tracks
andrew@1 93 recordedTracks.drawTracks();
andrew@7 94
andrew@2 95 ofSetColor(255);
andrew@2 96 // bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
andrew@9 97
andrew@9 98 setScreenDisplayTimes();
andrew@6 99 drawBayesianDistributions();
andrew@8 100
andrew@11 101 //bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
andrew@6 102 //bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
andrew@11 103 //bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
andrew@6 104
andrew@7 105 ofDrawBitmapString("pitch "+ofToString(recentPitch, 2)+", Time "+ofToString(recentTime, 0), 20, 20);
andrew@9 106
andrew@9 107 ofDrawBitmapString("pos "+ofToString(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.playPosition), 200,600);
andrew@6 108 }
andrew@6 109
andrew@9 110 void AudioEventMatcher::setScreenDisplayTimes(){
andrew@9 111 screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
andrew@9 112 // if (!followingLiveInput){
andrew@9 113
andrew@9 114 screenStartTimeMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.drawParams.windowStartFrame);
andrew@9 115 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
andrew@9 116
andrew@9 117 //need PRECISION in this alignment
andrew@9 118
andrew@9 119
andrew@9 120 /*}else{
andrew@9 121
andrew@9 122 screenStartTimeMillis = (int)(currentAlignmentPosition/screenWidthMillis) * screenWidthMillis;
andrew@9 123 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
andrew@9 124 }*/
andrew@9 125 }
andrew@9 126
andrew@6 127 void AudioEventMatcher::drawBayesianDistributions(){
andrew@6 128
andrew@6 129
andrew@6 130 int startIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenStartTimeMillis);
andrew@6 131 int endIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenEndTimeMillis);
andrew@4 132
andrew@6 133 bayesianStruct.posterior.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesPositionWindow);
andrew@6 134
andrew@6 135 string tmpString = "start "+ofToString(screenStartTimeMillis)+" (index "+ofToString(startIndex)+"), end "+ofToString(screenEndTimeMillis);
andrew@6 136 ofDrawBitmapString(tmpString, bayesPositionWindow.x+20, bayesPositionWindow.y+20);
andrew@4 137
andrew@8 138 // bayesianStruct.likelihood.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesLikelihoodWindow);
andrew@2 139
andrew@6 140 bayesianStruct.relativeSpeedPosterior.drawConstrainedVector(0, bayesianStruct.relativeSpeedPosterior.arraySize, 0, ofGetWidth(), bayesTempoWindow);
andrew@6 141
andrew@3 142 string tmpStr = "zero is "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(0));
andrew@3 143 tmpStr += " offsetis "+ofToString(bayesianStruct.posterior.offset);
andrew@3 144 tmpStr += " screenWidth = "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis));
andrew@3 145 ofDrawBitmapString(tmpStr, 20,140);
andrew@3 146 tmpStr = "best est "+ofToString(bayesianStruct.bestEstimate);
andrew@3 147 ofDrawBitmapString(tmpStr, 20, 180);
andrew@3 148
andrew@8 149 ofDrawBitmapString("screenwidth "+ofToString(screenWidthMillis), 20, 800);
andrew@3 150
andrew@9 151 //green line at current best estimate
andrew@8 152 ofSetColor(0,255,0);
andrew@8 153 double currentEstimateIndex = (currentAlignmentPosition - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
andrew@8 154 ofLine(currentEstimateIndex, bayesPositionWindow.y, currentEstimateIndex, bayesPositionWindow.y + bayesPositionWindow.height);
andrew@7 155
andrew@7 156 //draw track by track likelihoods
andrew@7 157 for (int i = 0; i <recordedTracks.numberOfAudioTracks;i++){
andrew@7 158 ofSetColor(200,255,50);
andrew@8 159 likelihoodVisualisation[i].drawConstrainedVector(likelihoodVisualisation[i].getRealTermsAsIndex(screenStartTimeMillis), likelihoodVisualisation[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
andrew@11 160
andrew@11 161 ofSetColor(0,255,150);
andrew@11 162 recentPriors[i].drawConstrainedVector(recentPriors[i].getRealTermsAsIndex(screenStartTimeMillis), recentPriors[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
andrew@11 163
andrew@11 164
andrew@8 165 ofSetColor(255);
andrew@8 166 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 167 }
andrew@8 168
andrew@8 169 int priorStartIndex = recentPrior.getRealTermsAsIndex(screenStartTimeMillis);
andrew@8 170 int priorEndIndex = recentPrior.getRealTermsAsIndex(screenEndTimeMillis);
andrew@10 171 ofSetColor(0,200,200);//recent prior
andrew@8 172 recentPrior.drawConstrainedVector(priorStartIndex, priorEndIndex, 0, ofGetWidth(), bayesPositionWindow);
andrew@8 173
andrew@10 174 ofSetColor(255,0,100);//purple prior
andrew@11 175 bayesianStruct.prior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesPositionWindow);
andrew@11 176
andrew@11 177 ofSetColor(255,0,0);
andrew@11 178 projectedPrior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesLikelihoodWindow);
andrew@7 179
andrew@1 180 }
andrew@1 181
andrew@6 182 void AudioEventMatcher::newPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
andrew@7 183 if (pitchIn > 0){
andrew@1 184 liveInput.addPitchEvent(pitchIn, timeIn);
andrew@4 185
andrew@10 186 //printPosteriorMAPinfo();
andrew@11 187
andrew@7 188 matchNewPitchEvent(channel, pitchIn, timeIn);//main pitch matching fn
andrew@7 189
andrew@7 190 likelihoodVisualisation[1] = bayesianStruct.likelihood;
andrew@7 191
andrew@7 192 recentPitch = pitchIn;//for drawing
andrew@7 193 recentTime = timeIn;
andrew@7 194 }
andrew@8 195
andrew@2 196 }
andrew@2 197
andrew@6 198 void AudioEventMatcher::newKickEvent(const double& timeIn){
andrew@6 199 // liveInput.addKickEvent(timeIn);
andrew@2 200 matchNewOnsetEvent(0, timeIn);
andrew@7 201 likelihoodVisualisation[0] = bayesianStruct.likelihood;
andrew@2 202 }
andrew@2 203
andrew@6 204 void AudioEventMatcher::newKickEvent(const int& channel, const double& timeIn){
andrew@6 205 // liveInput.addKickEvent(timeIn);
andrew@6 206 matchNewOnsetEvent(channel, timeIn);
andrew@7 207 likelihoodVisualisation[0] = bayesianStruct.likelihood;
andrew@6 208 }
andrew@6 209
andrew@2 210
andrew@2 211 void AudioEventMatcher::newSnareEvent(const double& timeIn){
andrew@6 212 matchNewOnsetEvent(2, timeIn);
andrew@7 213 likelihoodVisualisation[2] = bayesianStruct.likelihood;
andrew@7 214 }
andrew@7 215
andrew@7 216
andrew@7 217 void AudioEventMatcher::newSnareEvent(const int& channel, const double& timeIn){
andrew@7 218 matchNewOnsetEvent(channel, timeIn);
andrew@7 219 likelihoodVisualisation[2] = bayesianStruct.likelihood;
andrew@2 220 }
andrew@2 221
andrew@2 222 //Needs just to set bounds for the matching process, not have TimeIn
andrew@2 223 void AudioEventMatcher::matchNewOnsetEvent(const int& channel, const double& timeIn){
andrew@3 224
andrew@6 225 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@10 226
andrew@2 227 //start at beginning but OPTIMISE later
andrew@9 228 double onsetLikelihoodToNoise = 0.3;
andrew@2 229
andrew@2 230 double likelihoodWidth = 40;
andrew@2 231
andrew@2 232 bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
andrew@2 233 bayesianStruct.likelihood.zero();//set to zero
andrew@2 234
andrew@2 235 double quantity = 1;//likelihoodToNoiseRatio / numberOfMatches;
andrew@2 236 int numberOfMatchesFound = 0;
andrew@2 237
andrew@2 238
andrew@10 239 double startMatchingTime = bayesianStruct.likelihood.offset;
andrew@10 240 double endMatchingTime = bayesianStruct.likelihood.offset + matchWindowWidth;
andrew@2 241
andrew@2 242 if (channel <= recordedTracks.numberOfAudioTracks){
andrew@2 243 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
andrew@2 244 double millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
andrew@10 245 if (millisTime >= startMatchingTime && millisTime <= endMatchingTime){
andrew@2 246 bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, likelihoodWidth, quantity);
andrew@2 247 numberOfMatchesFound++;
andrew@6 248 // printf("Adding Gaussian for onset at time %f offset %f\n", millisTime, bayesianStruct.likelihood.offset);
andrew@2 249
andrew@2 250 }
andrew@2 251 }
andrew@2 252 }
andrew@2 253
andrew@11 254 if (numberOfMatchesFound > 0){
andrew@3 255 // bayesianStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesianStruct.likelihood.length);
andrew@3 256 bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(onsetLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@2 257 bayesianStruct.likelihood.renormalise();
andrew@2 258
andrew@8 259 bayesianStruct.calculatePosterior();
andrew@10 260 lastAlignmentTime = timeIn;//use TIMESTAMP
andrew@10 261 recentEventTime[channel] = timeIn;//ofGetElapsedTimeMillis() - startTime;
andrew@11 262
andrew@11 263 recentPriors[channel] = bayesianStruct.prior;
andrew@11 264
andrew@11 265 }
andrew@11 266
andrew@11 267
andrew@6 268
andrew@3 269 }
andrew@3 270
andrew@3 271
andrew@3 272
andrew@3 273 void AudioEventMatcher::matchNewPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
andrew@3 274 //start at beginning but OPTIMISE later
andrew@10 275 /*printf("TIME %i\n", ofGetElapsedTimeMillis());
andrew@10 276 //tmp debug
andrew@10 277 updateBestAlignmentPosition();
andrew@10 278 printf("current alignment best estimate %f\n", currentAlignmentPosition);
andrew@10 279 */
andrew@6 280 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@8 281
andrew@7 282 //set the lielihoods by matching the pitched note
andrew@7 283
andrew@9 284 double pitchLikelihoodToNoise = 0.7;//more noise
andrew@3 285 int numberOfMatches = 0;
andrew@3 286 bayesianStruct.likelihood.zero();//set to zero
andrew@3 287
andrew@3 288 double quantity = 0;
andrew@3 289 if (channel <= recordedTracks.numberOfAudioTracks){
andrew@3 290 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
andrew@3 291
andrew@3 292 if (checkMatch(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn)) {
andrew@7 293 quantity = getPitchDistance(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn, 10);
andrew@3 294 bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, 30, quantity);
andrew@3 295 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = true;
andrew@3 296 numberOfMatches++;
andrew@3 297 }
andrew@3 298 else{
andrew@3 299 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = false;
andrew@3 300 }
andrew@3 301
andrew@3 302 }
andrew@3 303 }
andrew@6 304
andrew@8 305 recentPrior = bayesianStruct.prior;
andrew@8 306
andrew@8 307
andrew@6 308 if (numberOfMatches > 0){//no point updating unless there is a match
andrew@7 309
andrew@6 310 bayesianStruct.likelihood.addConstant(numberOfMatches*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@4 311
andrew@4 312 //tmp set likelihood constant and calculate using that
andrew@6 313 //bayesianStruct.likelihood.zero();
andrew@6 314 //bayesianStruct.likelihood.addConstant(1);
andrew@7 315
andrew@6 316 bayesianStruct.calculatePosterior();
andrew@11 317 lastAlignmentTime = timeIn;//has to use the STAMPED time
andrew@11 318 recentEventTime[channel] = timeIn;
andrew@11 319
andrew@11 320 recentPriors[channel] = bayesianStruct.prior;
andrew@6 321 }
andrew@4 322
andrew@11 323
andrew@1 324 }
andrew@1 325
andrew@3 326 double AudioEventMatcher::getPitchDistance(const double& pitchOne, const double& pitchTwo, const double& scale){
andrew@3 327
andrew@3 328 double distance = abs(pitchOne - pitchTwo);
andrew@3 329 if (distance < scale)
andrew@3 330 distance = 1 - (distance/scale);
andrew@3 331 else
andrew@3 332 distance = 0;
andrew@3 333
andrew@3 334 // printf("[pitch distance %f vs %f = %f\n", pitchOne, pitchTwo, distance);
andrew@3 335 return distance;
andrew@3 336
andrew@3 337 }
andrew@3 338
andrew@3 339
andrew@3 340 bool AudioEventMatcher::checkMatch(const double& recordedPitch, const double& livePitch){
andrew@3 341 if (abs(recordedPitch - livePitch) < 40)
andrew@3 342 return true;
andrew@3 343 else
andrew@3 344 return false;
andrew@3 345 }
andrew@3 346
andrew@3 347
andrew@1 348
andrew@1 349 void AudioEventMatcher::windowResized(const int& w, const int& h){
andrew@1 350 recordedTracks.windowResized(w,h);
andrew@3 351 bayesTempoWindow.resized(w,h);
andrew@3 352 bayesPositionWindow.resized(w,h);
andrew@3 353 }
andrew@3 354
andrew@10 355 /*
andrew@10 356
andrew@10 357 void printPosteriorMAPinfo(){ //tmp print stuff
andrew@10 358 printf("New pitch MAP post estimate now %i, ", bayesianStruct.posterior.MAPestimate);
andrew@10 359 double tmp = bayesianStruct.posterior.getMAPestimate();
andrew@10 360 printf(" getting it %f and offset %f == %f ms\n", tmp, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(tmp));
andrew@10 361
andrew@10 362 }
andrew@10 363 */
andrew@3 364