changeset 74:b9aa663a607b refactors

Pull out feature extractor calls from Matcher, remove MatchFeeder, have only the feeder-from-features and use that in MatchVampPlugin
author Chris Cannam
date Wed, 19 Nov 2014 11:59:03 +0000
parents 7e3c1bc0984a
children e1a5f3095ba6 9be9182d4321
files Makefile.inc src/FeatureExtractor.cpp src/FeatureExtractor.h src/MatchFeatureFeeder.cpp src/MatchFeatureFeeder.h src/MatchFeeder.cpp src/MatchFeeder.h src/MatchVampPlugin.cpp src/MatchVampPlugin.h src/Matcher.cpp src/Matcher.h
diffstat 11 files changed, 193 insertions(+), 491 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.inc	Wed Nov 19 10:48:27 2014 +0000
+++ b/Makefile.inc	Wed Nov 19 11:59:03 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/FeatureExtractor.cpp	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/FeatureExtractor.cpp	Wed Nov 19 11:59:03 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	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/FeatureExtractor.h	Wed Nov 19 11:59:03 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/MatchFeatureFeeder.cpp	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/MatchFeatureFeeder.cpp	Wed Nov 19 11:59:03 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);
+    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	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/MatchFeatureFeeder.h	Wed Nov 19 11:59:03 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	Wed Nov 19 10:48:27 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,215 +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);
-    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()) {
-        cerr << "feedBlock: q1 empty, feeding 2" << endl;
-        f2 = feed2();
-    } else if (q2.empty()) {
-        cerr << "feedBlock: q2 empty, feeding 1" << endl;
-        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 {
-//        cerr << "run counts: " << pm1->m_runCount << ", " << pm2->m_runCount << endl;
-        switch (finder->getExpandDirection
-                (pm1->m_frameCount-1, pm2->m_frameCount-1)) {
-        case Matcher::AdvanceThis:
-//            cerr << "feeding 1" << endl;
-            f1 = feed1();
-            break;
-        case Matcher::AdvanceOther:
-//            cerr << "feeding 2" << endl;
-            f2 = feed2();
-            break;
-        case Matcher::AdvanceBoth:
-//            cerr << "feeding 1 and 2" << endl;
-            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	Wed Nov 19 10:48:27 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	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/MatchVampPlugin.cpp	Wed Nov 19 11:59:03 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	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/MatchVampPlugin.h	Wed Nov 19 11:59:03 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	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/Matcher.cpp	Wed Nov 19 11:59:03 2014 +0000
@@ -25,36 +25,9 @@
 
 //#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
@@ -111,21 +84,6 @@
     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(vector<double> feature)
 {
--- a/src/Matcher.h	Wed Nov 19 10:48:27 2014 +0000
+++ b/src/Matcher.h	Wed Nov 19 11:59:03 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,7 +111,7 @@
      */
     void setOtherMatcher(Matcher *p) {
         m_otherMatcher = p;
-    } // setOtherMatcher()
+    }
 
     int getFrameCount() { 
         return m_frameCount;
@@ -130,6 +120,18 @@
     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.
      *
@@ -217,34 +219,6 @@
     /** 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);
-
     /** Updates an entry in the distance matrix and the optimal path matrix.
      *
      *  @param i the frame number of this Matcher
@@ -315,7 +289,6 @@
 
     bool m_initialised;
 
-    FeatureExtractor m_featureExtractor;
     DistanceMetric m_metric;
     
     friend class MatchFeeder;