annotate src/Matcher.cpp @ 71:cba231851957 refactors

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