changeset 132:926c292fa3ff fixedlag

fixed lag smoothing for pitch track working
author Matthias Mauch <mail@matthiasmauch.net>
date Fri, 03 Jul 2015 17:34:38 +0100
parents b877df85ad9e
children 83978b93aac1
files LocalCandidatePYIN.cpp Makefile.inc Makefile.osx MonoNote.cpp MonoNoteHMM.cpp MonoNoteHMM.h MonoPitch.cpp MonoPitch.h MonoPitchHMM.cpp MonoPitchHMM.h PYinVamp.cpp PYinVamp.h SparseHMM.cpp SparseHMM.h win32-build/pyin.pro
diffstat 15 files changed, 193 insertions(+), 268 deletions(-) [+]
line wrap: on
line diff
--- a/LocalCandidatePYIN.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ b/LocalCandidatePYIN.cpp	Fri Jul 03 17:34:38 2015 +0100
@@ -12,7 +12,7 @@
 */
 
 #include "LocalCandidatePYIN.h"
-#include "MonoPitch.h"
+#include "MonoPitchHMM.h"
 #include "YinUtil.h"
 
 #include "vamp-sdk/FFT.h"
@@ -345,7 +345,7 @@
     }
 
     // MONO-PITCH STUFF
-    MonoPitch mp;
+    MonoPitchHMM hmm(0);
     size_t nFrame = m_timestamp.size();
     vector<vector<float> > pitchTracks;
     vector<float> freqSum = vector<float>(m_nCandidate);
