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 {
|
cannam@0
|
26 if (!p1->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;
|
cannam@0
|
47 index2 = i2 - pm1->first[i1];
|
cannam@0
|
48 }
|
cannam@0
|
49 return (i1 >= 0) && (i2 >= pm1->first[i1]) && (i2 < pm1->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 {
|
cannam@0
|
55 range[0] = pm1->first[row];
|
cannam@0
|
56 range[1] = pm1->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 {
|
cannam@0
|
62 range[0] = pm2->first[col];
|
cannam@0
|
63 range[1] = pm2->last[col];
|
cannam@0
|
64 } // getRowRange()
|
cannam@0
|
65
|
cannam@0
|
66 int
|
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
|
cannam@0
|
72 int
|
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));
|
cannam@0
|
103 if (!find(row, col+1))
|
cannam@0
|
104 return ADVANCE_THIS;
|
cannam@0
|
105 if (!find(row+1, col))
|
cannam@0
|
106 return ADVANCE_OTHER;
|
cannam@0
|
107 }
|
cannam@0
|
108 return ((bestRow==row)? ADVANCE_THIS: 0) |
|
cannam@0
|
109 ((bestCol==col)? ADVANCE_OTHER: 0);
|
cannam@0
|
110
|
cannam@0
|
111 } // getExpandDirection()
|
cannam@0
|
112
|
cannam@0
|
113 unsigned char
|
cannam@0
|
114 Finder::getDistance(int row, int col)
|
cannam@0
|
115 {
|
cannam@0
|
116 if (find(row, col)) {
|
cannam@0
|
117 return pm1->distance[row][col - pm1->first[row]];
|
cannam@0
|
118 }
|
cannam@0
|
119 std::cerr << "getDistance(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
120 throw "getDistance index out of bounds";
|
cannam@0
|
121 } // getDistance()/2
|
cannam@0
|
122
|
cannam@0
|
123 void
|
cannam@0
|
124 Finder::setDistance(int row, int col, unsigned char b)
|
cannam@0
|
125 {
|
cannam@0
|
126 if (find(row, col)) {
|
cannam@0
|
127 pm1->distance[row][col - pm1->first[row]] = b;
|
cannam@0
|
128 return;
|
cannam@0
|
129 }
|
cannam@0
|
130 std::cerr << "setDistance(" << row << "," << col << "," << b << "): out of bounds" << std::endl;
|
cannam@0
|
131 throw "setDistance index out of bounds";
|
cannam@0
|
132 } // setDistance()
|
cannam@0
|
133
|
cannam@0
|
134 int
|
cannam@0
|
135 Finder::getPathCost(int row, int col)
|
cannam@0
|
136 {
|
cannam@0
|
137 if (find(row, col)) // "1" avoids div by 0 below
|
cannam@0
|
138 return pm1->bestPathCost[row][col - pm1->first[row]]*100/ (1+row+col);
|
cannam@0
|
139 std::cerr << "getPathCost(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
140 throw "getPathCost index out of bounds";
|
cannam@0
|
141 } // getPathCost()
|
cannam@0
|
142
|
cannam@0
|
143 int
|
cannam@0
|
144 Finder::getRawPathCost(int row, int col)
|
cannam@0
|
145 {
|
cannam@0
|
146 if (find(row, col))
|
cannam@0
|
147 return pm1->bestPathCost[row][col - pm1->first[row]];
|
cannam@0
|
148 std::cerr << "getRawPathCost(" << row << "," << col << "): out of bounds" << std::endl;
|
cannam@0
|
149 throw "getRawPathCost index out of bounds";
|
cannam@0
|
150 } // getRawPathCost()
|
cannam@0
|
151
|
cannam@0
|
152 void
|
cannam@0
|
153 Finder::setPathCost(int row, int col, int i)
|
cannam@0
|
154 {
|
cannam@0
|
155 if (find(row, col)) {
|
cannam@0
|
156 pm1->bestPathCost[row][col - pm1->first[row]] = i;
|
cannam@0
|
157 return;
|
cannam@0
|
158 }
|
cannam@0
|
159 std::cerr << "setPathCost(" << row << "," << col << "," << i << "): out of bounds" << std::endl;
|
cannam@0
|
160 throw "setPathCost index out of bounds";
|
cannam@0
|
161 } // setPathCost()
|
cannam@0
|
162
|
cannam@0
|
163 unsigned char
|
cannam@0
|
164 Finder::getDistance()
|
cannam@0
|
165 {
|
cannam@0
|
166 return pm1->distance[index1][index2];
|
cannam@0
|
167 } // getDistance()/0
|
cannam@0
|
168
|
cannam@0
|
169 void
|
cannam@0
|
170 Finder::setDistance(int b)
|
cannam@0
|
171 {
|
cannam@0
|
172 pm1->distance[index1][index2] = (unsigned char)b;
|
cannam@0
|
173 } // setDistance()
|
cannam@0
|
174
|
cannam@0
|
175 int
|
cannam@0
|
176 Finder::getPathCost()
|
cannam@0
|
177 {
|
cannam@0
|
178 return pm1->bestPathCost[index1][index2];
|
cannam@0
|
179 } // getPathCost()
|
cannam@0
|
180
|
cannam@0
|
181 void
|
cannam@0
|
182 Finder::setPathCost(int i)
|
cannam@0
|
183 {
|
cannam@0
|
184 pm1->bestPathCost[index1][index2] = i;
|
cannam@0
|
185 } // setPathCost()
|
cannam@0
|
186
|
cannam@0
|
187 void
|
cannam@0
|
188 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
|
cannam@0
|
189 {
|
cannam@0
|
190 if (!find(r1,c1)) {
|
cannam@0
|
191 std::cerr << "recalculatePathCostMatrix(" << r1 << "," << c1 << "): out of bounds" << std::endl;
|
cannam@0
|
192 throw "recalculatePathCostMatrix index out of bounds";
|
cannam@0
|
193 }
|
cannam@0
|
194 int thisRowStart, c;
|
cannam@0
|
195 int prevRowStart = 0, prevRowStop = 0;
|
cannam@0
|
196 for (int r = r1; r <= r2; r++) {
|
cannam@0
|
197 thisRowStart = pm1->first[r];
|
cannam@0
|
198 if (thisRowStart < c1)
|
cannam@0
|
199 thisRowStart = c1;
|
cannam@0
|
200 for (c = thisRowStart; c <= c2; c++) {
|
cannam@0
|
201 if (find(r,c)) {
|
cannam@0
|
202 int i2 = index2;
|
cannam@0
|
203 int newCost = pm1->distance[r][i2];
|
cannam@0
|
204 int dir = 0;
|
cannam@0
|
205 if (r > r1) { // not first row
|
cannam@0
|
206 int min = -1;
|
cannam@0
|
207 if ((c > prevRowStart) && (c <= prevRowStop)) {
|
cannam@0
|
208 // diagonal from (r-1,c-1)
|
cannam@0
|
209 min = pm1->bestPathCost[r-1][c-pm1->first[r-1]-1] +
|
cannam@0
|
210 newCost * 2;
|
cannam@0
|
211 dir = ADVANCE_BOTH;
|
cannam@0
|
212 }
|
cannam@0
|
213 if ((c >= prevRowStart) && (c < prevRowStop)) {
|
cannam@0
|
214 // vertical from (r-1,c)
|
cannam@0
|
215 int cost = pm1->bestPathCost[r-1][c-pm1->first[r-1]] +
|
cannam@0
|
216 newCost;
|
cannam@0
|
217 if ((min == -1) || (cost < min)) {
|
cannam@0
|
218 min = cost;
|
cannam@0
|
219 dir = ADVANCE_THIS;
|
cannam@0
|
220 }
|
cannam@0
|
221 }
|
cannam@0
|
222 if (c > thisRowStart) {
|
cannam@0
|
223 // horizontal from (r,c-1)
|
cannam@0
|
224 int cost =pm1->bestPathCost[r][i2-1]+newCost;
|
cannam@0
|
225 if ((min == -1) || (cost < min)) {
|
cannam@0
|
226 min = cost;
|
cannam@0
|
227 dir = ADVANCE_OTHER;
|
cannam@0
|
228 }
|
cannam@0
|
229 }
|
cannam@0
|
230 pm1->bestPathCost[r][i2] = min;
|
cannam@0
|
231 } else if (c > thisRowStart) { // first row
|
cannam@0
|
232 // horizontal from (r,c-1)
|
cannam@0
|
233 pm1->bestPathCost[r][i2] = pm1->bestPathCost[r][i2-1] +
|
cannam@0
|
234 newCost;
|
cannam@0
|
235 dir = ADVANCE_OTHER;
|
cannam@0
|
236 }
|
cannam@0
|
237 if ((r != r1) || (c != c1)) {
|
cannam@0
|
238 pm1->distance[r][i2] = (unsigned char)
|
cannam@0
|
239 ((pm1->distance[r][i2] & MASK) | dir);
|
cannam@0
|
240 }
|
cannam@0
|
241 } else
|
cannam@0
|
242 break; // end of row
|
cannam@0
|
243 }
|
cannam@0
|
244 prevRowStart = thisRowStart;
|
cannam@0
|
245 prevRowStop = c;
|
cannam@0
|
246 }
|
cannam@0
|
247 } // recalculatePathCostMatrix()
|
Chris@30
|
248
|
Chris@30
|
249 int
|
Chris@30
|
250 Finder::retrievePath(vector<int> &pathx, vector<int> &pathy)
|
Chris@30
|
251 {
|
Chris@30
|
252 int x = pm2->getFrameCount() - 1;
|
Chris@30
|
253 int y = pm1->getFrameCount() - 1;
|
Chris@30
|
254
|
Chris@30
|
255 pathx.clear();
|
Chris@30
|
256 pathy.clear();
|
Chris@30
|
257
|
Chris@30
|
258 while (find(y, x) && ((x > 0) || (y > 0))) {
|
Chris@30
|
259
|
Chris@30
|
260 pathx.push_back(x);
|
Chris@30
|
261 pathy.push_back(y);
|
Chris@30
|
262
|
Chris@30
|
263 switch (getDistance() & ADVANCE_BOTH) {
|
Chris@30
|
264 case ADVANCE_THIS: y--; break;
|
Chris@30
|
265 case ADVANCE_OTHER: x--; break;
|
Chris@30
|
266 case ADVANCE_BOTH: x--; y--; break;
|
Chris@30
|
267 default: // this would indicate a bug, but we wouldn't want to hang
|
Chris@30
|
268 cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
|
Chris@30
|
269 if (x > y) x--; else y--; break;
|
Chris@30
|
270 }
|
Chris@30
|
271 }
|
Chris@30
|
272
|
Chris@30
|
273 std::reverse(pathx.begin(), pathx.end());
|
Chris@30
|
274 std::reverse(pathy.begin(), pathy.end());
|
Chris@30
|
275
|
Chris@30
|
276 int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
|
Chris@30
|
277
|
Chris@30
|
278 return smoothedLen;
|
Chris@30
|
279 }
|
Chris@30
|
280
|
Chris@30
|
281
|