annotate test/perf-test.js @ 117:2e8aacb7f883

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