@@ -359,11 +359,11 @@
     for (size_t iCandidate = 0; iCandidate < m_nCandidate; ++iCandidate)
     {
         pitchTracks.push_back(vector<float>(nFrame));
-        vector<vector<pair<double,double> > > tempPitchProb;
+        vector<pair<double,double> > tempPitchProb;
+        vector<vector<double> > tempObsProb;
         float centrePitch = 45 + 3 * iCandidate;
 
         for (size_t iFrame = 0; iFrame < nFrame; ++iFrame) {
-            tempPitchProb.push_back(vector<pair<double,double> >());
             float sumProb = 0;
             float pitch = 0;
             float prob = 0;
@@ -374,16 +374,26 @@
                     boost::math::pdf(normalDist, pitch-centrePitch) /
                     maxNormalDist * 2;
                 sumProb += prob;
-                tempPitchProb[iFrame].push_back(
+                tempPitchProb.push_back(
                     pair<double,double>(pitch,prob));
             }
             for (size_t iProb = 0; iProb < m_pitchProb[iFrame].size(); ++iProb)
             {
-                tempPitchProb[iFrame][iProb].second /= sumProb;
+                tempPitchProb[iProb].second /= sumProb;
             }
+            tempObsProb.push_back(hmm.calculateObsProb(tempPitchProb));
         }
 
-        vector<float> mpOut = mp.process(tempPitchProb);
+        vector<int> rawPitchPath = hmm.decodeViterbi(tempObsProb);
+        vector<float> mpOut;
+
+        for (size_t iFrame = 0; iFrame < rawPitchPath.size(); ++iFrame)
+        {
+            float freq = hmm.nearestFreq(rawPitchPath[iFrame], 
+                                                m_pitchProb[iFrame]);
+            mpOut.push_back(freq); // for note processing below
+        }
+
         float prevFreq = 0;
         for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
         {
--- a/Makefile.inc	Fri Jul 03 14:09:05 2015 +0100
+++ b/Makefile.inc	Fri Jul 03 17:34:38 2015 +0100
@@ -15,7 +15,6 @@
            Yin.cpp \
            YinUtil.cpp \
            MonoNote.cpp \
-           MonoPitch.cpp \
            MonoNoteParameters.cpp \
            SparseHMM.cpp \
            MonoNoteHMM.cpp \
@@ -65,15 +64,14 @@
 # DO NOT DELETE
 
 libmain.o: PYinVamp.h Yin.h MeanFilter.h YinVamp.h LocalCandidatePYIN.h
-LocalCandidatePYIN.o: LocalCandidatePYIN.h Yin.h MeanFilter.h MonoPitch.h
+LocalCandidatePYIN.o: LocalCandidatePYIN.h Yin.h MeanFilter.h
 LocalCandidatePYIN.o: MonoPitchHMM.h SparseHMM.h YinUtil.h
 MonoNote.o: MonoNote.h MonoNoteHMM.h MonoNoteParameters.h SparseHMM.h
 MonoNoteHMM.o: MonoNoteHMM.h MonoNoteParameters.h SparseHMM.h
 MonoNoteParameters.o: MonoNoteParameters.h
-MonoPitch.o: MonoPitch.h MonoPitchHMM.h SparseHMM.h
 MonoPitchHMM.o: MonoPitchHMM.h SparseHMM.h
 PYinVamp.o: PYinVamp.h Yin.h MeanFilter.h MonoNote.h MonoNoteHMM.h
-PYinVamp.o: MonoNoteParameters.h SparseHMM.h MonoPitch.h MonoPitchHMM.h
+PYinVamp.o: MonoNoteParameters.h SparseHMM.h MonoPitchHMM.h
 SparseHMM.o: SparseHMM.h
 Yin.o: Yin.h MeanFilter.h YinUtil.h
 YinUtil.o: YinUtil.h MeanFilter.h
@@ -90,7 +88,6 @@
 LocalCandidatePYIN.o: Yin.h MeanFilter.h
 MonoNote.o: MonoNoteHMM.h MonoNoteParameters.h SparseHMM.h
 MonoNoteHMM.o: MonoNoteParameters.h SparseHMM.h
-MonoPitch.o: MonoPitchHMM.h SparseHMM.h
 MonoPitchHMM.o: SparseHMM.h
 PYinVamp.o: Yin.h MeanFilter.h
 Yin.o: MeanFilter.h
--- a/Makefile.osx	Fri Jul 03 14:09:05 2015 +0100
+++ b/Makefile.osx	Fri Jul 03 17:34:38 2015 +0100
@@ -1,5 +1,5 @@
 ARCHFLAGS := -arch x86_64 -mmacosx-version-min=10.7 -stdlib=libc++ 
-CFLAGS := $(ARCHFLAGS) -O3 -I../vamp-plugin-sdk -I/usr/local/include -Wall -fPIC 
+CFLAGS := $(ARCHFLAGS) -O3 -I../vamp-plugin-sdk -I/usr/local/include -Wall -fPIC -g
 CXXFLAGS := $(CFLAGS)
 
 LDFLAGS := -L../vamp-plugin-sdk -L../vamp-plugin-sdk -lvamp-sdk $(ARCHFLAGS) -L/usr/local/lib 
--- a/MonoNote.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ b/MonoNote.cpp	Fri Jul 03 17:34:38 2015 +0100
@@ -22,7 +22,7 @@
 using std::pair;
 
 MonoNote::MonoNote() :
-    hmm()
+    hmm(0)
 {
 }
 
--- a/MonoNoteHMM.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ b/MonoNoteHMM.cpp	Fri Jul 03 17:34:38 2015 +0100
@@ -21,7 +21,8 @@
 using std::vector;
 using std::pair;
 
-MonoNoteHMM::MonoNoteHMM() :
+MonoNoteHMM::MonoNoteHMM(int fixedLag) :
+    SparseHMM(fixedLag),
     par()
 {
     build();
--- a/MonoNoteHMM.h	Fri Jul 03 14:09:05 2015 +0100
+++ b/MonoNoteHMM.h	Fri Jul 03 17:34:38 2015 +0100
@@ -27,7 +27,7 @@
 class MonoNoteHMM : public SparseHMM
 {
 public:
-    MonoNoteHMM();
+    MonoNoteHMM(int fixedLag);
     const std::vector<double> calculateObsProb(const vector<pair<double, double> >);
     double getMidiPitch(size_t index);
     double getFrequency(size_t index);
--- a/MonoPitch.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    pYIN - A fundamental frequency estimator for monophonic audio
-    Centre for Digital Music, Queen Mary, University of London.
-    
-    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 "MonoPitch.h"
-#include "MonoPitchHMM.h"
-#include <vector>
-
-#include <cstdio>
-#include <cmath>
-#include <complex>
-
-using std::vector;
-using std::pair;
-
-MonoPitch::MonoPitch() :
-    hmm()
-{
-}
-
-MonoPitch::~MonoPitch()
-{
-}
-
-const vector<float>
-MonoPitch::process(const vector<vector<pair<double, double> > > pitchProb)
-{
-    // std::cerr << "before observation prob calculation" << std::endl;
-    vector<vector<double> > obsProb;
-    for (size_t iFrame = 0; iFrame < pitchProb.size(); ++iFrame)
-    {
-        obsProb.push_back(hmm.calculateObsProb(pitchProb[iFrame]));
-    }
-        
-    vector<float> out; 
-    
-    // std::cerr << "before Viterbi decoding" << obsProb.size() << "ng" << obsProb[1].size() << std::endl;
-    vector<int> path = hmm.decodeViterbi(obsProb);
-    // std::cerr << "after Viterbi decoding" << std::endl;
-    
-    for (size_t iFrame = 0; iFrame < path.size(); ++iFrame)
-    {
-        // std::cerr << path[iFrame] << " " << hmm.m_freqs[path[iFrame]] << std::endl;
-        float hmmFreq = hmm.m_freqs[path[iFrame]];
-        float bestFreq = 0;
-        float leastDist = 10000;
-        if (hmmFreq > 0)
-        {
-            // This was a Yin estimate, so try to get original pitch estimate back
-            // ... a bit hacky, since we could have direclty saved the frequency
-            // that was assigned to the HMM bin in hmm.calculateObsProb -- but would
-            // have had to rethink the interface of that method.
-            for (size_t iPitch = 0; iPitch < pitchProb[iFrame].size(); ++iPitch)
-            {
-                float freq = 440. * std::pow(2, (pitchProb[iFrame][iPitch].first - 69)/12);
-                float dist = std::abs(hmmFreq-freq);
-                if (dist < leastDist)
-                {
-                    leastDist = dist;
-                    bestFreq = freq;
-                }
-            }
-        } else {
-            bestFreq = hmmFreq;
-        }
-        out.push_back(bestFreq);
-    }
-    return(out);
-}
--- a/MonoPitch.h	Fri Jul 03 14:09:05 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    pYIN - A fundamental frequency estimator for monophonic audio
-    Centre for Digital Music, Queen Mary, University of London.
-    
-    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 _MONOPITCH_H_
-#define _MONOPITCH_H_
-
-#include "MonoPitchHMM.h"
-
-#include <iostream>
-#include <vector>
-#include <exception>
-
-using std::vector;
-using std::pair;
-
-class MonoPitch {
-public:
-    MonoPitch();
-    virtual ~MonoPitch();
-    
-    // pitchProb is a frame-wise vector carrying a vector of pitch-probability pairs
-    const vector<float> process(const vector<vector<pair<double, double> > > pitchProb);
-private:
-    MonoPitchHMM hmm;
-};
-
-#endif
--- a/MonoPitchHMM.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ b/MonoPitchHMM.cpp	Fri Jul 03 17:34:38 2015 +0100
@@ -22,8 +22,8 @@
 using std::vector;
 using std::pair;
 
-MonoPitchHMM::MonoPitchHMM() :
-SparseHMM(),
+MonoPitchHMM::MonoPitchHMM(int fixedLag) :
+SparseHMM(fixedLag),
 m_minFreq(61.735),
 m_nBPS(5),
 m_nPitch(0),
@@ -156,3 +156,41 @@
     m_delta = vector<double>(m_nState);
     m_oldDelta = vector<double>(m_nState);
 }
+
+/*
+Takes a state number and a pitch-prob vector, then finds the pitch that would
+have been closest to the pitch of the state. Easy to understand? ;)
+*/
+const float
+MonoPitchHMM::nearestFreq(int state, vector<pair<double, double> > pitchProb)
+{
+    float hmmFreq = m_freqs[state];
+    // std::cerr << "hmmFreq " << hmmFreq << std::endl;
+    float bestFreq = 0;
+    float leastDist = 10000;
+    if (hmmFreq > 0)
+    {
+        // This was a Yin estimate, so try to get original pitch estimate back
+        // ... a bit hacky, since we could have direclty saved the frequency
+        // that was assigned to the HMM bin in hmm.calculateObsProb -- but would
+        // have had to rethink the interface of that method.
+
+        // std::cerr << "pitch prob size " << pitchProb.size() << std::endl;
+
+        for (size_t iPt = 0; iPt < pitchProb.size(); ++iPt)
+        {
+            float freq = 440. * 
+                         std::pow(2, 
+                                  (pitchProb[iPt].first - 69)/12);
+            float dist = std::abs(hmmFreq-freq);
+            if (dist < leastDist)
+            {
+                leastDist = dist;
+                bestFreq = freq;
+            }
+        }
+    } else {
+        bestFreq = hmmFreq;
+    }
+    return bestFreq;
+}
\ No newline at end of file
--- a/MonoPitchHMM.h	Fri Jul 03 14:09:05 2015 +0100
+++ b/MonoPitchHMM.h	Fri Jul 03 17:34:38 2015 +0100
@@ -26,10 +26,10 @@
 class MonoPitchHMM : public SparseHMM
 {
 public:
-    MonoPitchHMM();
+    MonoPitchHMM(int fixedLag);
     const std::vector<double> calculateObsProb(const vector<pair<double, double> >);
-    // double getMidiPitch(size_t index);
-    // double getFrequency(size_t index);
+    const float nearestFreq(int state, vector<pair<double, double> > pitchProb);
+
     void build();
     double m_minFreq; // 82.40689f/2
     size_t m_nBPS;
--- a/PYinVamp.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ b/PYinVamp.cpp	Fri Jul 03 17:34:38 2015 +0100
@@ -13,7 +13,6 @@
 
 #include "PYinVamp.h"
 #include "MonoNote.h"
-#include "MonoPitch.h"
 #include "MonoPitchHMM.h"
 
 #include "vamp-sdk/FFT.h"
@@ -45,13 +44,13 @@
     m_oSmoothedPitchTrack(0),
     m_oNotes(0),
     m_threshDistr(2.0f),
-    m_fixedLag(0.0f),
+    m_fixedLag(1.0f),
     m_outputUnvoiced(0.0f),
     m_preciseTime(0.0f),
     m_lowAmp(0.1f),
     m_onsetSensitivity(0.7f),
     m_pruneThresh(0.1f),
-    m_pitchHmm(),
+    m_pitchHmm(0),
     m_pitchProb(0),
     m_timestamp(0),
     m_level(0)
@@ -442,6 +441,9 @@
     m_yin.setThresholdDistr(m_threshDistr);
     m_yin.setFrameSize(m_blockSize);
     m_yin.setFast(!m_preciseTime);
+
+    if (m_fixedLag == 1.f) m_pitchHmm = MonoPitchHMM(100);
+    else                   m_pitchHmm = MonoPitchHMM(0);
     
     m_pitchProb.clear();
     m_timestamp.clear();
@@ -493,21 +495,50 @@
         }
     }
 
-    if (m_fixedLag == 0.f)
+    vector<double> tempObsProb = m_pitchHmm.calculateObsProb(tempPitchProb);
+    if (m_timestamp.empty())
     {
-        vector<double> tempObsProb = m_pitchHmm.calculateObsProb(tempPitchProb);
-        if (m_timestamp.empty())
+        m_pitchHmm.initialise(tempObsProb);
+    } else {
+        m_pitchHmm.process(tempObsProb);
+    }
+
+    m_pitchProb.push_back(tempPitchProb);
+    m_timestamp.push_back(timestamp);
+
+    int lag = m_pitchHmm.m_fixedLag;
+
+    if (m_fixedLag == 1.f)
+    {
+        if (m_timestamp.size() == lag + 1)
         {
-            m_pitchHmm.initialise(tempObsProb);
-        } else {
-            m_pitchHmm.process(tempObsProb);
+            m_timestamp.pop_front();
+            m_pitchProb.pop_front();
+
+            Feature f;
+            f.hasTimestamp = true;
+            vector<int> rawPitchPath = m_pitchHmm.track();
+            float freq = m_pitchHmm.nearestFreq(rawPitchPath[0], 
+                                                m_pitchProb[0]);
+            f.timestamp = m_timestamp[0];
+            f.values.clear();
+
+            // different output modes
+            if (freq < 0 && (m_outputUnvoiced==0))
+            {
+
+            } else {
+                if (m_outputUnvoiced == 1)
+                {
+                    f.values.push_back(fabs(freq));
+                } else {
+                    f.values.push_back(freq);
+                }
+                fs[m_oSmoothedPitchTrack].push_back(f);
+            }
         }
-        m_pitchProb.push_back(tempPitchProb);
-    } else {
-        // Damn, so I need the hmm right here! Sadly it isn't defined here yet.
-        // Perhaps I could re-design the whole shabang 
     }
-    m_timestamp.push_back(timestamp);
+
 
     // F0 CANDIDATES
     Feature f;
@@ -560,16 +591,16 @@
 
     // ================== P I T C H  T R A C K =================================
 
-    vector<int> rawPitchPath = m_pitchHmm.finalise();
+    vector<int> rawPitchPath = m_pitchHmm.track();
     vector<float> mpOut; 
     
     for (size_t iFrame = 0; iFrame < rawPitchPath.size(); ++iFrame)
     {
-        float freq = pitchState2Freq(rawPitchPath[iFrame], m_pitchProb[iFrame]);
+        float freq = m_pitchHmm.nearestFreq(rawPitchPath[iFrame], 
+                                            m_pitchProb[iFrame]);
         mpOut.push_back(freq); // for note processing below
         
         f.timestamp = m_timestamp[iFrame];
-        // std::cerr << f.timestamp << std::endl;
         f.values.clear();
 
         // different output modes
@@ -582,114 +613,70 @@
         }
         fs[m_oSmoothedPitchTrack].push_back(f);
     }
-
-    // for (size_t iFrame = 0; iFrame < mpOut.size(); ++iFrame)
-    // {
-    //     if (mpOut[iFrame] < 0 && (m_outputUnvoiced==0)) continue;
-
-    //     if (m_outputUnvoiced == 1)
-    //     {
-    //         f.values.push_back(fabs(mpOut[iFrame]));
-    //     } else {
-    //         f.values.push_back(mpOut[iFrame]);
-    //     }
-        
-    //     fs[m_oSmoothedPitchTrack].push_back(f);
-    // }
     
     // ======================== N O T E S ======================================
-    MonoNote mn;
-    std::vector<std::vector<std::pair<double, double> > > smoothedPitch;
-    for (size_t iFrame = 0; iFrame < mpOut.size(); ++iFrame) {
-        std::vector<std::pair<double, double> > temp;
-        if (mpOut[iFrame] > 0)
-        {
-            double tempPitch = 12 * 
-                               std::log(mpOut[iFrame]/440)/std::log(2.) + 69;
-            temp.push_back(std::pair<double,double>(tempPitch, .9));
-        }
-        smoothedPitch.push_back(temp);
-    }
-    // vector<MonoNote::FrameOutput> mnOut = mn.process(m_pitchProb);
-    vector<MonoNote::FrameOutput> mnOut = mn.process(smoothedPitch);
+    // MonoNote mn;
+    // std::vector<std::vector<std::pair<double, double> > > smoothedPitch;
+    // for (size_t iFrame = 0; iFrame < mpOut.size(); ++iFrame) {
+    //     std::vector<std::pair<double, double> > temp;
+    //     if (mpOut[iFrame] > 0)
+    //     {
+    //         double tempPitch = 12 * 
+    //                            std::log(mpOut[iFrame]/440)/std::log(2.) + 69;
+    //         temp.push_back(std::pair<double,double>(tempPitch, .9));
+    //     }
+    //     smoothedPitch.push_back(temp);
+    // }
+    // // vector<MonoNote::FrameOutput> mnOut = mn.process(m_pitchProb);
+    // vector<MonoNote::FrameOutput> mnOut = mn.process(smoothedPitch);
     
-    // turning feature into a note feature
-    f.hasTimestamp = true;
-    f.hasDuration = true;
-    f.values.clear();
+    // // turning feature into a note feature
+    // f.hasTimestamp = true;
+    // f.hasDuration = true;
+    // f.values.clear();
         
-    int onsetFrame = 0;
-    bool isVoiced = 0;
-    bool oldIsVoiced = 0;
-    size_t nFrame = m_pitchProb.size();
+    // int onsetFrame = 0;
+    // bool isVoiced = 0;
+    // bool oldIsVoiced = 0;
+    // size_t nFrame = m_pitchProb.size();
 
-    float minNoteFrames = (m_inputSampleRate*m_pruneThresh) / m_stepSize;
+    // float minNoteFrames = (m_inputSampleRate*m_pruneThresh) / m_stepSize;
     
-    // the body of the loop below should be in a function/method
-    std::vector<float> notePitchTrack; // collects pitches for one note at a time
-    for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
-    {
-        isVoiced = mnOut[iFrame].noteState < 3
-                   && smoothedPitch[iFrame].size() > 0
-                   && (iFrame >= nFrame-2
-                       || ((m_level[iFrame]/m_level[iFrame+2]) > 
-                        m_onsetSensitivity));
-        if (isVoiced && iFrame != nFrame-1)
-        {
-            if (oldIsVoiced == 0) // beginning of a note
-            {
-                onsetFrame = iFrame;
-            }
-            float pitch = smoothedPitch[iFrame][0].first;
-            notePitchTrack.push_back(pitch); // add to the note's pitch track
-        } else { // not currently voiced
-            if (oldIsVoiced == 1) // end of note
-            {
-                if (notePitchTrack.size() >= minNoteFrames)
-                {
-                    std::sort(notePitchTrack.begin(), notePitchTrack.end());
-                    float medianPitch = notePitchTrack[notePitchTrack.size()/2];
-                    float medianFreq = std::pow(2,(medianPitch - 69) / 12) * 440;
-                    f.values.clear();
-                    f.values.push_back(medianFreq);
-                    f.timestamp = m_timestamp[onsetFrame];
-                    f.duration = m_timestamp[iFrame] - m_timestamp[onsetFrame];
-                    fs[m_oNotes].push_back(f);
-                }
-                notePitchTrack.clear();
-            }
-        }
-        oldIsVoiced = isVoiced;
-    }
+    // // the body of the loop below should be in a function/method
+    // std::vector<float> notePitchTrack; // collects pitches for one note at a time
+    // for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
+    // {
+    //     isVoiced = mnOut[iFrame].noteState < 3
+    //                && smoothedPitch[iFrame].size() > 0
+    //                && (iFrame >= nFrame-2
+    //                    || ((m_level[iFrame]/m_level[iFrame+2]) > 
+    //                     m_onsetSensitivity));
+    //     if (isVoiced && iFrame != nFrame-1)
+    //     {
+    //         if (oldIsVoiced == 0) // beginning of a note
+    //         {
+    //             onsetFrame = iFrame;
+    //         }
+    //         float pitch = smoothedPitch[iFrame][0].first;
+    //         notePitchTrack.push_back(pitch); // add to the note's pitch track
+    //     } else { // not currently voiced
+    //         if (oldIsVoiced == 1) // end of note
+    //         {
+    //             if (notePitchTrack.size() >= minNoteFrames)
+    //             {
+    //                 std::sort(notePitchTrack.begin(), notePitchTrack.end());
+    //                 float medianPitch = notePitchTrack[notePitchTrack.size()/2];
+    //                 float medianFreq = std::pow(2,(medianPitch - 69) / 12) * 440;
+    //                 f.values.clear();
+    //                 f.values.push_back(medianFreq);
+    //                 f.timestamp = m_timestamp[onsetFrame];
+    //                 f.duration = m_timestamp[iFrame] - m_timestamp[onsetFrame];
+    //                 fs[m_oNotes].push_back(f);
+    //             }
+    //             notePitchTrack.clear();
+    //         }
+    //     }
+    //     oldIsVoiced = isVoiced;
+    // }
     return fs;
 }
