changeset 35:3faa4e3eedac

Fix object structure for process input in perf test
author Chris Cannam
date Mon, 26 Sep 2016 16:18:44 +0100
parents 0eafc96a039c
children 34480328bf5c
files Makefile.inc.em base64.js perf-test-node.js perf-test.html perf-test.js
diffstat 5 files changed, 291 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.inc.em	Sun Sep 25 09:47:56 2016 +0100
+++ b/Makefile.inc.em	Mon Sep 26 16:18:44 2016 +0100
@@ -42,7 +42,7 @@
 #OPTFLAGS	:= -g3
 OPTFLAGS	:= -O3 -ffast-math
 
-DEFINES		:= -DSINGLE_PRECISION_FFT $(DEFINES)
+DEFINES		:= $(DEFINES)
 
 CXXFLAGS	:= -std=c++11 -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base64.js	Mon Sep 26 16:18:44 2016 +0100
@@ -0,0 +1,116 @@
+'use strict'
+
+if (typeof document === "undefined") {
+    exports.byteLength = byteLength
+    exports.toByteArray = toByteArray
+    exports.fromByteArray = fromByteArray
+}
+
+var lookup = []
+var revLookup = []
+var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
+
+var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+for (var i = 0, len = code.length; i < len; ++i) {
+  lookup[i] = code[i]
+  revLookup[code.charCodeAt(i)] = i
+}
+
+revLookup['-'.charCodeAt(0)] = 62
+revLookup['_'.charCodeAt(0)] = 63
+
+function placeHoldersCount (b64) {
+  var len = b64.length
+  if (len % 4 > 0) {
+    throw new Error('Invalid string. Length must be a multiple of 4')
+  }
+
+  // the number of equal signs (place holders)
+  // if there are two placeholders, than the two characters before it
+  // represent one byte
+  // if there is only one, then the three characters before it represent 2 bytes
+  // this is just a cheap hack to not do indexOf twice
+  return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
+}
+
+function byteLength (b64) {
+  // base64 is 4/3 + up to two characters of the original data
+  return b64.length * 3 / 4 - placeHoldersCount(b64)
+}
+
+function toByteArray (b64) {
+  var i, j, l, tmp, placeHolders, arr
+  var len = b64.length
+  placeHolders = placeHoldersCount(b64)
+
+  arr = new Arr(len * 3 / 4 - placeHolders)
+
+  // if there are placeholders, only get up to the last complete 4 chars
+  l = placeHolders > 0 ? len - 4 : len
+
+  var L = 0
+
+  for (i = 0, j = 0; i < l; i += 4, j += 3) {
+    tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
+    arr[L++] = (tmp >> 16) & 0xFF
+    arr[L++] = (tmp >> 8) & 0xFF
+    arr[L++] = tmp & 0xFF
+  }
+
+  if (placeHolders === 2) {
+    tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
+    arr[L++] = tmp & 0xFF
+  } else if (placeHolders === 1) {
+    tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
+    arr[L++] = (tmp >> 8) & 0xFF
+    arr[L++] = tmp & 0xFF
+  }
+
+  return arr
+}
+
+function tripletToBase64 (num) {
+  return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
+}
+
+function encodeChunk (uint8, start, end) {
+  var tmp
+  var output = []
+  for (var i = start; i < end; i += 3) {
+    tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
+    output.push(tripletToBase64(tmp))
+  }
+  return output.join('')
+}
+
+function fromByteArray (uint8) {
+  var tmp
+  var len = uint8.length
+  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
+  var output = ''
+  var parts = []
+  var maxChunkLength = 16383 // must be multiple of 3
+
+  // go through the array every three bytes, we'll deal with trailing stuff later
+  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
+    parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
+  }
+
+  // pad the end with zeros, but make sure to not forget the extra bytes
+  if (extraBytes === 1) {
+    tmp = uint8[len - 1]
+    output += lookup[tmp >> 2]
+    output += lookup[(tmp << 4) & 0x3F]
+    output += '=='
+  } else if (extraBytes === 2) {
+    tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
+    output += lookup[tmp >> 10]
+    output += lookup[(tmp >> 4) & 0x3F]
+    output += lookup[(tmp << 2) & 0x3F]
+    output += '='
+  }
+
+  parts.push(output)
+
+  return parts.join('')
+}
--- a/perf-test-node.js	Sun Sep 25 09:47:56 2016 +0100
+++ b/perf-test-node.js	Mon Sep 26 16:18:44 2016 +0100
@@ -1,6 +1,7 @@
 "use strict";
 
 var VampExamplePlugins = require("./VampExamplePlugins");
+var base64 = require("./base64");
 var exampleModule = VampExamplePlugins();
 
 // It is possible to declare both parameters and return values as
