annotate test/perf-test-node.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
rev   line source
c@117 1 "use strict";
c@117 2
c@125 3 var extractorName = "VampExamplePlugins";
c@125 4 var pluginKey = "vamp-example-plugins:zerocrossing";
cannam@175 5 var extractor = require("../examples/vamp-example-plugins/" + extractorName + "_w.umd");
c@117 6 var base64 = require("./base64");
c@117 7
cannam@175 8 extractor().then(function (extractorModule) {
c@117 9
cannam@175 10 // It is possible to declare both parameters and return values as
cannam@175 11 // "string", in which case Emscripten will take care of
cannam@175 12 // conversions. But it's not clear how one would manage memory for
cannam@175 13 // newly-constructed returned C strings -- the returned pointer from
cannam@175 14 // piperRequestJson would appear (?) to be thrown away by the
cannam@175 15 // Emscripten string converter if we declare it as returning a string,
cannam@175 16 // so we have no opportunity to pass it to piperFreeJson, which
cannam@175 17 // suggests this would leak memory if the string isn't static. Not
cannam@175 18 // wholly sure though. Anyway, passing and returning pointers (as
cannam@175 19 // numbers) means we can manage the Emscripten heap memory however we
cannam@175 20 // want in our request wrapper function below.
c@117 21
cannam@175 22 var piperRequestJson = extractorModule.cwrap(
cannam@175 23 'piperRequestJson', 'number', ['number']
cannam@175 24 );
c@117 25
cannam@175 26 var piperProcessRaw = extractorModule.cwrap(
cannam@175 27 "piperProcessRaw", "number", ["number", "number", "number", "number"]
cannam@175 28 );
c@117 29
cannam@175 30 var piperFreeJson = extractorModule.cwrap(
cannam@175 31 'piperFreeJson', 'void', ['number']
cannam@175 32 );
c@117 33
cannam@175 34 function note(blah) {
cannam@175 35 console.log(blah);
cannam@175 36 }
c@117 37
cannam@175 38 function comment(blah) {
cannam@175 39 console.log(blah);
cannam@175 40 }
c@117 41
cannam@175 42 function processRaw(request) {
cannam@175 43
cannam@175 44 const nChannels = request.processInput.inputBuffers.length;
cannam@175 45 const nFrames = request.processInput.inputBuffers[0].length;
c@117 46
cannam@175 47 const buffersPtr = extractorModule._malloc(nChannels * 4);
cannam@175 48 const buffers = new Uint32Array(
cannam@175 49 extractorModule.HEAPU8.buffer, buffersPtr, nChannels);
cannam@175 50
cannam@175 51 for (let i = 0; i < nChannels; ++i) {
cannam@175 52 const framesPtr = extractorModule._malloc(nFrames * 4);
cannam@175 53 const frames = new Float32Array(
cannam@175 54 extractorModule.HEAPU8.buffer, framesPtr, nFrames);
cannam@175 55 frames.set(request.processInput.inputBuffers[i]);
cannam@175 56 buffers[i] = framesPtr;
cannam@175 57 }
cannam@175 58
cannam@175 59 const responseJson = piperProcessRaw(
cannam@175 60 request.handle,
cannam@175 61 buffersPtr,
cannam@175 62 request.processInput.timestamp.s,
cannam@175 63 request.processInput.timestamp.n);
cannam@175 64
cannam@175 65 for (let i = 0; i < nChannels; ++i) {
cannam@175 66 extractorModule._free(buffers[i]);
cannam@175 67 }
cannam@175 68 extractorModule._free(buffersPtr);
cannam@175 69
cannam@176 70 const responseJstr = extractorModule.UTF8ToString(responseJson);
cannam@175 71 const response = JSON.parse(responseJstr);
cannam@175 72
cannam@175 73 piperFreeJson(responseJson);
cannam@175 74
cannam@175 75 return response;
c@117 76 }
c@117 77
cannam@175 78 function makeTimestamp(seconds) {
cannam@175 79 if (seconds >= 0.0) {
cannam@175 80 return {
cannam@175 81 s: Math.floor(seconds),
cannam@175 82 n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5)
cannam@175 83 };
c@117 84 } else {
cannam@175 85 const { s, n } = makeTimestamp(-seconds);
cannam@175 86 return { s: -s, n: -n };
c@117 87 }
c@117 88 }
c@117 89
cannam@175 90 function frame2timestamp(frame, rate) {
cannam@175 91 return makeTimestamp(frame / rate);
c@117 92 }
c@117 93
cannam@175 94 function request(jsonStr) {
cannam@175 95 note("Request JSON = " + jsonStr);
cannam@175 96 var m = extractorModule;
cannam@175 97 // Inspection reveals that intArrayFromString converts the string
cannam@175 98 // from utf16 to utf8, which is what we want (though the docs
cannam@175 99 // don't mention this). Note the *Cstr values are Emscripten heap
cannam@175 100 // pointers
cannam@175 101 var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL);
cannam@175 102 var outCstr = piperRequestJson(inCstr);
cannam@175 103 m._free(inCstr);
cannam@176 104 var result = m.UTF8ToString(outCstr);
cannam@175 105 piperFreeJson(outCstr);
cannam@175 106 note("Returned JSON = " + result);
cannam@175 107 return result;
c@117 108 }
c@117 109
cannam@175 110 function myFromBase64(b64) {
cannam@175 111 while (b64.length % 4 > 0) { b64 += "="; }
cannam@175 112 let conv = new Float32Array(base64.toByteArray(b64).buffer);
cannam@175 113 return conv;
cannam@175 114 }
c@117 115
cannam@175 116 function convertWireFeature(wfeature) {
cannam@175 117 let out = {};
cannam@175 118 if (wfeature.timestamp != null) {
cannam@175 119 out.timestamp = wfeature.timestamp;
cannam@175 120 }
cannam@175 121 if (wfeature.duration != null) {
cannam@175 122 out.duration = wfeature.duration;
cannam@175 123 }
cannam@175 124 if (wfeature.label != null) {
cannam@175 125 out.label = wfeature.label;
cannam@175 126 }
cannam@175 127 const vv = wfeature.featureValues;
cannam@175 128 if (vv != null) {
cannam@175 129 if (typeof vv === "string") {
cannam@175 130 out.featureValues = myFromBase64(vv);
cannam@175 131 } else {
cannam@175 132 out.featureValues = new Float32Array(vv);
cannam@175 133 }
cannam@175 134 }
cannam@175 135 return out;
cannam@175 136 }
c@117 137
cannam@175 138 function convertWireFeatureList(wfeatures) {
cannam@175 139 return wfeatures.map(convertWireFeature);
cannam@175 140 }
cannam@175 141
cannam@175 142 function responseToFeatureSet(response) {
cannam@175 143 const features = new Map();
cannam@175 144 const processResponse = response.result;
cannam@175 145 const wireFeatures = processResponse.features;
cannam@175 146 Object.keys(wireFeatures).forEach(key => {
cannam@175 147 return features.set(key, convertWireFeatureList(wireFeatures[key]));
cannam@175 148 });
cannam@175 149 return features;
cannam@175 150 }
cannam@175 151
cannam@175 152 function test(extractorModule) {
cannam@175 153
cannam@175 154 const rate = 44100;
cannam@175 155
cannam@175 156 comment("Loading zero crossings plugin...");
cannam@175 157 let result = request('{"method":"load","params": {"key":"' + pluginKey + '","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
cannam@175 158
cannam@175 159 const blockSize = 1024;
cannam@175 160
cannam@175 161 result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": ' + blockSize + ', "stepSize": ' + blockSize + '}, "channelCount": 1 }}}');
cannam@175 162
cannam@175 163 const nblocks = 1000;
cannam@175 164
cannam@175 165 const makeBlock = (n => {
cannam@175 166 return {
cannam@175 167 timestamp : frame2timestamp(n * blockSize, rate),
cannam@175 168 inputBuffers : [
cannam@175 169 new Float32Array(Array.from(Array(blockSize).keys(),
cannam@175 170 n => n / blockSize))
cannam@175 171 ],
cannam@175 172 }
cannam@175 173 });
cannam@175 174
cannam@175 175 const blocks = Array.from(Array(nblocks).keys(), makeBlock);
cannam@175 176
cannam@175 177 comment("Now processing " + nblocks + " blocks of 1024 samples each...");
cannam@175 178
cannam@175 179 let total = 0;
cannam@175 180
cannam@175 181 let start = (new Date()).getTime();
cannam@175 182 comment("Start at " + start);
cannam@175 183
cannam@175 184 for (let i = 0; i < nblocks; ++i) {
cannam@175 185 result = processRaw({
cannam@175 186 "handle": 1,
cannam@175 187 "processInput": blocks[i]
cannam@175 188 });
cannam@175 189 let features = responseToFeatureSet(result);
cannam@175 190 let count = features.get("counts")[0].featureValues[0];
cannam@175 191 total += count;
cannam@175 192 }
cannam@175 193
cannam@175 194 let finish = (new Date()).getTime();
cannam@175 195 comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
cannam@175 196
cannam@175 197 comment("Total = " + total);
cannam@175 198
cannam@175 199 comment("Again...");
cannam@175 200
cannam@175 201 total = 0;
cannam@175 202
cannam@175 203 start = (new Date()).getTime();
cannam@175 204 comment("Start at " + start);
cannam@175 205
cannam@175 206 for (let i = 0; i < nblocks; ++i) {
cannam@175 207 result = processRaw({
cannam@175 208 "handle": 1,
cannam@175 209 "processInput": blocks[i]
cannam@175 210 });
cannam@175 211 let features = responseToFeatureSet(result);
cannam@175 212 let count = features.get("counts")[0].featureValues[0];
cannam@175 213 total += count;
cannam@175 214 }
cannam@175 215
cannam@175 216 finish = (new Date()).getTime();
cannam@175 217 comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
cannam@175 218
cannam@175 219 comment("Total = " + total);
cannam@175 220
cannam@175 221 comment("Cleaning up the plugin and getting any remaining features...");
cannam@175 222 result = request('{"method":"finish","params":{"handle":1}}');
cannam@175 223 }
cannam@175 224
cannam@175 225 test();
cannam@175 226 });