changeset 46:1623751c4549

Switch from 60bpo to 120 - seems to work better (?) with little performance downside. Also avoid recalculating second feature if not needed for fine tuning, and extend regression test
author Chris Cannam
date Tue, 09 Jul 2019 15:09:33 +0100
parents 0fec1c76c92b
children f28b34e7ce8d
files src/TuningDifference.cpp src/TuningDifference.h test/expected.csv test/expected/input_vamp_tuning-difference_tuning-difference_cents.csv test/expected/input_vamp_tuning-difference_tuning-difference_rotfeature.csv test/expected/input_vamp_tuning-difference_tuning-difference_tuningfreq.csv test/regression.sh
diffstat 7 files changed, 63 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/src/TuningDifference.cpp	Tue Jul 09 12:34:25 2019 +0100
+++ b/src/TuningDifference.cpp	Tue Jul 09 15:09:33 2019 +0100
@@ -42,7 +42,7 @@
 
 TuningDifference::TuningDifference(float inputSampleRate) :
     Plugin(inputSampleRate),
-    m_bpo(60),
+    m_bpo(120),
     m_refChroma(new Chromagram(paramsForTuningFrequency(440.))),
     m_blockSize(0),
     m_frameCount(0),
@@ -86,7 +86,7 @@
 {
     // Increment this each time you release a version that behaves
     // differently from the previous one
-    return 2;
+    return 3;
 }
 
 string
@@ -159,7 +159,7 @@
     
     desc.identifier = "finetuning";
     desc.name = "Fine tuning";
-    desc.description = "Use a fine tuning stage to increase nominal resolution from 20 cents to 1 cent.";
+    desc.description = "Use a fine tuning stage to increase nominal resolution from 10 cents to 1 cent.";
     desc.minValue = 0;
     desc.maxValue = 1;
     desc.defaultValue = (defaultFineTuning ? 1.f : 0.f);
@@ -415,6 +415,16 @@
     return FeatureSet();
 }
 
+void
+TuningDifference::rotateFeature(TFeature &r, int rotation) const
+{
+    if (rotation < 0) {
+        rotate(r.begin(), r.begin() - rotation, r.end());
+    } else {
+        rotate(r.begin(), r.end() - rotation, r.end());
+    }
+}
+
 double
 TuningDifference::featureDistance(const TFeature &other, int rotation) const
 {
@@ -426,11 +436,7 @@
 	// makes this chroma match an un-rotated reference, then this
 	// chroma must have initially been lower than the reference.
 	TFeature r(other);
-	if (rotation < 0) {
-	    rotate(r.begin(), r.begin() - rotation, r.end());
-	} else {
-	    rotate(r.begin(), r.end() - rotation, r.end());
-	}
+        rotateFeature(r, rotation);
 	return distance(m_refFeature, r);
     }
 }
@@ -455,12 +461,11 @@
 }
 
 pair<int, double>
-TuningDifference::findFineFrequency(int coarseCents, double coarseScore)
+TuningDifference::findFineFrequency(int coarseCents)
 {
     int coarseResolution = 1200 / m_bpo;
     int searchDistance = coarseResolution/2 - 1;
 
-    double bestScore = coarseScore;
     int bestCents = coarseCents;
     double bestHz = frequencyForCentsAbove440(coarseCents);
 
@@ -469,11 +474,20 @@
         return pair<int, double>(bestCents, bestHz);
     }
     
-    cerr << "corresponding coarse Hz " << bestHz << " scores " << coarseScore << endl;
+    //!!! This is kind of absurd - all this brute force but all we're
+    //!!! really doing is aligning two very short signals at
+    //!!! sub-sample level - let's rewrite it someday
+    
+    cerr << "findFineFrequency: coarse frequency is " << bestHz << endl;
     cerr << "searchDistance = " << searchDistance << endl;
