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@15
|
17
|
andrew@17
|
18 pitchLikelihoodToNoise = 0.7;//more noise
|
andrew@16
|
19
|
andrew@17
|
20 onsetLikelihoodToNoise = 0.5;
|
andrew@17
|
21 onsetLikelihoodWidth = 10;//in ms
|
andrew@15
|
22
|
andrew@0
|
23 setArraySizes();
|
andrew@3
|
24
|
andrew@3
|
25 usingRealTime = false;
|
andrew@3
|
26 bayesianStruct.realTimeMode = &usingRealTime;
|
andrew@7
|
27 recentPitch = 0;
|
andrew@8
|
28 currentAlignmentPosition = 0;
|
andrew@14
|
29
|
andrew@15
|
30
|
andrew@9
|
31
|
andrew@9
|
32 followingLiveInput = true;
|
andrew@15
|
33 startedPlaying = false;
|
andrew@20
|
34 recordedTempoIndex = 0;
|
andrew@20
|
35 // temporal.setUpEventTimeMatrix();
|
andrew@20
|
36 // recordedTempoData.setUpEventTimeMatrix();
|
andrew@0
|
37 }
|
andrew@0
|
38
|
andrew@14
|
39
|
andrew@19
|
40
|
andrew@19
|
41
|
andrew@7
|
42 void AudioEventMatcher::setWindowDimensions(){
|
andrew@7
|
43 double startHeight = recordedTracks.numberOfAudioTracks * recordedTracks.trackScreenHeight;
|
andrew@7
|
44 double heightAvailable = 1 - startHeight;
|
andrew@19
|
45 heightAvailable /= NUMBER_OF_CHANNELS;
|
andrew@7
|
46
|
andrew@7
|
47 bayesPositionWindow.setToRelativeSize(0, startHeight, 1, heightAvailable);
|
andrew@7
|
48 bayesLikelihoodWindow.setToRelativeSize(0, startHeight + 1*heightAvailable, 1, heightAvailable);
|
andrew@7
|
49 bayesTempoWindow.setToRelativeSize(0, startHeight + 2*heightAvailable, 1, heightAvailable);
|
andrew@7
|
50
|
andrew@7
|
51
|
andrew@7
|
52 }
|
andrew@0
|
53
|
andrew@0
|
54 void AudioEventMatcher::setArraySizes(){
|
andrew@0
|
55 bayesianStruct.resetSpeedSize(200);
|
andrew@0
|
56 bayesianStruct.setRelativeSpeedScalar(0.01);
|
andrew@0
|
57 bayesianStruct.setSpeedPrior(1.0);
|
andrew@0
|
58 bayesianStruct.relativeSpeedPrior.getMaximum();
|
andrew@0
|
59
|
andrew@0
|
60 bayesianStruct.resetSize(matchWindowWidth);
|
andrew@0
|
61 bayesianStruct.setPositionDistributionScalar(1);
|
andrew@0
|
62
|
andrew@0
|
63 }
|
andrew@0
|
64
|
andrew@16
|
65 void AudioEventMatcher::loadAudioFiles(){
|
andrew@16
|
66 recordedTracks.loadTestAudio();
|
andrew@16
|
67 synchroniser.fileLengthSamples = recordedTracks.loadedAudioFiles[0].fileLoader.totalNumberOfSamples;
|
andrew@16
|
68 printf("synchroniser has %f samples\n", synchroniser.fileLengthSamples);
|
andrew@20
|
69
|
andrew@20
|
70 calculateRecordedTempoData();
|
andrew@20
|
71 printf("\n\nFIRST PASS: FINAL recorded tempo is %f\n", recordedTempoData.playingTempo);
|
andrew@20
|
72 setTempoPrior(recordedTempoData.playingTempo);
|
andrew@20
|
73 calculateRecordedTempoData();//now calculate again using better prior
|
andrew@20
|
74
|
andrew@20
|
75 printf("\n\nSECOND PASS: FINAL recorded tempo is %f\n", recordedTempoData.playingTempo);
|
andrew@20
|
76 printf("GLOBAL TEMPO of RECORDED FILES\n");
|
andrew@20
|
77 recordedTempoData.printTempoTimes();
|
andrew@20
|
78 }
|
andrew@20
|
79
|
andrew@20
|
80 void AudioEventMatcher::setTempoPrior(double tempo){
|
andrew@20
|
81 recordedTempoData.zero();
|
andrew@20
|
82 recordedTempoData.tempoPosterior.zero();
|
andrew@20
|
83 recordedTempoData.tempoPosterior.addGaussianShapeFromRealTime(tempo, 3, 1);
|
andrew@20
|
84
|
andrew@20
|
85 }
|
andrew@20
|
86
|
andrew@20
|
87 void AudioEventMatcher::calculateRecordedTempoData(){
|
andrew@20
|
88 int indexForOnsets[3];
|
andrew@20
|
89 indexForOnsets[0] = 0;
|
andrew@20
|
90 indexForOnsets[1] = 0;
|
andrew@20
|
91 indexForOnsets[2] = 0;
|
andrew@20
|
92 int kickTime, snareTime;
|
andrew@20
|
93 while (indexForOnsets[0] < recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.chromaOnsets.size() ||
|
andrew@20
|
94 indexForOnsets[2] < recordedTracks.loadedAudioFiles[2].fileLoader.onsetDetect.chromaOnsets.size()) {
|
andrew@20
|
95
|
andrew@20
|
96 setNextOnsetTime(0, kickTime, &indexForOnsets[0]);
|
andrew@20
|
97 setNextOnsetTime(2, snareTime, &indexForOnsets[0]);
|
andrew@20
|
98
|
andrew@20
|
99 if (kickTime < snareTime){
|
andrew@20
|
100 printf("update kick at %i\n", kickTime);
|
andrew@20
|
101 recordedTempoData.updateTempo(0, kickTime);
|
andrew@20
|
102 printf("recorded tempo is %f\n", recordedTempoData.playingTempo);
|
andrew@20
|
103 indexForOnsets[0]++;
|
andrew@20
|
104 }else {
|
andrew@20
|
105 printf("update snare at %i\n", snareTime);
|
andrew@20
|
106 recordedTempoData.updateTempo(2, snareTime);
|
andrew@20
|
107 printf("recorded tempo is %f\n", recordedTempoData.playingTempo);
|
andrew@20
|
108 indexForOnsets[2]++;
|
andrew@20
|
109 }
|
andrew@20
|
110 }//end while
|
andrew@20
|
111
|
andrew@20
|
112
|
andrew@20
|
113 }
|
andrew@20
|
114
|
andrew@20
|
115 void AudioEventMatcher::setNextOnsetTime(const int& channel, int& time, int* indexForOnsets){
|
andrew@20
|
116 if (indexForOnsets[channel] < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size()){
|
andrew@20
|
117 time = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[indexForOnsets[channel]].millisTime;
|
andrew@20
|
118 }
|
andrew@20
|
119 else {
|
andrew@20
|
120 time = 2147483647;//infinity
|
andrew@20
|
121 }
|
andrew@16
|
122 }
|
andrew@16
|
123
|
andrew@9
|
124 void AudioEventMatcher::startPlaying(){
|
andrew@3
|
125 bayesianStruct.setStartPlaying();
|
andrew@8
|
126 currentAlignmentPosition = 0;
|
andrew@8
|
127 startTime = ofGetElapsedTimeMillis();
|
andrew@11
|
128
|
andrew@11
|
129 projectedPrior = bayesianStruct.prior;
|
andrew@15
|
130 startedPlaying = true;
|
andrew@17
|
131 synchroniser.reset();
|
andrew@19
|
132 temporal.reset();
|
andrew@17
|
133
|
andrew@20
|
134 recordedTempoIndex = 0;
|
andrew@20
|
135 recordedTempo = recordedTempoData.globalTempo[recordedTempoIndex];
|
andrew@20
|
136
|
andrew@20
|
137 currentSpeedRatio = 1;
|
andrew@20
|
138
|
andrew@20
|
139 //SET TEMPO PRIOR for Speed Ratio
|
andrew@20
|
140 //the update this
|
andrew@20
|
141 setSpeedRatioDistribution(currentSpeedRatio);
|
andrew@3
|
142 //bayesianStruct.posterior.printArray();
|
andrew@3
|
143 }
|
andrew@3
|
144
|
andrew@9
|
145
|
andrew@20
|
146 void AudioEventMatcher::setSpeedRatioDistribution(const double& speedRatio){
|
andrew@20
|
147 bayesianStruct.relativeSpeedPosterior.zero();
|
andrew@20
|
148 bayesianStruct.relativeSpeedPosterior.addToIndex(bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(speedRatio), 1);
|
andrew@20
|
149 bayesianStruct.relativeSpeedPosterior.addGaussianShapeFromRealTime(1, 0.06, 0.5);
|
andrew@20
|
150 }
|
andrew@20
|
151
|
andrew@15
|
152 void AudioEventMatcher::stopPlaying(){
|
andrew@15
|
153 startedPlaying = false;
|
andrew@19
|
154 temporal.printEventTimes();
|
andrew@15
|
155 }
|
andrew@15
|
156
|
andrew@9
|
157 void AudioEventMatcher::updatePosition(){
|
andrew@19
|
158
|
andrew@19
|
159 if (startedPlaying){
|
andrew@9
|
160 if (!followingLiveInput)
|
andrew@9
|
161 recordedTracks.updatePosition();
|
andrew@19
|
162 else
|
andrew@9
|
163 recordedTracks.updatePositionToMillis(currentAlignmentPosition);
|
andrew@9
|
164
|
andrew@20
|
165 updateBestAlignmentPosition();
|
andrew@19
|
166 }
|
andrew@19
|
167
|
andrew@20
|
168 updateRecordedTempo();
|
andrew@20
|
169
|
andrew@19
|
170 temporal.tempoPosterior.addGaussianShape(temporal.tempoPosterior.MAPestimate, temporal.tempoArraySize / 4, 0.5 );
|
andrew@9
|
171 }
|
andrew@9
|
172
|
andrew@20
|
173 void AudioEventMatcher::updateRecordedTempo(){
|
andrew@20
|
174 //tempo of equivalent recorded position is updated
|
andrew@20
|
175 while(currentAlignmentPosition > recordedTempoData.globalTempoTimes[recordedTempoIndex]){
|
andrew@20
|
176 recordedTempoIndex++;
|
andrew@20
|
177 }
|
andrew@20
|
178 recordedTempo = recordedTempoData.globalTempo[recordedTempoIndex];
|
andrew@20
|
179 double tmpRatio = currentSpeedRatio;
|
andrew@20
|
180 currentSpeedRatio = temporal.playingTempo / recordedTempo;
|
andrew@20
|
181 if (currentSpeedRatio != tmpRatio)
|
andrew@20
|
182 setSpeedRatioDistribution(currentSpeedRatio);
|
andrew@20
|
183 }
|
andrew@20
|
184
|
andrew@8
|
185 void AudioEventMatcher::updateBestAlignmentPosition(){
|
andrew@10
|
186 //THIS DEALS WITH WHERE WE ARE NOW! ON THE SCREEN
|
andrew@10
|
187 //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
|
188
|
andrew@10
|
189 int newTime = ofGetElapsedTimeMillis() - startTime;
|
andrew@10
|
190 // double tmp = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);;
|
andrew@10
|
191 // double timetmp = (newTime - lastAlignmentTime);
|
andrew@10
|
192 // double speedtmp = bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
|
andrew@11
|
193 // currentAlignmentTime = newTime;
|
andrew@9
|
194 currentAlignmentPosition = bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate);
|
andrew@10
|
195 currentAlignmentPosition += (newTime - lastAlignmentTime) * bayesianStruct.relativeSpeedPosterior.getIndexInRealTerms(bayesianStruct.relativeSpeedPosterior.MAPestimate);
|
andrew@10
|
196
|
andrew@16
|
197
|
andrew@17
|
198 synchroniser.updateRecordedPosition(currentAlignmentPosition, newTime);
|
andrew@16
|
199
|
andrew@16
|
200 synchroniser.updateOutputSpeed();
|
andrew@16
|
201
|
andrew@11
|
202 bayesianStruct.projectDistribution(newTime, currentAlignmentPosition, projectedPrior);//prior gets updated to where we are now
|
andrew@11
|
203
|
andrew@10
|
204 // 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
|
205 }
|
andrew@8
|
206
|
andrew@0
|
207 void AudioEventMatcher::draw(){
|
andrew@6
|
208 //draw some outlines in blue
|
andrew@3
|
209 ofSetColor(20,200,200);
|
andrew@3
|
210 bayesPositionWindow.drawOutline();
|
andrew@3
|
211 bayesTempoWindow.drawOutline();
|
andrew@0
|
212
|
andrew@6
|
213 //draw the scrolling audio tracks
|
andrew@1
|
214 recordedTracks.drawTracks();
|
andrew@7
|
215
|
andrew@2
|
216 ofSetColor(255);
|
andrew@2
|
217 // bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
|
andrew@9
|
218
|
andrew@9
|
219 setScreenDisplayTimes();
|
andrew@6
|
220 drawBayesianDistributions();
|
andrew@8
|
221
|
andrew@11
|
222 //bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
|
andrew@6
|
223 //bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
|
andrew@11
|
224 //bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
|
andrew@18
|
225 string tmpStr = "pitch "+ofToString(recentPitch, 2);
|
andrew@18
|
226 tmpStr += " Nearest "+ofToString(pitchOfNearestMatch,2);
|
andrew@18
|
227 tmpStr += " dist "+ofToString(distanceOfNearestMatch, 2);
|
andrew@18
|
228 tmpStr += ", Time "+ofToString(recentTime, 0);
|
andrew@18
|
229 ofDrawBitmapString(tmpStr, 20, 20);
|
andrew@18
|
230
|
andrew@18
|
231
|
andrew@9
|
232
|
andrew@16
|
233 string alignString = " align "+ofToString(currentAlignmentPosition, 2);
|
andrew@16
|
234 alignString += " playing "+ofToString(synchroniser.playingPositionRatio, 5);
|
andrew@17
|
235 alignString += " pos "+ofToString(synchroniser.playingPositionMillis,0)+" ms";
|
andrew@17
|
236 alignString += " rec pos "+ofToString(synchroniser.recordedPositionMillis,0)+" ms";
|
andrew@16
|
237 ofDrawBitmapString(alignString, 20, 50);
|
andrew@16
|
238
|
andrew@9
|
239 ofDrawBitmapString("pos "+ofToString(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.playPosition), 200,600);
|
andrew@20
|
240
|
andrew@20
|
241 temporal.drawTempoArray(bayesLikelihoodWindow);
|
andrew@20
|
242
|
andrew@20
|
243 drawRecordedTempo();
|
andrew@20
|
244 drawPlayingTempo();
|
andrew@20
|
245
|
andrew@20
|
246
|
andrew@6
|
247 }
|
andrew@20
|
248
|
andrew@20
|
249 void AudioEventMatcher::drawRecordedTempo(){
|
andrew@6
|
250
|
andrew@20
|
251 int xTempoIndex = ofGetWidth() * (recordedTempo - recordedTempoData.minimumTempoInterval)/(recordedTempoData.maximumTempoInterval - recordedTempoData.minimumTempoInterval);
|
andrew@20
|
252 ofSetColor(0, 200, 0);
|
andrew@20
|
253 ofLine(xTempoIndex, bayesLikelihoodWindow.y, xTempoIndex, bayesLikelihoodWindow.y + bayesLikelihoodWindow.height);
|
andrew@20
|
254 ofDrawBitmapString(ofToString(recordedTempo), xTempoIndex, bayesLikelihoodWindow.y + 10);
|
andrew@20
|
255 }
|
andrew@20
|
256
|
andrew@20
|
257 void AudioEventMatcher::drawPlayingTempo(){
|
andrew@20
|
258
|
andrew@20
|
259 int xTempoIndex = ofGetWidth() * (temporal.playingTempo - temporal.minimumTempoInterval)/(temporal.maximumTempoInterval - temporal.minimumTempoInterval);
|
andrew@20
|
260 ofSetColor(200, 0, 200);
|
andrew@20
|
261 ofLine(xTempoIndex, bayesLikelihoodWindow.y, xTempoIndex, bayesLikelihoodWindow.y + bayesLikelihoodWindow.height);
|
andrew@20
|
262 ofDrawBitmapString(ofToString(recordedTempo), xTempoIndex, bayesLikelihoodWindow.y + 10);
|
andrew@20
|
263
|
andrew@20
|
264 int xSpeedRatioIndex = (double)(temporal.tempoPosterior.getIndexInRealTerms(currentSpeedRatio)*ofGetWidth())/(double)temporal.tempoPosterior.arraySize;
|
andrew@20
|
265 ofSetColor(200,0,0);
|
andrew@20
|
266 ofLine(xSpeedRatioIndex, bayesTempoWindow.y, xSpeedRatioIndex, bayesTempoWindow.y + bayesTempoWindow.height);
|
andrew@20
|
267 ofDrawBitmapString(ofToString(currentSpeedRatio), 100, bayesTempoWindow.y+50);
|
andrew@20
|
268
|
andrew@20
|
269 }
|
andrew@20
|
270
|
andrew@20
|
271
|
andrew@9
|
272 void AudioEventMatcher::setScreenDisplayTimes(){
|
andrew@9
|
273 screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
|
andrew@9
|
274 // if (!followingLiveInput){
|
andrew@9
|
275
|
andrew@9
|
276 screenStartTimeMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.drawParams.windowStartFrame);
|
andrew@9
|
277 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
|
andrew@9
|
278
|
andrew@9
|
279 //need PRECISION in this alignment
|
andrew@9
|
280
|
andrew@9
|
281
|
andrew@9
|
282 /*}else{
|
andrew@9
|
283
|
andrew@9
|
284 screenStartTimeMillis = (int)(currentAlignmentPosition/screenWidthMillis) * screenWidthMillis;
|
andrew@9
|
285 screenEndTimeMillis = screenStartTimeMillis + screenWidthMillis;
|
andrew@9
|
286 }*/
|
andrew@9
|
287 }
|
andrew@9
|
288
|
andrew@6
|
289 void AudioEventMatcher::drawBayesianDistributions(){
|
andrew@6
|
290
|
andrew@6
|
291
|
andrew@6
|
292 int startIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenStartTimeMillis);
|
andrew@6
|
293 int endIndex = bayesianStruct.posterior.getRealTermsAsIndex(screenEndTimeMillis);
|
andrew@4
|
294
|
andrew@6
|
295 bayesianStruct.posterior.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesPositionWindow);
|
andrew@6
|
296
|
andrew@6
|
297 string tmpString = "start "+ofToString(screenStartTimeMillis)+" (index "+ofToString(startIndex)+"), end "+ofToString(screenEndTimeMillis);
|
andrew@6
|
298 ofDrawBitmapString(tmpString, bayesPositionWindow.x+20, bayesPositionWindow.y+20);
|
andrew@4
|
299
|
andrew@8
|
300 // bayesianStruct.likelihood.drawConstrainedVector(startIndex, endIndex, 0, ofGetWidth(), bayesLikelihoodWindow);
|
andrew@2
|
301
|
andrew@6
|
302 bayesianStruct.relativeSpeedPosterior.drawConstrainedVector(0, bayesianStruct.relativeSpeedPosterior.arraySize, 0, ofGetWidth(), bayesTempoWindow);
|
andrew@6
|
303
|
andrew@3
|
304 string tmpStr = "zero is "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(0));
|
andrew@3
|
305 tmpStr += " offsetis "+ofToString(bayesianStruct.posterior.offset);
|
andrew@3
|
306 tmpStr += " screenWidth = "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis));
|
andrew@3
|
307 ofDrawBitmapString(tmpStr, 20,140);
|
andrew@3
|
308 tmpStr = "best est "+ofToString(bayesianStruct.bestEstimate);
|
andrew@3
|
309 ofDrawBitmapString(tmpStr, 20, 180);
|
andrew@3
|
310
|
andrew@8
|
311 ofDrawBitmapString("screenwidth "+ofToString(screenWidthMillis), 20, 800);
|
andrew@3
|
312
|
andrew@9
|
313 //green line at current best estimate
|
andrew@13
|
314 ofSetColor(0,255,0);//green scrolling line best position
|
andrew@8
|
315 double currentEstimateIndex = (currentAlignmentPosition - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
|
andrew@8
|
316 ofLine(currentEstimateIndex, bayesPositionWindow.y, currentEstimateIndex, bayesPositionWindow.y + bayesPositionWindow.height);
|
andrew@7
|
317
|
andrew@16
|
318
|
andrew@16
|
319 ofSetColor(0,255,255);//synchroniser position
|
andrew@16
|
320 currentEstimateIndex = (synchroniser.playingPositionMillis - screenStartTimeMillis)*ofGetWidth()/screenWidthMillis;
|
andrew@16
|
321 ofLine(currentEstimateIndex, bayesLikelihoodWindow.y, currentEstimateIndex, bayesLikelihoodWindow.y + bayesPositionWindow.height);
|
andrew@16
|
322
|
andrew@16
|
323
|
andrew@16
|
324
|
andrew@7
|
325 //draw track by track likelihoods
|
andrew@7
|
326 for (int i = 0; i <recordedTracks.numberOfAudioTracks;i++){
|
andrew@13
|
327 ofSetColor(200,255,50);//channel likelihoods in yellow
|
andrew@8
|
328 likelihoodVisualisation[i].drawConstrainedVector(likelihoodVisualisation[i].getRealTermsAsIndex(screenStartTimeMillis), likelihoodVisualisation[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
|
andrew@11
|
329
|
andrew@13
|
330 ofSetColor(0,255,150);//channel priors
|
andrew@11
|
331 recentPriors[i].drawConstrainedVector(recentPriors[i].getRealTermsAsIndex(screenStartTimeMillis), recentPriors[i].getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), recordedTracks.loadedAudioFiles[i].fileLoader.onsetDetect.window);
|
andrew@11
|
332
|
andrew@11
|
333
|
andrew@8
|
334 ofSetColor(255);
|
andrew@8
|
335 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
|
336 }
|
andrew@8
|
337
|
andrew@13
|
338 int priorStartIndex = bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis);
|
andrew@13
|
339 int priorEndIndex = bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis);
|
andrew@13
|
340 // ofSetColor(0,200,200);//recent prior
|
andrew@13
|
341 // recentPrior.drawConstrainedVector(priorStartIndex, priorEndIndex, 0, ofGetWidth(), bayesPositionWindow);
|
andrew@8
|
342
|
andrew@10
|
343 ofSetColor(255,0,100);//purple prior
|
andrew@11
|
344 bayesianStruct.prior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesPositionWindow);
|
andrew@11
|
345
|
andrew@11
|
346 ofSetColor(255,0,0);
|
andrew@13
|
347 projectedPrior.drawConstrainedVector(bayesianStruct.prior.getRealTermsAsIndex(screenStartTimeMillis), bayesianStruct.prior.getRealTermsAsIndex(screenEndTimeMillis), 0, ofGetWidth(), bayesPositionWindow);
|
andrew@7
|
348
|
andrew@20
|
349
|
andrew@19
|
350
|
andrew@1
|
351 }
|
andrew@1
|
352
|
andrew@6
|
353 void AudioEventMatcher::newPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
|
andrew@7
|
354 if (pitchIn > 0){
|
andrew@1
|
355 liveInput.addPitchEvent(pitchIn, timeIn);
|
andrew@4
|
356
|
andrew@10
|
357 //printPosteriorMAPinfo();
|
andrew@11
|
358
|
andrew@7
|
359 matchNewPitchEvent(channel, pitchIn, timeIn);//main pitch matching fn
|
andrew@7
|
360
|
andrew@7
|
361 likelihoodVisualisation[1] = bayesianStruct.likelihood;
|
andrew@7
|
362
|
andrew@7
|
363 recentPitch = pitchIn;//for drawing
|
andrew@7
|
364 recentTime = timeIn;
|
andrew@7
|
365 }
|
andrew@8
|
366
|
andrew@2
|
367 }
|
andrew@2
|
368
|
andrew@6
|
369 void AudioEventMatcher::newKickEvent(const double& timeIn){
|
andrew@6
|
370 // liveInput.addKickEvent(timeIn);
|
andrew@2
|
371 matchNewOnsetEvent(0, timeIn);
|
andrew@7
|
372 likelihoodVisualisation[0] = bayesianStruct.likelihood;
|
andrew@2
|
373 }
|
andrew@2
|
374
|
andrew@6
|
375 void AudioEventMatcher::newKickEvent(const int& channel, const double& timeIn){
|
andrew@6
|
376 // liveInput.addKickEvent(timeIn);
|
andrew@6
|
377 matchNewOnsetEvent(channel, timeIn);
|
andrew@7
|
378 likelihoodVisualisation[0] = bayesianStruct.likelihood;
|
andrew@6
|
379 }
|
andrew@6
|
380
|
andrew@2
|
381
|
andrew@2
|
382 void AudioEventMatcher::newSnareEvent(const double& timeIn){
|
andrew@6
|
383 matchNewOnsetEvent(2, timeIn);
|
andrew@7
|
384 likelihoodVisualisation[2] = bayesianStruct.likelihood;
|
andrew@7
|
385 }
|
andrew@7
|
386
|
andrew@7
|
387
|
andrew@7
|
388 void AudioEventMatcher::newSnareEvent(const int& channel, const double& timeIn){
|
andrew@7
|
389 matchNewOnsetEvent(channel, timeIn);
|
andrew@7
|
390 likelihoodVisualisation[2] = bayesianStruct.likelihood;
|
andrew@2
|
391 }
|
andrew@2
|
392
|
andrew@2
|
393 //Needs just to set bounds for the matching process, not have TimeIn
|
andrew@2
|
394 void AudioEventMatcher::matchNewOnsetEvent(const int& channel, const double& timeIn){
|
andrew@3
|
395
|
andrew@6
|
396 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
|
andrew@10
|
397
|
andrew@2
|
398 //start at beginning but OPTIMISE later
|
andrew@15
|
399
|
andrew@2
|
400
|
andrew@2
|
401 bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
|
andrew@2
|
402 bayesianStruct.likelihood.zero();//set to zero
|
andrew@2
|
403
|
andrew@2
|
404 double quantity = 1;//likelihoodToNoiseRatio / numberOfMatches;
|
andrew@2
|
405 int numberOfMatchesFound = 0;
|
andrew@2
|
406
|
andrew@2
|
407
|
andrew@10
|
408 double startMatchingTime = bayesianStruct.likelihood.offset;
|
andrew@10
|
409 double endMatchingTime = bayesianStruct.likelihood.offset + matchWindowWidth;
|
andrew@2
|
410
|
andrew@2
|
411 if (channel <= recordedTracks.numberOfAudioTracks){
|
andrew@2
|
412 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
|
andrew@2
|
413 double millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
|
andrew@10
|
414 if (millisTime >= startMatchingTime && millisTime <= endMatchingTime){
|
andrew@14
|
415 bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, onsetLikelihoodWidth, quantity);
|
andrew@2
|
416 numberOfMatchesFound++;
|
andrew@6
|
417 // printf("Adding Gaussian for onset at time %f offset %f\n", millisTime, bayesianStruct.likelihood.offset);
|
andrew@2
|
418
|
andrew@2
|
419 }
|
andrew@2
|
420 }
|
andrew@2
|
421 }
|
andrew@2
|
422
|
andrew@11
|
423 if (numberOfMatchesFound > 0){
|
andrew@3
|
424 // bayesianStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesianStruct.likelihood.length);
|
andrew@3
|
425 bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(onsetLikelihoodToNoise*bayesianStruct.likelihood.length));
|
andrew@2
|
426 bayesianStruct.likelihood.renormalise();
|
andrew@2
|
427
|
andrew@8
|
428 bayesianStruct.calculatePosterior();
|
andrew@10
|
429 lastAlignmentTime = timeIn;//use TIMESTAMP
|
andrew@10
|
430 recentEventTime[channel] = timeIn;//ofGetElapsedTimeMillis() - startTime;
|
andrew@11
|
431
|
andrew@11
|
432 recentPriors[channel] = bayesianStruct.prior;
|
andrew@13
|
433 projectedPrior = bayesianStruct.prior;
|
andrew@19
|
434
|
andrew@19
|
435
|
andrew@19
|
436 temporal.updateTempo(channel, timeIn);
|
andrew@11
|
437 }
|
andrew@11
|
438
|
andrew@11
|
439
|
andrew@6
|
440
|
andrew@3
|
441 }
|
andrew@3
|
442
|
andrew@3
|
443
|
andrew@3
|
444
|
andrew@3
|
445 void AudioEventMatcher::matchNewPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
|
andrew@3
|
446 //start at beginning but OPTIMISE later
|
andrew@10
|
447 /*printf("TIME %i\n", ofGetElapsedTimeMillis());
|
andrew@10
|
448 //tmp debug
|
andrew@10
|
449 updateBestAlignmentPosition();
|
andrew@10
|
450 printf("current alignment best estimate %f\n", currentAlignmentPosition);
|
andrew@10
|
451 */
|
andrew@6
|
452 bayesianStruct.updateBayesianDistributions(timeIn);//moves the posterior up into prior given the time interval and calculates new offsets
|
andrew@8
|
453
|
andrew@7
|
454 //set the lielihoods by matching the pitched note
|
andrew@7
|
455
|
andrew@15
|
456
|
andrew@3
|
457 int numberOfMatches = 0;
|
andrew@3
|
458 bayesianStruct.likelihood.zero();//set to zero
|
andrew@18
|
459 double newOnsetTime;
|
andrew@18
|
460 double closestDistance = INFINITY;
|
andrew@3
|
461
|
andrew@3
|
462 double quantity = 0;
|
andrew@3
|
463 if (channel <= recordedTracks.numberOfAudioTracks){
|
andrew@3
|
464 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
|
andrew@3
|
465
|
andrew@3
|
466 if (checkMatch(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn)) {
|
andrew@18
|
467 quantity = getPitchDistance(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn, 8);
|
andrew@18
|
468
|
andrew@3
|
469 bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, 30, quantity);
|
andrew@3
|
470 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = true;
|
andrew@3
|
471 numberOfMatches++;
|
andrew@3
|
472 }
|
andrew@3
|
473 else{
|
andrew@3
|
474 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = false;
|
andrew@3
|
475 }
|
andrew@18
|
476 //checking nearest pitch
|
andrew@18
|
477 newOnsetTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
|
andrew@18
|
478 if (abs(newOnsetTime - currentAlignmentPosition) < closestDistance){
|
andrew@18
|
479 closestDistance = abs(newOnsetTime - currentAlignmentPosition);
|
andrew@18
|
480 pitchOfNearestMatch = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch;
|
andrew@18
|
481 distanceOfNearestMatch = quantity;
|
andrew@18
|
482 }
|
andrew@3
|
483
|
andrew@3
|
484 }
|
andrew@3
|
485 }
|
andrew@6
|
486
|
andrew@8
|
487
|
andrew@8
|
488
|
andrew@6
|
489 if (numberOfMatches > 0){//no point updating unless there is a match
|
andrew@7
|
490
|
andrew@6
|
491 bayesianStruct.likelihood.addConstant(numberOfMatches*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));
|
andrew@4
|
492
|
andrew@4
|
493 //tmp set likelihood constant and calculate using that
|
andrew@6
|
494 //bayesianStruct.likelihood.zero();
|
andrew@6
|
495 //bayesianStruct.likelihood.addConstant(1);
|
andrew@7
|
496
|
andrew@6
|
497 bayesianStruct.calculatePosterior();
|
andrew@11
|
498 lastAlignmentTime = timeIn;//has to use the STAMPED time
|
andrew@11
|
499 recentEventTime[channel] = timeIn;
|
andrew@11
|
500
|
andrew@11
|
501 recentPriors[channel] = bayesianStruct.prior;
|
andrew@13
|
502 projectedPrior = bayesianStruct.prior;
|
andrew@19
|
503
|
andrew@19
|
504 temporal.eventTimes[channel].push_back(timeIn);
|
andrew@6
|
505 }
|
andrew@4
|
506
|
andrew@11
|
507
|
andrew@1
|
508 }
|
andrew@1
|
509
|
andrew@3
|
510 double AudioEventMatcher::getPitchDistance(const double& pitchOne, const double& pitchTwo, const double& scale){
|
andrew@3
|
511
|
andrew@18
|
512 double scaleFactor = scale * pitchOne / 110.0;
|
andrew@16
|
513
|
andrew@18
|
514 int multiplicationFactor = 1;
|
andrew@18
|
515 if (pitchTwo > 0){
|
andrew@18
|
516 int multiplicationFactor = round(pitchOne/pitchTwo);
|
andrew@18
|
517 }
|
andrew@16
|
518
|
andrew@18
|
519 double distance = abs(pitchOne - pitchTwo*multiplicationFactor);
|
andrew@16
|
520 if (distance < scaleFactor)
|
andrew@16
|
521 distance = 1 - (distance/scaleFactor);
|
andrew@3
|
522 else
|
andrew@3
|
523 distance = 0;
|
andrew@3
|
524
|
andrew@3
|
525 // printf("[pitch distance %f vs %f = %f\n", pitchOne, pitchTwo, distance);
|
andrew@3
|
526 return distance;
|
andrew@3
|
527
|
andrew@3
|
528 }
|
andrew@3
|
529
|
andrew@3
|
530
|
andrew@3
|
531 bool AudioEventMatcher::checkMatch(const double& recordedPitch, const double& livePitch){
|
andrew@18
|
532
|
andrew@18
|
533 if (livePitch > 0){
|
andrew@18
|
534 int multiplicationFactor = (int)(round(recordedPitch/livePitch));
|
andrew@18
|
535
|
andrew@18
|
536 if (abs(recordedPitch - livePitch * multiplicationFactor) < 16)
|
andrew@3
|
537 return true;
|
andrew@3
|
538 else
|
andrew@3
|
539 return false;
|
andrew@18
|
540 }else {
|
andrew@18
|
541 return false;
|
andrew@18
|
542 }
|
andrew@18
|
543
|
andrew@3
|
544 }
|
andrew@3
|
545
|
andrew@3
|
546
|
andrew@1
|
547
|
andrew@1
|
548 void AudioEventMatcher::windowResized(const int& w, const int& h){
|
andrew@1
|
549 recordedTracks.windowResized(w,h);
|
andrew@3
|
550 bayesTempoWindow.resized(w,h);
|
andrew@3
|
551 bayesPositionWindow.resized(w,h);
|
andrew@3
|
552 }
|
andrew@3
|
553
|
andrew@10
|
554 /*
|
andrew@10
|
555
|
andrew@10
|
556 void printPosteriorMAPinfo(){ //tmp print stuff
|
andrew@10
|
557 printf("New pitch MAP post estimate now %i, ", bayesianStruct.posterior.MAPestimate);
|
andrew@10
|
558 double tmp = bayesianStruct.posterior.getMAPestimate();
|
andrew@10
|
559 printf(" getting it %f and offset %f == %f ms\n", tmp, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(tmp));
|
andrew@10
|
560
|
andrew@10
|
561 }
|
andrew@10
|
562 */
|
andrew@3
|
563
|