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: }