changeset 83:10e76188c846 refactors

Expose diagonal weight as a parameter
author Chris Cannam
date Thu, 27 Nov 2014 10:49:11 +0000
parents 3616d541d69e
children de7034e93dd0
files Makefile.linux src/Finder.cpp src/MatchVampPlugin.cpp src/Matcher.cpp src/Matcher.h
diffstat 5 files changed, 69 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.linux	Thu Nov 27 10:14:07 2014 +0000
+++ b/Makefile.linux	Thu Nov 27 10:49:11 2014 +0000
@@ -1,6 +1,6 @@
 
-CXXFLAGS	+= -fPIC -ffast-math -O3 -Wall -Werror -Wfloat-conversion
-#CXXFLAGS	+= -fPIC -g -Wall -Werror
+#CXXFLAGS	+= -fPIC -ffast-math -O3 -Wall -Werror -Wfloat-conversion
+CXXFLAGS	+= -fPIC -g -Wall -Werror -DPERFORM_ERROR_CHECKS=1
 
 LDFLAGS		+= -shared -Wl,-Bstatic -lvamp-sdk -Wl,-Bdynamic -Wl,-Bsymbolic -Wl,-z,defs -lpthread -Wl,--version-script=vamp-plugin.map
 
--- a/src/Finder.cpp	Thu Nov 27 10:14:07 2014 +0000
+++ b/src/Finder.cpp	Thu Nov 27 10:49:11 2014 +0000
@@ -92,6 +92,8 @@
 {
     int prevRowStart = 0, prevRowStop = 0;
 
+    float diagonalWeight = m_m->getDiagonalWeight();
+    
     for (int r = r1; r <= r2; r++) {
 
         pair<int, int> colRange = m_m->getColRange(r);
@@ -108,7 +110,7 @@
                 double min = -1;
                 if ((c > prevRowStart) && (c <= prevRowStop)) {
                     // diagonal from (r-1,c-1)
-                    min = m_m->getPathCost(r-1, c-1) + newCost * 2;
+                    min = m_m->getPathCost(r-1, c-1) + newCost * diagonalWeight;
                     dir = Matcher::AdvanceBoth;
                 }
                 if ((c >= prevRowStart) && (c < prevRowStop)) {
@@ -161,6 +163,8 @@
 
     int prevRowStart = 0, prevRowStop = 0;
 
+    float diagonalWeight = m_m->getDiagonalWeight();
+    
     for (int r = r1; r <= r2; r++) {
 
         pair<int, int> colRange = m_m->getColRange(r);
@@ -178,9 +182,9 @@
                 double min = -1;
                 if ((c > prevRowStart) && (c <= prevRowStop)) {
                     // diagonal from (r-1,c-1)
-                    min = m_m->getPathCost(r-1, c-1) + newCost * 2;
+                    min = m_m->getPathCost(r-1, c-1) + newCost * diagonalWeight;
                     err.prevCost = m_m->getPathCost(r-1, c-1);
-                    err.distance = newCost * 2;
+                    err.distance = newCost * diagonalWeight;
                     dir = Matcher::AdvanceBoth;
                 }
                 if ((c >= prevRowStart) && (c < prevRowStop)) {
@@ -210,8 +214,10 @@
 
                 if (c > rowStart) {
                     // horizontal from (r,c-1)
+                    updateTo = m_m->getPathCost(r, c-1) + newCost;
+                    err.prevCost = m_m->getPathCost(r, c-1);
+                    err.distance = newCost;
                     dir = Matcher::AdvanceOther;
-                    updateTo = m_m->getPathCost(r, c-1) + newCost;
                 }
             }
 
@@ -262,10 +268,13 @@
         cerr << "\nWARNING: Checking path-cost matrix returned mismatch:" << endl;
         cerr << "Type: " << err.type << endl;
         cerr << "At row " << err.r << ", column " << err.c << " advancing "
-             << err.advance << "\nPrev cost " << err.prevCost
+             << Matcher::advanceToString(err.advance)
+             << "\nPrev cost " << err.prevCost
              << " plus distance " << err.distance << " gives "
              << err.costShouldBe << ", but matrix contains " << err.costWas
-             << "\n" << endl;
+             << endl;
+        cerr << "Note: diagonal weight = " << m_m->getDiagonalWeight() << endl;
+        cerr << endl;
     }
 #endif
 
--- a/src/MatchVampPlugin.cpp	Thu Nov 27 10:14:07 2014 +0000
+++ b/src/MatchVampPlugin.cpp	Thu Nov 27 10:49:11 2014 +0000
@@ -233,6 +233,16 @@
     desc.unit = "s";
     list.push_back(desc);
 
+    desc.identifier = "diagonalweight";
+    desc.name = "Diagonal Weight";
+    desc.description = "Weight applied to cost of diagonal step relative to horizontal or vertical step. The default of 2.0 is good for gross tracking of quite different performances; closer to 1.0 produces a smoother path for performances more similar in tempo";
+    desc.minValue = 1.0;
+    desc.maxValue = 3.0;
+    desc.defaultValue = 2.0;
+    desc.isQuantized = false;
+    desc.unit = "";
+    list.push_back(desc);
+    
     desc.identifier = "smooth";
     desc.name = "Smooth Path";
     desc.description = "Smooth the path by replacing steps with diagonals";
@@ -262,6 +272,8 @@
         return m_feParams.useChromaFrequencyMap ? 1.0 : 0.0;
     } else if (name == "gradientlimit") {
         return m_params.maxRunCount;
+    } else if (name == "diagonalweight") {
+        return m_params.diagonalWeight;
     } else if (name == "zonewidth") {
         return (float)m_params.blockTime;
     } else if (name == "smooth") {
@@ -286,6 +298,8 @@
         m_feParams.useChromaFrequencyMap = (value > 0.5);
     } else if (name == "gradientlimit") {
         m_params.maxRunCount = int(value + 0.1);
+    } else if (name == "diagonalweight") {
+        m_params.diagonalWeight = value;
     } else if (name == "zonewidth") {
         m_params.blockTime = value;
     } else if (name == "smooth") {
--- a/src/Matcher.cpp	Thu Nov 27 10:14:07 2014 +0000
+++ b/src/Matcher.cpp	Thu Nov 27 10:49:11 2014 +0000
@@ -339,10 +339,16 @@
 void
 Matcher::updateValue(int i, int j, Advance dir, double value, float dMN)
 {
+    float weighted = dMN;
+    if (dir == AdvanceBoth) {
+        weighted *= m_params.diagonalWeight;
+    }
+    
     if (m_firstPM) {
 
         m_distance[i][j - m_first[i]] = dMN;
-        setPathCost(i, j, dir, value + (dir == AdvanceBoth ? dMN*2: dMN));
+        
+        setPathCost(i, j, dir, value + weighted);
 
     } else {
 
@@ -359,7 +365,7 @@
         }
 
         m_otherMatcher->m_distance[j][idx] = dMN;
-        m_otherMatcher->setPathCost(j, i, dir, value + (dir == AdvanceBoth ? dMN*2: dMN));
+        m_otherMatcher->setPathCost(j, i, dir, value + weighted);
     }
 }
 
--- a/src/Matcher.h	Thu Nov 27 10:14:07 2014 +0000
+++ b/src/Matcher.h	Thu Nov 27 10:49:11 2014 +0000
@@ -42,6 +42,15 @@
         AdvanceThis,
         AdvanceOther
     };
+    static string advanceToString(Advance a) {
+        switch (a) {
+        case AdvanceNone: return "AdvanceNone";
+        case AdvanceBoth: return "AdvanceBoth";
+        case AdvanceThis: return "AdvanceThis";
+        case AdvanceOther: return "AdvanceOther";
+        }
+        return "(unknown)";
+    }
 
     struct Parameters {
 
@@ -51,7 +60,8 @@
             hopTime(hopTime_),
             fftSize(fftSize_),
             blockTime(10.0),
-            maxRunCount(3)
+            maxRunCount(3),
+            diagonalWeight(2.0)
         {}
 
         /** Sample rate of audio */
@@ -62,13 +72,15 @@
 
         /** Spacing of audio frames (determines the amount of overlap or
          *  skip between frames). This value is expressed in
-         *  seconds. */
+         *  seconds.
+         */
         double hopTime;
         
         /** Size of an FFT frame in samples. Note that the data passed
          *  in to Matcher is already in the frequency domain, so this
          *  expresses the size of the frame that the caller will be
-         *  providing. */
+         *  providing.
+         */
         int fftSize;
         
         /** The width of the search band (error margin) around the current
@@ -83,6 +95,17 @@
          *  processed.
          */
         int maxRunCount;
+
+        /** Weight applied to cost of diagonal step relative to
+         *  horizontal or vertical step. The default of 2.0 means that
+         *  a diagonal is not favoured over horizontal+vertical
+         *  combined, which is good when maintaining gross tracking of
+         *  performances that may have wildly differing speeds but
+         *  which also leads to quite jaggy paths. A more typical
+         *  normal DTW approach for performances with similar speeds
+         *  might use 1.0 or something close to it.
+         */
+        float diagonalWeight;
     };
 
     /** Constructor for Matcher.
@@ -131,6 +154,10 @@
         return m_otherMatcher->getFrameCount();
     }
 
+    float getDiagonalWeight() {
+        return m_params.diagonalWeight;
+    }
+    
     /** 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