-
-float
-PYinVamp::pitchState2Freq(int state, vector<pair<double, double> > pitchProb)
-{
-    float hmmFreq = m_pitchHmm.m_freqs[state];
-    float bestFreq = 0;
-    float leastDist = 10000;
-    if (hmmFreq > 0)
-    {
-        // This was a Yin estimate, so try to get original pitch estimate back
-        // ... a bit hacky, since we could have direclty saved the frequency
-        // that was assigned to the HMM bin in hmm.calculateObsProb -- but would
-        // have had to rethink the interface of that method.
-        for (size_t iPt = 0; iPt < pitchProb.size(); ++iPt)
-        {
-            float freq = 440. * 
-                         std::pow(2, 
-                                  (pitchProb[iPt].first - 69)/12);
-            float dist = std::abs(hmmFreq-freq);
-            if (dist < leastDist)
-            {
-                leastDist = dist;
-                bestFreq = freq;
-            }
-        }
-    } else {
-        bestFreq = hmmFreq;
-    }
-    return bestFreq;
-}
\ No newline at end of file
--- a/PYinVamp.h	Fri Jul 03 14:09:05 2015 +0100
+++ b/PYinVamp.h	Fri Jul 03 17:34:38 2015 +0100
@@ -56,8 +56,6 @@
 
     FeatureSet getRemainingFeatures();
 
