changeset 568:e9716e9a44e4

Fixes to tests for frequency preservation in resampling
author Chris Cannam
date Mon, 12 May 2014 17:46:31 +0100
parents 5f88c437edde
children b218e2362246
files src/may/stream/test/test_manipulate.yeti src/may/stream/test/test_resample.yeti
diffstat 2 files changed, 65 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/src/may/stream/test/test_manipulate.yeti	Mon May 12 17:46:19 2014 +0100
+++ b/src/may/stream/test/test_manipulate.yeti	Mon May 12 17:46:31 2014 +0100
@@ -6,7 +6,7 @@
 syn = load may.stream.syntheticstream;
 manip = load may.stream.manipulate;
 
-{ compare, compareUsing, assert } = load may.test;
+{ compare, compareUsing, compareMatrices, assert } = load may.test;
 
 makeTests name withUnknown =
    (maybeDuration n str =
@@ -673,6 +673,12 @@
         ( str.close (); true )
 ),
 
+"spaced-picked-\(name)": \(
+    originals = manip.duplicated 2 (maybeDuration 2000 (syn.sinusoid 3000 440));
+    str = manip.picked 16 (manip.spaced 16 originals[0]);
+    compareMatrices 1e-12 (str.read 2000) (originals[1].read 2000);
+),
+
 //!!! still no tests for multi-channel inputs
 
 ]);
--- a/src/may/stream/test/test_resample.yeti	Mon May 12 17:46:19 2014 +0100
+++ b/src/may/stream/test/test_resample.yeti	Mon May 12 17:46:31 2014 +0100
@@ -13,54 +13,78 @@
 
 { compare, compareUsing, compareMatrices, assert, time } = load may.test;
 
-measureFrequency sampleRate cycles stream =
+eps = 1e-14;
+
+measureFrequency cycles stream =
    (case stream.available of
     Known n:
        (v = channels.mixedDown (stream.read n);
-        var firstCrossing = -1;
-        var lastCrossing = -1;
-        var nCrossings = 0;
-        for [0..vec.length v - 2] do i:
-            if nCrossings < cycles and vec.at v i <= 0 and vec.at v (i+1) > 0 then
-                if firstCrossing < 0 then
-                    firstCrossing := i;
-                fi;
-                lastCrossing := i;
-                nCrossings := nCrossings + 1;
+        var firstPeak = -1;
+        var lastPeak = -1;
+        var nPeaks = 0;
+        // count +ve peaks
+        len = vec.length v;
+        for [int(len/4) .. len - 2] do i:
+            // allow some fuzz
+            x0 = int (10000 * vec.at v (i-1));
+            x1 = int (10000 * vec.at v (i));
+            x2 = int (10000 * vec.at v (i+1));
+            if nPeaks < (cycles + 1) and
+                x1 > 0 and x1 > x0 and x1 >= x2 then
+                if firstPeak < 0 then firstPeak := i; fi;
+                lastPeak := i;
+                nPeaks := nPeaks + 1;
             fi;
         done;
-        if nCrossings < 2 then 0
+        if nPeaks < 2 then 0
         else
-            cycle = (lastCrossing - firstCrossing) / (nCrossings - 1);
-            freq = sampleRate / cycle;
-            eprintln "lastCrossing = \(lastCrossing), firstCrossing = \(firstCrossing), dist = \(lastCrossing - firstCrossing), nCycles = \(nCrossings - 1), cycle = \(cycle), freq = \(freq)";
-            freq;
+            cycle = (lastPeak - firstPeak) / (nPeaks - 1);
+            stream.sampleRate / cycle;
         fi);
    _: failWith "Expected stream duration to be known";
    esac);
 
-testFrequencyIntegrity freq sourceRate targetRate =
-   (// Test that downsample and then upsample again produces a signal
-    // at the same frequency as the original (i.e. the overall speed
-    // is retained exactly, regardless of the SNR on the way).
-    nCycles = 5000;
+testFrequencyIntegrityWith sort freq sourceRate forward backward =
+   (// Test that downsample and then upsample again (or vice versa,
+    // depending on forward and backward manipulators) produces a
+    // signal at the same frequency as the original (i.e. the overall
+    // speed is retained exactly, regardless of the SNR on the way).
+    nCycles = 200;
     duration = int (nCycles * sourceRate / freq);
-    originals = manip.duplicated 2
+    originals = manip.duplicated 3
        (manip.withDuration duration (syn.sinusoid sourceRate freq));
-    there = manip.duplicated 2
-       (resample.resampledTo targetRate originals[0]);
-    back = resample.resampledTo sourceRate there[0];
-    origFreq = measureFrequency sourceRate (nCycles - 2) originals[1];
-    backFreq = measureFrequency sourceRate (nCycles - 2) back;
+    there = manip.duplicated 3 (forward originals[0]);
+    back = manip.duplicated 3 (backward there[0]);
+    origFreq = measureFrequency (nCycles/2) originals[1];
+    backFreq = measureFrequency (nCycles/2) back[1];
     if not (compare backFreq origFreq) then
-        iFreq = measureFrequency targetRate (nCycles - 2) there[1];
-        eprintln "** note: rate conversion \(sourceRate) -> \(targetRate) -> \(sourceRate)";
-        eprintln "** frequency measured from intermediate stream: \(iFreq)";
+        eprintln "** note: rate conversion \(sourceRate) -> \(there[1].sampleRate) -> \(sourceRate)";
+        examples = array (map do s:
+            channels.mixedDown (s[2].read 500)
+        done [ originals, there, back ]);
         false
     else
         true
     fi);
 
+testFrequencyIntegrity freq sourceRate targetRate =
+    if sourceRate / targetRate == int (sourceRate / targetRate) then
+        testFrequencyIntegrityWith "decimated" freq sourceRate
+           (resample.decimated (sourceRate / targetRate))
+           (resample.interpolated (sourceRate / targetRate))
+    elif targetRate / sourceRate == int (targetRate / sourceRate) then
+        testFrequencyIntegrityWith "interpolated" freq sourceRate
+           (resample.interpolated (targetRate / sourceRate))
+           (resample.decimated (targetRate / sourceRate))
+    else
+        true
+    fi and
+        testFrequencyIntegrityWith
+           (if sourceRate > targetRate then "downsampled" else "upsampled" fi)
+            freq sourceRate
+           (resample.resampledTo targetRate)
+           (resample.resampledTo sourceRate);
+
 [
 
 // Test for duration of decimated stream (does not test contents, that
@@ -258,27 +282,19 @@
 ),
 
 "down-up-2": \(
-    testFrequencyIntegrity 440 44100 22050;
-),
-
-"down-up-5": \(
-    testFrequencyIntegrity 440 44100 8820;
+    testFrequencyIntegrity 441 44100 22050;
 ),
 
 "down-up-16": \(
-    testFrequencyIntegrity 440 48000 3000;
+    testFrequencyIntegrity 300 48000 3000;
 ),
 
 "up-down-2": \(
-    testFrequencyIntegrity 440 44100 88200;
-),
-
-"up-down-5": \(
-    testFrequencyIntegrity 440 9600 48000;
+    testFrequencyIntegrity 441 44100 88200;
 ),
 
 "up-down-16": \(
-    testFrequencyIntegrity 440 3000 48000;
+    testFrequencyIntegrity 300 3000 48000;
 ),
 
 "same-rate": \(