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