-    float pitchState2Freq(int state, vector<pair<double, double> > pitchProb);
-
 protected:
     size_t m_channels;
     size_t m_stepSize;
@@ -83,8 +81,8 @@
 
     MonoPitchHMM m_pitchHmm;
 
-    vector<vector<pair<double, double> > > m_pitchProb;
-    vector<Vamp::RealTime> m_timestamp;
+    deque<vector<pair<double, double> > > m_pitchProb;
+    deque<Vamp::RealTime> m_timestamp;
     vector<float> m_level;
 };
 
--- a/SparseHMM.cpp	Fri Jul 03 14:09:05 2015 +0100
+++ b/SparseHMM.cpp	Fri Jul 03 17:34:38 2015 +0100
@@ -19,7 +19,8 @@
 using std::vector;
 using std::pair;
 
-SparseHMM::SparseHMM() :
+SparseHMM::SparseHMM(int fixedLag) :
+    m_fixedLag(fixedLag),
     m_nState(0),
     m_nTrans(0),
     m_init(0),
@@ -61,7 +62,7 @@
         process(obsProb[iFrame]);
     }
 
-    vector<int> path = finalise();
+    vector<int> path = track();
     return(path);
 }
 
@@ -153,11 +154,20 @@
         }
         m_scale.push_back(1.0);
     }
