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