c@117: "use strict"; c@117: c@117: function test() { c@117: cannam@175: VampExamplePluginsModule().then(function(exampleModule) { cannam@175: cannam@175: // It is possible to declare both parameters and return values cannam@175: // as "string", in which case Emscripten will take care of cannam@175: // conversions. But it's not clear how one would manage memory cannam@175: // for newly-constructed returned C strings -- the returned cannam@175: // pointer from piperRequestJson would appear (?) to be thrown cannam@175: // away by the Emscripten string converter if we declare it as cannam@175: // returning a string, so we have no opportunity to pass it to cannam@175: // piperFreeJson, which suggests this would leak memory if the cannam@175: // string isn't static. Not wholly sure though. Anyway, cannam@175: // passing and returning pointers (as numbers) means we can cannam@175: // manage the Emscripten heap memory however we want in our cannam@175: // request wrapper function below. c@117: cannam@175: var piperRequestJson = exampleModule.cwrap( cannam@175: 'piperRequestJson', 'number', ['number'] cannam@175: ); c@117: cannam@175: var piperProcessRaw = exampleModule.cwrap( cannam@175: "piperProcessRaw", "number", ["number", "number", "number", "number"] cannam@175: ); c@117: cannam@175: var piperFreeJson = exampleModule.cwrap( cannam@175: 'piperFreeJson', 'void', ['number'] cannam@175: ); c@117: cannam@175: function note(blah) { cannam@175: document.getElementById("test-result").innerHTML += blah + "
"; c@117: } cannam@175: cannam@175: function comment(blah) { cannam@175: note("
" + blah + ""); cannam@175: } cannam@175: cannam@175: function processRaw(request) { cannam@175: cannam@175: const nChannels = request.processInput.inputBuffers.length; cannam@175: const nFrames = request.processInput.inputBuffers[0].length; cannam@175: cannam@175: const buffersPtr = exampleModule._malloc(nChannels * 4); cannam@175: const buffers = new Uint32Array( cannam@175: exampleModule.HEAPU8.buffer, buffersPtr, nChannels); cannam@175: cannam@175: for (let i = 0; i < nChannels; ++i) { cannam@175: const framesPtr = exampleModule._malloc(nFrames * 4); cannam@175: const frames = new Float32Array( cannam@175: exampleModule.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: exampleModule._free(buffers[i]); cannam@175: } cannam@175: exampleModule._free(buffersPtr); cannam@175: cannam@176: const responseJstr = exampleModule.UTF8ToString(responseJson); cannam@175: const response = JSON.parse(responseJstr); cannam@175: cannam@175: piperFreeJson(responseJson); cannam@175: cannam@175: return response; cannam@175: } cannam@175: 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: }; cannam@175: } else { cannam@175: const { s, n } = makeTimestamp(-seconds); cannam@175: return { s: -s, n: -n }; cannam@175: } cannam@175: } cannam@175: cannam@175: function frame2timestamp(frame, rate) { cannam@175: return makeTimestamp(frame / rate); cannam@175: } cannam@175: cannam@175: function request(jsonStr) { cannam@175: note("Request JSON = " + jsonStr); cannam@175: var m = exampleModule; 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: var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); cannam@175: var outCstr = piperRequestJson(inCstr); cannam@175: m._free(inCstr); cannam@176: var result = m.UTF8ToString(outCstr); cannam@175: piperFreeJson(outCstr); cannam@175: note("Returned JSON = " + result); cannam@175: return result; cannam@175: } cannam@175: cannam@175: function myFromBase64(b64) { cannam@175: while (b64.length % 4 > 0) { b64 += "="; } cannam@175: let conv = new Float32Array(toByteArray(b64).buffer); cannam@175: return conv; cannam@175: } cannam@175: cannam@175: function convertWireFeature(wfeature) { cannam@175: let out = {}; cannam@175: if (wfeature.timestamp != null) { cannam@175: out.timestamp = wfeature.timestamp; cannam@175: } 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); cannam@175: } cannam@175: } cannam@175: return out; cannam@175: } cannam@175: cannam@175: function convertWireFeatureList(wfeatures) { cannam@175: return wfeatures.map(convertWireFeature); cannam@175: } cannam@175: 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: const rate = 44100; cannam@175: cannam@175: comment("Loading zero crossings plugin..."); cannam@175: let result = request('{"method":"load","params": {"key":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}'); cannam@175: cannam@175: const blockSize = 1024; cannam@175: cannam@175: comment("Configuring plugin..."); cannam@175: result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": ' + blockSize + ', "stepSize": ' + blockSize + '}, "channelCount": 1}}}'); 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: comment("Now processing " + nblocks + " blocks of 1024 samples each..."); cannam@175: cannam@175: let total = 0; cannam@175: cannam@175: let start = (new Date()).getTime(); cannam@175: comment("Start at " + start); cannam@175: cannam@175: for (let i = 0; i < nblocks; ++i) { cannam@175: result = processRaw({ cannam@175: "handle": 1, cannam@175: "processInput": blocks[i] cannam@175: }); cannam@175: let features = responseToFeatureSet(result); cannam@175: let count = features.get("counts")[0].featureValues[0]; cannam@175: total += count; cannam@175: } cannam@175: cannam@175: let finish = (new Date()).getTime(); cannam@175: comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); cannam@175: cannam@175: comment("Total = " + total); cannam@175: cannam@175: comment("Again..."); cannam@175: cannam@175: total = 0; cannam@175: cannam@175: start = (new Date()).getTime(); cannam@175: comment("Start at " + start); cannam@175: cannam@175: for (let i = 0; i < nblocks; ++i) { cannam@175: result = processRaw({ cannam@175: "handle": 1, cannam@175: "processInput": blocks[i] cannam@175: }); cannam@175: let features = responseToFeatureSet(result); cannam@175: let count = features.get("counts")[0].featureValues[0]; cannam@175: total += count; cannam@175: } cannam@175: cannam@175: finish = (new Date()).getTime(); cannam@175: comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); cannam@175: cannam@175: comment("Total = " + total); cannam@175: cannam@175: comment("Cleaning up the plugin and getting any remaining features..."); cannam@175: result = request('{"method":"finish","params":{"handle":1}}'); c@117: }); c@117: } c@117: c@117: window.onload = function() { c@117: test(); c@117: }