annotate perf-test.js @ 35:3faa4e3eedac

Fix object structure for process input in perf test
author Chris Cannam
date Mon, 26 Sep 2016 16:18:44 +0100
parents e9a0f66ee81c
children 34480328bf5c
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@33 40 const nFrames = request.processInput.inputBuffers[0].values.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@33 50 frames.set(request.processInput.inputBuffers[i].values);
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@33 65 const response = JSON.parse(
Chris@33 66 exampleModule.Pointer_stringify(responseJson));
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@35 122 if (wfeature.b64values != null && wfeature.b64values !== "") {
Chris@35 123 out.values = myFromBase64(wfeature.b64values);
Chris@35 124 } else if (wfeature.values != null) {
Chris@35 125 out.values = new Float32Array(wfeature.values);
Chris@35 126 }
Chris@35 127 return out;
Chris@35 128 }
Chris@35 129
Chris@35 130 function convertWireFeatureList(wfeatures) {
Chris@35 131 return wfeatures.map(convertWireFeature);
Chris@35 132 }
Chris@35 133
Chris@35 134 function responseToFeatureSet(response) {
Chris@35 135 const features = new Map();
Chris@35 136 const processResponse = response.content;
Chris@35 137 const wireFeatures = processResponse.features;
Chris@35 138 Object.keys(wireFeatures).forEach(key => {
Chris@35 139 return features.set(key, convertWireFeatureList(wireFeatures[key]));
Chris@35 140 });
Chris@35 141 return features;
Chris@35 142 }
Chris@35 143
Chris@33 144 function test() {
Chris@33 145
Chris@35 146 const rate = 44100;
Chris@35 147
Chris@33 148 comment("Loading zero crossings plugin...");
Chris@35 149 let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}');
Chris@33 150
Chris@33 151 const blockSize = 1024;
Chris@33 152
Chris@33 153 result = request('{"type":"configure","content":{"pluginHandle":1,"configuration":{"blockSize": ' + blockSize + ', "channelCount": 1, "stepSize": ' + blockSize + '}}}');
Chris@33 154
Chris@33 155 const nblocks = 1000;
Chris@33 156
Chris@35 157 const makeBlock = (n => {
Chris@35 158 return {
Chris@35 159 timestamp : frame2timestamp(n * blockSize, rate),
Chris@35 160 inputBuffers : [
Chris@35 161 { values : new Float32Array(
Chris@35 162 Array.from(Array(blockSize).keys(),
Chris@35 163 n => n / blockSize)) }
Chris@35 164 ],
Chris@35 165 }
Chris@35 166 });
Chris@35 167
Chris@35 168 const blocks = Array.from(Array(nblocks).keys(), makeBlock);
Chris@33 169
Chris@33 170 comment("Now processing " + nblocks + " blocks of 1024 samples each...");
Chris@33 171
Chris@35 172 let total = 0;
Chris@35 173
Chris@33 174 let start = (new Date()).getTime();
Chris@33 175 comment("Start at " + start);
Chris@33 176
Chris@33 177 for (let i = 0; i < nblocks; ++i) {
Chris@33 178 result = processRaw({
Chris@33 179 "pluginHandle": 1,
Chris@35 180 "processInput": blocks[i]
Chris@33 181 });
Chris@35 182 let features = responseToFeatureSet(result);
Chris@35 183 let count = features.get("counts")[0].values[0];
Chris@35 184 total += count;
Chris@33 185 }
Chris@33 186
Chris@33 187 let finish = (new Date()).getTime();
Chris@33 188 comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
Chris@33 189
Chris@35 190 comment("Total = " + total);
Chris@35 191
Chris@33 192 comment("Again...");
Chris@35 193
Chris@35 194 total = 0;
Chris@33 195
Chris@33 196 start = (new Date()).getTime();
Chris@33 197 comment("Start at " + start);
Chris@33 198
Chris@33 199 for (let i = 0; i < nblocks; ++i) {
Chris@33 200 result = processRaw({
Chris@33 201 "pluginHandle": 1,
Chris@35 202 "processInput": blocks[i]
Chris@33 203 });
Chris@35 204 let features = responseToFeatureSet(result);
Chris@35 205 let count = features.get("counts")[0].values[0];
Chris@35 206 total += count;
Chris@33 207 }
Chris@33 208
Chris@33 209 finish = (new Date()).getTime();
Chris@33 210 comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
Chris@33 211
Chris@35 212 comment("Total = " + total);
Chris@35 213
Chris@33 214 comment("Cleaning up the plugin and getting any remaining features...");
Chris@33 215 result = request('{"type":"finish","content":{"pluginHandle":1}}');
Chris@33 216 }
Chris@33 217
Chris@33 218 window.onload = function() {
Chris@33 219 test();
Chris@33 220 }