Chris@58
|
1 "use strict";
|
Chris@58
|
2
|
Chris@58
|
3 function note(blah) {
|
Chris@58
|
4 console.log(blah);
|
Chris@58
|
5 }
|
Chris@58
|
6
|
Chris@59
|
7 if (process.argv.length < 3 || process.argv.length > 4) {
|
Chris@59
|
8 note("\nUsage: " + process.argv[0] + " <librarypath> [<pluginkey>]");
|
Chris@59
|
9 note("e.g. " + process.argv[0] + " ./VampExamplePlugins.js");
|
Chris@58
|
10 note("e.g. " + process.argv[0] + " ./VampExamplePlugins.js vamp-example-plugins:zerocrossing");
|
Chris@59
|
11 throw "Wrong number of command-line args (1 or 2 expected)"
|
Chris@58
|
12 }
|
Chris@58
|
13
|
Chris@58
|
14 var libraryPath = process.argv[2];
|
Chris@59
|
15
|
Chris@59
|
16 var pluginKey = "";
|
Chris@59
|
17 if (process.argv.length > 3) {
|
Chris@59
|
18 pluginKey = process.argv[3];
|
Chris@59
|
19 }
|
Chris@58
|
20
|
Chris@58
|
21 var base64 = require("./base64");
|
Chris@58
|
22
|
Chris@58
|
23 note("Loading library \"" + libraryPath + "\"...");
|
Chris@58
|
24 var extractor = require(libraryPath);
|
Chris@58
|
25 var extractorModule = extractor();
|
Chris@58
|
26
|
Chris@58
|
27 var piperRequestJson = extractorModule.cwrap(
|
Chris@58
|
28 'piperRequestJson', 'number', ['number']
|
Chris@58
|
29 );
|
Chris@58
|
30
|
Chris@58
|
31 var piperProcessRaw = extractorModule.cwrap(
|
Chris@58
|
32 "piperProcessRaw", "number", ["number", "number", "number", "number"]
|
Chris@58
|
33 );
|
Chris@58
|
34
|
Chris@58
|
35 var piperFreeJson = extractorModule.cwrap(
|
Chris@58
|
36 'piperFreeJson', 'void', ['number']
|
Chris@58
|
37 );
|
Chris@58
|
38
|
Chris@58
|
39 function processRaw(request) {
|
Chris@58
|
40
|
Chris@58
|
41 const nChannels = request.processInput.inputBuffers.length;
|
Chris@58
|
42 const nFrames = request.processInput.inputBuffers[0].length;
|
Chris@58
|
43
|
Chris@58
|
44 const buffersPtr = extractorModule._malloc(nChannels * 4);
|
Chris@58
|
45 const buffers = new Uint32Array(
|
Chris@58
|
46 extractorModule.HEAPU8.buffer, buffersPtr, nChannels);
|
Chris@58
|
47
|
Chris@58
|
48 for (let i = 0; i < nChannels; ++i) {
|
Chris@58
|
49 const framesPtr = extractorModule._malloc(nFrames * 4);
|
Chris@58
|
50 const frames = new Float32Array(
|
Chris@58
|
51 extractorModule.HEAPU8.buffer, framesPtr, nFrames);
|
Chris@58
|
52 frames.set(request.processInput.inputBuffers[i]);
|
Chris@58
|
53 buffers[i] = framesPtr;
|
Chris@58
|
54 }
|
Chris@58
|
55
|
Chris@58
|
56 const responseJson = piperProcessRaw(
|
Chris@58
|
57 request.handle,
|
Chris@58
|
58 buffersPtr,
|
Chris@58
|
59 request.processInput.timestamp.s,
|
Chris@58
|
60 request.processInput.timestamp.n);
|
Chris@58
|
61
|
Chris@58
|
62 for (let i = 0; i < nChannels; ++i) {
|
Chris@58
|
63 extractorModule._free(buffers[i]);
|
Chris@58
|
64 }
|
Chris@58
|
65 extractorModule._free(buffersPtr);
|
Chris@58
|
66
|
Chris@58
|
67 const responseJstr = extractorModule.Pointer_stringify(responseJson);
|
Chris@58
|
68 const response = JSON.parse(responseJstr);
|
Chris@58
|
69
|
Chris@58
|
70 piperFreeJson(responseJson);
|
Chris@58
|
71
|
Chris@58
|
72 return response;
|
Chris@58
|
73 }
|
Chris@58
|
74
|
Chris@58
|
75 function makeTimestamp(seconds) {
|
Chris@58
|
76 if (seconds >= 0.0) {
|
Chris@58
|
77 return {
|
Chris@58
|
78 s: Math.floor(seconds),
|
Chris@58
|
79 n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5)
|
Chris@58
|
80 };
|
Chris@58
|
81 } else {
|
Chris@58
|
82 const { s, n } = makeTimestamp(-seconds);
|
Chris@58
|
83 return { s: -s, n: -n };
|
Chris@58
|
84 }
|
Chris@58
|
85 }
|
Chris@58
|
86
|
Chris@58
|
87 function frame2timestamp(frame, rate) {
|
Chris@58
|
88 return makeTimestamp(frame / rate);
|
Chris@58
|
89 }
|
Chris@58
|
90
|
Chris@58
|
91 function request(req) {
|
Chris@58
|
92 var jsonStr = JSON.stringify(req);
|
Chris@58
|
93 var m = extractorModule;
|
Chris@58
|
94 // Inspection reveals that intArrayFromString converts the string
|
Chris@58
|
95 // from utf16 to utf8, which is what we want (though the docs
|
Chris@58
|
96 // don't mention this). Note the *Cstr values are Emscripten heap
|
Chris@58
|
97 // pointers
|
Chris@58
|
98 let inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL);
|
Chris@58
|
99 let outCstr = piperRequestJson(inCstr);
|
Chris@58
|
100 m._free(inCstr);
|
Chris@58
|
101 const responseJstr = m.Pointer_stringify(outCstr);
|
Chris@58
|
102 const response = JSON.parse(responseJstr);
|
Chris@58
|
103 piperFreeJson(outCstr);
|
Chris@58
|
104 return response;
|
Chris@58
|
105 }
|
Chris@58
|
106
|
Chris@58
|
107 function myFromBase64(b64) {
|
Chris@58
|
108 while (b64.length % 4 > 0) { b64 += "="; }
|
Chris@58
|
109 let conv = new Float32Array(base64.toByteArray(b64).buffer);
|
Chris@58
|
110 return conv;
|
Chris@58
|
111 }
|
Chris@58
|
112
|
Chris@58
|
113 function convertWireFeature(wfeature) {
|
Chris@58
|
114 let out = {};
|
Chris@58
|
115 if (wfeature.timestamp != null) {
|
Chris@58
|
116 out.timestamp = wfeature.timestamp;
|
Chris@58
|
117 }
|
Chris@58
|
118 if (wfeature.duration != null) {
|
Chris@58
|
119 out.duration = wfeature.duration;
|
Chris@58
|
120 }
|
Chris@58
|
121 if (wfeature.label != null) {
|
Chris@58
|
122 out.label = wfeature.label;
|
Chris@58
|
123 }
|
Chris@58
|
124 const vv = wfeature.featureValues;
|
Chris@58
|
125 if (vv != null) {
|
Chris@58
|
126 if (typeof vv === "string") {
|
Chris@58
|
127 out.featureValues = myFromBase64(vv);
|
Chris@58
|
128 } else {
|
Chris@58
|
129 out.featureValues = new Float32Array(vv);
|
Chris@58
|
130 }
|
Chris@58
|
131 }
|
Chris@58
|
132 return out;
|
Chris@58
|
133 }
|
Chris@58
|
134
|
Chris@58
|
135 function convertWireFeatureList(wfeatures) {
|
Chris@58
|
136 return wfeatures.map(convertWireFeature);
|
Chris@58
|
137 }
|
Chris@58
|
138
|
Chris@58
|
139 function responseToFeatureSet(response) {
|
Chris@58
|
140 const features = new Map();
|
Chris@58
|
141 const processResponse = response.result;
|
Chris@58
|
142 const wireFeatures = processResponse.features;
|
Chris@58
|
143 Object.keys(wireFeatures).forEach(key => {
|
Chris@58
|
144 return features.set(key, convertWireFeatureList(wireFeatures[key]));
|
Chris@58
|
145 });
|
Chris@58
|
146 return features;
|
Chris@58
|
147 }
|
Chris@58
|
148
|
Chris@59
|
149 function checkSuccess(response) {
|
Chris@59
|
150 if (response.error) {
|
Chris@59
|
151 console.log("Request type " + response.method + " failed: " +
|
Chris@59
|
152 response.error.message);
|
Chris@59
|
153 throw response.error.message;
|
Chris@59
|
154 }
|
Chris@59
|
155 }
|
Chris@59
|
156
|
Chris@58
|
157 function test() {
|
Chris@58
|
158
|
Chris@58
|
159 const rate = 44100;
|
Chris@59
|
160
|
Chris@59
|
161 note("Listing plugins...");
|
Chris@59
|
162 let response = request({
|
Chris@59
|
163 method: "list"
|
Chris@59
|
164 });
|
Chris@59
|
165 checkSuccess(response);
|
Chris@59
|
166
|
Chris@59
|
167 if (pluginKey === "") {
|
Chris@59
|
168 pluginKey = response.result.available[0].key;
|
Chris@59
|
169 note("Loading first plugin \"" + pluginKey + "\"...");
|
Chris@59
|
170 } else {
|
Chris@59
|
171 note("Loading requested plugin \"" + pluginKey + "\"...");
|
Chris@59
|
172 }
|
Chris@58
|
173
|
Chris@59
|
174 response = request({
|
Chris@58
|
175 method: "load",
|
Chris@58
|
176 params: {
|
Chris@58
|
177 key: pluginKey,
|
Chris@58
|
178 inputSampleRate: rate,
|
Chris@58
|
179 adapterFlags: ["AdaptAllSafe"]
|
Chris@58
|
180 }
|
Chris@58
|
181 });
|
Chris@59
|
182 checkSuccess(response);
|
Chris@58
|
183
|
Chris@59
|
184 const blockSize = response.result.defaultConfiguration.blockSize;
|
Chris@59
|
185 const stepSize = response.result.defaultConfiguration.stepSize;
|
Chris@58
|
186
|
Chris@58
|
187 response = request({
|
Chris@58
|
188 method: "configure",
|
Chris@58
|
189 params: {
|
Chris@58
|
190 handle: 1,
|
Chris@58
|
191 configuration: {
|
Chris@58
|
192 blockSize: blockSize,
|
Chris@59
|
193 stepSize: stepSize,
|
Chris@59
|
194 channelCount: 1
|
Chris@58
|
195 }
|
Chris@58
|
196 }
|
Chris@58
|
197 });
|
Chris@59
|
198 checkSuccess(response);
|
Chris@58
|
199
|
Chris@58
|
200 const nblocks = 1000;
|
Chris@58
|
201
|
Chris@58
|
202 const makeBlock = (n => {
|
Chris@58
|
203 return {
|
Chris@58
|
204 timestamp : frame2timestamp(n * blockSize, rate),
|
Chris@58
|
205 inputBuffers : [
|
Chris@58
|
206 new Float32Array(Array.from(Array(blockSize).keys(),
|
Chris@58
|
207 n => n / blockSize))
|
Chris@58
|
208 ],
|
Chris@58
|
209 }
|
Chris@58
|
210 });
|
Chris@58
|
211
|
Chris@58
|
212 const blocks = Array.from(Array(nblocks).keys(), makeBlock);
|
Chris@58
|
213
|
Chris@58
|
214 note("Now processing " + nblocks + " blocks of 1024 samples each...");
|
Chris@58
|
215
|
Chris@58
|
216 let featureCount = 0;
|
Chris@58
|
217
|
Chris@58
|
218 for (let i = 0; i < nblocks; ++i) {
|
Chris@58
|
219 response = processRaw({
|
Chris@58
|
220 "handle": 1,
|
Chris@58
|
221 "processInput": blocks[i]
|
Chris@58
|
222 });
|
Chris@58
|
223 let features = responseToFeatureSet(response);
|
Chris@58
|
224 for (let featureList of features.values()) {
|
Chris@58
|
225 featureCount += featureList.length;
|
Chris@58
|
226 }
|
Chris@58
|
227 }
|
Chris@58
|
228
|
Chris@58
|
229 note("Cleaning up the plugin and getting any remaining features...");
|
Chris@58
|
230 response = request({
|
Chris@58
|
231 method: "finish",
|
Chris@58
|
232 params: {
|
Chris@58
|
233 handle: 1
|
Chris@58
|
234 }
|
Chris@58
|
235 });
|
Chris@59
|
236 checkSuccess(response);
|
Chris@58
|
237
|
Chris@58
|
238 let features = responseToFeatureSet(response);
|
Chris@58
|
239 for (let featureList of features.values()) {
|
Chris@58
|
240 featureCount += featureList.length;
|
Chris@58
|
241 }
|
Chris@58
|
242
|
Chris@58
|
243 note("Done, total number of features across all outputs = " + featureCount);
|
Chris@58
|
244 }
|
Chris@58
|
245
|
Chris@58
|
246 test();
|
Chris@58
|
247
|