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