annotate perf-test-node.js @ 36:34480328bf5c

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