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