# HG changeset patch # User Chris Cannam # Date 1474730764 -3600 # Node ID e9a0f66ee81c4679b3d9ccac068033b67e51eebf # Parent 30028b3c95e45185c848bfbda0b3c738bbfcbeaf Quick in-browser performance test diff -r 30028b3c95e4 -r e9a0f66ee81c perf-test.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/perf-test.html Sat Sep 24 16:26:04 2016 +0100 @@ -0,0 +1,21 @@ + + + + VamPipe Adapter Test + + + + + + + +

VamPipe Adapter Test

+ +

+ + + diff -r 30028b3c95e4 -r e9a0f66ee81c perf-test.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/perf-test.js Sat Sep 24 16:26:04 2016 +0100 @@ -0,0 +1,148 @@ +"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 +// vampipeRequestJson 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 vampipeFreeJson, 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 vampipeRequestJson = exampleModule.cwrap( + 'vampipeRequestJson', 'number', ['number'] +); + +var vampipeProcessRaw = exampleModule.cwrap( + "vampipeProcessRaw", "number", ["number", "number", "number", "number"] +); + +var vampipeFreeJson = exampleModule.cwrap( + 'vampipeFreeJson', 'void', ['number'] +); + +function note(blah) { + document.getElementById("test-result").innerHTML += blah + "
"; +} + +function comment(blah) { + note("
" + blah + ""); +} + +function processRaw(request) { + + const nChannels = request.processInput.inputBuffers.length; + const nFrames = request.processInput.inputBuffers[0].values.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].values); + buffers[i] = framesPtr; + } + + const responseJson = vampipeProcessRaw( + request.pluginHandle, + buffersPtr, + request.processInput.timestamp.s, + request.processInput.timestamp.n); + + for (let i = 0; i < nChannels; ++i) { + exampleModule._free(buffers[i]); + } + exampleModule._free(buffersPtr); + + const response = JSON.parse( + exampleModule.Pointer_stringify(responseJson)); + + vampipeFreeJson(responseJson); + + return response; +} + +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 = vampipeRequestJson(inCstr); + m._free(inCstr); + var result = m.Pointer_stringify(outCstr); + vampipeFreeJson(outCstr); + note("Returned JSON = " + result); + return result; +} + +function test() { + + comment("Loading zero crossings plugin..."); + let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":44100,"adapterFlags":["AdaptAllSafe"]}}'); + + const blockSize = 1024; + + result = request('{"type":"configure","content":{"pluginHandle":1,"configuration":{"blockSize": ' + blockSize + ', "channelCount": 1, "stepSize": ' + blockSize + '}}}'); + + const nblocks = 1000; + + let processInputBuffers = [new Float32Array( + Array.from(Array(blockSize).keys(), n => n / blockSize)) + ]; + + comment("Now processing " + nblocks + " blocks of 1024 samples each..."); + + 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 + } + }); + } + + let finish = (new Date()).getTime(); + comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); + + comment("Again..."); + + 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 + } + }); + } + + finish = (new Date()).getTime(); + comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); + + comment("Cleaning up the plugin and getting any remaining features..."); + result = request('{"type":"finish","content":{"pluginHandle":1}}'); +} + +window.onload = function() { + test(); +}