@@ -71,6 +72,22 @@
     return response;
 }
 
+function makeTimestamp(seconds) {
+    if (seconds >= 0.0) {
+        return {
+            s: Math.floor(seconds),
+            n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5)
+        };
+    } else {
+        const { s, n } = makeTimestamp(-seconds);
+        return { s: -s, n: -n };
+    }
+}
+
+function frame2timestamp(frame, rate) {
+    return makeTimestamp(frame / rate);
+}
+
 function request(jsonStr) {
     note("Request JSON = " + jsonStr);
     var m = exampleModule;
@@ -87,10 +104,51 @@
     return result;
 }
 
+function myFromBase64(b64) {
+    while (b64.length % 4 > 0) { b64 += "="; }
+    let conv = new Float32Array(base64.toByteArray(b64).buffer);
+    return conv;
+}
+
+function convertWireFeature(wfeature) {
+    let out = {};
+    if (wfeature.timestamp != null) {
+        out.timestamp = wfeature.timestamp;
+    }
+    if (wfeature.duration != null) {
+        out.duration = wfeature.duration;
+    }
+    if (wfeature.label != null) {
+        out.label = wfeature.label;
+    }
+    if (wfeature.b64values != null && wfeature.b64values !== "") {
+        out.values = myFromBase64(wfeature.b64values);
+    } else if (wfeature.values != null) {
+        out.values = new Float32Array(wfeature.values);
+    }
+    return out;
+}
+
+function convertWireFeatureList(wfeatures) {
+    return wfeatures.map(convertWireFeature);
+}
+
+function responseToFeatureSet(response) {
+    const features = new Map();
+    const processResponse = response.content;
+    const wireFeatures = processResponse.features;
+    Object.keys(wireFeatures).forEach(key => {
+        return features.set(key, convertWireFeatureList(wireFeatures[key]));
+    });
+    return features;
+}
+
 function test() {
 
+    const rate = 44100;
+    
     comment("Loading zero crossings plugin...");
-    let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":44100,"adapterFlags":["AdaptAllSafe"]}}');
+    let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
 
     const blockSize = 1024;
 
@@ -98,48 +156,63 @@
 
     const nblocks = 1000;
 
-    let processInputBuffers = [new Float32Array(
-        Array.from(Array(blockSize).keys(), n => n / blockSize))
-    ];
+    const makeBlock = (n => { 
+        return {
+            timestamp : frame2timestamp(n * blockSize, rate),
+            inputBuffers : [
+                { values : new Float32Array(
+                    Array.from(Array(blockSize).keys(),
+                               n => n / blockSize)) }
+            ],
+        }
+    });
+    
+    const blocks = Array.from(Array(nblocks).keys(), makeBlock);
     
     comment("Now processing " + nblocks + " blocks of 1024 samples each...");
 
+    let total = 0;
+    
     let start = (new Date()).getTime();
     comment("Start at " + start);
     
     for (let i = 0; i < nblocks; ++i) {
-	let ts = { "s": i, "n": 0 }; // wholly bogus, but ZC plugin doesn't use it
 	result = processRaw({
 	    "pluginHandle": 1,
-	    "processInput": {
-		"timestamp": ts,
-		"inputBuffers": processInputBuffers
-	    }
+	    "processInput": blocks[i]
 	});
+        let features = responseToFeatureSet(result);
+        let count = features.get("counts")[0].values[0];
+        total += count;
     }
 
     let finish = (new Date()).getTime();
     comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
 
+    comment("Total = " + total);
+
     comment("Again...");
+
+    total = 0;
     
     start = (new Date()).getTime();
     comment("Start at " + start);
     
     for (let i = 0; i < nblocks; ++i) {
-	let ts = { "s": i, "n": 0 }; // wholly bogus, but ZC plugin doesn't use it
 	result = processRaw({
 	    "pluginHandle": 1,
-	    "processInput": {
-		"timestamp": ts,
-		"inputBuffers": processInputBuffers
-	    }
+	    "processInput": blocks[i]
 	});
+        let features = responseToFeatureSet(result);
+        let count = features.get("counts")[0].values[0];
+        total += count;
     }
 
     finish = (new Date()).getTime();
     comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
     
+    comment("Total = " + total);
+    
     comment("Cleaning up the plugin and getting any remaining features...");
     result = request('{"type":"finish","content":{"pluginHandle":1}}');
 }
--- a/perf-test.html	Sun Sep 25 09:47:56 2016 +0100
+++ b/perf-test.html	Mon Sep 26 16:18:44 2016 +0100
@@ -10,6 +10,7 @@
     </style>
 
     <script src="VampExamplePlugins.js"></script>
