view perf-test-node.js @ 34:0eafc96a039c

Add quick perf test that can be run under node instead of in-browser
author Chris Cannam
date Sun, 25 Sep 2016 09:47:56 +0100
parents
children 3faa4e3eedac
line wrap: on
line source
"use strict";

var VampExamplePlugins = require("./VampExamplePlugins");
var exampleModule = VampExamplePlugins();

// 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) {
    console.log(blah);
}

function comment(blah) {
    console.log(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}}');
}

test();