+
+    double bestScore = 0;
+    bool firstScore = true;
     
     for (int sign = -1; sign <= 1; sign += 2) {
-	for (int offset = 1; offset <= searchDistance; ++offset) {
+	for (int offset = (sign < 0 ? 0 : 1);
+             offset <= searchDistance;
+             ++offset) {
 
 	    int fineCents = coarseCents + sign * offset;
 
@@ -487,11 +501,12 @@
 		 << ", Hz = " << fineHz << ", score " << fineScore
 		 << " (best score so far " << bestScore << ")" << endl;
 	    
-	    if (fineScore < bestScore) {
+	    if ((fineScore < bestScore) || firstScore) {
 		cerr << "is good!" << endl;
 		bestScore = fineScore;
 		bestCents = fineCents;
 		bestHz = fineHz;
+                firstScore = false;
 	    } else {
 		break;
 	    }
@@ -530,24 +545,17 @@
 
     cerr << "rotation " << rotation << " -> cents " << coarseCents << endl;
 
-    double coarseHz = frequencyForCentsAbove440(coarseCents);
-
-    TFeature coarseFeature;
-    if (rotation == 0) {
-        coarseFeature = otherFeature;
-    } else {
-        coarseFeature = computeFeatureFromSignal(m_other, coarseHz);
+    TFeature coarseFeature = otherFeature;
+    if (rotation != 0) {
+        rotateFeature(coarseFeature, rotation);
     }
-    double coarseScore = featureDistance(coarseFeature);
-
-    cerr << "corresponding Hz " << coarseHz << " scores " << coarseScore << endl;
 
     //!!! This should be returning the fine chroma, not the coarse
     f.values.clear();
     for (auto v: coarseFeature) f.values.push_back(float(v));
     fs[m_outputs["rotfeature"]].push_back(f);
 
-    pair<int, double> fine = findFineFrequency(coarseCents, coarseScore);
+    pair<int, double> fine = findFineFrequency(coarseCents);
     int fineCents = fine.first;
     double fineHz = fine.second;
 
--- a/src/TuningDifference.h	Tue Jul 09 12:34:25 2019 +0100
+++ b/src/TuningDifference.h	Tue Jul 09 15:09:33 2019 +0100
@@ -77,9 +77,10 @@
     Chromagram::Parameters paramsForTuningFrequency(double hz) const;
     TFeature computeFeatureFromTotals(const TFeature &totals) const;
     TFeature computeFeatureFromSignal(const Signal &signal, double hz) const;
+    void rotateFeature(TFeature &feature, int rotation) const;
     double featureDistance(const TFeature &other, int rotation = 0) const;
     int findBestRotation(const TFeature &other) const;
-    std::pair<int, double> findFineFrequency(int coarseCents, double coarseScore);
+    std::pair<int, double> findFineFrequency(int coarseCents);
 
     mutable std::map<string, int> m_outputs;
 };
--- a/test/expected.csv	Tue Jul 09 12:34:25 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-0.000000000,-233
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/expected/input_vamp_tuning-difference_tuning-difference_cents.csv	Tue Jul 09 15:09:33 2019 +0100
@@ -0,0 +1,1 @@
+0.000000000,-234
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/expected/input_vamp_tuning-difference_tuning-difference_rotfeature.csv	Tue Jul 09 15:09:33 2019 +0100
@@ -0,0 +1,1 @@
+0.000000000,0.00854703,0.00931918,0.0106465,0.0122647,0.0138192,0.014525,0.0127565,0.00961516,0.00781605,0.00713913,0.0070496,0.00689058,0.00667673,0.0068509,0.00697572,0.00703986,0.00728556,0.00729718,0.00780589,0.00881012,0.00924105,0.00981542,0.0101795,0.0101861,0.0116841,0.0133551,0.0125837,0.0100393,0.00916784,0.00966024,0.00968428,0.00985668,0.010838,0.0113878,0.0130886,0.0154244,0.0147851,0.0124133,0.0108901,0.00978958,0.0085204,0.00787676,0.00806659,0.0081296,0.00795278,0.00725857,0.00640027,0.00608774,0.00564443,0.00578425,0.00616674,0.00679678,0.007764,0.00836082,0.00926527,0.0107817,0.010324,0.0083828,0.00728106,0.00676606,0.00674762,0.00674128,0.00633739,0.00626942,0.0063055,0.00667407,0.00670015,0.00663743,0.00677829,0.00728373,0.00811677,0.00894148,0.00974516,0.0115456,0.0143484,0.0156408,0.0138188,0.0104981,0.0084953,0.00746052,0.00668289,0.00641624,0.00640881,0.00641969,0.00688589,0.00793875,0.00798556,0.00685937,0.00603812,0.00571649,0.00562601,0.00541857,0.00519544,0.00539675,0.00576307,0.00631954,0.006413,0.00577577,0.00547572,0.00525969,0.00539033,0.00607725,0.00694533,0.00714786,0.00706424,0.00740421,0.00741943,0.00665752,0.00614163,0.00611627,0.00605784,0.00607328,0.0063531,0.007423,0.00861123,0.00968742,0.00927132,0.00812644,0.00785267,0.00828707
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/expected/input_vamp_tuning-difference_tuning-difference_tuningfreq.csv	Tue Jul 09 15:09:33 2019 +0100
@@ -0,0 +1,1 @@
+0.000000000,384.372
--- a/test/regression.sh	Tue Jul 09 12:34:25 2019 +0100
+++ b/test/regression.sh	Tue Jul 09 15:09:33 2019 +0100
@@ -7,8 +7,6 @@
 source_url=https://code.soundsoftware.ac.uk/attachments/download/1698/Zweieck-Duell.ogg
 
 testfile="$mydir/input.ogg"
-outfile="$mydir/output.csv"
-expfile="$mydir/expected.csv"
 
 if sonic-annotator -v >/dev/null ; then
     :
@@ -45,26 +43,41 @@
 rubberband -p -2.34 "$wavfile" "$lowfile"
 
 VAMP_PATH="$mydir/.." \
+         time \
 	 sonic-annotator \
-	 -d vamp:tuning-difference:tuning-difference \
+	 -d vamp:tuning-difference:tuning-difference:cents \
+	 -d vamp:tuning-difference:tuning-difference:tuningfreq \
+	 -d vamp:tuning-difference:tuning-difference:rotfeature \
 	 -w csv \
 	 --csv-omit-filename \
-	 --csv-one-file "$outfile" \
+         --csv-basedir "$mydir/output" \
 	 --csv-force \
          --multiplex \
 	 "$testfile" \
 	 "$lowfile"
 
-if cmp "$outfile" "$expfile" ; then
-    echo 
-    echo PASS
+failed=""
+
+for expected in "$mydir"/expected/*.csv ; do
+    outfile="$mydir"/output/$(basename $expected)
+    if cmp "$outfile" "$expected" ; then
+        echo "PASS: $outfile"
+    else
+        echo
+        echo "*** FAIL: Result does not match expected output. Diff follows:"
+        echo
+        sdiff -w 60 "$outfile" "$expected"
+        echo
+        failed="$failed $outfile"
+    fi
+done
+
+if [ -n "$failed" ]; then
+    echo "Some tests failed: $failed"
+    exit 1
+else
+    echo "All tests passed"
     exit 0
-else
-    echo
-    echo "*** FAIL: Result does not match expected output. Diff follows:"
-    echo
-    sdiff -w 60 "$outfile" "$expfile"
-    exit 1
 fi