+
+    if (m_fixedLag > 0 && m_psi.size() > m_fixedLag)
+    {
+        m_psi.pop_front();
+        m_scale.pop_front();
+    }
+
+    // std::cerr << m_fixedLag << " " << m_psi.size() << std::endl;
+
     return 0;
 }
 
-vector<int> 
-SparseHMM::finalise()
+const vector<int>
+SparseHMM::track()
 {
     // initialise backward step
     size_t nFrame = m_psi.size();
--- a/SparseHMM.h	Fri Jul 03 14:09:05 2015 +0100
+++ b/SparseHMM.h	Fri Jul 03 17:34:38 2015 +0100
@@ -25,7 +25,7 @@
 class SparseHMM
 {
 public:
-    SparseHMM();
+    SparseHMM(int fixedLag);
     virtual const std::vector<double>
                            calculateObsProb(const vector<pair<double, double> >);
     virtual void           build();
@@ -33,8 +33,9 @@
     void                   reset();
     void                   initialise(vector<double> firstObs);
     int                    process(vector<double> newObs);
-    vector<int>            finalise();
+    const vector<int>      track();
     // "sparse" HMM definition
+    int m_fixedLag;
     int m_nState;
     int m_nTrans;
     vector<double> m_init;
--- a/win32-build/pyin.pro	Fri Jul 03 14:09:05 2015 +0100
+++ b/win32-build/pyin.pro	Fri Jul 03 17:34:38 2015 +0100
@@ -13,7 +13,6 @@
     ../Yin.cpp \
     ../SparseHMM.cpp \
     ../MonoPitchHMM.cpp \
-    ../MonoPitch.cpp \
     ../MonoNoteParameters.cpp \
     ../MonoNoteHMM.cpp \
     ../MonoNote.cpp \
@@ -27,7 +26,6 @@
     ../Yin.h \
     ../SparseHMM.h \
     ../MonoPitchHMM.h \
-    ../MonoPitch.h \
     ../MonoNoteParameters.h \
     ../MonoNoteHMM.h \
     ../MonoNote.h \