changeset 68:e15e684d2af4

Some tricky tests for returned values from the test plugin
author Chris Cannam
date Wed, 14 Jan 2015 14:31:01 +0000
parents 6f6a54963ce8
children b34d3b58da98
files test/test_process.py vamp/__init__.py
diffstat 2 files changed, 113 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/test/test_process.py	Wed Jan 14 12:43:36 2015 +0000
+++ b/test/test_process.py	Wed Jan 14 14:31:01 2015 +0000
@@ -7,14 +7,88 @@
 
 rate = 44100
 
-def test_process():
-    buf = np.zeros(10240)
-    results = vamp.process(buf, rate, testPluginKey, {}, [ "input-timestamp" ])
-    print("results = " + str(list(results)))
-    return True
+# Throughout this file we have the assumption that the plugin gets run with a
+# blocksize of 1024, and with a step of 1024 for the time-domain version or 512
+# for the frequency-domain one. That is certainly expected to be the norm for a
+# plugin like this that declares no preference, and the Python Vamp module is
+# expected to follow the norm
 
-def test_process_freq():
-    buf = np.zeros(10240)
-    results = vamp.process(buf, rate, testPluginKeyFreq, {}, [ "input-timestamp" ])
-    print("results = " + str(list(results)))
-    return True
+blocksize = 1024
+
+def input_data(n):
+    # start at 1, not 0 so that all elts are non-zero
+    return np.arange(n) + 1    
+
+def test_process_n():
+    buf = input_data(blocksize)
+    results = list(vamp.process(buf, rate, testPluginKey, {}, [ "input-summary" ]))
+    assert len(results) == 1
+
+def test_process_freq_n():
+    buf = input_data(blocksize)
+    results = list(vamp.process(buf, rate, testPluginKeyFreq, {}, [ "input-summary" ]))
+    assert len(results) == 2 # one complete block starting at zero, one half-full
+
+def test_process_summary_param():
+    buf = input_data(blocksize * 10)
+    results = list(vamp.process(buf, rate, testPluginKey, { "produce_output": 0 }, [ "input-summary" ]))
+    assert len(results) == 0
+
+def test_process_summary_param_bool():
+    buf = input_data(blocksize * 10)
+    results = list(vamp.process(buf, rate, testPluginKey, { "produce_output": False }, [ "input-summary" ]))
+    assert len(results) == 0
+
+def test_process_summary():
+    buf = input_data(blocksize * 10)
+    results = list(vamp.process(buf, rate, testPluginKey, {}, [ "input-summary" ]))
+    assert len(results) == 10
+    # the input-summary output contains, for each process block, the value of
+    # the first sample in the process block input plus the number of samples
+    # above some epsilon
+    for i in range(len(results)):
+        # each feature has a single value, equal to the number of non-zero elts
+        # in the input block (which is all of them, i.e. the blocksize) plus
+        # the first elt (which is i * blockSize + 1)
+        expected = blocksize + i * blocksize + 1
+        actual = results[i]["values"][0]
+        assert actual == expected
+
+def test_process_freq_summary():
+    buf = input_data(blocksize * 10)
+    results = list(vamp.process(buf, rate, testPluginKeyFreq, {}, [ "input-summary" ]))
+    assert len(results) == 20
+    for i in range(len(results)):
+        #
+        # sort of as above, but much much subtler:
+        #
+        # * the input block is converted to frequency domain but then converted
+        # back within the plugin, so the values being reported are time-domain
+        # ones but with windowing and FFT shift
+        # 
+        # * the effect of FFT shift is that the first element in the
+        # re-converted frame is actually the one that was at the start of the
+        # second half of the original frame
+        #
+        # * and the last block is only half-full, so the "first" elt in that
+        # one, which actually comes from just after the middle of the block,
+        # will be zero
+        #
+        # * windowing does not affect the value of the first elt, because
+        # (before fft shift) it came from the peak of the window shape where
+        # the window value is 1
+        #
+        # * but windowing does affect the number of non-zero elts, because the
+        # asymmetric window used has one value very close to zero in it
+        #
+        # * the step size (the increment in input value from one block to the
+        # next) is only half the block size
+        #
+        expected = i * (blocksize/2) + blocksize/2 + 1   # "first" elt
+        if (i == len(results)-1):
+            expected = 0
+        expected = expected + blocksize - 1              # non-zero elts
+        actual = results[i]["values"][0]
+        eps = 1e-6
+        assert abs(actual - expected) < eps
+
--- a/vamp/__init__.py	Wed Jan 14 12:43:36 2015 +0000
+++ b/vamp/__init__.py	Wed Jan 14 14:31:01 2015 +0000
@@ -1,5 +1,7 @@
 '''A high-level interface to the vampyhost extension module, for quickly and easily running Vamp audio analysis plugins on audio files and buffers.'''
 
+###!!! todo: move all the real code out of __init__.py
+
 import vampyhost
 import numpy
 
@@ -24,13 +26,38 @@
         yield frame
         i = i + stepSize
 
-def process(data, samplerate, key, parameters = {}, outputs = []):
-#!!! docstring
+
+def loadAndConfigureFor(data, samplerate, key, parameters):
     plug = vampyhost.loadPlugin(key, samplerate,
                                 vampyhost.AdaptInputDomain +
                                 vampyhost.AdaptChannelCount)
 
+    plug.setParameterValues(parameters)
+
+    stepSize = plug.getPreferredStepSize()
+    blockSize = plug.getPreferredBlockSize()
+
+    if blockSize == 0:
+        blockSize = 1024
+    if stepSize == 0:
+        stepSize = blockSize ##!!! or blockSize/2, but check this with input domain adapter
+
+    channels = 1
+    if data.ndim > 1:
+        channels = data.shape[0]
+
+    plug.initialise(channels, stepSize, blockSize)
+    return (plug, stepSize, blockSize)
+
+
+def process(data, samplerate, key, parameters = {}, outputs = []):
+#!!! docstring
+
+    plug, stepSize, blockSize = loadAndConfigureFor(data, samplerate, key, parameters)
+
     plugOuts = plug.getOutputs()
+    if plugOuts == []:
+        return
 
     outIndices = dict(zip([o["identifier"] for o in plugOuts],
                           range(0, len(plugOuts))))  # id -> n
@@ -43,17 +70,6 @@
 
     singleOutput = (len(outputs) == 1)
 
-    stepSize = plug.getPreferredStepSize()
-    blockSize = plug.getPreferredBlockSize()
-    if blockSize == 0:
-        blockSize = 1024
-    if stepSize == 0:
-        stepSize = blockSize ##!!! or blockSize/2, but check this with input domain adapter
-    channels = 1
-    if data.ndim > 1:
-        channels = data.shape[0]
-
-    plug.initialise(channels, stepSize, blockSize)
     ff = framesFromArray(data, stepSize, blockSize)
     fi = 0