+    <script src="base64.js"></script>
     <script src="perf-test.js"></script>
   </head>
   <body>
--- a/perf-test.js	Sun Sep 25 09:47:56 2016 +0100
+++ b/perf-test.js	Mon Sep 26 16:18:44 2016 +0100
@@ -70,6 +70,22 @@
     return response;
 }
 
+function makeTimestamp(seconds) {
+    if (seconds >= 0.0) {
+        return {
+            s: Math.floor(seconds),
+            n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5)
+        };
+    } else {
+        const { s, n } = makeTimestamp(-seconds);
+        return { s: -s, n: -n };
+    }
+}
+
+function frame2timestamp(frame, rate) {
+    return makeTimestamp(frame / rate);
+}
+
 function request(jsonStr) {
     note("Request JSON = " + jsonStr);
     var m = exampleModule;
@@ -86,10 +102,51 @@
     return result;
 }
 
+function myFromBase64(b64) {
+    while (b64.length % 4 > 0) { b64 += "="; }
+    let conv = new Float32Array(toByteArray(b64).buffer);
+    return conv;
+}
+
+function convertWireFeature(wfeature) {
+    let out = {};
+    if (wfeature.timestamp != null) {
+        out.timestamp = wfeature.timestamp;
+    }
+    if (wfeature.duration != null) {
+        out.duration = wfeature.duration;
+    }
+    if (wfeature.label != null) {
+        out.label = wfeature.label;
+    }
+    if (wfeature.b64values != null && wfeature.b64values !== "") {
+        out.values = myFromBase64(wfeature.b64values);
+    } else if (wfeature.values != null) {
+        out.values = new Float32Array(wfeature.values);
+    }
+    return out;
+}
+
+function convertWireFeatureList(wfeatures) {
+    return wfeatures.map(convertWireFeature);
+}
+
+function responseToFeatureSet(response) {
+    const features = new Map();
+    const processResponse = response.content;
+    const wireFeatures = processResponse.features;
+    Object.keys(wireFeatures).forEach(key => {
+        return features.set(key, convertWireFeatureList(wireFeatures[key]));
+    });
+    return features;
+}
+
 function test() {
 
+    const rate = 44100;
+    
     comment("Loading zero crossings plugin...");
-    let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":44100,"adapterFlags":["AdaptAllSafe"]}}');
+    let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
 
     const blockSize = 1024;
 
@@ -97,48 +154,63 @@
 
     const nblocks = 1000;
 
-    let processInputBuffers = [new Float32Array(
-        Array.from(Array(blockSize).keys(), n => n / blockSize))
-    ];
+    const makeBlock = (n => { 
+        return {
+            timestamp : frame2timestamp(n * blockSize, rate),
+            inputBuffers : [
+                { values : new Float32Array(
+                    Array.from(Array(blockSize).keys(),
+                               n => n / blockSize)) }
+            ],
+        }
+    });
+    
+    const blocks = Array.from(Array(nblocks).keys(), makeBlock);
     
     comment("Now processing " + nblocks + " blocks of 1024 samples each...");
 
+    let total = 0;
+    
     let start = (new Date()).getTime();
     comment("Start at " + start);
     
     for (let i = 0; i < nblocks; ++i) {
-	let ts = { "s": i, "n": 0 }; // wholly bogus, but ZC plugin doesn't use it
 	result = processRaw({
 	    "pluginHandle": 1,
-	    "processInput": {
-		"timestamp": ts,
-		"inputBuffers": processInputBuffers
-	    }
+	    "processInput": blocks[i]
 	});
+        let features = responseToFeatureSet(result);
+        let count = features.get("counts")[0].values[0];
+        total += count;
     }
 
     let finish = (new Date()).getTime();
     comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
 
+    comment("Total = " + total);
+
     comment("Again...");
+
+    total = 0;
     
     start = (new Date()).getTime();
     comment("Start at " + start);
     
     for (let i = 0; i < nblocks; ++i) {
-	let ts = { "s": i, "n": 0 }; // wholly bogus, but ZC plugin doesn't use it
 	result = processRaw({
 	    "pluginHandle": 1,
-	    "processInput": {
-		"timestamp": ts,
-		"inputBuffers": processInputBuffers
-	    }
+	    "processInput": blocks[i]
 	});
+        let features = responseToFeatureSet(result);
+        let count = features.get("counts")[0].values[0];
+        total += count;
     }
 
     finish = (new Date()).getTime();
     comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
     
+    comment("Total = " + total);
+    
     comment("Cleaning up the plugin and getting any remaining features...");
     result = request('{"type":"finish","content":{"pluginHandle":1}}');
 }