annotate perf-test-node.js @ 105:4845fbb1a516

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