andrew@19
|
1 /*
|
andrew@19
|
2 * TempoFollower.cpp
|
andrew@19
|
3 * MultipleAudioMathcher
|
andrew@19
|
4 *
|
andrew@19
|
5 * Created by Andrew on 09/02/2012.
|
andrew@19
|
6 * Copyright 2012 QMUL. All rights reserved.
|
andrew@19
|
7 *
|
andrew@19
|
8 */
|
andrew@19
|
9
|
andrew@19
|
10 #include "TempoFollower.h"
|
andrew@19
|
11
|
andrew@19
|
12 TempoFollower::TempoFollower(){
|
andrew@20
|
13
|
andrew@20
|
14
|
andrew@20
|
15
|
andrew@20
|
16
|
andrew@19
|
17 maximumTempoInterval = 600;
|
andrew@19
|
18 minimumTempoInterval = 200;
|
andrew@19
|
19 tempoArraySize = maximumTempoInterval - minimumTempoInterval;;
|
andrew@19
|
20
|
andrew@19
|
21 tempoPrior.createVector(tempoArraySize);
|
andrew@19
|
22 tempoPosterior.createVector(tempoArraySize);
|
andrew@19
|
23 tempoLikelihood.createVector(tempoArraySize);
|
andrew@19
|
24
|
andrew@19
|
25 tempoPosterior.addConstant(1);
|
andrew@19
|
26
|
andrew@19
|
27 tempoPrior.offset = minimumTempoInterval;
|
andrew@19
|
28 tempoPosterior.offset = minimumTempoInterval;
|
andrew@19
|
29 tempoLikelihood.offset = minimumTempoInterval;
|
andrew@19
|
30
|
andrew@19
|
31 tempoPrior.scalar = 1;
|
andrew@19
|
32 tempoPosterior.scalar = 1;
|
andrew@19
|
33 tempoLikelihood.scalar = 1;
|
andrew@20
|
34
|
andrew@20
|
35 intervalsToTest[0] = 1;
|
andrew@20
|
36 intervalsToTest[1] = 2;
|
andrew@20
|
37 intervalsToTest[2] = 4;
|
andrew@20
|
38 intervalsToTest[3] = 8;
|
andrew@20
|
39 intervalsToTest[4] = 16;
|
andrew@20
|
40
|
andrew@20
|
41 setUpEventTimeMatrix();
|
andrew@19
|
42 }
|
andrew@19
|
43
|
andrew@20
|
44 void TempoFollower::zero(){
|
andrew@20
|
45 eventTimes.clear();
|
andrew@20
|
46 tempoIntervals.clear();
|
andrew@20
|
47 divisions.clear();
|
andrew@20
|
48 tempo.clear();
|
andrew@20
|
49 globalTempo.clear();
|
andrew@20
|
50 globalTempoTimes.clear();
|
andrew@20
|
51 setUpEventTimeMatrix();
|
andrew@20
|
52 }
|
andrew@19
|
53
|
andrew@19
|
54 void TempoFollower::reset(){
|
andrew@55
|
55 printf("reset temporal follower\n");
|
andrew@19
|
56 tempoPrior.zero();
|
andrew@19
|
57 tempoPosterior.addConstant(1);
|
andrew@20
|
58
|
andrew@19
|
59 }
|
andrew@19
|
60
|
andrew@19
|
61
|
andrew@19
|
62 void TempoFollower::setUpEventTimeMatrix(){
|
andrew@19
|
63 for (int i = 0;i < NUMBER_OF_CHANNELS;i++){
|
andrew@19
|
64 IntVector v;
|
andrew@19
|
65 eventTimes.push_back(v);
|
andrew@19
|
66
|
andrew@19
|
67 DoubleMatrix m;
|
andrew@19
|
68 tempoIntervals.push_back(m);
|
andrew@20
|
69
|
andrew@20
|
70 IntMatrix h;
|
andrew@20
|
71 divisions.push_back(h);
|
andrew@20
|
72
|
andrew@20
|
73 DoubleVector d;
|
andrew@20
|
74 tempo.push_back(d);
|
andrew@20
|
75
|
andrew@19
|
76 }
|
andrew@19
|
77
|
andrew@19
|
78 //[channel][index]
|
andrew@19
|
79 }
|
andrew@19
|
80
|
andrew@19
|
81
|
andrew@19
|
82
|
andrew@19
|
83 void TempoFollower::printEventTimes(){
|
andrew@19
|
84 for (int i = 0;i < NUMBER_OF_CHANNELS;i++){
|
andrew@19
|
85 printf("CHANNEL %i EVENT TIMES...\n", i);
|
andrew@19
|
86 for (int j = 0;j < eventTimes[i].size();j++)
|
andrew@19
|
87 printf("%i\n", eventTimes[i][j]);
|
andrew@19
|
88 }
|
andrew@19
|
89 }
|
andrew@19
|
90
|
andrew@19
|
91
|
andrew@20
|
92 void TempoFollower::printTempoTimes(){
|
andrew@20
|
93
|
andrew@20
|
94 for (int i = 0;i < globalTempo.size();i++){
|
andrew@20
|
95 printf("Time %i : tempo %f\n", globalTempoTimes[i], globalTempo[i]);
|
andrew@20
|
96 }
|
andrew@20
|
97 }
|
andrew@20
|
98
|
andrew@19
|
99 void TempoFollower::updateTempo(const int& channel, const int& timeIn){
|
andrew@19
|
100
|
andrew@19
|
101 eventTimes[channel].push_back(timeIn);
|
andrew@19
|
102
|
andrew@19
|
103 int interval = 0;
|
andrew@19
|
104 int intervalLimit = 3600;
|
andrew@19
|
105
|
andrew@19
|
106 int recentIndex = eventTimes[channel].size()-1;
|
andrew@19
|
107
|
andrew@19
|
108 DoubleVector d;
|
andrew@20
|
109 IntVector div;
|
andrew@19
|
110 int recentEvent = eventTimes[channel][recentIndex];
|
andrew@19
|
111
|
andrew@19
|
112 for (int i = 1;i < eventTimes[channel].size() && interval < intervalLimit;i++){
|
andrew@19
|
113 interval = eventTimes[channel][recentIndex] - eventTimes[channel][recentIndex - i];
|
andrew@19
|
114
|
andrew@20
|
115 for (int k = 0;k < 3;k++){
|
andrew@19
|
116
|
andrew@20
|
117 double divisionInEighthNotes = intervalsToTest[k];
|
andrew@19
|
118 double testInterval = interval / divisionInEighthNotes;
|
andrew@20
|
119 if (testTempoInterval(channel, testInterval, d)){
|
andrew@55
|
120 printf("channel %i interval %i at division %.0f == tempo update %f\n", channel, interval, divisionInEighthNotes, testInterval);
|
andrew@20
|
121 div.push_back((int)divisionInEighthNotes);
|
andrew@20
|
122 }
|
andrew@19
|
123 }
|
andrew@19
|
124 }
|
andrew@19
|
125
|
andrew@19
|
126 (tempoIntervals[channel]).push_back(d);
|
andrew@20
|
127 (divisions[channel]).push_back(div);
|
andrew@19
|
128 updateTempoDistribution(d);
|
andrew@19
|
129
|
andrew@20
|
130 tempo[channel].push_back(playingTempo);
|
andrew@20
|
131
|
andrew@20
|
132 globalTempo.push_back(playingTempo);
|
andrew@20
|
133 globalTempoTimes.push_back(timeIn);
|
andrew@20
|
134
|
andrew@20
|
135
|
andrew@19
|
136 }
|
andrew@19
|
137
|
andrew@19
|
138
|
andrew@19
|
139 bool TempoFollower::testTempoInterval(const int& channel, const double& testInterval, DoubleVector& d){
|
andrew@19
|
140
|
andrew@19
|
141 bool updated = false;
|
andrew@19
|
142 if (testInterval >= minimumTempoInterval && testInterval <= maximumTempoInterval){
|
andrew@19
|
143 d.push_back(testInterval);
|
andrew@19
|
144 updated = true;
|
andrew@19
|
145 }
|
andrew@19
|
146 return updated;
|
andrew@19
|
147 }
|
andrew@19
|
148
|
andrew@19
|
149 void TempoFollower::updateTempoDistribution(const DoubleVector& d){
|
andrew@19
|
150 double tempoLikelihoodToNoiseRatio = 0.8;
|
andrew@20
|
151 tempoLikelihoodStdDev = 4;
|
andrew@20
|
152
|
andrew@19
|
153 tempoLikelihood.zero();
|
andrew@19
|
154 double amount = tempoLikelihoodToNoiseRatio/d.size();
|
andrew@19
|
155 for (int i = 0;i < d.size();i++){
|
andrew@20
|
156 tempoLikelihood.addGaussianShapeFromRealTime(d[i], tempoLikelihoodStdDev, amount);
|
andrew@19
|
157 }
|
andrew@19
|
158 tempoLikelihood.addConstant(0.1*(1-tempoLikelihoodToNoiseRatio));
|
andrew@19
|
159 calculatePosterior();
|
andrew@19
|
160 tempoPosterior.renormalise();
|
andrew@19
|
161 playingTempo = tempoPosterior.getIndexInRealTerms(tempoPosterior.getMAPestimate());
|
andrew@20
|
162
|
andrew@19
|
163 }
|
andrew@19
|
164
|
andrew@19
|
165
|
andrew@19
|
166
|
andrew@19
|
167 void TempoFollower::calculatePosterior(){
|
andrew@19
|
168 tempoPrior.copyFromDynamicVector(tempoPosterior);
|
andrew@19
|
169 for (int i = 0;i < tempoArraySize;i++){
|
andrew@19
|
170 tempoPosterior.array[i] = tempoLikelihood.array[i] * tempoPrior.array[i];
|
andrew@19
|
171 }
|
andrew@19
|
172 }
|
andrew@19
|
173
|
andrew@19
|
174 void TempoFollower::drawTempoArray(ofxWindowRegion& window){
|
andrew@19
|
175 ofSetColor(150,0,250);
|
andrew@19
|
176 tempoPosterior.drawConstrainedVector(0, tempoArraySize, 0, ofGetWidth(), window);
|
andrew@20
|
177
|
andrew@20
|
178 ofSetColor(150,150,150);
|
andrew@20
|
179 tempoLikelihood.drawConstrainedVector(0, tempoArraySize, 0, ofGetWidth(), window);
|
andrew@20
|
180
|
andrew@50
|
181 if (printOutput)
|
andrew@19
|
182 ofDrawBitmapString("tempo "+ofToString(playingTempo), window.x+ 20, window.y + 40);
|
andrew@20
|
183
|
andrew@20
|
184
|
andrew@20
|
185 for (int channel = 0;channel < 3;channel+=2){
|
andrew@20
|
186 int index = tempoIntervals[channel].size()-1;
|
andrew@20
|
187
|
andrew@20
|
188 for (int i = 0;i < 9 && index - i>=0 ;i++){
|
andrew@20
|
189 for (int j = 0;j < tempoIntervals[channel][index - i].size();j++){
|
andrew@20
|
190 ofSetColor(channel*125, 0, 250);
|
andrew@20
|
191 ofCircle(window.x+((tempoIntervals[channel][index - i][j] - minimumTempoInterval)*window.width)/tempoArraySize, window.y+6+(window.height*i/9.0), 4);
|
andrew@20
|
192 ofSetColor(255,255,255);
|
andrew@20
|
193 ofDrawBitmapString(ofToString(divisions[channel][index-i][j]), (window.x+((tempoIntervals[channel][index - i][j] - minimumTempoInterval)*window.width)/tempoArraySize)-3, window.y+8+(window.height*i/9.0));
|
andrew@20
|
194 }
|
andrew@20
|
195 }
|
andrew@20
|
196 }
|
andrew@19
|
197 }
|
andrew@20
|
198
|