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