Mercurial > hg > match-vamp
changeset 75:e1a5f3095ba6 cheap_diagonals
Merge from branch refactors
author | Chris Cannam |
---|---|
date | Wed, 19 Nov 2014 12:13:28 +0000 |
parents | 7aa1ab3db7c9 (current diff) b9aa663a607b (diff) |
children | 0042b4d42167 |
files | src/Finder.cpp src/Finder.h src/MatchFeeder.cpp src/MatchFeeder.h src/Matcher.cpp |
diffstat | 14 files changed, 587 insertions(+), 834 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.inc Tue Nov 18 10:33:12 2014 +0000 +++ b/Makefile.inc Wed Nov 19 12:13:28 2014 +0000 @@ -23,18 +23,17 @@ # DO NOT DELETE src/DistanceMetric.o: src/DistanceMetric.h -src/MatchFeeder.o: src/MatchFeeder.h src/Matcher.h src/DistanceMetric.h -src/MatchFeeder.o: src/Finder.h src/Path.o: src/Path.h src/MatchFeatureFeeder.o: src/MatchFeatureFeeder.h src/Matcher.h src/MatchFeatureFeeder.o: src/DistanceMetric.h src/Finder.h +src/FeatureExtractor.o: src/FeatureExtractor.h src/Finder.o: src/Finder.h src/Matcher.h src/DistanceMetric.h src/Path.h src/Matcher.o: src/Matcher.h src/DistanceMetric.h src/MatchVampPlugin.o: src/MatchVampPlugin.h src/Matcher.h -src/MatchVampPlugin.o: src/DistanceMetric.h src/MatchFeeder.h src/Finder.h -src/MatchVampPlugin.o: src/Path.h -src/MatchFeeder.o: src/Matcher.h src/DistanceMetric.h src/Finder.h +src/MatchVampPlugin.o: src/DistanceMetric.h src/FeatureExtractor.h +src/MatchVampPlugin.o: src/MatchFeatureFeeder.h src/Finder.h src/Path.h src/MatchFeatureFeeder.o: src/Matcher.h src/DistanceMetric.h src/Finder.h src/Finder.o: src/Matcher.h src/DistanceMetric.h src/Matcher.o: src/DistanceMetric.h src/MatchVampPlugin.o: src/Matcher.h src/DistanceMetric.h +src/MatchVampPlugin.o: src/FeatureExtractor.h
--- a/src/DistanceMetric.cpp Tue Nov 18 10:33:12 2014 +0000 +++ b/src/DistanceMetric.cpp Wed Nov 19 12:13:28 2014 +0000 @@ -32,6 +32,8 @@ assert(int(f2.size()) == featureSize); for (int i = 0; i < featureSize; i++) { + assert(f1[i] >= 0); + assert(f2[i] >= 0); d += fabs(f1[i] - f2[i]); sum += f1[i] + f2[i]; }
--- a/src/FeatureExtractor.cpp Tue Nov 18 10:33:12 2014 +0000 +++ b/src/FeatureExtractor.cpp Wed Nov 19 12:13:28 2014 +0000 @@ -28,17 +28,22 @@ m_params(parameters), m_ltAverage(0) { - if (m_params.useChromaFrequencyMap) { - m_featureSize = 13; - } else { - m_featureSize = 84; - } - + m_featureSize = getFeatureSizeFor(parameters); m_prevFrame = vector<double>(m_featureSize, 0.0); makeFreqMap(); } +int +FeatureExtractor::getFeatureSizeFor(Parameters parameters) +{ + if (parameters.useChromaFrequencyMap) { + return 13; + } else { + return 84; + } +} + void FeatureExtractor::makeFreqMap() { @@ -110,6 +115,28 @@ } rms = sqrt(rms / (m_params.fftSize/2)); + return postProcess(frame, rms); +} + +vector<double> +FeatureExtractor::process(const float *cframe) +{ + vector<double> frame(m_featureSize, 0.0); + + double rms = 0; + for (int i = 0; i <= m_params.fftSize/2; i++) { + double mag = cframe[i*2] * cframe[i*2] + cframe[i*2+1] * cframe[i*2+1]; + rms += mag; + frame[m_freqMap[i]] += mag; + } + rms = sqrt(rms / (m_params.fftSize/2)); + + return postProcess(frame, rms); +} + +vector<double> +FeatureExtractor::postProcess(const vector<double> &frame, double rms) +{ vector<double> feature(m_featureSize, 0.0); double totalEnergy = 0;
--- a/src/FeatureExtractor.h Tue Nov 18 10:33:12 2014 +0000 +++ b/src/FeatureExtractor.h Wed Nov 19 12:13:28 2014 +0000 @@ -103,6 +103,12 @@ * Return the feature vector size that will be returned from process(). */ int getFeatureSize() const { return m_featureSize; } + + /** + * Return the feature vector size that would be returned from + * process() with these parameters. + */ + static int getFeatureSizeFor(Parameters params); /** * Process one frequency-domain audio frame (provided as real & @@ -121,6 +127,21 @@ std::vector<double> process(const std::vector<double> &real, const std::vector<double> &imag); + /** + * Process one frequency-domain audio frame, provided as a single + * array of alternating real and imaginary components. Input array + * must have at least 2 * (params.fftSize/2 + 1) elements. + * + * Operates by mapping the frequency bins into a part-linear + * part-logarithmic array, then (optionally) computing the + * half-wave rectified spectral difference from the previous + * frame, then (optionally) normalising to a sum of 1. + * + * Return value is the frame (post-processed, with warping, + * rectification, and normalisation as appropriate). + */ + std::vector<double> process(const float *carray); + protected: /** Make either standard or chroma map, depending on m_params */ void makeFreqMap(); @@ -136,6 +157,8 @@ /** Creates a map of FFT frequency bins to semitone chroma bins. */ void makeChromaFrequencyMap(); + std::vector<double> postProcess(const std::vector<double> &, double rms); + /** Configuration parameters */ Parameters m_params;
--- a/src/Finder.cpp Tue Nov 18 10:33:12 2014 +0000 +++ b/src/Finder.cpp Wed Nov 19 12:13:28 2014 +0000 @@ -20,58 +20,26 @@ #include <algorithm> +using namespace std; -Finder::Finder(Matcher *p1, Matcher *p2) +Finder::Finder(Matcher *pm) { - if (!p1->m_firstPM) - std::cerr << "Warning: wrong args in Finder()" << std::endl; - pm1 = p1; - pm2 = p2; - index1 = 0; - index2 = 0; - rowRange = new int[2]; - colRange = new int[2]; - duration1 = -1; - duration2 = -1; + m_m = pm; + m_duration1 = -1; + m_duration2 = -1; } // constructor Finder::~Finder() { - delete[] rowRange; - delete[] colRange; } void Finder::setDurations(int d1, int d2) { - duration1 = d1; - duration2 = d2; + m_duration1 = d1; + m_duration2 = d2; } -bool -Finder::find(int i1, int i2) -{ - if (i1 >= 0) { - index1 = i1; - index2 = i2 - pm1->m_first[i1]; - } - return (i1 >= 0) && (i2 >= pm1->m_first[i1]) && (i2 < pm1->m_last[i1]); -} // find() - -void -Finder::getColRange(int row, int *range) -{ - range[0] = pm1->m_first[row]; - range[1] = pm1->m_last[row]; -} // getColRange() - -void -Finder::getRowRange(int col, int *range) -{ - range[0] = pm2->m_first[col]; - range[1] = pm2->m_last[col]; -} // getRowRange() - Matcher::Advance Finder::getExpandDirection(int row, int col) { @@ -81,40 +49,35 @@ Matcher::Advance Finder::getExpandDirection(int row, int col, bool check) { - double min = getPathCost(row, col); - bestRow = row; - bestCol = col; - getRowRange(col, rowRange); - if (rowRange[1] > row+1) - rowRange[1] = row+1; // don't cheat by looking at future :) - for (int index = rowRange[0]; index < rowRange[1]; index++) { - double tmp = getPathCost(index, col); + double min = m_m->getPathCost(row, col); + + int bestRow = row; + int bestCol = col; + + pair<int, int> rowRange = m_m->getRowRange(col); + if (rowRange.second > row+1) { + rowRange.second = row+1; // don't cheat by looking at future :) + } + for (int index = rowRange.first; index < rowRange.second; index++) { + double tmp = m_m->getPathCost(index, col); if (tmp < min) { min = tmp; bestRow = index; } } - getColRange(row, colRange); - if (colRange[1] > col+1) - colRange[1] = col+1; // don't cheat by looking at future :) - for (int index = colRange[0]; index < colRange[1]; index++) { - double tmp = getPathCost(row, index); + + pair<int, int> colRange = m_m->getColRange(row); + if (colRange.second > col+1) { + colRange.second = col+1; // don't cheat by looking at future :) + } + for (int index = colRange.first; index < colRange.second; index++) { + double tmp = m_m->getPathCost(row, index); if (tmp < min) { min = tmp; bestCol = index; bestRow = row; } } - // System.err.print(" BEST: " + bestRow + " " + bestCol + " " + check); - // System.err.println(" " + pm1->frameCount + " " + pm2->frameCount); - if (check) { - // System.err.println(find(row+1, col) + " " + find(row, col+1)); - if (!find(row, col+1)) { - return Matcher::AdvanceThis; - } else if (!find(row+1, col)) { - return Matcher::AdvanceOther; - } - } if (bestRow == row) { if (bestCol == col) { @@ -128,174 +91,91 @@ return Matcher::AdvanceNone; } -} // getExpandDirection() - -float -Finder::getDistance(int row, int col) -{ - if (find(row, col)) { - return pm1->m_distance[row][col - pm1->m_first[row]]; - } - std::cerr << "getDistance(" << row << "," << col << "): out of bounds" << std::endl; - throw "getDistance index out of bounds"; -} // getDistance()/2 - -void -Finder::setDistance(int row, int col, float b) -{ - if (find(row, col)) { - pm1->m_distance[row][col - pm1->m_first[row]] = b; - return; - } - std::cerr << "setDistance(" << row << "," << col << "," << b << "): out of bounds" << std::endl; - throw "setDistance index out of bounds"; -} // setDistance() - -double -Finder::getPathCost(int row, int col) -{ - if (find(row, col)) // "1" avoids div by 0 below - return pm1->m_bestPathCost[row][col - pm1->m_first[row]] / float(1+row+col); - std::cerr << "getPathCost(" << row << "," << col << "): out of bounds" << std::endl; - throw "getPathCost index out of bounds"; -} // getPathCost() - -double -Finder::getRawPathCost(int row, int col) -{ - if (find(row, col)) - return pm1->m_bestPathCost[row][col - pm1->m_first[row]]; - std::cerr << "getRawPathCost(" << row << "," << col << "): out of bounds" << std::endl; - throw "getRawPathCost index out of bounds"; -} // getRawPathCost() - -void -Finder::setPathCost(int row, int col, double cost) -{ - if (find(row, col)) { - pm1->m_bestPathCost[row][col - pm1->m_first[row]] = cost; - return; - } - std::cerr << "setPathCost(" << row << "," << col << "," << cost << "): out of bounds" << std::endl; - throw "setPathCost index out of bounds"; -} // setPathCost() - -Matcher::Advance -Finder::getAdvance() -{ - return pm1->m_advance[index1][index2]; } void -Finder::setAdvance(Matcher::Advance a) -{ - pm1->m_advance[index1][index2] = a; -} - -float -Finder::getDistance() -{ - return pm1->m_distance[index1][index2]; -} // getDistance()/0 - -void -Finder::setDistance(float b) -{ - pm1->m_distance[index1][index2] = b; -} // setDistance() - -double -Finder::getPathCost() -{ - return pm1->m_bestPathCost[index1][index2]; -} // getPathCost() - -void -Finder::setPathCost(double cost) -{ - pm1->m_bestPathCost[index1][index2] = cost; -} // setPathCost() - -void Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2) { float diagonalWeight = sqrtf(2.f); - if (!find(r1,c1)) { - std::cerr << "recalculatePathCostMatrix(" << r1 << "," << c1 << "): out of bounds" << std::endl; - throw "recalculatePathCostMatrix index out of bounds"; + int prevRowStart = 0, prevRowStop = 0; + + for (int r = r1; r <= r2; r++) { + + pair<int, int> colRange = m_m->getColRange(r); + + int rowStart = max(c1, colRange.first); + int rowStop = min(c2 + 1, colRange.second); + + for (int c = rowStart; c < rowStop; c++) { + + float newCost = m_m->getDistance(r, c); + Matcher::Advance dir = Matcher::AdvanceNone; + + if (r > r1) { // not first row + double min = -1; + if ((c > prevRowStart) && (c <= prevRowStop)) { + // diagonal from (r-1,c-1) + min = m_m->getPathCost(r-1, c-1) + newCost * diagonalWeight; + dir = Matcher::AdvanceBoth; + } + if ((c >= prevRowStart) && (c < prevRowStop)) { + // vertical from (r-1,c) + double cost = m_m->getPathCost(r-1, c) + newCost; + if ((min < 0) || (cost < min)) { + min = cost; + dir = Matcher::AdvanceThis; + } + } + if (c > rowStart) { + // horizontal from (r,c-1) + double cost = m_m->getPathCost(r, c-1) + newCost; + if ((min < 0) || (cost < min)) { + min = cost; + dir = Matcher::AdvanceOther; + } + } + + m_m->setPathCost(r, c, dir, min); + + } else if (c > rowStart) { // first row + // horizontal from (r,c-1) + m_m->setPathCost(r, c, Matcher::AdvanceOther, + m_m->getPathCost(r, c-1) + newCost); + } + } + + prevRowStart = rowStart; + prevRowStop = rowStop; } - int thisRowStart, c; - int prevRowStart = 0, prevRowStop = 0; - for (int r = r1; r <= r2; r++) { - thisRowStart = pm1->m_first[r]; - if (thisRowStart < c1) - thisRowStart = c1; - for (c = thisRowStart; c <= c2; c++) { - if (find(r,c)) { - int i2 = index2; - float newCost = pm1->m_distance[r][i2]; - Matcher::Advance dir = Matcher::AdvanceNone; - if (r > r1) { // not first row - double min = -1; - if ((c > prevRowStart) && (c <= prevRowStop)) { - // diagonal from (r-1,c-1) - min = pm1->m_bestPathCost[r-1][c-pm1->m_first[r-1]-1] + - newCost * diagonalWeight; - dir = Matcher::AdvanceBoth; - } - if ((c >= prevRowStart) && (c < prevRowStop)) { - // vertical from (r-1,c) - double cost = pm1->m_bestPathCost[r-1][c-pm1->m_first[r-1]] + - newCost; - if ((min == -1) || (cost < min)) { - min = cost; - dir = Matcher::AdvanceThis; - } - } - if (c > thisRowStart) { - // horizontal from (r,c-1) - double cost =pm1->m_bestPathCost[r][i2-1]+newCost; - if ((min == -1) || (cost < min)) { - min = cost; - dir = Matcher::AdvanceOther; - } - } - pm1->m_bestPathCost[r][i2] = min; - } else if (c > thisRowStart) { // first row - // horizontal from (r,c-1) - pm1->m_bestPathCost[r][i2] = pm1->m_bestPathCost[r][i2-1] + - newCost; - dir = Matcher::AdvanceOther; - } - pm1->m_advance[r][i2] = dir; - } else - break; // end of row - } - prevRowStart = thisRowStart; - prevRowStop = c; - } -} // recalculatePathCostMatrix() +} int Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy) { - int ex = pm2->getFrameCount() - 1; - int ey = pm1->getFrameCount() - 1; + pathx.clear(); + pathy.clear(); + + int ex = m_m->getOtherFrameCount() - 1; + int ey = m_m->getFrameCount() - 1; + + if (ex < 0 || ey < 0) { + return 0; + } int x = ex; int y = ey; // cerr << "before: x = " << x << ", y = " << y << endl; - if (duration2 > 0 && duration2 < pm2->getFrameCount()) { - x = duration2 - 1; + if (m_duration2 > 0 && m_duration2 < m_m->getOtherFrameCount()) { + x = m_duration2 - 1; } - if (duration1 > 0 && duration1 < pm1->getFrameCount()) { - y = duration1 - 1; + if (m_duration1 > 0 && m_duration1 < m_m->getFrameCount()) { + y = m_duration1 - 1; } - if (!find(y, x)) { + if (!m_m->isAvailable(y, x)) { // Path did not pass through the expected end point -- // probably means the pieces are substantially different in // the later bits. Reset the expected end point to the end of @@ -307,34 +187,31 @@ recalculatePathCostMatrix(0, 0, y, x); - pathx.clear(); - pathy.clear(); - // cerr << "start: x = " << x << ", y = " << y << endl; - while (find(y, x) && ((x > 0) || (y > 0))) { + while (m_m->isAvailable(y, x) && (x > 0 || y > 0)) { // cerr << "x = " << x << ", y = " << y; pathx.push_back(x); pathy.push_back(y); - switch (getAdvance()) { + switch (m_m->getAdvance(y, x)) { case Matcher::AdvanceThis: -// cerr << ", going down (dist = " << (int)getDistance() << ")" << endl; +// cerr << ", going down (dist = " << getDistance() << ")" << endl; y--; break; case Matcher::AdvanceOther: -// cerr << ", going left (dist = " << (int)getDistance() << ")" << endl; +// cerr << ", going left (dist = " << getDistance() << ")" << endl; x--; break; case Matcher::AdvanceBoth: -// cerr << ", going diag (dist = " << (int)getDistance() << ")" << endl; +// cerr << ", going diag (dist = " << getDistance() << ")" << endl; x--; y--; break; case Matcher::AdvanceNone: // this would indicate a bug, but we wouldn't want to hang -// cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl; + cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl; if (x > y) { x--; } else { @@ -344,8 +221,13 @@ } } - std::reverse(pathx.begin(), pathx.end()); - std::reverse(pathy.begin(), pathy.end()); + if (x > 0 || y > 0) { + cerr << "WARNING: Ran out of available path at (" << y << "," << x + << ")!" << endl; + } + + reverse(pathx.begin(), pathx.end()); + reverse(pathy.begin(), pathy.end()); if (smooth) { int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
--- a/src/Finder.h Tue Nov 18 10:33:12 2014 +0000 +++ b/src/Finder.h Wed Nov 19 12:13:28 2014 +0000 @@ -22,22 +22,10 @@ #include "Matcher.h" -/** Maps cost matrix coordinates into an efficient - * (linear instead of quadratic space) representation. - * Stores result of most recent mapping for fast - * sequential access. - */ -class Finder { - -protected: - Matcher *pm1, *pm2; - int index1, index2, bestRow, bestCol; - int *rowRange; - int *colRange; - int duration1, duration2; - +class Finder +{ public: - Finder(Matcher *p1, Matcher *p2); + Finder(Matcher *pm); ~Finder(); @@ -50,42 +38,9 @@ */ void setDurations(int d1, int d2); - /** Sets up the instance variables to point to the given - * coordinate in the distance matrix. - * - * @param i1 frameNumber in the first Matcher - * @param i2 frameNumber in the second Matcher - * @return true iff the point (i2,i1) is represented in the distance matrix - */ - bool find(int i1, int i2); - - /** Returns the range [lo,hi) of legal column indices for the - * given row. */ - void getColRange(int row, int *range); - - /** Returns the range [lo,hi) of legal row indices for the given - * column. */ - void getRowRange(int col, int *range); - Matcher::Advance getExpandDirection(int row, int col); Matcher::Advance getExpandDirection(int row, int col, bool check); - - float getDistance(int row, int col); - void setDistance(int row, int col, float b); - - double getPathCost(int row, int col); - double getRawPathCost(int row, int col); //!!!??? - void setPathCost(int row, int col, double i); - - Matcher::Advance getAdvance(); - void setAdvance(Matcher::Advance a); - float getDistance(); - void setDistance(float b); - - double getPathCost(); - void setPathCost(double i); - /** Calculates a rectangle of the path cost matrix so that the * minimum cost path between the bottom left and top right * corners can be computed. Caches previous values to avoid @@ -110,7 +65,11 @@ * @param smooth whether to smooth the path before returning it */ int retrievePath(bool smooth, std::vector<int> &pathx, std::vector<int> &pathy); - -}; // class Finder + +protected: + Matcher *m_m; + int m_duration1; + int m_duration2; +}; #endif
--- a/src/MatchFeatureFeeder.cpp Tue Nov 18 10:33:12 2014 +0000 +++ b/src/MatchFeatureFeeder.cpp Wed Nov 19 12:13:28 2014 +0000 @@ -19,14 +19,14 @@ using std::vector; MatchFeatureFeeder::MatchFeatureFeeder(Matcher *m1, Matcher *m2) : - pm1(m1), pm2(m2) + m_pm1(m1), m_pm2(m2) { - finder = new Finder(m1, m2); + m_finder = new Finder(m1); } MatchFeatureFeeder::~MatchFeatureFeeder() { - delete finder; + delete m_finder; } void @@ -39,14 +39,14 @@ // empty. Then it returns, to be called again with more data. if (!f1.empty()) { - q1.push(f1); + m_q1.push(f1); } if (!f2.empty()) { - q2.push(f2); + m_q2.push(f2); } - while (!q1.empty() && !q2.empty()) { + while (!m_q1.empty() && !m_q2.empty()) { feedBlock(); } } @@ -54,7 +54,7 @@ void MatchFeatureFeeder::finish() { - while (!q1.empty() || !q2.empty()) { + while (!m_q1.empty() || !m_q2.empty()) { feedBlock(); } } @@ -62,20 +62,20 @@ void MatchFeatureFeeder::feedBlock() { - if (q1.empty()) { // ended + if (m_q1.empty()) { // ended feed2(); - } else if (q2.empty()) { // ended + } else if (m_q2.empty()) { // ended feed1(); - } else if (pm1->m_frameCount < pm1->m_blockSize) { // fill initial block + } else if (m_pm1->m_frameCount < m_pm1->m_blockSize) { // fill initial block feed1(); feed2(); - } else if (pm1->m_runCount >= pm1->m_params.maxRunCount) { // slope constraints + } else if (m_pm1->m_runCount >= m_pm1->m_params.maxRunCount) { // slope constraints feed2(); - } else if (pm2->m_runCount >= pm2->m_params.maxRunCount) { + } else if (m_pm2->m_runCount >= m_pm2->m_params.maxRunCount) { feed1(); } else { - switch (finder->getExpandDirection - (pm1->m_frameCount-1, pm2->m_frameCount-1)) { + switch (m_finder->getExpandDirection + (m_pm1->m_frameCount-1, m_pm2->m_frameCount-1)) { case Matcher::AdvanceThis: feed1(); break; @@ -87,7 +87,7 @@ feed2(); break; case Matcher::AdvanceNone: - cerr << "finder says AdvanceNone!" << endl; + cerr << "m_finder says AdvanceNone!" << endl; break; } } @@ -96,14 +96,14 @@ void MatchFeatureFeeder::feed1() { - pm1->consumeFeatureVector(q1.front()); - q1.pop(); + m_pm1->consumeFeatureVector(m_q1.front()); + m_q1.pop(); } void MatchFeatureFeeder::feed2() { - pm2->consumeFeatureVector(q2.front()); - q2.pop(); + m_pm2->consumeFeatureVector(m_q2.front()); + m_q2.pop(); }
--- a/src/MatchFeatureFeeder.h Tue Nov 18 10:33:12 2014 +0000 +++ b/src/MatchFeatureFeeder.h Wed Nov 19 12:13:28 2014 +0000 @@ -48,19 +48,19 @@ */ void finish(); - Finder *getFinder() { return finder; } + Finder *getFinder() { return m_finder; } protected: void feedBlock(); void feed1(); void feed2(); - Finder *finder; - Matcher *pm1; - Matcher *pm2; + Finder *m_finder; + Matcher *m_pm1; + Matcher *m_pm2; - std::queue<std::vector<double> > q1; - std::queue<std::vector<double> > q2; + std::queue<std::vector<double> > m_q1; + std::queue<std::vector<double> > m_q2; }; #endif
--- a/src/MatchFeeder.cpp Tue Nov 18 10:33:12 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Vamp feature extraction plugin using the MATCH audio alignment - algorithm. - - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2007 Simon Dixon, Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "MatchFeeder.h" - -using std::vector; - -MatchFeeder::MatchFeeder(Matcher *m1, Matcher *m2) : - pm1(m1), pm2(m2), n(0), lastIn1(0), lastIn2(0) -{ - fftSize = m1->m_params.fftSize; - finder = new Finder(m1, m2); - reBuffer = new double[fftSize/2+1]; - imBuffer = new double[fftSize/2+1]; -} - -MatchFeeder::~MatchFeeder() -{ - delete[] imBuffer; - delete[] reBuffer; - while (!q1.empty()) { - delete[] q1.front(); - q1.pop(); - } - while (!q2.empty()) { - delete[] q2.front(); - q2.pop(); - } - delete finder; -} - -void -MatchFeeder::feed(const float *const *input) -{ - // We maintain two FIFO queues of audio data frame block pointers, - // one per input stream. When the match-feeder function is - // entered, it knows that it has at least one block in each queue. - // It loops, processing up to one block per matcher, until a queue - // is empty. Then it returns, to be called again with more data. - - prepare(input); - - while (!q1.empty() && !q2.empty()) { - (void)feedBlock(); - } -} - -MatchFeeder::Features -MatchFeeder::feedAndGetFeatures(const float *const *input) -{ - prepare(input); - - Features all; - - while (!q1.empty() && !q2.empty()) { - Features ff = feedBlock(); - all.f1.insert(all.f1.end(), ff.f1.begin(), ff.f1.end()); - all.f2.insert(all.f2.end(), ff.f2.begin(), ff.f2.end()); - } - - return all; -} - -void -MatchFeeder::finish() -{ - while (!q1.empty() || !q2.empty()) { - (void)feedBlock(); - } -} - -MatchFeeder::Features -MatchFeeder::finishAndGetFeatures() -{ - Features all; - - while (!q1.empty() || !q2.empty()) { - Features ff = feedBlock(); - all.f1.insert(all.f1.end(), ff.f1.begin(), ff.f1.end()); - all.f2.insert(all.f2.end(), ff.f2.begin(), ff.f2.end()); - } - - return all; -} - -void -MatchFeeder::prepare(const float *const *input) -{ - float threshold = 1e-5f; - - float *block = new float[fftSize+2]; - float rms = 0; - - for (size_t i = 0; i < fftSize+2; ++i) { - block[i] = input[0][i]; - rms += block[i] * block[i]; - } - rms = sqrtf(rms / (fftSize+2)); - if (rms > threshold) { - lastIn1 = n; - } - q1.push(block); - - block = new float[fftSize+2]; - rms = 0; - - for (size_t i = 0; i < fftSize+2; ++i) { - block[i] = input[1][i]; - rms += block[i] * block[i]; - } - rms = sqrtf(rms / (fftSize+2)); - if (rms > threshold) { - lastIn2 = n; - } - q2.push(block); - - ++n; - finder->setDurations(lastIn1, lastIn2); -} - -MatchFeeder::Features -MatchFeeder::feedBlock() -{ - Features ff; - vector<double> f1, f2; - - if (q1.empty()) { - f2 = feed2(); - } else if (q2.empty()) { - f1 = feed1(); - } else if (pm1->m_frameCount < pm1->m_blockSize) { // fill initial block -// std::cerr << "feeding initial block" << std::endl; - f1 = feed1(); - f2 = feed2(); - } else if (pm1->m_runCount >= pm1->m_params.maxRunCount) { // slope constraints -// std::cerr << "pm1 too slopey" << std::endl; - f2 = feed2(); - } else if (pm2->m_runCount >= pm2->m_params.maxRunCount) { -// std::cerr << "pm2 too slopey" << std::endl; - f1 = feed1(); - } else { - switch (finder->getExpandDirection - (pm1->m_frameCount-1, pm2->m_frameCount-1)) { - case Matcher::AdvanceThis: - f1 = feed1(); - break; - case Matcher::AdvanceOther: - f2 = feed2(); - break; - case Matcher::AdvanceBoth: - f1 = feed1(); - f2 = feed2(); - break; - case Matcher::AdvanceNone: - cerr << "finder says AdvanceNone!" << endl; - break; - } - } - - if (!f1.empty()) ff.f1.push_back(f1); - if (!f2.empty()) ff.f2.push_back(f2); - return ff; -} - -vector<double> -MatchFeeder::feed1() -{ -// std::cerr << "feed1" << std::endl; - float *block = q1.front(); - q1.pop(); - for (size_t i = 0; i <= fftSize/2; ++i) { - reBuffer[i] = block[i*2]; - } - for (size_t i = 0; i <= fftSize/2; ++i) { - imBuffer[i] = block[i*2+1]; - } - delete[] block; - return pm1->consumeFrame(reBuffer, imBuffer); -} - -vector<double> -MatchFeeder::feed2() -{ -// std::cerr << "feed2" << std::endl; - float *block = q2.front(); - q2.pop(); - for (size_t i = 0; i <= fftSize/2; ++i) { - reBuffer[i] = block[i*2]; - } - for (size_t i = 0; i <= fftSize/2; ++i) { - imBuffer[i] = block[i*2+1]; - } - delete[] block; - return pm2->consumeFrame(reBuffer, imBuffer); -} -
--- a/src/MatchFeeder.h Tue Nov 18 10:33:12 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ -/* - Vamp feature extraction plugin using the MATCH audio alignment - algorithm. - - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2007 Simon Dixon, Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _MATCH_FEEDER_H_ -#define _MATCH_FEEDER_H_ - -#include "Matcher.h" -#include "Finder.h" - -#include <queue> -#include <vector> - -class MatchFeeder -{ -public: - MatchFeeder(Matcher *m1, Matcher *m2); - ~MatchFeeder(); - - /** - * Feed the two supplied channels of frequency-domain input data - * to feeders 1 and 2 respectively, as appropriate (depending on - * their advance status). - */ - void feed(const float *const *input); - - /** - * Indicate that the input has come to an end. - */ - void finish(); - - struct Features { - std::vector<std::vector<double> > f1; - std::vector<std::vector<double> > f2; - }; - - /** - * Feed the two supplied channels of frequency-domain input data - * to matchers 1 and 2 respectively, as appropriate (depending on - * their advance status) and return any new feature vectors - * calculated by the two feeders. - */ - Features feedAndGetFeatures(const float *const *input); - - /** - * Indicate that the input has come to an end, and return any - * remaining features. - */ - Features finishAndGetFeatures(); - - Finder *getFinder() { return finder; } - -protected: - void prepare(const float *const *input); - Features feedBlock(); - std::vector<double> feed1(); - std::vector<double> feed2(); - - Finder *finder; - Matcher *pm1; - Matcher *pm2; - - size_t fftSize; - double *reBuffer; - double *imBuffer; - - std::queue<float *> q1; - std::queue<float *> q2; - - int n; - int lastIn1; - int lastIn2; -}; - -#endif
--- a/src/MatchVampPlugin.cpp Tue Nov 18 10:33:12 2014 +0000 +++ b/src/MatchVampPlugin.cpp Wed Nov 19 12:13:28 2014 +0000 @@ -17,7 +17,8 @@ #include "MatchVampPlugin.h" #include "Matcher.h" -#include "MatchFeeder.h" +#include "MatchFeatureFeeder.h" +#include "FeatureExtractor.h" #include "Path.h" #include <vamp/vamp.h> @@ -56,6 +57,9 @@ m_begin(true), m_locked(false), m_smooth(true), + m_frameNo(0), + m_lastFrameIn1(0), + m_lastFrameIn2(0), m_params(inputSampleRate, defaultStepTime, m_blockSize), m_defaultParams(inputSampleRate, defaultStepTime, m_blockSize), m_feParams(inputSampleRate, m_blockSize), @@ -77,9 +81,11 @@ #endif } - pm1 = 0; - pm2 = 0; - feeder = 0; + m_pm1 = 0; + m_pm2 = 0; + m_fe1 = 0; + m_fe2 = 0; + m_feeder = 0; // std::cerr << "MatchVampPlugin::MatchVampPlugin(" << this << "): extant = " << ++extant << std::endl; } @@ -87,9 +93,11 @@ { // std::cerr << "MatchVampPlugin::~MatchVampPlugin(" << this << "): extant = " << --extant << std::endl; - delete feeder; - delete pm1; - delete pm2; + delete m_feeder; + delete m_fe1; + delete m_fe2; + delete m_pm1; + delete m_pm2; if (m_locked) { #ifdef _WIN32 @@ -303,10 +311,12 @@ m_params.hopTime = m_stepTime; m_params.fftSize = m_blockSize; m_feParams.fftSize = m_blockSize; - pm1 = new Matcher(m_params, m_feParams, 0); - pm2 = new Matcher(m_params, m_feParams, pm1); - pm1->setOtherMatcher(pm2); - feeder = new MatchFeeder(pm1, pm2); + m_fe1 = new FeatureExtractor(m_feParams); + m_fe2 = new FeatureExtractor(m_feParams); + m_pm1 = new Matcher(m_params, 0, m_fe1->getFeatureSize()); + m_pm2 = new Matcher(m_params, m_pm1, m_fe2->getFeatureSize()); + m_pm1->setOtherMatcher(m_pm2); + m_feeder = new MatchFeatureFeeder(m_pm1, m_pm2); } bool @@ -337,12 +347,21 @@ void MatchVampPlugin::reset() { - delete feeder; - delete pm1; - delete pm2; - feeder = 0; - pm1 = 0; - pm2 = 0; + delete m_feeder; + delete m_fe1; + delete m_fe2; + delete m_pm1; + delete m_pm2; + + m_feeder = 0; + m_fe1 = 0; + m_fe2 = 0; + m_pm1 = 0; + m_pm2 = 0; + + m_frameNo = 0; + m_lastFrameIn1 = 0; + m_lastFrameIn2 = 0; createMatchers(); m_begin = true; @@ -454,6 +473,18 @@ return list; } +bool +MatchVampPlugin::aboveThreshold(const float *frame) +{ + float threshold = 1e-5f; + float rms = 0.f; + for (int i = 0; i < m_blockSize/2 + 2; ++i) { + rms += frame[i] * frame[i]; + } + rms = sqrtf(rms / (m_blockSize/2 + 2)); + return (rms > threshold); +} + MatchVampPlugin::FeatureSet MatchVampPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp) @@ -473,62 +504,47 @@ // std::cerr << timestamp.toString(); - MatchFeeder::Features ff = feeder->feedAndGetFeatures(inputBuffers); + if (aboveThreshold(inputBuffers[0])) m_lastFrameIn1 = m_frameNo; + if (aboveThreshold(inputBuffers[1])) m_lastFrameIn2 = m_frameNo; + + vector<double> f1 = m_fe1->process(inputBuffers[0]); + vector<double> f2 = m_fe2->process(inputBuffers[1]); + + m_feeder->feed(f1, f2); FeatureSet returnFeatures; Feature f; f.hasTimestamp = false; - for (int i = 0; i < (int)ff.f1.size(); ++i) { - f.values.clear(); - for (int j = 0; j < (int)ff.f1[i].size(); ++j) { - f.values.push_back(float(ff.f1[i][j])); - } - returnFeatures[m_aFeaturesOutNo].push_back(f); + f.values.clear(); + for (int j = 0; j < (int)f1.size(); ++j) { + f.values.push_back(float(f1[j])); } + returnFeatures[m_aFeaturesOutNo].push_back(f); - for (int i = 0; i < (int)ff.f2.size(); ++i) { - f.values.clear(); - for (int j = 0; j < (int)ff.f2[i].size(); ++j) { - f.values.push_back(float(ff.f2[i][j])); - } - returnFeatures[m_bFeaturesOutNo].push_back(f); + f.values.clear(); + for (int j = 0; j < (int)f2.size(); ++j) { + f.values.push_back(float(f2[j])); } + returnFeatures[m_bFeaturesOutNo].push_back(f); // std::cerr << "."; // std::cerr << std::endl; + ++m_frameNo; + return returnFeatures; } MatchVampPlugin::FeatureSet MatchVampPlugin::getRemainingFeatures() { + m_feeder->finish(); + FeatureSet returnFeatures; - MatchFeeder::Features ff = feeder->finishAndGetFeatures(); - - Feature f; - f.hasTimestamp = false; - - for (int i = 0; i < (int)ff.f1.size(); ++i) { - f.values.clear(); - for (int j = 0; j < (int)ff.f1[i].size(); ++j) { - f.values.push_back(float(ff.f1[i][j])); - } - returnFeatures[m_aFeaturesOutNo].push_back(f); - } - - for (int i = 0; i < (int)ff.f2.size(); ++i) { - f.values.clear(); - for (int j = 0; j < (int)ff.f2[i].size(); ++j) { - f.values.push_back(float(ff.f2[i][j])); - } - returnFeatures[m_bFeaturesOutNo].push_back(f); - } - - Finder *finder = feeder->getFinder(); + Finder *finder = m_feeder->getFinder(); std::vector<int> pathx; std::vector<int> pathy; int len = finder->retrievePath(m_smooth, pathx, pathy); @@ -594,12 +610,12 @@ prevy = y; } - delete feeder; - delete pm1; - delete pm2; - feeder = 0; - pm1 = 0; - pm2 = 0; + delete m_feeder; + delete m_pm1; + delete m_pm2; + m_feeder = 0; + m_pm1 = 0; + m_pm2 = 0; if (m_locked) { #ifdef _WIN32
--- a/src/MatchVampPlugin.h Tue Nov 18 10:33:12 2014 +0000 +++ b/src/MatchVampPlugin.h Wed Nov 19 12:13:28 2014 +0000 @@ -28,7 +28,7 @@ #include "Matcher.h" #include "FeatureExtractor.h" -class MatchFeeder; +class MatchFeatureFeeder; class MatchVampPlugin : public Vamp::Plugin { @@ -67,10 +67,13 @@ protected: void createMatchers(); + bool aboveThreshold(const float *); - Matcher *pm1; - Matcher *pm2; - MatchFeeder *feeder; + Matcher *m_pm1; + Matcher *m_pm2; + FeatureExtractor *m_fe1; + FeatureExtractor *m_fe2; + MatchFeatureFeeder *m_feeder; Vamp::RealTime m_startTime; int m_stepSize; @@ -81,6 +84,10 @@ bool m_locked; bool m_smooth; + int m_frameNo; + int m_lastFrameIn1; + int m_lastFrameIn2; + Matcher::Parameters m_params; Matcher::Parameters m_defaultParams;
--- a/src/Matcher.cpp Tue Nov 18 10:33:12 2014 +0000 +++ b/src/Matcher.cpp Wed Nov 19 12:13:28 2014 +0000 @@ -21,38 +21,13 @@ #include <cstdlib> #include <cassert> +using namespace std; + //#define DEBUG_MATCHER 1 -Matcher::Matcher(Parameters parameters, - FeatureExtractor::Parameters feParams, - Matcher *p) : - m_params(parameters), - m_featureExtractor(feParams), - m_metric(parameters.distanceNorm) -{ -#ifdef DEBUG_MATCHER - cerr << "Matcher::Matcher(" << m_params.sampleRate << ", " << p << ")" << endl; -#endif - - m_otherMatcher = p; // the first matcher will need this to be set later - m_firstPM = (!p); - m_frameCount = 0; - m_runCount = 0; - m_featureSize = m_featureExtractor.getFeatureSize(); - m_blockSize = 0; - - m_blockSize = lrint(m_params.blockTime / m_params.hopTime); -#ifdef DEBUG_MATCHER - cerr << "Matcher: m_blockSize = " << m_blockSize << endl; -#endif - - m_initialised = false; -} - Matcher::Matcher(Parameters parameters, Matcher *p, int m_featureSize_) : m_params(parameters), m_featureSize(m_featureSize_), - m_featureExtractor(FeatureExtractor::Parameters(m_params.sampleRate, m_params.fftSize)), // unused default config m_metric(parameters.distanceNorm) { #ifdef DEBUG_MATCHER @@ -86,7 +61,7 @@ if (m_initialised) return; m_frames = vector<vector<double> > - (m_blockSize, vector<double>(m_featureSize, 0)); + (m_blockSize, vector<double>(m_featureSize, -1.0)); m_distXSize = m_blockSize * 2; @@ -102,31 +77,15 @@ Matcher::size() { int distSize = (m_params.maxRunCount + 1) * m_blockSize; - m_bestPathCost.resize(m_distXSize, vector<double>(distSize, 0)); - m_distance.resize(m_distXSize, vector<float>(distSize, 0)); + m_bestPathCost.resize(m_distXSize, vector<double>(distSize, -1)); + m_distance.resize(m_distXSize, vector<float>(distSize, -1)); m_advance.resize(m_distXSize, vector<Advance>(distSize, AdvanceNone)); - m_distYSizes.resize(m_distXSize, distSize); m_first.resize(m_distXSize, 0); m_last.resize(m_distXSize, 0); } -vector<double> -Matcher::consumeFrame(double *reBuffer, double *imBuffer) -{ - if (!m_initialised) init(); - - vector<double> real(reBuffer, reBuffer + m_params.fftSize/2 + 1); - vector<double> imag(imBuffer, imBuffer + m_params.fftSize/2 + 1); - vector<double> feature = m_featureExtractor.process(real, imag); - int frameIndex = m_frameCount % m_blockSize; - m_frames[frameIndex] = feature; - calcAdvance(); - - return feature; -} - void -Matcher::consumeFeatureVector(std::vector<double> feature) +Matcher::consumeFeatureVector(vector<double> feature) { if (!m_initialised) init(); int frameIndex = m_frameCount % m_blockSize; @@ -153,22 +112,30 @@ // distance[m_frameCount], and then truncate // distance[m_frameCount-m_blockSize] to its first len elements. // Same for bestPathCost. -/* - std::cerr << "Matcher(" << this << "): moving " << distYSizes[m_frameCount - m_blockSize] << " from " << m_frameCount - m_blockSize << " to " - << m_frameCount << ", allocating " << len << " for " - << m_frameCount - m_blockSize << std::endl; -*/ - m_distance[m_frameCount] = m_distance[m_frameCount - m_blockSize]; - m_distance[m_frameCount - m_blockSize].resize(len, 0); - m_bestPathCost[m_frameCount] = m_bestPathCost[m_frameCount - m_blockSize]; - m_bestPathCost[m_frameCount - m_blockSize].resize(len, 0); + vector<float> dOld = m_distance[m_frameCount - m_blockSize]; + vector<float> dNew(len, -1.f); - m_advance[m_frameCount] = m_advance[m_frameCount - m_blockSize]; - m_advance[m_frameCount - m_blockSize].resize(len, AdvanceNone); + vector<double> bpcOld = m_bestPathCost[m_frameCount - m_blockSize]; + vector<double> bpcNew(len, -1.0); + + vector<Advance> adOld = m_advance[m_frameCount - m_blockSize]; + vector<Advance> adNew(len, AdvanceNone); + + for (int i = 0; i < len; ++i) { + dNew[i] = dOld[i]; + bpcNew[i] = bpcOld[i]; + adNew[i] = adOld[i]; + } - m_distYSizes[m_frameCount] = m_distYSizes[m_frameCount - m_blockSize]; - m_distYSizes[m_frameCount - m_blockSize] = len; + m_distance[m_frameCount] = dOld; + m_distance[m_frameCount - m_blockSize] = dNew; + + m_bestPathCost[m_frameCount] = bpcOld; + m_bestPathCost[m_frameCount - m_blockSize] = bpcNew; + + m_advance[m_frameCount] = adOld; + m_advance[m_frameCount - m_blockSize] = adNew; } int stop = m_otherMatcher->m_frameCount; @@ -194,43 +161,43 @@ mn = dMN; if ((m_frameCount == 0) && (index == 0)) // first element - setValue(0, 0, AdvanceNone, 0, dMN); + updateValue(0, 0, AdvanceNone, 0, dMN); else if (m_frameCount == 0) // first row - setValue(0, index, AdvanceOther, - getValue(0, index-1, true), dMN); + updateValue(0, index, AdvanceOther, + getPathCost(0, index-1), dMN); else if (index == 0) // first column - setValue(m_frameCount, index, AdvanceThis, - getValue(m_frameCount - 1, 0, true), dMN); + updateValue(m_frameCount, index, AdvanceThis, + getPathCost(m_frameCount - 1, 0), dMN); else if (index == m_otherMatcher->m_frameCount - m_blockSize) { // missing value(s) due to cutoff // - no previous value in current row (resp. column) // - no diagonal value if prev. dir. == curr. dirn - double min2 = getValue(m_frameCount - 1, index, true); + double min2 = getPathCost(m_frameCount - 1, index); // if ((m_firstPM && (first[m_frameCount - 1] == index)) || // (!m_firstPM && (m_last[index-1] < m_frameCount))) if (m_first[m_frameCount - 1] == index) - setValue(m_frameCount, index, AdvanceThis, min2, dMN); + updateValue(m_frameCount, index, AdvanceThis, min2, dMN); else { - double min1 = getValue(m_frameCount - 1, index - 1, true); + double min1 = getPathCost(m_frameCount - 1, index - 1); if (min1 + dMN <= min2) - setValue(m_frameCount, index, AdvanceBoth, min1,dMN); + updateValue(m_frameCount, index, AdvanceBoth, min1,dMN); else - setValue(m_frameCount, index, AdvanceThis, min2,dMN); + updateValue(m_frameCount, index, AdvanceThis, min2,dMN); } } else { - double min1 = getValue(m_frameCount, index-1, true); - double min2 = getValue(m_frameCount - 1, index, true); - double min3 = getValue(m_frameCount - 1, index-1, true); + double min1 = getPathCost(m_frameCount, index-1); + double min2 = getPathCost(m_frameCount - 1, index); + double min3 = getPathCost(m_frameCount - 1, index-1); if (min1 <= min2) { if (min3 + dMN <= min1) - setValue(m_frameCount, index, AdvanceBoth, min3,dMN); + updateValue(m_frameCount, index, AdvanceBoth, min3,dMN); else - setValue(m_frameCount, index, AdvanceOther,min1,dMN); + updateValue(m_frameCount, index, AdvanceOther,min1,dMN); } else { if (min3 + dMN <= min2) - setValue(m_frameCount, index, AdvanceBoth, min3,dMN); + updateValue(m_frameCount, index, AdvanceBoth, min3,dMN); else - setValue(m_frameCount, index, AdvanceThis, min2,dMN); + updateValue(m_frameCount, index, AdvanceThis, min2,dMN); } } m_otherMatcher->m_last[index]++; @@ -242,55 +209,178 @@ m_otherMatcher->m_runCount = 0; } +bool +Matcher::isInRange(int i, int j) +{ + if (m_firstPM) { + return ((i >= 0) && + (i < int(m_first.size())) && + (j >= m_first[i]) && + (j < int(m_first[i] + m_bestPathCost[i].size()))); + } else { + return m_otherMatcher->isInRange(j, i); + } +} + +bool +Matcher::isAvailable(int i, int j) +{ + if (m_firstPM) { + if (isInRange(i, j)) { + return (m_bestPathCost[i][j - m_first[i]] >= 0); + } else { + return false; + } + } else { + return m_otherMatcher->isAvailable(j, i); + } +} + +pair<int, int> +Matcher::getColRange(int i) +{ + if (i < 0 || i >= int(m_first.size())) { + cerr << "ERROR: Matcher::getColRange(" << i << "): Index out of range" + << endl; + throw "Index out of range"; + } else { + return pair<int, int>(m_first[i], m_last[i]); + } +} + +pair<int, int> +Matcher::getRowRange(int i) +{ + return m_otherMatcher->getColRange(i); +} + +float +Matcher::getDistance(int i, int j) +{ + if (m_firstPM) { + if (!isInRange(i, j)) { + cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): " + << "Location is not in range" << endl; + throw "Distance not available"; + } + float dist = m_distance[i][j - m_first[i]]; + if (dist < 0) { + cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): " + << "Location is in range, but distance (" + << dist << ") is invalid or has not been set" << endl; + throw "Distance not available"; + } + return dist; + } else { + return m_otherMatcher->getDistance(j, i); + } +} + +void +Matcher::setDistance(int i, int j, float distance) +{ + if (m_firstPM) { + if (!isInRange(i, j)) { + cerr << "ERROR: Matcher::setDistance(" << i << ", " << j << ", " + << distance << "): Location is out of range" << endl; + throw "Indices out of range"; + } + m_distance[i][j - m_first[i]] = distance; + } else { + m_otherMatcher->setDistance(j, i, distance); + } +} + double -Matcher::getValue(int i, int j, bool firstAttempt) +Matcher::getPathCost(int i, int j) { - if (m_firstPM) + if (m_firstPM) { + if (!isAvailable(i, j)) { + if (!isInRange(i, j)) { + cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): " + << "Location is not in range" << endl; + } else { + cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): " + << "Location is in range, but pathCost (" + << m_bestPathCost[i][j - m_first[i]] + << ") is invalid or has not been set" << endl; + } + throw "Path cost not available"; + } return m_bestPathCost[i][j - m_first[i]]; - else - return m_otherMatcher->m_bestPathCost[j][i - m_otherMatcher->m_first[j]]; -} // getValue() - + } else { + return m_otherMatcher->getPathCost(j, i); + } +} + void -Matcher::setValue(int i, int j, Advance dir, double value, float dMN) +Matcher::setPathCost(int i, int j, Advance dir, double pathCost) { - float diagonalWeight = sqrtf(2.f); - - if (dir == AdvanceBoth) { - dMN *= diagonalWeight; - } - if (m_firstPM) { - - int jdx = j - m_first[i]; - m_distance[i][jdx] = dMN; - m_advance[i][jdx] = dir; - m_bestPathCost[i][jdx] = value + dMN; - + if (!isInRange(i, j)) { + cerr << "ERROR: Matcher::setPathCost(" << i << ", " << j << ", " + << dir << ", " << pathCost + << "): Location is out of range" << endl; + throw "Indices out of range"; + } + m_advance[i][j - m_first[i]] = dir; + m_bestPathCost[i][j - m_first[i]] = pathCost; } else { - if (dir == AdvanceThis) { dir = AdvanceOther; } else if (dir == AdvanceOther) { dir = AdvanceThis; } + m_otherMatcher->setPathCost(j, i, dir, pathCost); + } + +} + +void +Matcher::updateValue(int i, int j, Advance dir, double value, float dMN) +{ + float diagonalWeight = sqrtf(2.f); + + float weight = 1.f; + if (dir == AdvanceBoth) { + weight *= diagonalWeight; + } + + if (m_firstPM) { + + m_distance[i][j - m_first[i]] = dMN; + setPathCost(i, j, dir, value + weight * dMN); + + } else { int idx = i - m_otherMatcher->m_first[j]; - if (idx == (int)m_otherMatcher->m_distYSizes[j]) { + if (idx == (int)m_otherMatcher->m_distance[j].size()) { // This should never happen, but if we allow arbitrary // pauses in either direction, and arbitrary lengths at // end, it is better than a segmentation fault. - std::cerr << "Emergency resize: " << idx << " -> " << idx * 2 << std::endl; - m_otherMatcher->m_distYSizes[j] = idx * 2; - m_otherMatcher->m_bestPathCost[j].resize(idx * 2, 0); - m_otherMatcher->m_distance[j].resize(idx * 2, 0); + cerr << "Emergency resize: " << idx << " -> " << idx * 2 << endl; + m_otherMatcher->m_bestPathCost[j].resize(idx * 2, -1); + m_otherMatcher->m_distance[j].resize(idx * 2, -1); m_otherMatcher->m_advance[j].resize(idx * 2, AdvanceNone); } m_otherMatcher->m_distance[j][idx] = dMN; - m_otherMatcher->m_advance[j][idx] = dir; - m_otherMatcher->m_bestPathCost[j][idx] = value + dMN; + m_otherMatcher->setPathCost(j, i, dir, value + weight * dMN); } -} // setValue() +} +Matcher::Advance +Matcher::getAdvance(int i, int j) +{ + if (m_firstPM) { + if (!isInRange(i, j)) { + cerr << "ERROR: Matcher::getAdvance(" << i << ", " << j << "): " + << "Location is not in range" << endl; + throw "Advance not available"; + } + return m_advance[i][j - m_first[i]]; + } else { + return m_otherMatcher->getAdvance(j, i); + } +}
--- a/src/Matcher.h Tue Nov 18 10:33:12 2014 +0000 +++ b/src/Matcher.h Wed Nov 19 12:13:28 2014 +0000 @@ -23,16 +23,15 @@ #include <cmath> #include "DistanceMetric.h" -#include "FeatureExtractor.h" using std::vector; using std::string; using std::cerr; using std::endl; -/** Represents an audio stream that can be matched to another audio - * stream of the same piece of music. The matching algorithm uses - * dynamic time warping. +/** Represents an audio feature stream that can be matched to another + * audio stream of the same piece of music. The matching algorithm + * uses dynamic time warping. */ class Matcher { @@ -87,28 +86,19 @@ }; /** Constructor for Matcher. - * - * @param p The Matcher representing the performance with which - * this one is going to be matched. Some information is shared - * between the two matchers (currently one possesses the distance - * matrix and optimal path matrix). - */ - Matcher(Parameters parameters, - FeatureExtractor::Parameters featureParams, - Matcher *p); - - /** Constructor for Matcher using externally supplied features. - * A Matcher made using this constructor will not carry out its - * own feature extraction from frequency-domain audio data, but - * instead will accept arbitrary feature frames calculated by - * some external code. + * + * A Matcher expects to be provided with feature vectors + * calculated by some external code (for example, a + * FeatureExtractor). Call consumeFeatureVector to provide each + * feature frame. * * @param p The Matcher representing the performance with which * this one is going to be matched. Some information is shared * between the two matchers (currently one possesses the distance * matrix and optimal path matrix). * - * @param featureSize Number of values in each feature vector. + * @param featureSize Number of values in each of the feature + * vectors that will be provided. */ Matcher(Parameters parameters, Matcher *p, int featureSize); @@ -121,12 +111,107 @@ */ void setOtherMatcher(Matcher *p) { m_otherMatcher = p; - } // setOtherMatcher() + } int getFrameCount() { return m_frameCount; } + int getOtherFrameCount() { + return m_otherMatcher->getFrameCount(); + } + + /** Processes a feature vector frame, presumably calculated from + * audio data by some external code such as a FeatureExtractor. + * Calculates the distance to all frames stored in the + * otherMatcher and stores in the distance matrix, before + * updating the optimal path matrix using the dynamic time + * warping algorithm. + * + * The supplied feature must be of the size that was passed as + * featureSize to the constructor. + */ + void consumeFeatureVector(std::vector<double> feature); + + /** Tests whether a location is in range in the minimum cost matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @return true if the location is in range + */ + bool isInRange(int i, int j); + + /** Tests whether a location is available in the minimum cost matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @return true if the location is in range and contains a valid cost + */ + bool isAvailable(int i, int j); + + /** Returns the valid range of frames in the other Matcher for the + * given frame in this Matcher's minimum cost matrix. + * + * @param i the frame number of this Matcher + * @return the first, last pair of frame numbers for the other + * Matcher. Note that the last frame is exclusive (last valid + * frame + 1). + */ + std::pair<int, int> getColRange(int i); + + /** Returns the valid range of frames in this Matcher for the + * given frame in the other Matcher's minimum cost matrix. + * + * @param i the frame number of the other Matcher + * @return the first, last pair of frame numbers for this + * Matcher. Note that the last frame is exclusive (last valid + * frame + 1). + */ + std::pair<int, int> getRowRange(int i); + + /** Retrieves a value from the distance matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @return the distance metric at this location + */ + float getDistance(int i, int j); + + /** Sets a value to the distance matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @param value the distance metric to set for this location + */ + void setDistance(int i, int j, float value); + + /** Retrieves a value from the minimum cost matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @return the cost of the minimum cost path to this location + */ + double getPathCost(int i, int j); + + /** Sets a value and an advance direction to the minimum cost matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @param dir the direction from which this position is reached with + * minimum cost + * @param value the cost of the minimum cost path to set for this location + */ + void setPathCost(int i, int j, Advance dir, double value); + + /** Retrieves an advance direction from the matrix. + * + * @param i the frame number of this Matcher + * @param j the frame number of the other Matcher + * @return the direction from which this position is reached with + * minimum cost + */ + Advance getAdvance(int i, int j); + protected: /** Create internal structures and reset. */ void init(); @@ -134,43 +219,7 @@ /** The distXSize value has changed: resize internal buffers. */ void size(); - /** Process a frequency-domain frame of audio data using the - * built-in FeatureExtractor, then calculating the distance to - * all frames stored in the otherMatcher and storing them in the - * distance matrix, and finally updating the optimal path matrix - * using the dynamic time warping algorithm. - * - * Return value is the frame (post-processed, with warping, - * rectification, and normalisation as appropriate). - * - * The Matcher must have been constructed using the constructor - * without an external featureSize parameter in order to use this - * function. (Otherwise it will be expecting you to call - * consumeFeatureVector.) - */ - std::vector<double> consumeFrame(double *reBuffer, double *imBuffer); - - /** Processes a feature vector frame (presumably calculated from - * audio data by some external code). As consumeFrame, except - * that it does not calculate a feature from audio data but - * instead uses the supplied feature directly. - * - * The Matcher must have been constructed using the constructor - * that accepts an external featureSize parameter in order to - * use this function. The supplied feature must be of the size - * that was passed to the constructor. - */ - void consumeFeatureVector(std::vector<double> feature); - - /** Retrieves values from the minimum cost matrix. - * - * @param i the frame number of this Matcher - * @param j the frame number of the other Matcher - * @return the cost of the minimum cost path to this location - */ - double getValue(int i, int j, bool firstAttempt); - - /** Stores entries in the distance matrix and the optimal path matrix. + /** Updates an entry in the distance matrix and the optimal path matrix. * * @param i the frame number of this Matcher * @param j the frame number of the other Matcher @@ -179,7 +228,7 @@ * @param value the cost of the minimum path except the current step * @param dMN the distance cost between the two frames */ - void setValue(int i, int j, Advance dir, double value, float dMN); + void updateValue(int i, int j, Advance dir, double value, float dMN); void calcAdvance(); @@ -234,23 +283,17 @@ vector<int> m_first; vector<int> m_last; - /** Height of each column in distance, path cost, and advance - * direction matrices. */ - vector<int> m_distYSizes; - /** Width of distance, path cost, and advance direction matrices * and first and last vectors */ int m_distXSize; bool m_initialised; - FeatureExtractor m_featureExtractor; DistanceMetric m_metric; friend class MatchFeeder; friend class MatchFeatureFeeder; - friend class Finder; - + }; // class Matcher #endif