annotate perf-test-node.js @ 110:2f621b00747e

Merge from branch jsonrpc
author Chris Cannam <c.cannam@qmul.ac.uk>
date Thu, 06 Oct 2016 14:33:12 +0100
parents 9d20eb251fbc
children 90bf9d9f9c95
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@109 57 request.handle,
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@109 141 const processResponse = response.result;
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@109 154 let result = request('{"method":"load","params": {"key":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
c@103 155
c@103 156 const blockSize = 1024;
c@103 157
c@109 158 result = request('{"method":"configure","params":{"handle":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@109 183 "handle": 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@109 205 "handle": 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@109 219 result = request('{"method":"finish","params":{"handle":1}}');
c@103 220 }
c@103 221
c@103 222 test();
c@103 223