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