cannam@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@0
|
2
|
cannam@0
|
3 /*
|
cannam@0
|
4 Vamp feature extraction plugin using the MATCH audio alignment
|
cannam@0
|
5 algorithm.
|
cannam@0
|
6
|
cannam@0
|
7 Centre for Digital Music, Queen Mary, University of London.
|
cannam@0
|
8 This file copyright 2007 Simon Dixon, Chris Cannam and QMUL.
|
cannam@0
|
9
|
cannam@0
|
10 This program is free software; you can redistribute it and/or
|
cannam@0
|
11 modify it under the terms of the GNU General Public License as
|
cannam@0
|
12 published by the Free Software Foundation; either version 2 of the
|
cannam@0
|
13 License, or (at your option) any later version. See the file
|
cannam@0
|
14 COPYING included with this distribution for more information.
|
cannam@0
|
15 */
|
cannam@0
|
16
|
cannam@0
|
17 #include "Finder.h"
|
cannam@0
|
18
|
Chris@30
|
19 #include "Path.h"
|
Chris@30
|
20
|
Chris@30
|
21 #include <algorithm>
|
Chris@30
|
22
|
cannam@0
|
23
|
cannam@0
|
24 Finder::Finder(Matcher *p1, Matcher *p2)
|
cannam@0
|
25 {
|
Chris@43
|
26 if (!p1->m_firstPM)
|
cannam@0
|
27 std::cerr << "Warning: wrong args in Finder()" << std::endl;
|
cannam@0
|
28 pm1 = p1;
|
cannam@0
|
29 pm2 = p2;
|
cannam@0
|
30 index1 = 0;
|
cannam@0
|
31 index2 = 0;
|
cannam@0
|
32 rowRange = new int[2];
|
cannam@0
|
33 colRange = new int[2];
|
Chris@60
|
34 duration1 = -1;
|
Chris@60
|
35 duration2 = -1;
|
cannam@0
|
36 } // constructor
|
cannam@0
|
37
|
cannam@0
|
38 Finder::~Finder()
|
cannam@0
|
39 {
|
cannam@0
|
40 delete[] rowRange;
|
cannam@0
|
41 delete[] colRange;
|
cannam@0
|
42 }
|
cannam@0
|
43
|
Chris@60
|
44 void
|
Chris@60
|
45 Finder::setDurations(int d1, int d2)
|
Chris@60
|
46 {
|
Chris@60
|
47 duration1 = d1;
|
Chris@60
|
48 duration2 = d2;
|
Chris@60
|
49 }
|
Chris@60
|
50
|
cannam@0
|
51 bool
|
cannam@0
|
52 Finder::find(int i1, int i2)
|
cannam@0
|
53 {
|
Chris@69
|
54 if ((i1 >= 0) && (i1 < (int)pm1->m_first.size()) &&
|
Chris@69
|
55 (i2 >= pm1->m_first[i1]) && (i2 < pm1->m_last[i1])) {
|
cannam@0
|
56 index1 = i1;
|
Chris@43
|
57 index2 = i2 - pm1->m_first[i1];
|
Chris@69
|
58 return true;
|
Chris@69
|
59 } else {
|
Chris@69
|
60 return false;
|
cannam@0
|
61 }
|
cannam@0
|
62 } // find()
|
cannam@0
|
63
|
cannam@0
|
64 void
|
cannam@0
|
65 Finder::getColRange(int row, int *range)
|
cannam@0
|
66 {
|
Chris@43
|
67 range[0] = pm1->m_first[row];
|
Chris@43
|
68 range[1] = pm1->m_last[row];
|
cannam@0
|
69 } // getColRange()
|
cannam@0
|
70
|
cannam@0
|
71 void
|
cannam@0
|
72 Finder::getRowRange(int col, int *range)
|
cannam@0
|
73 {
|
Chris@43
|
74 range[0] = pm2->m_first[col];
|
Chris@43
|
75 range[1] = pm2->m_last[col];
|
cannam@0
|
76 } // getRowRange()
|
cannam@0
|
77
|
Chris@45
|
78 Matcher::Advance
|
cannam@0
|
79 Finder::getExpandDirection(int row, int col)
|
cannam@0
|
80 {
|
cannam@0
|
81 return getExpandDirection(row, col, false);
|
cannam@0
|
82 } // getExpandDirection()
|
cannam@0
|
83
|
Chris@45
|
84 Matcher::Advance
|
cannam@0
|
85 Finder::getExpandDirection(int row, int col, bool check)
|
cannam@0
|
86 {
|
Chris@53
|
87 double min = getPathCost(row, col);
|
cannam@0
|
88 bestRow = row;
|
cannam@0
|
89 bestCol = col;
|
cannam@0
|
90 getRowRange(col, rowRange);
|
cannam@0
|
91 if (rowRange[1] > row+1)
|
cannam@0
|
92 rowRange[1] = row+1; // don't cheat by looking at future :)
|
cannam@0
|
93 for (int index = rowRange[0]; index < rowRange[1]; index++) {
|
Chris@53
|
94 double tmp = getPathCost(index, col);
|
cannam@0
|
95 if (tmp < min) {
|
cannam@0
|
96 min = tmp;
|
cannam@0
|
97 bestRow = index;
|
cannam@0
|
98 }
|
cannam@0
|
99 }
|
cannam@0
|
100 getColRange(row, colRange);
|
cannam@0
|
101 if (colRange[1] > col+1)
|
cannam@0
|
102 colRange[1] = col+1; // don't cheat by looking at future :)
|
cannam@0
|
103 for (int index = colRange[0]; index < colRange[1]; index++) {
|
Chris@53
|
104 double tmp = getPathCost(row, index);
|
cannam@0
|
105 if (tmp < min) {
|
cannam@0
|
106 min = tmp;
|
cannam@0
|
107 bestCol = index;
|
cannam@0
|
108 bestRow = row;
|
cannam@0
|
109 }
|
cannam@0
|
110 }
|
cannam@0
|
111 // System.err.print(" BEST: " + bestRow + " " + bestCol + " " + check);
|
cannam@0
|
112 // System.err.println(" " + pm1->frameCount + " " + pm2->frameCount);
|
cannam@0
|
113 if (check) {
|
cannam@0
|
114 // System.err.println(find(row+1, col) + " " + find(row, col+1));
|
Chris@45
|
115 if (!find(row, col+1)) {
|
Chris@45
|
116 return Matcher::AdvanceThis;
|
Chris@45
|
117 } else if (!find(row+1, col)) {
|
Chris@45
|
118 return Matcher::AdvanceOther;
|
Chris@45
|
119 }
|
cannam@0
|
120 }
|
Chris@45
|
121
|
Chris@45
|
122 if (bestRow == row) {
|
Chris@45
|
123 if (bestCol == col) {
|
Chris@45
|
124 return Matcher::AdvanceBoth;
|
Chris@45
|
125 } else {
|
Chris@45
|
126 return Matcher::AdvanceThis;
|
Chris@45
|
127 }
|
Chris@45
|
128 } else if (bestCol == col) {
|
Chris@45
|
129 return Matcher::AdvanceOther;
|
Chris@45
|
130 } else {
|
Chris@46
|
131 return Matcher::AdvanceNone;
|
Chris@45
|
132 }
|
cannam@0
|
133
|
cannam@0
|
134 } // getExpandDirection()
|
cannam@0
|
135
|
Chris@45
|
136 float
|
cannam@0
|
137 Finder::getDistance(int row, int col)
|
cannam@0
|
138 {
|
cannam@0
|
139 if (find(row, col)) {
|
Chris@43
|
140 return pm1->m_distance[row][col - pm1->m_first[row]];
|
cannam@0
|
141 }
|
cannam@0
|
142 std::cerr << "getDistance(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
143 throw "getDistance index out of bounds";
|
cannam@0
|
144 } // getDistance()/2
|
cannam@0
|
145
|
cannam@0
|
146 void
|
Chris@45
|
147 Finder::setDistance(int row, int col, float b)
|
cannam@0
|
148 {
|
cannam@0
|
149 if (find(row, col)) {
|
Chris@43
|
150 pm1->m_distance[row][col - pm1->m_first[row]] = b;
|
cannam@0
|
151 return;
|
cannam@0
|
152 }
|
cannam@0
|
153 std::cerr << "setDistance(" << row << "," << col << "," << b << "): out of bounds" << std::endl;
|
cannam@0
|
154 throw "setDistance index out of bounds";
|
cannam@0
|
155 } // setDistance()
|
cannam@0
|
156
|
Chris@53
|
157 double
|
cannam@0
|
158 Finder::getPathCost(int row, int col)
|
cannam@0
|
159 {
|
cannam@0
|
160 if (find(row, col)) // "1" avoids div by 0 below
|
Chris@45
|
161 return pm1->m_bestPathCost[row][col - pm1->m_first[row]] / float(1+row+col);
|
cannam@0
|
162 std::cerr << "getPathCost(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
163 throw "getPathCost index out of bounds";
|
cannam@0
|
164 } // getPathCost()
|
cannam@0
|
165
|
Chris@53
|
166 double
|
cannam@0
|
167 Finder::getRawPathCost(int row, int col)
|
cannam@0
|
168 {
|
cannam@0
|
169 if (find(row, col))
|
Chris@43
|
170 return pm1->m_bestPathCost[row][col - pm1->m_first[row]];
|
cannam@0
|
171 std::cerr << "getRawPathCost(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
172 throw "getRawPathCost index out of bounds";
|
cannam@0
|
173 } // getRawPathCost()
|
cannam@0
|
174
|
cannam@0
|
175 void
|
Chris@53
|
176 Finder::setPathCost(int row, int col, double cost)
|
cannam@0
|
177 {
|
cannam@0
|
178 if (find(row, col)) {
|
Chris@45
|
179 pm1->m_bestPathCost[row][col - pm1->m_first[row]] = cost;
|
cannam@0
|
180 return;
|
cannam@0
|
181 }
|
Chris@45
|
182 std::cerr << "setPathCost(" << row << "," << col << "," << cost << "): out of bounds" << std::endl;
|
cannam@0
|
183 throw "setPathCost index out of bounds";
|
cannam@0
|
184 } // setPathCost()
|
cannam@0
|
185
|
Chris@45
|
186 Matcher::Advance
|
Chris@45
|
187 Finder::getAdvance()
|
Chris@45
|
188 {
|
Chris@45
|
189 return pm1->m_advance[index1][index2];
|
Chris@45
|
190 }
|
Chris@45
|
191
|
Chris@45
|
192 void
|
Chris@45
|
193 Finder::setAdvance(Matcher::Advance a)
|
Chris@45
|
194 {
|
Chris@45
|
195 pm1->m_advance[index1][index2] = a;
|
Chris@45
|
196 }
|
Chris@45
|
197
|
Chris@45
|
198 float
|
cannam@0
|
199 Finder::getDistance()
|
cannam@0
|
200 {
|
Chris@43
|
201 return pm1->m_distance[index1][index2];
|
cannam@0
|
202 } // getDistance()/0
|
cannam@0
|
203
|
cannam@0
|
204 void
|
Chris@45
|
205 Finder::setDistance(float b)
|
cannam@0
|
206 {
|
Chris@45
|
207 pm1->m_distance[index1][index2] = b;
|
cannam@0
|
208 } // setDistance()
|
cannam@0
|
209
|
Chris@53
|
210 double
|
cannam@0
|
211 Finder::getPathCost()
|
cannam@0
|
212 {
|
Chris@43
|
213 return pm1->m_bestPathCost[index1][index2];
|
cannam@0
|
214 } // getPathCost()
|
cannam@0
|
215
|
cannam@0
|
216 void
|
Chris@53
|
217 Finder::setPathCost(double cost)
|
cannam@0
|
218 {
|
Chris@45
|
219 pm1->m_bestPathCost[index1][index2] = cost;
|
cannam@0
|
220 } // setPathCost()
|
cannam@0
|
221
|
cannam@0
|
222 void
|
cannam@0
|
223 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
|
cannam@0
|
224 {
|
cannam@0
|
225 if (!find(r1,c1)) {
|
cannam@0
|
226 std::cerr << "recalculatePathCostMatrix(" << r1 << "," << c1 << "): out of bounds" << std::endl;
|
cannam@0
|
227 throw "recalculatePathCostMatrix index out of bounds";
|
cannam@0
|
228 }
|
cannam@0
|
229 int thisRowStart, c;
|
cannam@0
|
230 int prevRowStart = 0, prevRowStop = 0;
|
cannam@0
|
231 for (int r = r1; r <= r2; r++) {
|
Chris@43
|
232 thisRowStart = pm1->m_first[r];
|
cannam@0
|
233 if (thisRowStart < c1)
|
cannam@0
|
234 thisRowStart = c1;
|
cannam@0
|
235 for (c = thisRowStart; c <= c2; c++) {
|
cannam@0
|
236 if (find(r,c)) {
|
cannam@0
|
237 int i2 = index2;
|
Chris@52
|
238 float newCost = pm1->m_distance[r][i2];
|
Chris@45
|
239 Matcher::Advance dir = Matcher::AdvanceNone;
|
cannam@0
|
240 if (r > r1) { // not first row
|
Chris@53
|
241 double min = -1;
|
cannam@0
|
242 if ((c > prevRowStart) && (c <= prevRowStop)) {
|
cannam@0
|
243 // diagonal from (r-1,c-1)
|
Chris@43
|
244 min = pm1->m_bestPathCost[r-1][c-pm1->m_first[r-1]-1] +
|
cannam@0
|
245 newCost * 2;
|
Chris@45
|
246 dir = Matcher::AdvanceBoth;
|
cannam@0
|
247 }
|
cannam@0
|
248 if ((c >= prevRowStart) && (c < prevRowStop)) {
|
cannam@0
|
249 // vertical from (r-1,c)
|
Chris@53
|
250 double cost = pm1->m_bestPathCost[r-1][c-pm1->m_first[r-1]] +
|
cannam@0
|
251 newCost;
|
Chris@70
|
252 if ((min < 0) || (cost < min)) {
|
cannam@0
|
253 min = cost;
|
Chris@45
|
254 dir = Matcher::AdvanceThis;
|
cannam@0
|
255 }
|
cannam@0
|
256 }
|
cannam@0
|
257 if (c > thisRowStart) {
|
cannam@0
|
258 // horizontal from (r,c-1)
|
Chris@70
|
259 double cost = pm1->m_bestPathCost[r][i2-1]+newCost;
|
Chris@70
|
260 if ((min < 0) || (cost < min)) {
|
cannam@0
|
261 min = cost;
|
Chris@45
|
262 dir = Matcher::AdvanceOther;
|
cannam@0
|
263 }
|
cannam@0
|
264 }
|
Chris@43
|
265 pm1->m_bestPathCost[r][i2] = min;
|
cannam@0
|
266 } else if (c > thisRowStart) { // first row
|
cannam@0
|
267 // horizontal from (r,c-1)
|
Chris@43
|
268 pm1->m_bestPathCost[r][i2] = pm1->m_bestPathCost[r][i2-1] +
|
cannam@0
|
269 newCost;
|
Chris@45
|
270 dir = Matcher::AdvanceOther;
|
cannam@0
|
271 }
|
Chris@45
|
272 pm1->m_advance[r][i2] = dir;
|
cannam@0
|
273 } else
|
cannam@0
|
274 break; // end of row
|
cannam@0
|
275 }
|
cannam@0
|
276 prevRowStart = thisRowStart;
|
cannam@0
|
277 prevRowStop = c;
|
cannam@0
|
278 }
|
cannam@0
|
279 } // recalculatePathCostMatrix()
|
Chris@30
|
280
|
Chris@30
|
281 int
|
Chris@31
|
282 Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy)
|
Chris@30
|
283 {
|
Chris@69
|
284 pathx.clear();
|
Chris@69
|
285 pathy.clear();
|
Chris@69
|
286
|
Chris@66
|
287 int ex = pm2->getFrameCount() - 1;
|
Chris@66
|
288 int ey = pm1->getFrameCount() - 1;
|
Chris@69
|
289
|
Chris@69
|
290 if (ex < 0 || ey < 0) {
|
Chris@69
|
291 return 0;
|
Chris@69
|
292 }
|
Chris@66
|
293
|
Chris@66
|
294 int x = ex;
|
Chris@66
|
295 int y = ey;
|
Chris@66
|
296
|
Chris@66
|
297 // cerr << "before: x = " << x << ", y = " << y << endl;
|
Chris@30
|
298
|
Chris@60
|
299 if (duration2 > 0 && duration2 < pm2->getFrameCount()) {
|
Chris@60
|
300 x = duration2 - 1;
|
Chris@60
|
301 }
|
Chris@60
|
302 if (duration1 > 0 && duration1 < pm1->getFrameCount()) {
|
Chris@60
|
303 y = duration1 - 1;
|
Chris@60
|
304 }
|
Chris@60
|
305
|
Chris@66
|
306 if (!find(y, x)) {
|
Chris@66
|
307 // Path did not pass through the expected end point --
|
Chris@66
|
308 // probably means the pieces are substantially different in
|
Chris@66
|
309 // the later bits. Reset the expected end point to the end of
|
Chris@66
|
310 // both files including any trailing silence.
|
Chris@66
|
311 cerr << "NOTE: Path did not pass through expected end point, inputs are probably significantly different" << endl;
|
Chris@66
|
312 x = ex;
|
Chris@66
|
313 y = ey;
|
Chris@66
|
314 }
|
Chris@66
|
315
|
Chris@55
|
316 recalculatePathCostMatrix(0, 0, y, x);
|
Chris@55
|
317
|
Chris@66
|
318 // cerr << "start: x = " << x << ", y = " << y << endl;
|
Chris@66
|
319
|
Chris@30
|
320 while (find(y, x) && ((x > 0) || (y > 0))) {
|
Chris@30
|
321
|
Chris@33
|
322 // cerr << "x = " << x << ", y = " << y;
|
Chris@33
|
323
|
Chris@30
|
324 pathx.push_back(x);
|
Chris@30
|
325 pathy.push_back(y);
|
Chris@30
|
326
|
Chris@45
|
327 switch (getAdvance()) {
|
Chris@45
|
328 case Matcher::AdvanceThis:
|
Chris@70
|
329 // cerr << ", going down (dist = " << getDistance() << ")" << endl;
|
Chris@33
|
330 y--;
|
Chris@33
|
331 break;
|
Chris@45
|
332 case Matcher::AdvanceOther:
|
Chris@70
|
333 // cerr << ", going left (dist = " << getDistance() << ")" << endl;
|
Chris@33
|
334 x--;
|
Chris@33
|
335 break;
|
Chris@45
|
336 case Matcher::AdvanceBoth:
|
Chris@70
|
337 // cerr << ", going diag (dist = " << getDistance() << ")" << endl;
|
Chris@33
|
338 x--;
|
Chris@33
|
339 y--;
|
Chris@33
|
340 break;
|
Chris@45
|
341 case Matcher::AdvanceNone: // this would indicate a bug, but we wouldn't want to hang
|
Chris@69
|
342 cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
|
Chris@33
|
343 if (x > y) {
|
Chris@33
|
344 x--;
|
Chris@33
|
345 } else {
|
Chris@33
|
346 y--;
|
Chris@33
|
347 }
|
Chris@33
|
348 break;
|
Chris@30
|
349 }
|
Chris@30
|
350 }
|
Chris@30
|
351
|
Chris@30
|
352 std::reverse(pathx.begin(), pathx.end());
|
Chris@30
|
353 std::reverse(pathy.begin(), pathy.end());
|
Chris@30
|
354
|
Chris@31
|
355 if (smooth) {
|
Chris@31
|
356 int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
|
Chris@31
|
357 return smoothedLen;
|
Chris@31
|
358 } else {
|
Chris@31
|
359 return pathx.size();
|
Chris@31
|
360 }
|
Chris@30
|
361 }
|
Chris@30
|
362
|
Chris@30
|
363
|