annotate src/Matcher.cpp @ 72:c3c50d5e05b7 refactors

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