c@127: "use strict"; c@127: c@127: function note(blah) { c@127: console.log(blah); c@127: } c@127: c@129: if (process.argv.length < 3 || process.argv.length > 4) { c@129: note("\nUsage: " + process.argv[0] + " []"); c@129: note("e.g. " + process.argv[0] + " ./VampExamplePlugins.js"); c@127: note("e.g. " + process.argv[0] + " ./VampExamplePlugins.js vamp-example-plugins:zerocrossing"); c@129: throw "Wrong number of command-line args (1 or 2 expected)" c@127: } c@127: c@127: var libraryPath = process.argv[2]; c@129: c@129: var pluginKey = ""; c@129: if (process.argv.length > 3) { c@129: pluginKey = process.argv[3]; c@129: } c@127: c@127: var base64 = require("./base64"); c@127: c@127: note("Loading library \"" + libraryPath + "\"..."); c@127: var extractor = require(libraryPath); c@127: var extractorModule = extractor(); c@127: c@127: var piperRequestJson = extractorModule.cwrap( c@127: 'piperRequestJson', 'number', ['number'] c@127: ); c@127: c@127: var piperProcessRaw = extractorModule.cwrap( c@127: "piperProcessRaw", "number", ["number", "number", "number", "number"] c@127: ); c@127: c@127: var piperFreeJson = extractorModule.cwrap( c@127: 'piperFreeJson', 'void', ['number'] c@127: ); c@127: c@127: function processRaw(request) { c@127: c@127: const nChannels = request.processInput.inputBuffers.length; c@127: const nFrames = request.processInput.inputBuffers[0].length; c@127: c@127: const buffersPtr = extractorModule._malloc(nChannels * 4); c@127: const buffers = new Uint32Array( c@127: extractorModule.HEAPU8.buffer, buffersPtr, nChannels); c@127: c@127: for (let i = 0; i < nChannels; ++i) { c@127: const framesPtr = extractorModule._malloc(nFrames * 4); c@127: const frames = new Float32Array( c@127: extractorModule.HEAPU8.buffer, framesPtr, nFrames); c@127: frames.set(request.processInput.inputBuffers[i]); c@127: buffers[i] = framesPtr; c@127: } c@127: c@127: const responseJson = piperProcessRaw( c@127: request.handle, c@127: buffersPtr, c@127: request.processInput.timestamp.s, c@127: request.processInput.timestamp.n); c@127: c@127: for (let i = 0; i < nChannels; ++i) { c@127: extractorModule._free(buffers[i]); c@127: } c@127: extractorModule._free(buffersPtr); c@127: c@127: const responseJstr = extractorModule.Pointer_stringify(responseJson); c@127: const response = JSON.parse(responseJstr); c@127: c@127: piperFreeJson(responseJson); c@127: c@127: return response; c@127: } c@127: c@127: function makeTimestamp(seconds) { c@127: if (seconds >= 0.0) { c@127: return { c@127: s: Math.floor(seconds), c@127: n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) c@127: }; c@127: } else { c@127: const { s, n } = makeTimestamp(-seconds); c@127: return { s: -s, n: -n }; c@127: } c@127: } c@127: c@127: function frame2timestamp(frame, rate) { c@127: return makeTimestamp(frame / rate); c@127: } c@127: c@127: function request(req) { c@127: var jsonStr = JSON.stringify(req); c@127: var m = extractorModule; c@127: // Inspection reveals that intArrayFromString converts the string c@127: // from utf16 to utf8, which is what we want (though the docs c@127: // don't mention this). Note the *Cstr values are Emscripten heap c@127: // pointers c@127: let inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); c@127: let outCstr = piperRequestJson(inCstr); c@127: m._free(inCstr); c@127: const responseJstr = m.Pointer_stringify(outCstr); c@127: const response = JSON.parse(responseJstr); c@127: piperFreeJson(outCstr); c@127: return response; c@127: } c@127: c@127: function myFromBase64(b64) { c@127: while (b64.length % 4 > 0) { b64 += "="; } c@127: let conv = new Float32Array(base64.toByteArray(b64).buffer); c@127: return conv; c@127: } c@127: c@127: function convertWireFeature(wfeature) { c@127: let out = {}; c@127: if (wfeature.timestamp != null) { c@127: out.timestamp = wfeature.timestamp; c@127: } c@127: if (wfeature.duration != null) { c@127: out.duration = wfeature.duration; c@127: } c@127: if (wfeature.label != null) { c@127: out.label = wfeature.label; c@127: } c@127: const vv = wfeature.featureValues; c@127: if (vv != null) { c@127: if (typeof vv === "string") { c@127: out.featureValues = myFromBase64(vv); c@127: } else { c@127: out.featureValues = new Float32Array(vv); c@127: } c@127: } c@127: return out; c@127: } c@127: c@127: function convertWireFeatureList(wfeatures) { c@127: return wfeatures.map(convertWireFeature); c@127: } c@127: c@127: function responseToFeatureSet(response) { c@127: const features = new Map(); c@127: const processResponse = response.result; c@127: const wireFeatures = processResponse.features; c@127: Object.keys(wireFeatures).forEach(key => { c@127: return features.set(key, convertWireFeatureList(wireFeatures[key])); c@127: }); c@127: return features; c@127: } c@127: c@129: function checkSuccess(response) { c@129: if (response.error) { c@129: console.log("Request type " + response.method + " failed: " + c@129: response.error.message); c@129: throw response.error.message; c@129: } c@129: } c@129: c@127: function test() { c@127: c@132: let start = (new Date()).getTime(); c@132: c@127: const rate = 44100; c@129: c@129: note("Listing plugins..."); c@129: let response = request({ c@129: method: "list" c@129: }); c@129: checkSuccess(response); c@129: c@129: if (pluginKey === "") { c@129: pluginKey = response.result.available[0].key; c@129: note("Loading first plugin \"" + pluginKey + "\"..."); c@129: } else { c@129: note("Loading requested plugin \"" + pluginKey + "\"..."); c@129: } c@127: c@129: response = request({ c@127: method: "load", c@127: params: { c@127: key: pluginKey, c@127: inputSampleRate: rate, c@127: adapterFlags: ["AdaptAllSafe"] c@127: } c@127: }); c@129: checkSuccess(response); c@127: c@129: const blockSize = response.result.defaultConfiguration.blockSize; c@129: const stepSize = response.result.defaultConfiguration.stepSize; c@127: c@127: response = request({ c@127: method: "configure", c@127: params: { c@127: handle: 1, c@127: configuration: { c@127: blockSize: blockSize, c@129: stepSize: stepSize, c@129: channelCount: 1 c@127: } c@127: } c@127: }); c@129: checkSuccess(response); c@127: c@127: const nblocks = 1000; c@127: c@127: const makeBlock = (n => { c@127: return { c@127: timestamp : frame2timestamp(n * blockSize, rate), c@127: inputBuffers : [ c@127: new Float32Array(Array.from(Array(blockSize).keys(), c@127: n => n / blockSize)) c@127: ], c@127: } c@127: }); c@127: c@127: const blocks = Array.from(Array(nblocks).keys(), makeBlock); c@127: c@127: note("Now processing " + nblocks + " blocks of 1024 samples each..."); c@127: c@127: let featureCount = 0; c@127: c@127: for (let i = 0; i < nblocks; ++i) { c@127: response = processRaw({ c@127: "handle": 1, c@127: "processInput": blocks[i] c@127: }); c@127: let features = responseToFeatureSet(response); c@127: for (let featureList of features.values()) { c@127: featureCount += featureList.length; c@127: } c@127: } c@127: c@127: note("Cleaning up the plugin and getting any remaining features..."); c@127: response = request({ c@127: method: "finish", c@127: params: { c@127: handle: 1 c@127: } c@127: }); c@129: checkSuccess(response); c@127: c@127: let features = responseToFeatureSet(response); c@127: for (let featureList of features.values()) { c@127: featureCount += featureList.length; c@127: } c@127: c@127: note("Done, total number of features across all outputs = " + featureCount); c@132: c@132: let finish = (new Date()).getTime(); c@132: note("Total time taken " + (finish - start) + " ms"); c@127: } c@127: c@127: test(); c@127: