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