Mercurial > hg > piper-vamp-js
view test/perf-test.js @ 176:eaf46e7647a0 tip master
Update for latest Emscripten - Pointer_stringify has apparently been deprecated for a while, and was removed in v1.38.27
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Wed, 27 Feb 2019 11:29:53 +0000 |
parents | a91b4defa581 |
children |
line wrap: on
line source
"use strict"; function test() { 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. 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.UTF8ToString(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.UTF8ToString(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}}'); }); } window.onload = function() { test(); }