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