view test/node-load-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 note(blah) {
    console.log(blah);
}

if (process.argv.length < 3 || process.argv.length > 4) {
    note("\nUsage: " + process.argv[0] + " <librarypath> [<pluginkey>]");
    note("e.g. " + process.argv[0] + " ./VampExamplePlugins.js");
    note("e.g. " + process.argv[0] + " ./VampExamplePlugins.js vamp-example-plugins:zerocrossing");
    throw "Wrong number of command-line args (1 or 2 expected)"
}

var libraryPath = process.argv[2];

var pluginKey = "";
if (process.argv.length > 3) {
    pluginKey = process.argv[3];
}

var base64 = require("./base64");

note("Loading library \"" + libraryPath + "\"...");
var extractor = require(libraryPath);

extractor().then(function(extractorModule) {

    var piperRequestJson = extractorModule.cwrap(
        'piperRequestJson', 'number', ['number']
    );

    var piperProcessRaw = extractorModule.cwrap(
        "piperProcessRaw", "number", ["number", "number", "number", "number"]
    );

    var piperFreeJson = extractorModule.cwrap(
        'piperFreeJson', 'void', ['number']
    );

    function processRaw(request) {
        
        const nChannels = request.processInput.inputBuffers.length;
        const nFrames = request.processInput.inputBuffers[0].length;

        const buffersPtr = extractorModule._malloc(nChannels * 4);
        const buffers = new Uint32Array(
            extractorModule.HEAPU8.buffer, buffersPtr, nChannels);

        for (let i = 0; i < nChannels; ++i) {
            const framesPtr = extractorModule._malloc(nFrames * 4);
            const frames = new Float32Array(
                extractorModule.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) {
            extractorModule._free(buffers[i]);
        }
        extractorModule._free(buffersPtr);

        const responseJstr = extractorModule.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(req) {
        var jsonStr = JSON.stringify(req);
        var m = extractorModule;
        // 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
        let inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL);
        let outCstr = piperRequestJson(inCstr);
        m._free(inCstr);
        const responseJstr = m.UTF8ToString(outCstr);
        const response = JSON.parse(responseJstr);
        piperFreeJson(outCstr);
        return response;
    }

    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;
        }
        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 checkSuccess(response) {
        if (response.error) {
	    console.log("Request type " + response.method + " failed: " +
		        response.error.message);
	    throw response.error.message;
        }
    }

    function test() {

        let start = (new Date()).getTime();

        const rate = 44100;

        note("Listing plugins...");
        let response = request({
	    method: "list"
        });
        checkSuccess(response);

        if (pluginKey === "") {
	    pluginKey = response.result.available[0].key;
	    note("Loading first plugin \"" + pluginKey + "\"...");
        } else {
	    note("Loading requested plugin \"" + pluginKey + "\"...");
        }
        
        response = request({
            method: "load",
            params: {
                key: pluginKey,
                inputSampleRate: rate,
                adapterFlags: ["AdaptAllSafe"]
            }
        });

        //    note("Load request returned: " + JSON.stringify(response) + "\n");
        
        checkSuccess(response);

        var blockSize = response.result.defaultConfiguration.framing.blockSize;
        var stepSize = response.result.defaultConfiguration.framing.stepSize;

        response = request({
            method: "configure",
            params: {
                handle: 1,
                configuration: {
		    framing: {
                        blockSize: blockSize,
                        stepSize: stepSize,
		    },
                    channelCount: 1
                }
            }
        });
        checkSuccess(response);

        blockSize = response.result.framing.blockSize;
        stepSize = response.result.framing.stepSize;
        
        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);
        
        note("Now processing " + nblocks + " blocks of 1024 samples each...");

        let featureCount = 0;
        
        for (let i = 0; i < nblocks; ++i) {
	    response = processRaw({
	        "handle": 1,
	        "processInput": blocks[i]
	    });
            let features = responseToFeatureSet(response);
            for (let featureList of features.values()) {
                featureCount += featureList.length;
            }
        }

        note("Cleaning up the plugin and getting any remaining features...");
        response = request({
            method: "finish",
            params: {
                handle: 1
            }
        });
        checkSuccess(response);

        let features = responseToFeatureSet(response);
        for (let featureList of features.values()) {
            featureCount += featureList.length;
        }
        
        note("Done, total number of features across all outputs = " + featureCount);

        let finish = (new Date()).getTime();
        note("Total time taken " + (finish - start) + " ms");
    }

    test();
});