annotate src/AudioEventMatcher.cpp @ 9:bc62266af280

Position scrolls with live input by updating the play position of onset detection to where the current best match is
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Fri, 03 Feb 2012 17:53:14 +0000
parents 572564b7cb85
children cbadb9d05d29
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@3 54 //bayesianStruct.posterior.printArray();
andrew@3 55 }
andrew@3 56
andrew@9 57
andrew@9 58 void AudioEventMatcher::updatePosition(){
andrew@9 59 if (!followingLiveInput)
andrew@9 60 recordedTracks.updatePosition();
andrew@9 61 else
andrew@9 62 recordedTracks.updatePositionToMillis(currentAlignmentPosition);
andrew@9 63
andrew@9 64 updateBestAlignmentPosition();
andrew@9 65 }
andrew@9 66
andrew@8 67 void AudioEventMatcher::updateBestAlignmentPosition(){
andrew@9 68 currentAlignmentPosition = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);
andrew@8 69 currentAlignmentPosition += (ofGetElapsedTimeMillis() - lastAlignmentTime) * bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
andrew@8 70 }
andrew@8 71
andrew@0 72 void AudioEventMatcher::draw(){
andrew@6 73 //draw some outlines in blue
andrew@3 74 ofSetColor(20,200,200);
andrew@3 75 bayesPositionWindow.drawOutline();
andrew@3 76 bayesTempoWindow.drawOutline();
andrew@0 77
andrew@6 78 //draw the scrolling audio tracks
andrew@1 79 recordedTracks.drawTracks();
andrew@7 80
andrew@7 81
andrew@2 82
andrew@2 83 ofSetColor(255);
andrew@2 84 // bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
andrew@9 85
andrew@9 86 setScreenDisplayTimes();
andrew@6 87 drawBayesianDistributions();
andrew@8 88
andrew@6 89 // bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
andrew@6 90
andrew@6 91 //bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
andrew@6 92
andrew@6 93 // bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
andrew@6 94
andrew@7 95 ofDrawBitmapString("pitch "+ofToString(recentPitch, 2)+", Time "+ofToString(recentTime, 0), 20, 20);
andrew@9 96
andrew@9 97 ofDrawBitmapString("pos "+ofToString(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.playPosition), 200,600);
andrew@6 98 }
andrew@6 99
andrew@9 100 void AudioEventMatcher::setScreenDisplayTimes(){
andrew@9 101 screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
andrew@9 102 // if (!followingLiveInput){
andrew@9 103
andrew@9 104 screenStartTimeMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.drawParams.windowStartFrame);
andrew@9 105 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
andrew@9 106
andrew@9 107 //need PRECISION in this alignment
andrew@9 108
andrew@9 109
andrew@9 110 /*}else{
andrew@9 111
andrew@9 112 screenStartTimeMillis = (int)(currentAlignmentPosition/screenWidthMillis) * screenWidthMillis;
andrew@9 113 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
andrew@9 114 }*/
andrew@9 115 }
andrew@9 116
andrew@6 117 void AudioEventMatcher::drawBayesianDistributions(){
andrew@6 118
andrew@6 119
andrew@6 120 int startIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenStartTimeMillis);
andrew@6 121 int endIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenEndTimeMillis);
andrew@4 122
andrew@6 123 bayesianStruct.posterior.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesPositionWindow);
andrew@6 124
andrew@6 125 string tmpString = "start "+ofToString(screenStartTimeMillis)+" (index "+ofToString(startIndex)+"), end "+ofToString(screenEndTimeMillis);
andrew@6 126 ofDrawBitmapString(tmpString, bayesPositionWindow.x+20, bayesPositionWindow.y+20);
andrew@4 127
andrew@8 128 // bayesianStruct.likelihood.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesLikelihoodWindow);
andrew@2 129
andrew@6 130 bayesianStruct.relativeSpeedPosterior.drawConstrainedVector(0, bayesianStruct.relativeSpeedPosterior.arraySize, 0, ofGetWidth(), bayesTempoWindow);
andrew@6 131
andrew@3 132 string tmpStr = "zero is "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(0));
andrew@3 133 tmpStr += " offsetis "+ofToString(bayesianStruct.posterior.offset);
andrew@3 134 tmpStr += " screenWidth = "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis));
andrew@3 135 ofDrawBitmapString(tmpStr, 20,140);
andrew@3 136 tmpStr = "best est "+ofToString(bayesianStruct.bestEstimate);
andrew@3 137 ofDrawBitmapString(tmpStr, 20, 180);
andrew@3 138
andrew@8 139 ofDrawBitmapString("screenwidth "+ofToString(screenWidthMillis), 20, 800);
andrew@3 140
andrew@9 141 //green line at current best estimate
andrew@8 142 ofSetColor(0,255,0);
andrew@8 143 double currentEstimateIndex = (currentAlignmentPosition - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
andrew@8 144 ofLine(currentEstimateIndex, bayesPositionWindow.y, currentEstimateIndex, bayesPositionWindow.y + bayesPositionWindow.height);
andrew@7 145
andrew@7 146 //draw track by track likelihoods
andrew@7 147 for (int i = 0; i <recordedTracks.numberOfAudioTracks;i++){
andrew@7 148 ofSetColor(200,255,50);
andrew@8 149 likelihoodVisualisation[i].drawConstrainedVector(likelihoodVisualisation[i].getRealTermsAsIndex(screenStartTimeMillis), likelihoodVisualisation[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
andrew@8 150 ofSetColor(255);
andrew@8 151 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 152 }
andrew@8 153
andrew@8 154 int priorStartIndex = recentPrior.getRealTermsAsIndex(screenStartTimeMillis);
andrew@8 155 int priorEndIndex = recentPrior.getRealTermsAsIndex(screenEndTimeMillis);
andrew@8 156 ofSetColor(0,200,200);
andrew@8 157 recentPrior.drawConstrainedVector(priorStartIndex, priorEndIndex, 0, ofGetWidth(), bayesPositionWindow);
andrew@8 158
andrew@8 159 // bayesianStruct.prior.addTriangularShape(100, 20, 0.4);
andrew@8 160 ofSetColor(255,0,100);
andrew@8 161 bayesianStruct.prior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesLikelihoodWindow);
andrew@7 162
andrew@1 163 }
andrew@1 164
andrew@6 165 void AudioEventMatcher::newPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
andrew@7 166 if (pitchIn > 0){
andrew@1 167 liveInput.addPitchEvent(pitchIn, timeIn);
andrew@4 168
andrew@4 169 //tmp print stuff
andrew@4 170 printf("New pitch MAP post estimate now %i, ", bayesianStruct.posterior.MAPestimate);
andrew@4 171 double tmp = bayesianStruct.posterior.getMAPestimate();
andrew@4 172 printf(" getting it %f and offset %f == %f ms\n", tmp, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(tmp));
andrew@4 173
andrew@7 174 matchNewPitchEvent(channel, pitchIn, timeIn);//main pitch matching fn
andrew@7 175
andrew@7 176 likelihoodVisualisation[1] = bayesianStruct.likelihood;
andrew@7 177
andrew@7 178 recentPitch = pitchIn;//for drawing
andrew@7 179 recentTime = timeIn;
andrew@7 180 }
andrew@8 181
andrew@8 182
andrew@2 183 }
andrew@2 184
andrew@6 185 void AudioEventMatcher::newKickEvent(const double& timeIn){
andrew@6 186 // liveInput.addKickEvent(timeIn);
andrew@2 187 matchNewOnsetEvent(0, timeIn);
andrew@7 188 likelihoodVisualisation[0] = bayesianStruct.likelihood;
andrew@2 189 }
andrew@2 190
andrew@6 191 void AudioEventMatcher::newKickEvent(const int& channel, const double& timeIn){
andrew@6 192 // liveInput.addKickEvent(timeIn);
andrew@6 193 matchNewOnsetEvent(channel, timeIn);
andrew@7 194 likelihoodVisualisation[0] = bayesianStruct.likelihood;
andrew@6 195 }
andrew@6 196
andrew@2 197
andrew@2 198 void AudioEventMatcher::newSnareEvent(const double& timeIn){
andrew@6 199 matchNewOnsetEvent(2, timeIn);
andrew@7 200 likelihoodVisualisation[2] = bayesianStruct.likelihood;
andrew@7 201 }
andrew@7 202
andrew@7 203
andrew@7 204 void AudioEventMatcher::newSnareEvent(const int& channel, const double& timeIn){
andrew@7 205 matchNewOnsetEvent(channel, timeIn);
andrew@7 206 likelihoodVisualisation[2] = bayesianStruct.likelihood;
andrew@2 207 }
andrew@2 208
andrew@2 209 //Needs just to set bounds for the matching process, not have TimeIn
andrew@2 210 void AudioEventMatcher::matchNewOnsetEvent(const int& channel, const double& timeIn){
andrew@3 211
andrew@3 212
andrew@6 213 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@6 214
andrew@2 215 //start at beginning but OPTIMISE later
andrew@9 216 double onsetLikelihoodToNoise = 0.3;
andrew@2 217
andrew@2 218 double likelihoodWidth = 40;
andrew@2 219
andrew@2 220 bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
andrew@2 221 bayesianStruct.likelihood.zero();//set to zero
andrew@2 222
andrew@2 223 double quantity = 1;//likelihoodToNoiseRatio / numberOfMatches;
andrew@2 224 int numberOfMatchesFound = 0;
andrew@2 225
andrew@2 226
andrew@2 227 double startTime = bayesianStruct.likelihood.offset;
andrew@2 228 double endTime = bayesianStruct.likelihood.offset + matchWindowWidth;
andrew@2 229
andrew@2 230 if (channel <= recordedTracks.numberOfAudioTracks){
andrew@2 231 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
andrew@2 232 double millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
andrew@2 233 if (millisTime >= startTime && millisTime <= endTime){
andrew@2 234 bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, likelihoodWidth, quantity);
andrew@2 235 numberOfMatchesFound++;
andrew@6 236 // printf("Adding Gaussian for onset at time %f offset %f\n", millisTime, bayesianStruct.likelihood.offset);
andrew@2 237
andrew@2 238 }
andrew@2 239 }
andrew@2 240 }
andrew@2 241
andrew@3 242 // bayesianStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesianStruct.likelihood.length);
andrew@3 243 bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(onsetLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@2 244 bayesianStruct.likelihood.renormalise();
andrew@2 245
andrew@8 246 bayesianStruct.calculatePosterior();
andrew@8 247
andrew@8 248 lastAlignmentTime = ofGetElapsedTimeMillis();
andrew@8 249 recentEventTime[channel] = ofGetElapsedTimeMillis() - startTime;
andrew@6 250
andrew@3 251 }
andrew@3 252
andrew@3 253
andrew@3 254
andrew@3 255 void AudioEventMatcher::matchNewPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
andrew@3 256 //start at beginning but OPTIMISE later
andrew@2 257
andrew@2 258
andrew@6 259 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
andrew@8 260
andrew@7 261 //set the lielihoods by matching the pitched note
andrew@7 262
andrew@9 263 double pitchLikelihoodToNoise = 0.7;//more noise
andrew@3 264 int numberOfMatches = 0;
andrew@3 265 bayesianStruct.likelihood.zero();//set to zero
andrew@3 266
andrew@3 267 double quantity = 0;
andrew@3 268 if (channel <= recordedTracks.numberOfAudioTracks){
andrew@3 269 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
andrew@3 270
andrew@3 271 if (checkMatch(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn)) {
andrew@7 272 quantity = getPitchDistance(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn, 10);
andrew@3 273 bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, 30, quantity);
andrew@3 274 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = true;
andrew@3 275 numberOfMatches++;
andrew@3 276 }
andrew@3 277 else{
andrew@3 278 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = false;
andrew@3 279 }
andrew@3 280
andrew@3 281 }
andrew@3 282 }
andrew@6 283
andrew@8 284 recentPrior = bayesianStruct.prior;
andrew@8 285
andrew@8 286
andrew@6 287 if (numberOfMatches > 0){//no point updating unless there is a match
andrew@7 288
andrew@6 289 bayesianStruct.likelihood.addConstant(numberOfMatches*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));
andrew@4 290
andrew@4 291 //tmp set likelihood constant and calculate using that
andrew@6 292 //bayesianStruct.likelihood.zero();
andrew@6 293 //bayesianStruct.likelihood.addConstant(1);
andrew@7 294
andrew@6 295 bayesianStruct.calculatePosterior();
andrew@6 296 }
andrew@4 297
andrew@8 298 lastAlignmentTime = ofGetElapsedTimeMillis();
andrew@8 299 recentEventTime[channel] = ofGetElapsedTimeMillis() - startTime;
andrew@1 300 }
andrew@1 301
andrew@3 302 double AudioEventMatcher::getPitchDistance(const double& pitchOne, const double& pitchTwo, const double& scale){
andrew@3 303
andrew@3 304 double distance = abs(pitchOne - pitchTwo);
andrew@3 305 if (distance < scale)
andrew@3 306 distance = 1 - (distance/scale);
andrew@3 307 else
andrew@3 308 distance = 0;
andrew@3 309
andrew@3 310 // printf("[pitch distance %f vs %f = %f\n", pitchOne, pitchTwo, distance);
andrew@3 311 return distance;
andrew@3 312
andrew@3 313 }
andrew@3 314
andrew@3 315
andrew@3 316 bool AudioEventMatcher::checkMatch(const double& recordedPitch, const double& livePitch){
andrew@3 317 if (abs(recordedPitch - livePitch) < 40)
andrew@3 318 return true;
andrew@3 319 else
andrew@3 320 return false;
andrew@3 321 }
andrew@3 322
andrew@3 323
andrew@1 324
andrew@1 325 void AudioEventMatcher::windowResized(const int& w, const int& h){
andrew@1 326 recordedTracks.windowResized(w,h);
andrew@3 327 bayesTempoWindow.resized(w,h);
andrew@3 328 bayesPositionWindow.resized(w,h);
andrew@3 329 }
andrew@3 330
andrew@3 331
andrew@3 332