annotate src/Matcher.cpp @ 86:f07b9b7f1ab6 refactors

Previous commit was a mistake: the ahead-of-time business is in Finder::getExpandDirection. In fact we were failing to swap advance directions in forward path when writing to the "other" finder. This does not actually affect the backward path calculation, but it does mean we can restore the sanity check.
author Chris Cannam
date Thu, 27 Nov 2014 12:08:16 +0000
parents de7034e93dd0
children fca28ed15c6e
rev   line source
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 "Matcher.h"
cannam@0 18
cannam@0 19 #include <iostream>
cannam@0 20
cannam@4 21 #include <cstdlib>
Chris@16 22 #include <cassert>
cannam@4 23
Chris@72 24 using namespace std;
Chris@72 25
Chris@10 26 //#define DEBUG_MATCHER 1
Chris@10 27
Chris@43 28 Matcher::Matcher(Parameters parameters, Matcher *p, int m_featureSize_) :
Chris@43 29 m_params(parameters),
Chris@43 30 m_featureSize(m_featureSize_),
Chris@43 31 m_metric(parameters.distanceNorm)
Chris@23 32 {
Chris@23 33 #ifdef DEBUG_MATCHER
Chris@43 34 cerr << "Matcher::Matcher(" << m_params.sampleRate << ", " << p << ", " << m_featureSize << ")" << endl;
Chris@23 35 #endif
Chris@23 36
Chris@43 37 m_otherMatcher = p; // the first matcher will need this to be set later
Chris@43 38 m_firstPM = (!p);
Chris@43 39 m_frameCount = 0;
Chris@43 40 m_runCount = 0;
Chris@43 41 m_blockSize = 0;
cannam@0 42
Chris@43 43 m_blockSize = lrint(m_params.blockTime / m_params.hopTime);
Chris@15 44 #ifdef DEBUG_MATCHER
Chris@43 45 cerr << "Matcher: m_blockSize = " << m_blockSize << endl;
Chris@15 46 #endif
cannam@0 47
Chris@43 48 m_initialised = false;
Chris@23 49 }
cannam@0 50
cannam@0 51 Matcher::~Matcher()
cannam@0 52 {
Chris@10 53 #ifdef DEBUG_MATCHER
Chris@15 54 cerr << "Matcher(" << this << ")::~Matcher()" << endl;
Chris@10 55 #endif
cannam@0 56 }
cannam@0 57
cannam@0 58 void
cannam@0 59 Matcher::init()
cannam@0 60 {
Chris@43 61 if (m_initialised) return;
cannam@0 62
Chris@43 63 m_frames = vector<vector<double> >
Chris@69 64 (m_blockSize, vector<double>(m_featureSize, -1.0));
cannam@0 65
Chris@43 66 m_distXSize = m_blockSize * 2;
Chris@45 67
Chris@41 68 size();
cannam@0 69
Chris@43 70 m_frameCount = 0;
Chris@43 71 m_runCount = 0;
Chris@38 72
Chris@43 73 m_initialised = true;
Chris@16 74 }
Chris@16 75
cannam@0 76 void
Chris@41 77 Matcher::size()
cannam@0 78 {
Chris@43 79 int distSize = (m_params.maxRunCount + 1) * m_blockSize;
Chris@71 80 m_bestPathCost.resize(m_distXSize, vector<double>(distSize, -1));
Chris@71 81 m_distance.resize(m_distXSize, vector<float>(distSize, -1));
Chris@45 82 m_advance.resize(m_distXSize, vector<Advance>(distSize, AdvanceNone));
Chris@43 83 m_first.resize(m_distXSize, 0);
Chris@43 84 m_last.resize(m_distXSize, 0);
Chris@38 85 }
cannam@0 86
Chris@23 87 void
Chris@72 88 Matcher::consumeFeatureVector(vector<double> feature)
Chris@23 89 {
Chris@43 90 if (!m_initialised) init();
Chris@43 91 int frameIndex = m_frameCount % m_blockSize;
Chris@43 92 m_frames[frameIndex] = feature;
Chris@23 93 calcAdvance();
Chris@21 94 }
Chris@21 95
Chris@21 96 void
Chris@21 97 Matcher::calcAdvance()
Chris@21 98 {
Chris@43 99 int frameIndex = m_frameCount % m_blockSize;
Chris@21 100
Chris@43 101 if (m_frameCount >= m_distXSize) {
Chris@43 102 m_distXSize *= 2;
Chris@41 103 size();
cannam@0 104 }
cannam@0 105
Chris@43 106 if (m_firstPM && (m_frameCount >= m_blockSize)) {
cannam@0 107
Chris@43 108 int len = m_last[m_frameCount - m_blockSize] -
Chris@43 109 m_first[m_frameCount - m_blockSize];
cannam@0 110
Chris@43 111 // We need to copy distance[m_frameCount-m_blockSize] to
Chris@43 112 // distance[m_frameCount], and then truncate
Chris@43 113 // distance[m_frameCount-m_blockSize] to its first len elements.
cannam@0 114 // Same for bestPathCost.
cannam@0 115
Chris@69 116 vector<float> dOld = m_distance[m_frameCount - m_blockSize];
Chris@71 117 vector<float> dNew(len, -1.f);
cannam@0 118
Chris@69 119 vector<double> bpcOld = m_bestPathCost[m_frameCount - m_blockSize];
Chris@71 120 vector<double> bpcNew(len, -1.0);
Chris@69 121
Chris@69 122 vector<Advance> adOld = m_advance[m_frameCount - m_blockSize];
Chris@69 123 vector<Advance> adNew(len, AdvanceNone);
Chris@69 124
Chris@69 125 for (int i = 0; i < len; ++i) {
Chris@69 126 dNew[i] = dOld[i];
Chris@69 127 bpcNew[i] = bpcOld[i];
Chris@69 128 adNew[i] = adOld[i];
Chris@69 129 }
Chris@45 130
Chris@69 131 m_distance[m_frameCount] = dOld;
Chris@69 132 m_distance[m_frameCount - m_blockSize] = dNew;
Chris@69 133
Chris@69 134 m_bestPathCost[m_frameCount] = bpcOld;
Chris@69 135 m_bestPathCost[m_frameCount - m_blockSize] = bpcNew;
Chris@69 136
Chris@69 137 m_advance[m_frameCount] = adOld;
Chris@69 138 m_advance[m_frameCount - m_blockSize] = adNew;
cannam@0 139 }
cannam@0 140
Chris@43 141 int stop = m_otherMatcher->m_frameCount;
Chris@43 142 int index = stop - m_blockSize;
Chris@86 143 if (index < 0) index = 0;
Chris@86 144
Chris@43 145 m_first[m_frameCount] = index;
Chris@43 146 m_last[m_frameCount] = stop;
cannam@0 147
cannam@0 148 for ( ; index < stop; index++) {
Chris@26 149
Chris@86 150 float distance = (float) m_metric.calcDistance
Chris@43 151 (m_frames[frameIndex],
Chris@45 152 m_otherMatcher->m_frames[index % m_blockSize]);
Chris@26 153
Chris@86 154 if ((m_frameCount == 0) && (index == 0)) { // first element
Chris@86 155
Chris@86 156 updateValue(0, 0, AdvanceNone,
Chris@86 157 0,
Chris@86 158 distance);
Chris@86 159
Chris@86 160 } else if (m_frameCount == 0) { // first row
Chris@86 161
Chris@71 162 updateValue(0, index, AdvanceOther,
Chris@86 163 getPathCost(0, index-1),
Chris@86 164 distance);
Chris@86 165
Chris@86 166 } else if (index == 0) { // first column
Chris@86 167
Chris@71 168 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 169 getPathCost(m_frameCount - 1, 0),
Chris@86 170 distance);
Chris@86 171
Chris@86 172 } else if (index == m_otherMatcher->m_frameCount - m_blockSize) {
Chris@86 173
cannam@0 174 // missing value(s) due to cutoff
cannam@0 175 // - no previous value in current row (resp. column)
cannam@0 176 // - no diagonal value if prev. dir. == curr. dirn
Chris@86 177
Chris@72 178 double min2 = getPathCost(m_frameCount - 1, index);
Chris@86 179
Chris@86 180 cerr << "NOTE: missing value at i = " << m_frameCount << ", j = "
Chris@86 181 << index << " (first = " << m_firstPM << ")" << endl;
Chris@86 182
Chris@43 183 // if ((m_firstPM && (first[m_frameCount - 1] == index)) ||
Chris@43 184 // (!m_firstPM && (m_last[index-1] < m_frameCount)))
Chris@86 185 if (m_first[m_frameCount - 1] == index) {
Chris@86 186
Chris@86 187 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 188 min2, distance);
Chris@86 189
Chris@86 190 } else {
Chris@86 191
Chris@72 192 double min1 = getPathCost(m_frameCount - 1, index - 1);
Chris@86 193 if (min1 + distance <= min2) {
Chris@86 194 updateValue(m_frameCount, index, AdvanceBoth,
Chris@86 195 min1, distance);
Chris@86 196 } else {
Chris@86 197 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 198 min2, distance);
Chris@86 199 }
cannam@0 200 }
Chris@86 201
cannam@0 202 } else {
Chris@86 203
Chris@86 204 double min1 = getPathCost(m_frameCount, index - 1);
Chris@72 205 double min2 = getPathCost(m_frameCount - 1, index);
Chris@86 206 double min3 = getPathCost(m_frameCount - 1, index - 1);
Chris@86 207
cannam@0 208 if (min1 <= min2) {
Chris@86 209 if (min3 + distance <= min1) {
Chris@86 210 updateValue(m_frameCount, index, AdvanceBoth,
Chris@86 211 min3, distance);
Chris@86 212 } else {
Chris@86 213 updateValue(m_frameCount, index, AdvanceOther,
Chris@86 214 min1, distance);
Chris@86 215 }
cannam@0 216 } else {
Chris@86 217 if (min3 + distance <= min2) {
Chris@86 218 updateValue(m_frameCount, index, AdvanceBoth,
Chris@86 219 min3, distance);
Chris@86 220 } else {
Chris@86 221 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 222 min2, distance);
Chris@86 223 }
cannam@0 224 }
cannam@0 225 }
Chris@86 226
Chris@43 227 m_otherMatcher->m_last[index]++;
cannam@0 228 } // loop for row (resp. column)
cannam@0 229
Chris@43 230 m_frameCount++;
Chris@43 231 m_runCount++;
cannam@0 232
Chris@43 233 m_otherMatcher->m_runCount = 0;
Chris@21 234 }
cannam@0 235
Chris@71 236 bool
Chris@71 237 Matcher::isInRange(int i, int j)
Chris@71 238 {
Chris@71 239 if (m_firstPM) {
Chris@71 240 return ((i >= 0) &&
Chris@71 241 (i < int(m_first.size())) &&
Chris@71 242 (j >= m_first[i]) &&
Chris@71 243 (j < int(m_first[i] + m_bestPathCost[i].size())));
Chris@71 244 } else {
Chris@71 245 return m_otherMatcher->isInRange(j, i);
Chris@71 246 }
Chris@71 247 }
Chris@71 248
Chris@71 249 bool
Chris@71 250 Matcher::isAvailable(int i, int j)
Chris@71 251 {
Chris@71 252 if (m_firstPM) {
Chris@71 253 if (isInRange(i, j)) {
Chris@71 254 return (m_bestPathCost[i][j - m_first[i]] >= 0);
Chris@71 255 } else {
Chris@71 256 return false;
Chris@71 257 }
Chris@71 258 } else {
Chris@71 259 return m_otherMatcher->isAvailable(j, i);
Chris@71 260 }
Chris@71 261 }
Chris@71 262
Chris@72 263 pair<int, int>
Chris@72 264 Matcher::getColRange(int i)
Chris@72 265 {
Chris@72 266 if (i < 0 || i >= int(m_first.size())) {
Chris@72 267 cerr << "ERROR: Matcher::getColRange(" << i << "): Index out of range"
Chris@72 268 << endl;
Chris@72 269 throw "Index out of range";
Chris@72 270 } else {
Chris@72 271 return pair<int, int>(m_first[i], m_last[i]);
Chris@72 272 }
Chris@72 273 }
Chris@72 274
Chris@72 275 pair<int, int>
Chris@72 276 Matcher::getRowRange(int i)
Chris@72 277 {
Chris@72 278 return m_otherMatcher->getColRange(i);
Chris@72 279 }
Chris@72 280
Chris@72 281 float
Chris@72 282 Matcher::getDistance(int i, int j)
Chris@72 283 {
Chris@72 284 if (m_firstPM) {
Chris@72 285 if (!isInRange(i, j)) {
Chris@72 286 cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): "
Chris@72 287 << "Location is not in range" << endl;
Chris@72 288 throw "Distance not available";
Chris@72 289 }
Chris@72 290 float dist = m_distance[i][j - m_first[i]];
Chris@72 291 if (dist < 0) {
Chris@72 292 cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): "
Chris@72 293 << "Location is in range, but distance ("
Chris@72 294 << dist << ") is invalid or has not been set" << endl;
Chris@72 295 throw "Distance not available";
Chris@72 296 }
Chris@72 297 return dist;
Chris@72 298 } else {
Chris@72 299 return m_otherMatcher->getDistance(j, i);
Chris@72 300 }
Chris@72 301 }
Chris@72 302
Chris@72 303 void
Chris@72 304 Matcher::setDistance(int i, int j, float distance)
Chris@72 305 {
Chris@72 306 if (m_firstPM) {
Chris@72 307 if (!isInRange(i, j)) {
Chris@72 308 cerr << "ERROR: Matcher::setDistance(" << i << ", " << j << ", "
Chris@72 309 << distance << "): Location is out of range" << endl;
Chris@72 310 throw "Indices out of range";
Chris@72 311 }
Chris@72 312 m_distance[i][j - m_first[i]] = distance;
Chris@72 313 } else {
Chris@72 314 m_otherMatcher->setDistance(j, i, distance);
Chris@72 315 }
Chris@72 316 }
Chris@72 317
Chris@53 318 double
Chris@72 319 Matcher::getPathCost(int i, int j)
cannam@0 320 {
Chris@71 321 if (m_firstPM) {
Chris@71 322 if (!isAvailable(i, j)) {
Chris@71 323 if (!isInRange(i, j)) {
Chris@72 324 cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): "
Chris@71 325 << "Location is not in range" << endl;
Chris@71 326 } else {
Chris@72 327 cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): "
Chris@72 328 << "Location is in range, but pathCost ("
Chris@71 329 << m_bestPathCost[i][j - m_first[i]]
Chris@71 330 << ") is invalid or has not been set" << endl;
Chris@71 331 }
Chris@72 332 throw "Path cost not available";
Chris@71 333 }
Chris@43 334 return m_bestPathCost[i][j - m_first[i]];
Chris@71 335 } else {
Chris@72 336 return m_otherMatcher->getPathCost(j, i);
Chris@71 337 }
Chris@71 338 }
Chris@71 339
Chris@71 340 void
Chris@72 341 Matcher::setPathCost(int i, int j, Advance dir, double pathCost)
Chris@71 342 {
Chris@71 343 if (m_firstPM) {
Chris@71 344 if (!isInRange(i, j)) {
Chris@72 345 cerr << "ERROR: Matcher::setPathCost(" << i << ", " << j << ", "
Chris@72 346 << dir << ", " << pathCost
Chris@72 347 << "): Location is out of range" << endl;
Chris@71 348 throw "Indices out of range";
Chris@71 349 }
Chris@72 350 m_advance[i][j - m_first[i]] = dir;
Chris@72 351 m_bestPathCost[i][j - m_first[i]] = pathCost;
Chris@71 352 } else {
Chris@72 353 if (dir == AdvanceThis) {
Chris@72 354 dir = AdvanceOther;
Chris@72 355 } else if (dir == AdvanceOther) {
Chris@72 356 dir = AdvanceThis;
Chris@72 357 }
Chris@72 358 m_otherMatcher->setPathCost(j, i, dir, pathCost);
Chris@71 359 }
Chris@72 360
Chris@71 361 }
cannam@0 362
cannam@0 363 void
Chris@71 364 Matcher::updateValue(int i, int j, Advance dir, double value, float dMN)
cannam@0 365 {
Chris@83 366 float weighted = dMN;
Chris@83 367 if (dir == AdvanceBoth) {
Chris@83 368 weighted *= m_params.diagonalWeight;
Chris@83 369 }
Chris@83 370
Chris@43 371 if (m_firstPM) {
Chris@45 372
Chris@72 373 m_distance[i][j - m_first[i]] = dMN;
Chris@84 374
Chris@84 375 if (i == 3 && j == 56) {
Chris@84 376 cerr << "i = "<< i << ", j = " << j << ", dir = "
Chris@84 377 << advanceToString(dir)
Chris@84 378 << ", dMN = " << dMN << ", diagonalWeight = "
Chris@84 379 << m_params.diagonalWeight << ", weighted = "
Chris@84 380 << weighted << ", result = " << value + weighted << endl;
Chris@84 381 }
Chris@83 382
Chris@83 383 setPathCost(i, j, dir, value + weighted);
Chris@45 384
cannam@0 385 } else {
Chris@45 386
Chris@86 387 if (dir == AdvanceThis) dir = AdvanceOther;
Chris@86 388 else if (dir == AdvanceOther) dir = AdvanceThis;
Chris@86 389
Chris@84 390 if (i == 56 && j == 3) {
Chris@84 391 cerr << "i = "<< i << ", j = " << j << ", dir = "
Chris@84 392 << advanceToString(dir)
Chris@84 393 << ", dMN = " << dMN << ", diagonalWeight = "
Chris@84 394 << m_params.diagonalWeight << ", weighted = "
Chris@84 395 << weighted << ", result = " << value + weighted << endl;
Chris@84 396 }
Chris@84 397
Chris@43 398 int idx = i - m_otherMatcher->m_first[j];
Chris@45 399
Chris@69 400 if (idx == (int)m_otherMatcher->m_distance[j].size()) {
cannam@0 401 // This should never happen, but if we allow arbitrary
cannam@0 402 // pauses in either direction, and arbitrary lengths at
cannam@0 403 // end, it is better than a segmentation fault.
Chris@72 404 cerr << "Emergency resize: " << idx << " -> " << idx * 2 << endl;
Chris@71 405 m_otherMatcher->m_bestPathCost[j].resize(idx * 2, -1);
Chris@71 406 m_otherMatcher->m_distance[j].resize(idx * 2, -1);
Chris@46 407 m_otherMatcher->m_advance[j].resize(idx * 2, AdvanceNone);
cannam@0 408 }
Chris@45 409
Chris@45 410 m_otherMatcher->m_distance[j][idx] = dMN;
Chris@83 411 m_otherMatcher->setPathCost(j, i, dir, value + weighted);
cannam@0 412 }
Chris@71 413 }
cannam@0 414
Chris@72 415 Matcher::Advance
Chris@72 416 Matcher::getAdvance(int i, int j)
Chris@72 417 {
Chris@72 418 if (m_firstPM) {
Chris@72 419 if (!isInRange(i, j)) {
Chris@72 420 cerr << "ERROR: Matcher::getAdvance(" << i << ", " << j << "): "
Chris@72 421 << "Location is not in range" << endl;
Chris@72 422 throw "Advance not available";
Chris@72 423 }
Chris@72 424 return m_advance[i][j - m_first[i]];
Chris@72 425 } else {
Chris@72 426 return m_otherMatcher->getAdvance(j, i);
Chris@72 427 }
Chris@72 428 }