annotate test/perf-test.js @ 176:eaf46e7647a0 tip master

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