annotate test/perf-test-node.js @ 169:9c746a913d7f

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