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
|
Chris@72
|
23 using namespace std;
|
cannam@0
|
24
|
Chris@72
|
25 Finder::Finder(Matcher *pm)
|
cannam@0
|
26 {
|
Chris@72
|
27 m_m = pm;
|
Chris@72
|
28 m_duration1 = -1;
|
Chris@72
|
29 m_duration2 = -1;
|
cannam@0
|
30 } // constructor
|
cannam@0
|
31
|
cannam@0
|
32 Finder::~Finder()
|
cannam@0
|
33 {
|
cannam@0
|
34 }
|
cannam@0
|
35
|
Chris@60
|
36 void
|
Chris@60
|
37 Finder::setDurations(int d1, int d2)
|
Chris@60
|
38 {
|
Chris@72
|
39 m_duration1 = d1;
|
Chris@72
|
40 m_duration2 = d2;
|
Chris@60
|
41 }
|
Chris@60
|
42
|
Chris@45
|
43 Matcher::Advance
|
cannam@0
|
44 Finder::getExpandDirection(int row, int col)
|
cannam@0
|
45 {
|
cannam@0
|
46 return getExpandDirection(row, col, false);
|
cannam@0
|
47 } // getExpandDirection()
|
cannam@0
|
48
|
Chris@45
|
49 Matcher::Advance
|
cannam@0
|
50 Finder::getExpandDirection(int row, int col, bool check)
|
cannam@0
|
51 {
|
Chris@72
|
52 double min = m_m->getPathCost(row, col);
|
Chris@72
|
53
|
Chris@72
|
54 int bestRow = row;
|
Chris@72
|
55 int bestCol = col;
|
Chris@72
|
56
|
Chris@72
|
57 pair<int, int> rowRange = m_m->getRowRange(col);
|
Chris@72
|
58 if (rowRange.second > row+1) {
|
Chris@72
|
59 rowRange.second = row+1; // don't cheat by looking at future :)
|
Chris@72
|
60 }
|
Chris@72
|
61 for (int index = rowRange.first; index < rowRange.second; index++) {
|
Chris@72
|
62 double tmp = m_m->getPathCost(index, col);
|
cannam@0
|
63 if (tmp < min) {
|
cannam@0
|
64 min = tmp;
|
cannam@0
|
65 bestRow = index;
|
cannam@0
|
66 }
|
cannam@0
|
67 }
|
Chris@72
|
68
|
Chris@72
|
69 pair<int, int> colRange = m_m->getColRange(row);
|
Chris@72
|
70 if (colRange.second > col+1) {
|
Chris@72
|
71 colRange.second = col+1; // don't cheat by looking at future :)
|
Chris@72
|
72 }
|
Chris@72
|
73 for (int index = colRange.first; index < colRange.second; index++) {
|
Chris@72
|
74 double tmp = m_m->getPathCost(row, index);
|
cannam@0
|
75 if (tmp < min) {
|
cannam@0
|
76 min = tmp;
|
cannam@0
|
77 bestCol = index;
|
cannam@0
|
78 bestRow = row;
|
cannam@0
|
79 }
|
cannam@0
|
80 }
|
Chris@72
|
81
|
Chris@45
|
82 if (bestRow == row) {
|
Chris@45
|
83 if (bestCol == col) {
|
Chris@45
|
84 return Matcher::AdvanceBoth;
|
Chris@45
|
85 } else {
|
Chris@45
|
86 return Matcher::AdvanceThis;
|
Chris@45
|
87 }
|
Chris@45
|
88 } else if (bestCol == col) {
|
Chris@45
|
89 return Matcher::AdvanceOther;
|
Chris@45
|
90 } else {
|
Chris@46
|
91 return Matcher::AdvanceNone;
|
Chris@45
|
92 }
|
cannam@0
|
93
|
Chris@73
|
94 }
|
cannam@0
|
95
|
cannam@0
|
96 void
|
cannam@0
|
97 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
|
cannam@0
|
98 {
|
Chris@72
|
99 int prevRowStart = 0, prevRowStop = 0;
|
Chris@72
|
100
|
Chris@72
|
101 for (int r = r1; r <= r2; r++) {
|
Chris@72
|
102
|
Chris@72
|
103 pair<int, int> colRange = m_m->getColRange(r);
|
Chris@72
|
104
|
Chris@72
|
105 int rowStart = max(c1, colRange.first);
|
Chris@72
|
106 int rowStop = min(c2 + 1, colRange.second);
|
Chris@72
|
107
|
Chris@72
|
108 for (int c = rowStart; c < rowStop; c++) {
|
Chris@72
|
109
|
Chris@72
|
110 float newCost = m_m->getDistance(r, c);
|
Chris@72
|
111 Matcher::Advance dir = Matcher::AdvanceNone;
|
Chris@72
|
112
|
Chris@72
|
113 if (r > r1) { // not first row
|
Chris@72
|
114 double min = -1;
|
Chris@72
|
115 if ((c > prevRowStart) && (c <= prevRowStop)) {
|
Chris@72
|
116 // diagonal from (r-1,c-1)
|
Chris@72
|
117 min = m_m->getPathCost(r-1, c-1) + newCost * 2;
|
Chris@72
|
118 dir = Matcher::AdvanceBoth;
|
Chris@72
|
119 }
|
Chris@72
|
120 if ((c >= prevRowStart) && (c < prevRowStop)) {
|
Chris@72
|
121 // vertical from (r-1,c)
|
Chris@72
|
122 double cost = m_m->getPathCost(r-1, c) + newCost;
|
Chris@72
|
123 if ((min < 0) || (cost < min)) {
|
Chris@72
|
124 min = cost;
|
Chris@72
|
125 dir = Matcher::AdvanceThis;
|
Chris@72
|
126 }
|
Chris@72
|
127 }
|
Chris@72
|
128 if (c > rowStart) {
|
Chris@72
|
129 // horizontal from (r,c-1)
|
Chris@72
|
130 double cost = m_m->getPathCost(r, c-1) + newCost;
|
Chris@72
|
131 if ((min < 0) || (cost < min)) {
|
Chris@72
|
132 min = cost;
|
Chris@72
|
133 dir = Matcher::AdvanceOther;
|
Chris@72
|
134 }
|
Chris@72
|
135 }
|
Chris@72
|
136
|
Chris@72
|
137 m_m->setPathCost(r, c, dir, min);
|
Chris@72
|
138
|
Chris@72
|
139 } else if (c > rowStart) { // first row
|
Chris@72
|
140 // horizontal from (r,c-1)
|
Chris@72
|
141 m_m->setPathCost(r, c, Matcher::AdvanceOther,
|
Chris@72
|
142 m_m->getPathCost(r, c-1) + newCost);
|
Chris@72
|
143 }
|
Chris@72
|
144 }
|
Chris@72
|
145
|
Chris@72
|
146 prevRowStart = rowStart;
|
Chris@72
|
147 prevRowStop = rowStop;
|
cannam@0
|
148 }
|
Chris@72
|
149 }
|
Chris@30
|
150
|
Chris@30
|
151 int
|
Chris@31
|
152 Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy)
|
Chris@30
|
153 {
|
Chris@69
|
154 pathx.clear();
|
Chris@69
|
155 pathy.clear();
|
Chris@69
|
156
|
Chris@72
|
157 int ex = m_m->getOtherFrameCount() - 1;
|
Chris@72
|
158 int ey = m_m->getFrameCount() - 1;
|
Chris@69
|
159
|
Chris@69
|
160 if (ex < 0 || ey < 0) {
|
Chris@69
|
161 return 0;
|
Chris@69
|
162 }
|
Chris@66
|
163
|
Chris@66
|
164 int x = ex;
|
Chris@66
|
165 int y = ey;
|
Chris@30
|
166
|
Chris@72
|
167 if (m_duration2 > 0 && m_duration2 < m_m->getOtherFrameCount()) {
|
Chris@72
|
168 x = m_duration2 - 1;
|
Chris@60
|
169 }
|
Chris@72
|
170 if (m_duration1 > 0 && m_duration1 < m_m->getFrameCount()) {
|
Chris@72
|
171 y = m_duration1 - 1;
|
Chris@60
|
172 }
|
Chris@79
|
173
|
Chris@79
|
174 // cerr << "before: x = " << x << ", y = " << y << endl;
|
Chris@60
|
175
|
Chris@72
|
176 if (!m_m->isAvailable(y, x)) {
|
Chris@66
|
177 // Path did not pass through the expected end point --
|
Chris@66
|
178 // probably means the pieces are substantially different in
|
Chris@66
|
179 // the later bits. Reset the expected end point to the end of
|
Chris@66
|
180 // both files including any trailing silence.
|
Chris@66
|
181 cerr << "NOTE: Path did not pass through expected end point, inputs are probably significantly different" << endl;
|
Chris@66
|
182 x = ex;
|
Chris@66
|
183 y = ey;
|
Chris@66
|
184 }
|
Chris@66
|
185
|
Chris@55
|
186 recalculatePathCostMatrix(0, 0, y, x);
|
Chris@55
|
187
|
Chris@66
|
188 // cerr << "start: x = " << x << ", y = " << y << endl;
|
Chris@66
|
189
|
Chris@72
|
190 while (m_m->isAvailable(y, x) && (x > 0 || y > 0)) {
|
Chris@30
|
191
|
Chris@33
|
192 // cerr << "x = " << x << ", y = " << y;
|
Chris@33
|
193
|
Chris@30
|
194 pathx.push_back(x);
|
Chris@30
|
195 pathy.push_back(y);
|
Chris@30
|
196
|
Chris@72
|
197 switch (m_m->getAdvance(y, x)) {
|
Chris@45
|
198 case Matcher::AdvanceThis:
|
Chris@70
|
199 // cerr << ", going down (dist = " << getDistance() << ")" << endl;
|
Chris@33
|
200 y--;
|
Chris@33
|
201 break;
|
Chris@45
|
202 case Matcher::AdvanceOther:
|
Chris@70
|
203 // cerr << ", going left (dist = " << getDistance() << ")" << endl;
|
Chris@33
|
204 x--;
|
Chris@33
|
205 break;
|
Chris@45
|
206 case Matcher::AdvanceBoth:
|
Chris@70
|
207 // cerr << ", going diag (dist = " << getDistance() << ")" << endl;
|
Chris@33
|
208 x--;
|
Chris@33
|
209 y--;
|
Chris@33
|
210 break;
|
Chris@45
|
211 case Matcher::AdvanceNone: // this would indicate a bug, but we wouldn't want to hang
|
Chris@69
|
212 cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
|
Chris@33
|
213 if (x > y) {
|
Chris@33
|
214 x--;
|
Chris@33
|
215 } else {
|
Chris@33
|
216 y--;
|
Chris@33
|
217 }
|
Chris@33
|
218 break;
|
Chris@30
|
219 }
|
Chris@30
|
220 }
|
Chris@30
|
221
|
Chris@72
|
222 if (x > 0 || y > 0) {
|
Chris@72
|
223 cerr << "WARNING: Ran out of available path at (" << y << "," << x
|
Chris@72
|
224 << ")!" << endl;
|
Chris@72
|
225 }
|
Chris@72
|
226
|
Chris@72
|
227 reverse(pathx.begin(), pathx.end());
|
Chris@72
|
228 reverse(pathy.begin(), pathy.end());
|
Chris@30
|
229
|
Chris@31
|
230 if (smooth) {
|
Chris@31
|
231 int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
|
Chris@31
|
232 return smoothedLen;
|
Chris@31
|
233 } else {
|
Chris@31
|
234 return pathx.size();
|
Chris@31
|
235 }
|
Chris@30
|
236 }
|
Chris@30
|
237
|
Chris@30
|
238
|