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];
|
cannam@0
|
34 } // constructor
|
cannam@0
|
35
|
cannam@0
|
36 Finder::~Finder()
|
cannam@0
|
37 {
|
cannam@0
|
38 delete[] rowRange;
|
cannam@0
|
39 delete[] colRange;
|
cannam@0
|
40 }
|
cannam@0
|
41
|
cannam@0
|
42 bool
|
cannam@0
|
43 Finder::find(int i1, int i2)
|
cannam@0
|
44 {
|
cannam@0
|
45 if (i1 >= 0) {
|
cannam@0
|
46 index1 = i1;
|
Chris@43
|
47 index2 = i2 - pm1->m_first[i1];
|
cannam@0
|
48 }
|
Chris@43
|
49 return (i1 >= 0) && (i2 >= pm1->m_first[i1]) && (i2 < pm1->m_last[i1]);
|
cannam@0
|
50 } // find()
|
cannam@0
|
51
|
cannam@0
|
52 void
|
cannam@0
|
53 Finder::getColRange(int row, int *range)
|
cannam@0
|
54 {
|
Chris@43
|
55 range[0] = pm1->m_first[row];
|
Chris@43
|
56 range[1] = pm1->m_last[row];
|
cannam@0
|
57 } // getColRange()
|
cannam@0
|
58
|
cannam@0
|
59 void
|
cannam@0
|
60 Finder::getRowRange(int col, int *range)
|
cannam@0
|
61 {
|
Chris@43
|
62 range[0] = pm2->m_first[col];
|
Chris@43
|
63 range[1] = pm2->m_last[col];
|
cannam@0
|
64 } // getRowRange()
|
cannam@0
|
65
|
Chris@45
|
66 Matcher::Advance
|
cannam@0
|
67 Finder::getExpandDirection(int row, int col)
|
cannam@0
|
68 {
|
cannam@0
|
69 return getExpandDirection(row, col, false);
|
cannam@0
|
70 } // getExpandDirection()
|
cannam@0
|
71
|
Chris@45
|
72 Matcher::Advance
|
cannam@0
|
73 Finder::getExpandDirection(int row, int col, bool check)
|
cannam@0
|
74 {
|
cannam@0
|
75 int min = getPathCost(row, col);
|
cannam@0
|
76 bestRow = row;
|
cannam@0
|
77 bestCol = col;
|
cannam@0
|
78 getRowRange(col, rowRange);
|
cannam@0
|
79 if (rowRange[1] > row+1)
|
cannam@0
|
80 rowRange[1] = row+1; // don't cheat by looking at future :)
|
cannam@0
|
81 for (int index = rowRange[0]; index < rowRange[1]; index++) {
|
cannam@0
|
82 int tmp = getPathCost(index, col);
|
cannam@0
|
83 if (tmp < min) {
|
cannam@0
|
84 min = tmp;
|
cannam@0
|
85 bestRow = index;
|
cannam@0
|
86 }
|
cannam@0
|
87 }
|
cannam@0
|
88 getColRange(row, colRange);
|
cannam@0
|
89 if (colRange[1] > col+1)
|
cannam@0
|
90 colRange[1] = col+1; // don't cheat by looking at future :)
|
cannam@0
|
91 for (int index = colRange[0]; index < colRange[1]; index++) {
|
cannam@0
|
92 int tmp = getPathCost(row, index);
|
cannam@0
|
93 if (tmp < min) {
|
cannam@0
|
94 min = tmp;
|
cannam@0
|
95 bestCol = index;
|
cannam@0
|
96 bestRow = row;
|
cannam@0
|
97 }
|
cannam@0
|
98 }
|
cannam@0
|
99 // System.err.print(" BEST: " + bestRow + " " + bestCol + " " + check);
|
cannam@0
|
100 // System.err.println(" " + pm1->frameCount + " " + pm2->frameCount);
|
cannam@0
|
101 if (check) {
|
cannam@0
|
102 // System.err.println(find(row+1, col) + " " + find(row, col+1));
|
Chris@45
|
103 if (!find(row, col+1)) {
|
Chris@45
|
104 return Matcher::AdvanceThis;
|
Chris@45
|
105 } else if (!find(row+1, col)) {
|
Chris@45
|
106 return Matcher::AdvanceOther;
|
Chris@45
|
107 }
|
cannam@0
|
108 }
|
Chris@45
|
109
|
Chris@45
|
110 if (bestRow == row) {
|
Chris@45
|
111 if (bestCol == col) {
|
Chris@45
|
112 return Matcher::AdvanceBoth;
|
Chris@45
|
113 } else {
|
Chris@45
|
114 return Matcher::AdvanceThis;
|
Chris@45
|
115 }
|
Chris@45
|
116 } else if (bestCol == col) {
|
Chris@45
|
117 return Matcher::AdvanceOther;
|
Chris@45
|
118 } else {
|
Chris@45
|
119 return Matcher::AdvanceBoth;
|
Chris@45
|
120 }
|
cannam@0
|
121
|
cannam@0
|
122 } // getExpandDirection()
|
cannam@0
|
123
|
Chris@45
|
124 float
|
cannam@0
|
125 Finder::getDistance(int row, int col)
|
cannam@0
|
126 {
|
cannam@0
|
127 if (find(row, col)) {
|
Chris@43
|
128 return pm1->m_distance[row][col - pm1->m_first[row]];
|
cannam@0
|
129 }
|
cannam@0
|
130 std::cerr << "getDistance(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
131 throw "getDistance index out of bounds";
|
cannam@0
|
132 } // getDistance()/2
|
cannam@0
|
133
|
cannam@0
|
134 void
|
Chris@45
|
135 Finder::setDistance(int row, int col, float b)
|
cannam@0
|
136 {
|
cannam@0
|
137 if (find(row, col)) {
|
Chris@43
|
138 pm1->m_distance[row][col - pm1->m_first[row]] = b;
|
cannam@0
|
139 return;
|
cannam@0
|
140 }
|
cannam@0
|
141 std::cerr << "setDistance(" << row << "," << col << "," << b << "): out of bounds" << std::endl;
|
cannam@0
|
142 throw "setDistance index out of bounds";
|
cannam@0
|
143 } // setDistance()
|
cannam@0
|
144
|
Chris@45
|
145 float
|
cannam@0
|
146 Finder::getPathCost(int row, int col)
|
cannam@0
|
147 {
|
cannam@0
|
148 if (find(row, col)) // "1" avoids div by 0 below
|
Chris@45
|
149 return pm1->m_bestPathCost[row][col - pm1->m_first[row]] / float(1+row+col);
|
cannam@0
|
150 std::cerr << "getPathCost(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
151 throw "getPathCost index out of bounds";
|
cannam@0
|
152 } // getPathCost()
|
cannam@0
|
153
|
Chris@45
|
154 float
|
cannam@0
|
155 Finder::getRawPathCost(int row, int col)
|
cannam@0
|
156 {
|
cannam@0
|
157 if (find(row, col))
|
Chris@43
|
158 return pm1->m_bestPathCost[row][col - pm1->m_first[row]];
|
cannam@0
|
159 std::cerr << "getRawPathCost(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
160 throw "getRawPathCost index out of bounds";
|
cannam@0
|
161 } // getRawPathCost()
|
cannam@0
|
162
|
cannam@0
|
163 void
|
Chris@45
|
164 Finder::setPathCost(int row, int col, float cost)
|
cannam@0
|
165 {
|
cannam@0
|
166 if (find(row, col)) {
|
Chris@45
|
167 pm1->m_bestPathCost[row][col - pm1->m_first[row]] = cost;
|
cannam@0
|
168 return;
|
cannam@0
|
169 }
|
Chris@45
|
170 std::cerr << "setPathCost(" << row << "," << col << "," << cost << "): out of bounds" << std::endl;
|
cannam@0
|
171 throw "setPathCost index out of bounds";
|
cannam@0
|
172 } // setPathCost()
|
cannam@0
|
173
|
Chris@45
|
174 Matcher::Advance
|
Chris@45
|
175 Finder::getAdvance()
|
Chris@45
|
176 {
|
Chris@45
|
177 return pm1->m_advance[index1][index2];
|
Chris@45
|
178 }
|
Chris@45
|
179
|
Chris@45
|
180 void
|
Chris@45
|
181 Finder::setAdvance(Matcher::Advance a)
|
Chris@45
|
182 {
|
Chris@45
|
183 pm1->m_advance[index1][index2] = a;
|
Chris@45
|
184 }
|
Chris@45
|
185
|
Chris@45
|
186 float
|
cannam@0
|
187 Finder::getDistance()
|
cannam@0
|
188 {
|
Chris@43
|
189 return pm1->m_distance[index1][index2];
|
cannam@0
|
190 } // getDistance()/0
|
cannam@0
|
191
|
cannam@0
|
192 void
|
Chris@45
|
193 Finder::setDistance(float b)
|
cannam@0
|
194 {
|
Chris@45
|
195 pm1->m_distance[index1][index2] = b;
|
cannam@0
|
196 } // setDistance()
|
cannam@0
|
197
|
Chris@45
|
198 float
|
cannam@0
|
199 Finder::getPathCost()
|
cannam@0
|
200 {
|
Chris@43
|
201 return pm1->m_bestPathCost[index1][index2];
|
cannam@0
|
202 } // getPathCost()
|
cannam@0
|
203
|
cannam@0
|
204 void
|
Chris@45
|
205 Finder::setPathCost(float cost)
|
cannam@0
|
206 {
|
Chris@45
|
207 pm1->m_bestPathCost[index1][index2] = cost;
|
cannam@0
|
208 } // setPathCost()
|
cannam@0
|
209
|
cannam@0
|
210 void
|
cannam@0
|
211 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
|
cannam@0
|
212 {
|
cannam@0
|
213 if (!find(r1,c1)) {
|
cannam@0
|
214 std::cerr << "recalculatePathCostMatrix(" << r1 << "," << c1 << "): out of bounds" << std::endl;
|
cannam@0
|
215 throw "recalculatePathCostMatrix index out of bounds";
|
cannam@0
|
216 }
|
cannam@0
|
217 int thisRowStart, c;
|
cannam@0
|
218 int prevRowStart = 0, prevRowStop = 0;
|
cannam@0
|
219 for (int r = r1; r <= r2; r++) {
|
Chris@43
|
220 thisRowStart = pm1->m_first[r];
|
cannam@0
|
221 if (thisRowStart < c1)
|
cannam@0
|
222 thisRowStart = c1;
|
cannam@0
|
223 for (c = thisRowStart; c <= c2; c++) {
|
cannam@0
|
224 if (find(r,c)) {
|
cannam@0
|
225 int i2 = index2;
|
Chris@43
|
226 int newCost = pm1->m_distance[r][i2];
|
Chris@45
|
227 Matcher::Advance dir = Matcher::AdvanceNone;
|
cannam@0
|
228 if (r > r1) { // not first row
|
cannam@0
|
229 int min = -1;
|
cannam@0
|
230 if ((c > prevRowStart) && (c <= prevRowStop)) {
|
cannam@0
|
231 // diagonal from (r-1,c-1)
|
Chris@43
|
232 min = pm1->m_bestPathCost[r-1][c-pm1->m_first[r-1]-1] +
|
cannam@0
|
233 newCost * 2;
|
Chris@45
|
234 dir = Matcher::AdvanceBoth;
|
cannam@0
|
235 }
|
cannam@0
|
236 if ((c >= prevRowStart) && (c < prevRowStop)) {
|
cannam@0
|
237 // vertical from (r-1,c)
|
Chris@43
|
238 int cost = pm1->m_bestPathCost[r-1][c-pm1->m_first[r-1]] +
|
cannam@0
|
239 newCost;
|
cannam@0
|
240 if ((min == -1) || (cost < min)) {
|
cannam@0
|
241 min = cost;
|
Chris@45
|
242 dir = Matcher::AdvanceThis;
|
cannam@0
|
243 }
|
cannam@0
|
244 }
|
cannam@0
|
245 if (c > thisRowStart) {
|
cannam@0
|
246 // horizontal from (r,c-1)
|
Chris@43
|
247 int cost =pm1->m_bestPathCost[r][i2-1]+newCost;
|
cannam@0
|
248 if ((min == -1) || (cost < min)) {
|
cannam@0
|
249 min = cost;
|
Chris@45
|
250 dir = Matcher::AdvanceOther;
|
cannam@0
|
251 }
|
cannam@0
|
252 }
|
Chris@43
|
253 pm1->m_bestPathCost[r][i2] = min;
|
cannam@0
|
254 } else if (c > thisRowStart) { // first row
|
cannam@0
|
255 // horizontal from (r,c-1)
|
Chris@43
|
256 pm1->m_bestPathCost[r][i2] = pm1->m_bestPathCost[r][i2-1] +
|
cannam@0
|
257 newCost;
|
Chris@45
|
258 dir = Matcher::AdvanceOther;
|
cannam@0
|
259 }
|
Chris@45
|
260 pm1->m_advance[r][i2] = dir;
|
cannam@0
|
261 } else
|
cannam@0
|
262 break; // end of row
|
cannam@0
|
263 }
|
cannam@0
|
264 prevRowStart = thisRowStart;
|
cannam@0
|
265 prevRowStop = c;
|
cannam@0
|
266 }
|
cannam@0
|
267 } // recalculatePathCostMatrix()
|
Chris@30
|
268
|
Chris@30
|
269 int
|
Chris@31
|
270 Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy)
|
Chris@30
|
271 {
|
Chris@30
|
272 int x = pm2->getFrameCount() - 1;
|
Chris@30
|
273 int y = pm1->getFrameCount() - 1;
|
Chris@30
|
274
|
Chris@30
|
275 pathx.clear();
|
Chris@30
|
276 pathy.clear();
|
Chris@30
|
277
|
Chris@30
|
278 while (find(y, x) && ((x > 0) || (y > 0))) {
|
Chris@30
|
279
|
Chris@33
|
280 // cerr << "x = " << x << ", y = " << y;
|
Chris@33
|
281
|
Chris@30
|
282 pathx.push_back(x);
|
Chris@30
|
283 pathy.push_back(y);
|
Chris@30
|
284
|
Chris@45
|
285 switch (getAdvance()) {
|
Chris@45
|
286 case Matcher::AdvanceThis:
|
Chris@33
|
287 // cerr << ", going down (dist = " << (int)getDistance() << ")" << endl;
|
Chris@33
|
288 y--;
|
Chris@33
|
289 break;
|
Chris@45
|
290 case Matcher::AdvanceOther:
|
Chris@33
|
291 // cerr << ", going left (dist = " << (int)getDistance() << ")" << endl;
|
Chris@33
|
292 x--;
|
Chris@33
|
293 break;
|
Chris@45
|
294 case Matcher::AdvanceBoth:
|
Chris@33
|
295 // cerr << ", going diag (dist = " << (int)getDistance() << ")" << endl;
|
Chris@33
|
296 x--;
|
Chris@33
|
297 y--;
|
Chris@33
|
298 break;
|
Chris@45
|
299 case Matcher::AdvanceNone: // this would indicate a bug, but we wouldn't want to hang
|
Chris@33
|
300 // cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
|
Chris@33
|
301 if (x > y) {
|
Chris@33
|
302 x--;
|
Chris@33
|
303 } else {
|
Chris@33
|
304 y--;
|
Chris@33
|
305 }
|
Chris@33
|
306 break;
|
Chris@30
|
307 }
|
Chris@30
|
308 }
|
Chris@30
|
309
|
Chris@30
|
310 std::reverse(pathx.begin(), pathx.end());
|
Chris@30
|
311 std::reverse(pathy.begin(), pathy.end());
|
Chris@30
|
312
|
Chris@31
|
313 if (smooth) {
|
Chris@31
|
314 int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
|
Chris@31
|
315 return smoothedLen;
|
Chris@31
|
316 } else {
|
Chris@31
|
317 return pathx.size();
|
Chris@31
|
318 }
|
Chris@30
|
319 }
|
Chris@30
|
320
|
Chris@30
|
321
|