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@3
|
16 bayesPositionWindow.setToRelativeSize(0, 0.4, 1, 0.2);
|
andrew@3
|
17 bayesTempoWindow.setToRelativeSize(0, 0.8, 1, 0.2);
|
andrew@3
|
18 bayesLikelihoodWindow.setToRelativeSize(0, 0.6, 1, 0.2);
|
andrew@3
|
19
|
andrew@0
|
20 setArraySizes();
|
andrew@3
|
21
|
andrew@3
|
22 usingRealTime = false;
|
andrew@3
|
23 bayesianStruct.realTimeMode = &usingRealTime;
|
andrew@0
|
24 }
|
andrew@0
|
25
|
andrew@0
|
26
|
andrew@0
|
27 void AudioEventMatcher::setArraySizes(){
|
andrew@0
|
28 bayesianStruct.resetSpeedSize(200);
|
andrew@0
|
29 bayesianStruct.setRelativeSpeedScalar(0.01);
|
andrew@0
|
30 bayesianStruct.setSpeedPrior(1.0);
|
andrew@0
|
31 bayesianStruct.relativeSpeedPrior.getMaximum();
|
andrew@0
|
32
|
andrew@0
|
33 bayesianStruct.resetSize(matchWindowWidth);
|
andrew@0
|
34 bayesianStruct.setPositionDistributionScalar(1);
|
andrew@0
|
35
|
andrew@0
|
36 }
|
andrew@0
|
37
|
andrew@3
|
38 void AudioEventMatcher::startPlaying(){
|
andrew@3
|
39 bayesianStruct.setStartPlaying();
|
andrew@3
|
40 //bayesianStruct.posterior.printArray();
|
andrew@3
|
41 }
|
andrew@3
|
42
|
andrew@0
|
43 void AudioEventMatcher::draw(){
|
andrew@3
|
44 ofSetColor(20,200,200);
|
andrew@3
|
45 bayesPositionWindow.drawOutline();
|
andrew@3
|
46 bayesTempoWindow.drawOutline();
|
andrew@0
|
47
|
andrew@1
|
48 recordedTracks.drawTracks();
|
andrew@2
|
49
|
andrew@2
|
50 ofSetColor(255);
|
andrew@2
|
51 // bayesianStruct.relativeSpeedPrior.drawVector(0, 200, bayesTempoWindow);
|
andrew@2
|
52
|
andrew@2
|
53 double screenWidthMillis = recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.framesToMillis(recordedTracks.loadedAudioFiles[0].fileLoader.onsetDetect.amplitudeNumber);
|
andrew@2
|
54
|
andrew@4
|
55 bayesianStruct.posterior.drawVector(0, bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
|
andrew@4
|
56
|
andrew@4
|
57 // bayesianStruct.posterior.drawVector(bayesianStruct.posterior.getRealTermsAsIndex(0), bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis), bayesPositionWindow);
|
andrew@4
|
58
|
andrew@4
|
59
|
andrew@3
|
60 bayesianStruct.likelihood.drawVector(bayesianStruct.likelihood.getRealTermsAsIndex(0), bayesianStruct.likelihood.getRealTermsAsIndex(screenWidthMillis), bayesLikelihoodWindow);
|
andrew@2
|
61
|
andrew@3
|
62 bayesianStruct.relativeSpeedPosterior.drawVector(0, bayesianStruct.relativeSpeedPosterior.getRealTermsAsIndex(2), bayesTempoWindow);
|
andrew@3
|
63
|
andrew@4
|
64
|
andrew@3
|
65 string tmpStr = "zero is "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(0));
|
andrew@3
|
66 tmpStr += " offsetis "+ofToString(bayesianStruct.posterior.offset);
|
andrew@3
|
67 tmpStr += " screenWidth = "+ofToString(bayesianStruct.posterior.getRealTermsAsIndex(screenWidthMillis));
|
andrew@3
|
68 ofDrawBitmapString(tmpStr, 20,140);
|
andrew@3
|
69 tmpStr = "best est "+ofToString(bayesianStruct.bestEstimate);
|
andrew@3
|
70 ofDrawBitmapString(tmpStr, 20, 180);
|
andrew@3
|
71
|
andrew@3
|
72 ofDrawBitmapString("screenamp "+ofToString(screenWidthMillis), 20, 100);
|
andrew@3
|
73
|
andrew@0
|
74
|
andrew@1
|
75 }
|
andrew@1
|
76
|
andrew@1
|
77
|
andrew@1
|
78 void AudioEventMatcher::newPitchEvent(const double& pitchIn, const double& timeIn){
|
andrew@1
|
79 liveInput.addPitchEvent(pitchIn, timeIn);
|
andrew@4
|
80
|
andrew@4
|
81 //tmp print stuff
|
andrew@4
|
82 printf("New pitch MAP post estimate now %i, ", bayesianStruct.posterior.MAPestimate);
|
andrew@4
|
83 double tmp = bayesianStruct.posterior.getMAPestimate();
|
andrew@4
|
84 printf(" getting it %f and offset %f == %f ms\n", tmp, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(tmp));
|
andrew@4
|
85
|
andrew@3
|
86 matchNewPitchEvent(0, pitchIn, timeIn);
|
andrew@2
|
87 }
|
andrew@2
|
88
|
andrew@2
|
89 void AudioEventMatcher::newKickEvent(const double& timeIn){
|
andrew@3
|
90 // liveInput.addKickEvent(time);
|
andrew@2
|
91 matchNewOnsetEvent(0, timeIn);
|
andrew@2
|
92 }
|
andrew@2
|
93
|
andrew@2
|
94
|
andrew@2
|
95 void AudioEventMatcher::newSnareEvent(const double& timeIn){
|
andrew@2
|
96 matchNewOnsetEvent(0, timeIn);
|
andrew@2
|
97 }
|
andrew@2
|
98
|
andrew@2
|
99 //Needs just to set bounds for the matching process, not have TimeIn
|
andrew@2
|
100 void AudioEventMatcher::matchNewOnsetEvent(const int& channel, const double& timeIn){
|
andrew@3
|
101
|
andrew@3
|
102
|
andrew@2
|
103 //start at beginning but OPTIMISE later
|
andrew@3
|
104 double onsetLikelihoodToNoise = 0.5;
|
andrew@2
|
105
|
andrew@2
|
106 double likelihoodWidth = 40;
|
andrew@2
|
107
|
andrew@2
|
108 bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
|
andrew@2
|
109 bayesianStruct.likelihood.zero();//set to zero
|
andrew@2
|
110
|
andrew@2
|
111 double quantity = 1;//likelihoodToNoiseRatio / numberOfMatches;
|
andrew@2
|
112 int numberOfMatchesFound = 0;
|
andrew@2
|
113
|
andrew@2
|
114
|
andrew@2
|
115 double startTime = bayesianStruct.likelihood.offset;
|
andrew@2
|
116 double endTime = bayesianStruct.likelihood.offset + matchWindowWidth;
|
andrew@2
|
117
|
andrew@2
|
118 if (channel <= recordedTracks.numberOfAudioTracks){
|
andrew@2
|
119 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
|
andrew@2
|
120 double millisTime = recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime;
|
andrew@2
|
121 if (millisTime >= startTime && millisTime <= endTime){
|
andrew@2
|
122 bayesianStruct.likelihood.addGaussianShapeFromRealTime(millisTime, likelihoodWidth, quantity);
|
andrew@2
|
123 numberOfMatchesFound++;
|
andrew@2
|
124 printf("Adding Gaussian for onset at time %f offset %f\n", millisTime, bayesianStruct.likelihood.offset);
|
andrew@2
|
125
|
andrew@2
|
126 }
|
andrew@2
|
127 }
|
andrew@2
|
128 }
|
andrew@2
|
129
|
andrew@3
|
130 // bayesianStruct.likelihood.addConstant((1-likelihoodToNoiseRatio)/bayesianStruct.likelihood.length);
|
andrew@3
|
131 bayesianStruct.likelihood.addConstant(numberOfMatchesFound*(1-onsetLikelihoodToNoise)/(onsetLikelihoodToNoise*bayesianStruct.likelihood.length));
|
andrew@3
|
132
|
andrew@2
|
133 bayesianStruct.likelihood.renormalise();
|
andrew@2
|
134
|
andrew@3
|
135
|
andrew@3
|
136 }
|
andrew@3
|
137
|
andrew@3
|
138
|
andrew@3
|
139
|
andrew@3
|
140 void AudioEventMatcher::matchNewPitchEvent(const int& channel, const double& pitchIn, const double& timeIn){
|
andrew@3
|
141 //start at beginning but OPTIMISE later
|
andrew@2
|
142
|
andrew@2
|
143
|
andrew@3
|
144 updateBayesianDistributions(timeIn);
|
andrew@3
|
145
|
andrew@3
|
146 ///set offsets
|
andrew@3
|
147 // bayesianStruct.likelihood.offset = bayesianStruct.prior.offset;
|
andrew@3
|
148 double pitchLikelihoodToNoise = 0.5;
|
andrew@3
|
149 int numberOfMatches = 0;
|
andrew@3
|
150 bayesianStruct.likelihood.zero();//set to zero
|
andrew@3
|
151
|
andrew@3
|
152 double quantity = 0;
|
andrew@3
|
153 if (channel <= recordedTracks.numberOfAudioTracks){
|
andrew@3
|
154 for (int i = 0;i < recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets.size();i++){
|
andrew@3
|
155
|
andrew@3
|
156 if (checkMatch(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn)) {
|
andrew@3
|
157 quantity = getPitchDistance(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].aubioPitch, pitchIn, 40);
|
andrew@3
|
158 bayesianStruct.likelihood.addGaussianShapeFromRealTime(recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].millisTime, 30, quantity);
|
andrew@3
|
159 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = true;
|
andrew@3
|
160 numberOfMatches++;
|
andrew@3
|
161 }
|
andrew@3
|
162 else{
|
andrew@3
|
163 recordedTracks.loadedAudioFiles[channel].fileLoader.onsetDetect.chromaOnsets[i].matched = false;
|
andrew@3
|
164 }
|
andrew@3
|
165
|
andrew@3
|
166 }
|
andrew@3
|
167 }
|
andrew@3
|
168
|
andrew@3
|
169 bayesianStruct.likelihood.addConstant(numberOfMatches*(1-pitchLikelihoodToNoise)/(pitchLikelihoodToNoise*bayesianStruct.likelihood.length));
|
andrew@3
|
170
|
andrew@3
|
171 recordedTracks.recentPitch = pitchIn;
|
andrew@4
|
172
|
andrew@4
|
173
|
andrew@4
|
174 //tmp set likelihood constant and calculate using that
|
andrew@4
|
175 bayesianStruct.likelihood.zero();
|
andrew@4
|
176 bayesianStruct.likelihood.addConstant(1);
|
andrew@4
|
177
|
andrew@4
|
178 bayesianStruct.calculatePosterior();
|
andrew@4
|
179
|
andrew@4
|
180 //tmp print stuff
|
andrew@4
|
181 printf("After CALC");
|
andrew@4
|
182 printPostOffset();
|
andrew@4
|
183
|
andrew@1
|
184 }
|
andrew@1
|
185
|
andrew@3
|
186 double AudioEventMatcher::getPitchDistance(const double& pitchOne, const double& pitchTwo, const double& scale){
|
andrew@3
|
187
|
andrew@3
|
188 double distance = abs(pitchOne - pitchTwo);
|
andrew@3
|
189 if (distance < scale)
|
andrew@3
|
190 distance = 1 - (distance/scale);
|
andrew@3
|
191 else
|
andrew@3
|
192 distance = 0;
|
andrew@3
|
193
|
andrew@3
|
194 // printf("[pitch distance %f vs %f = %f\n", pitchOne, pitchTwo, distance);
|
andrew@3
|
195 return distance;
|
andrew@3
|
196
|
andrew@3
|
197 }
|
andrew@3
|
198
|
andrew@3
|
199
|
andrew@3
|
200 bool AudioEventMatcher::checkMatch(const double& recordedPitch, const double& livePitch){
|
andrew@3
|
201 if (abs(recordedPitch - livePitch) < 40)
|
andrew@3
|
202 return true;
|
andrew@3
|
203 else
|
andrew@3
|
204 return false;
|
andrew@3
|
205 }
|
andrew@3
|
206
|
andrew@3
|
207
|
andrew@1
|
208
|
andrew@1
|
209 void AudioEventMatcher::windowResized(const int& w, const int& h){
|
andrew@1
|
210 recordedTracks.windowResized(w,h);
|
andrew@3
|
211 bayesTempoWindow.resized(w,h);
|
andrew@3
|
212 bayesPositionWindow.resized(w,h);
|
andrew@3
|
213 }
|
andrew@3
|
214
|
andrew@3
|
215
|
andrew@3
|
216
|
andrew@3
|
217 void AudioEventMatcher::updateBayesianDistributions(const double& newEventTime){
|
andrew@3
|
218 //MOVE INTO bayesianStruct?? XX
|
andrew@3
|
219
|
andrew@3
|
220 //NEED TO CHECK HERE THAT THEY HAVE THE SAME OFFSETS
|
andrew@3
|
221 bayesianStruct.prior.copyFromDynamicVector(bayesianStruct.posterior);//try the otehr way
|
andrew@3
|
222
|
andrew@3
|
223 //bayesianStruct.copyPriorToPosterior();
|
andrew@3
|
224 //need to get new MAP position and set the offset of the arrays
|
andrew@3
|
225 //currently bestEstimate is the approx for the new MAP position
|
andrew@4
|
226 int tmpMap = bayesianStruct.posterior.getMAPestimate();
|
andrew@4
|
227
|
andrew@3
|
228 double timeDifference = newEventTime - bayesianStruct.lastEventTime;
|
andrew@4
|
229 printf("updating distributions at time %f diff %f offset %f tmpmap est %i\n", newEventTime, timeDifference, bayesianStruct.posterior.offset, tmpMap);
|
andrew@3
|
230
|
andrew@3
|
231 //addnoise to the tempo distribution
|
andrew@3
|
232 //bayesianStruct.decaySpeedDistribution(timeDifference);
|
andrew@4
|
233
|
andrew@3
|
234 if (timeDifference > 50){
|
andrew@3
|
235 bayesianStruct.addGaussianNoiseToSpeedPosterior(timeDifference * 10.0 / 100.);
|
andrew@3
|
236 }
|
andrew@3
|
237
|
andrew@3
|
238 bayesianStruct.updateBestEstimate(timeDifference);
|
andrew@3
|
239 bayesianStruct.lastBestEstimateUpdateTime = newEventTime;//getTimeNow(timePlayed);
|
andrew@3
|
240
|
andrew@4
|
241 // printf("set new distrb max 0 or %f\n", bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2));
|
andrew@4
|
242
|
andrew@4
|
243 //tmp print stuff
|
andrew@4
|
244
|
andrew@4
|
245 printf("HALFWAY BEST ");
|
andrew@4
|
246 printPostOffset();
|
andrew@4
|
247
|
andrew@3
|
248 bayesianStruct.setNewDistributionOffsets(max(0., bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2)));
|
andrew@3
|
249 bayesianStruct.crossUpdateArrays(bayesianStruct.posterior, bayesianStruct.relativeSpeedPosterior, timeDifference);
|
andrew@3
|
250
|
andrew@4
|
251 //i.e. using the same offset as prior
|
andrew@4
|
252 bayesianStruct.posterior.offset = bayesianStruct.prior.offset;//
|
andrew@4
|
253
|
andrew@4
|
254 // float tmpPrior = max(0., bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2));// bayesianStruct.prior.offset = max(0., bayesianStruct.bestEstimate - (bayesianStruct.prior.scalar*bayesianStruct.prior.arraySize/2));
|
andrew@4
|
255 // printf("Using prior offset of %f not %f\n", tmpPrior, bayesianStruct.prior.offset);
|
andrew@4
|
256
|
andrew@3
|
257 bayesianStruct.lastEventTime = newEventTime;//bayesianStruct.lastEventTime = ofGetElapsedTimeMillis();
|
andrew@4
|
258
|
andrew@4
|
259
|
andrew@3
|
260 }
|
andrew@4
|
261
|
andrew@4
|
262 void AudioEventMatcher::printPostOffset(){
|
andrew@4
|
263 double tmp = bayesianStruct.posterior.getMAPestimate();
|
andrew@4
|
264 printf(" MAP index %i post offset %f == %f ms\n", bayesianStruct.posterior.MAPestimate, bayesianStruct.posterior.offset, bayesianStruct.posterior.getIndexInRealTerms(bayesianStruct.posterior.MAPestimate));
|
andrew@4
|
265 } |