diff test/perf-test.js @ 175:a91b4defa581

WebAssembly-related fixes and updates
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 13 Jun 2018 15:55:39 +0100
parents 2e8aacb7f883
children eaf46e7647a0
line wrap: on
line diff
--- a/test/perf-test.js	Tue Jun 12 14:58:05 2018 +0100
+++ b/test/perf-test.js	Wed Jun 13 15:55:39 2018 +0100
@@ -1,220 +1,223 @@
 "use strict";
 
-var exampleModule = VampExamplePluginsModule();
-
-// It is possible to declare both parameters and return values as
-// "string", in which case Emscripten will take care of
-// conversions. But it's not clear how one would manage memory for
-// newly-constructed returned C strings -- the returned pointer from
-// piperRequestJson would appear (?) to be thrown away by the
-// Emscripten string converter if we declare it as returning a string,
-// so we have no opportunity to pass it to piperFreeJson, which
-// suggests this would leak memory if the string isn't static. Not
-// wholly sure though. Anyway, passing and returning pointers (as
-// numbers) means we can manage the Emscripten heap memory however we
-// want in our request wrapper function below.
-
-var piperRequestJson = exampleModule.cwrap(
-    'piperRequestJson', 'number', ['number']
-);
-
-var piperProcessRaw = exampleModule.cwrap(
-    "piperProcessRaw", "number", ["number", "number", "number", "number"]
-);
-
-var piperFreeJson = exampleModule.cwrap(
-    'piperFreeJson', 'void', ['number']
-);
-
-function note(blah) {
-    document.getElementById("test-result").innerHTML += blah + "<br>";
-}
-
-function comment(blah) {
-    note("<br><i>" + blah + "</i>");
-}
-
-function processRaw(request) {
-    
-    const nChannels = request.processInput.inputBuffers.length;
-    const nFrames = request.processInput.inputBuffers[0].length;
-
-    const buffersPtr = exampleModule._malloc(nChannels * 4);
-    const buffers = new Uint32Array(
-        exampleModule.HEAPU8.buffer, buffersPtr, nChannels);
-
-    for (let i = 0; i < nChannels; ++i) {
-        const framesPtr = exampleModule._malloc(nFrames * 4);
-        const frames = new Float32Array(
-            exampleModule.HEAPU8.buffer, framesPtr, nFrames);
-        frames.set(request.processInput.inputBuffers[i]);
-        buffers[i] = framesPtr;
-    }
-    
-    const responseJson = piperProcessRaw(
-        request.handle,
-        buffersPtr,
-        request.processInput.timestamp.s,
-        request.processInput.timestamp.n);
-    
-    for (let i = 0; i < nChannels; ++i) {
-        exampleModule._free(buffers[i]);
-    }
-    exampleModule._free(buffersPtr);
-
-    const responseJstr = exampleModule.Pointer_stringify(responseJson);
-    const response = JSON.parse(responseJstr);
-    
-    piperFreeJson(responseJson);
-    
-    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;
-    // Inspection reveals that intArrayFromString converts the string
-    // from utf16 to utf8, which is what we want (though the docs
-    // don't mention this). Note the *Cstr values are Emscripten heap
-    // pointers
-    var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL);
-    var outCstr = piperRequestJson(inCstr);
-    m._free(inCstr);
-    var result = m.Pointer_stringify(outCstr);
-    piperFreeJson(outCstr);
-    note("Returned JSON = " + result);
-    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;
-    }
-    const vv = wfeature.featureValues;
-    if (vv != null) {
-        if (typeof vv === "string") {
-            out.featureValues = myFromBase64(vv);
-        } else {
-            out.featureValues = new Float32Array(vv);
-        }
-    }
-    return out;
-}
-
-function convertWireFeatureList(wfeatures) {
-    return wfeatures.map(convertWireFeature);
-}
-
-function responseToFeatureSet(response) {
-    const features = new Map();
-    const processResponse = response.result;
-    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('{"method":"load","params": {"key":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
+    VampExamplePluginsModule().then(function(exampleModule) {
+        
+        // It is possible to declare both parameters and return values
+        // as "string", in which case Emscripten will take care of
+        // conversions. But it's not clear how one would manage memory
+        // for newly-constructed returned C strings -- the returned
+        // pointer from piperRequestJson would appear (?) to be thrown
+        // away by the Emscripten string converter if we declare it as
+        // returning a string, so we have no opportunity to pass it to
+        // piperFreeJson, which suggests this would leak memory if the
+        // string isn't static. Not wholly sure though. Anyway,
+        // passing and returning pointers (as numbers) means we can
+        // manage the Emscripten heap memory however we want in our
+        // request wrapper function below.
 
-    const blockSize = 1024;
+        var piperRequestJson = exampleModule.cwrap(
+            'piperRequestJson', 'number', ['number']
+        );
 
-    result = request('{"method":"configure","params":{"handle":1,"configuration":{"blockSize": ' + blockSize + ', "channelCount": 1, "stepSize": ' + blockSize + '}}}');
+        var piperProcessRaw = exampleModule.cwrap(
+            "piperProcessRaw", "number", ["number", "number", "number", "number"]
+        );
 
-    const nblocks = 1000;
+        var piperFreeJson = exampleModule.cwrap(
+            'piperFreeJson', 'void', ['number']
+        );
 
-    const makeBlock = (n => { 
-        return {
-            timestamp : frame2timestamp(n * blockSize, rate),
-            inputBuffers : [
-                new Float32Array(Array.from(Array(blockSize).keys(),
-                                            n => n / blockSize))
-            ],
+        function note(blah) {
+            document.getElementById("test-result").innerHTML += blah + "<br>";
         }
+
+        function comment(blah) {
+            note("<br><i>" + blah + "</i>");
+        }
+
+        function processRaw(request) {
+            
+            const nChannels = request.processInput.inputBuffers.length;
+            const nFrames = request.processInput.inputBuffers[0].length;
+
+            const buffersPtr = exampleModule._malloc(nChannels * 4);
+            const buffers = new Uint32Array(
+                exampleModule.HEAPU8.buffer, buffersPtr, nChannels);
+
+            for (let i = 0; i < nChannels; ++i) {
+                const framesPtr = exampleModule._malloc(nFrames * 4);
+                const frames = new Float32Array(
+                    exampleModule.HEAPU8.buffer, framesPtr, nFrames);
+                frames.set(request.processInput.inputBuffers[i]);
+                buffers[i] = framesPtr;
+            }
+            
+            const responseJson = piperProcessRaw(
+                request.handle,
+                buffersPtr,
+                request.processInput.timestamp.s,
+                request.processInput.timestamp.n);
+            
+            for (let i = 0; i < nChannels; ++i) {
+                exampleModule._free(buffers[i]);
+            }
+            exampleModule._free(buffersPtr);
+
+            const responseJstr = exampleModule.Pointer_stringify(responseJson);
+            const response = JSON.parse(responseJstr);
+            
+            piperFreeJson(responseJson);
+            
+            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;
+            // Inspection reveals that intArrayFromString converts the string
+            // from utf16 to utf8, which is what we want (though the docs
+            // don't mention this). Note the *Cstr values are Emscripten heap
+            // pointers
+            var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL);
+            var outCstr = piperRequestJson(inCstr);
+            m._free(inCstr);
+            var result = m.Pointer_stringify(outCstr);
+            piperFreeJson(outCstr);
+            note("Returned JSON = " + result);
+            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;
+            }
+            const vv = wfeature.featureValues;
+            if (vv != null) {
+                if (typeof vv === "string") {
+                    out.featureValues = myFromBase64(vv);
+                } else {
+                    out.featureValues = new Float32Array(vv);
+                }
+            }
+            return out;
+        }
+
+        function convertWireFeatureList(wfeatures) {
+            return wfeatures.map(convertWireFeature);
+        }
+
+        function responseToFeatureSet(response) {
+            const features = new Map();
+            const processResponse = response.result;
+            const wireFeatures = processResponse.features;
+            Object.keys(wireFeatures).forEach(key => {
+                return features.set(key, convertWireFeatureList(wireFeatures[key]));
+            });
+            return features;
+        }
+
+        const rate = 44100;
+        
+        comment("Loading zero crossings plugin...");
+        let result = request('{"method":"load","params": {"key":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
+
+        const blockSize = 1024;
+
+        comment("Configuring plugin...");
+        result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": ' + blockSize + ', "stepSize": ' + blockSize + '}, "channelCount": 1}}}');
+
+        const nblocks = 1000;
+
+        const makeBlock = (n => { 
+            return {
+                timestamp : frame2timestamp(n * blockSize, rate),
+                inputBuffers : [
+                    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) {
+	    result = processRaw({
+	        "handle": 1,
+	        "processInput": blocks[i]
+	    });
+            let features = responseToFeatureSet(result);
+            let count = features.get("counts")[0].featureValues[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) {
+	    result = processRaw({
+	        "handle": 1,
+	        "processInput": blocks[i]
+	    });
+            let features = responseToFeatureSet(result);
+            let count = features.get("counts")[0].featureValues[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('{"method":"finish","params":{"handle":1}}');
     });
-    
-    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) {
-	result = processRaw({
-	    "handle": 1,
-	    "processInput": blocks[i]
-	});
-        let features = responseToFeatureSet(result);
-        let count = features.get("counts")[0].featureValues[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) {
-	result = processRaw({
-	    "handle": 1,
-	    "processInput": blocks[i]
-	});
-        let features = responseToFeatureSet(result);
-        let count = features.get("counts")[0].featureValues[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('{"method":"finish","params":{"handle":1}}');
 }
 
 window.onload = function() {