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
|