comparison host/vamp-simple-host.cpp @ 22:1eb44d33a371

* Rename simplehost to vamp-simple-host * Add a temporary test for validity of plugin instances with overlapping lifespans
author cannam
date Mon, 24 Apr 2006 12:58:27 +0000
parents host/simplehost.cpp@68b8b32a2070
children d97aafa99828
comparison
equal deleted inserted replaced
21:16eeab18bf72 22:1eb44d33a371
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Vamp
5
6 An API for audio analysis and feature extraction plugins.
7
8 Centre for Digital Music, Queen Mary, University of London.
9 Copyright 2006 Chris Cannam.
10 FFT code from Don Cross's public domain FFT implementation.
11
12 Permission is hereby granted, free of charge, to any person
13 obtaining a copy of this software and associated documentation
14 files (the "Software"), to deal in the Software without
15 restriction, including without limitation the rights to use, copy,
16 modify, merge, publish, distribute, sublicense, and/or sell copies
17 of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
31 Except as contained in this notice, the names of the Centre for
32 Digital Music; Queen Mary, University of London; and Chris Cannam
33 shall not be used in advertising or otherwise to promote the sale,
34 use or other dealings in this Software without prior written
35 authorization.
36 */
37
38 #include "PluginHostAdapter.h"
39 #include "vamp.h"
40
41 #include <iostream>
42 #include <sndfile.h>
43
44 #include "system.h"
45
46 #include <cmath>
47
48 using std::cout;
49 using std::cerr;
50 using std::endl;
51 using std::string;
52
53 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
54 void transformInput(float *, size_t);
55 void fft(unsigned int, bool, double *, double *, double *, double *);
56
57 /*
58 A very simple Vamp plugin host. Given the name of a plugin
59 library and the name of a sound file on the command line, it loads
60 the first plugin in the library and runs it on the sound file,
61 dumping the plugin's first output to stdout.
62 */
63
64 int main(int argc, char **argv)
65 {
66 if (argc < 2 || argc > 4) {
67 cerr << "Usage: " << argv[0] << " pluginlibrary.so[:plugin] [file.wav] [outputno]" << endl;
68 return 2;
69 }
70
71 cerr << endl << argv[0] << ": Running..." << endl;
72
73 string soname = argv[1];
74 string plugname = "";
75 string wavname;
76 if (argc >= 3) wavname = argv[2];
77
78 int sep = soname.find(":");
79 if (sep >= 0 && sep < soname.length()) {
80 plugname = soname.substr(sep + 1);
81 soname = soname.substr(0, sep);
82 }
83
84 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
85
86 if (!libraryHandle) {
87 cerr << argv[0] << ": Failed to open plugin library "
88 << soname << ": " << DLERROR() << endl;
89 return 1;
90 }
91
92 cerr << argv[0] << ": Opened plugin library " << soname << endl;
93
94 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
95 DLSYM(libraryHandle, "vampGetPluginDescriptor");
96
97 if (!fn) {
98 cerr << argv[0] << ": No Vamp descriptor function in library "
99 << soname << endl;
100 DLCLOSE(libraryHandle);
101 return 1;
102 }
103
104 cerr << argv[0] << ": Found plugin descriptor function" << endl;
105
106 int index = 0;
107 int plugnumber = -1;
108 const VampPluginDescriptor *descriptor = 0;
109
110 while ((descriptor = fn(index))) {
111
112 Vamp::PluginHostAdapter plugin(descriptor, 48000);
113 cerr << argv[0] << ": Plugin " << (index+1)
114 << " is \"" << plugin.getName() << "\"" << endl;
115
116 if (plugin.getName() == plugname) plugnumber = index;
117
118 cerr << "(testing overlap...)" << endl;
119 {
120 Vamp::PluginHostAdapter otherPlugin(descriptor, 48000);
121 cerr << "(other plugin reports min " << otherPlugin.getMinChannelCount() << " channels)" << endl;
122 }
123
124 ++index;
125 }
126
127 cerr << argv[0] << ": Done\n" << endl;
128
129 if (wavname == "") {
130 DLCLOSE(libraryHandle);
131 return 0;
132 }
133
134 if (plugnumber < 0) {
135 if (plugname != "") {
136 cerr << "ERROR: No such plugin as " << plugname << " in library"
137 << endl;
138 DLCLOSE(libraryHandle);
139 return 0;
140 } else {
141 plugnumber = 0;
142 }
143 }
144
145 descriptor = fn(plugnumber);
146 if (!descriptor) {
147 DLCLOSE(libraryHandle);
148 return 0;
149 }
150
151 SNDFILE *sndfile;
152 SF_INFO sfinfo;
153 memset(&sfinfo, 0, sizeof(SF_INFO));
154
155 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
156 if (!sndfile) {
157 cerr << "ERROR: Failed to open input file \"" << wavname << "\": "
158 << sf_strerror(sndfile) << endl;
159 DLCLOSE(libraryHandle);
160 return 1;
161 }
162
163 Vamp::PluginHostAdapter *plugin =
164 new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate);
165
166 cerr << "Running " << plugin->getName() << "..." << endl;
167
168 int blockSize = plugin->getPreferredBlockSize();
169 int stepSize = plugin->getPreferredStepSize();
170
171 cerr << "Preferred block size = " << blockSize << ", step size = "
172 << stepSize << endl;
173
174 if (blockSize == 0) blockSize = 1024;
175 if (stepSize == 0) stepSize = blockSize;
176
177 int channels = sfinfo.channels;
178
179 float *filebuf = new float[blockSize * channels];
180 float **plugbuf = new float*[channels];
181 for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize];
182
183 cerr << "Using block size = " << blockSize << ", step size = "
184 << stepSize << endl;
185
186 int minch = plugin->getMinChannelCount();
187 int maxch = plugin->getMaxChannelCount();
188 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
189
190 Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
191 Vamp::Plugin::OutputDescriptor od;
192
193 int output = 0;
194 if (argc == 4) output = atoi(argv[3]);
195
196 bool mix = false;
197
198 if (minch > channels || maxch < channels) {
199 if (minch == 1) {
200 cerr << "WARNING: Sound file has " << channels << " channels, mixing down to 1" << endl;
201 mix = true;
202 channels = 1;
203 } else {
204 cerr << "ERROR: Sound file has " << channels << " channels, out of range for plugin" << endl;
205 goto done;
206 }
207 }
208
209 if (outputs.empty()) {
210 cerr << "Plugin has no outputs!" << endl;
211 goto done;
212 }
213
214 if (int(outputs.size()) <= output) {
215 cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
216 goto done;
217 }
218
219 od = outputs[output];
220 cerr << "Output is " << od.name << endl;
221
222 plugin->initialise(channels, stepSize, blockSize);
223
224 for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
225
226 int count;
227
228 if (sf_seek(sndfile, i, SEEK_SET) < 0) {
229 cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
230 break;
231 }
232
233 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
234 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
235 break;
236 }
237
238 for (int c = 0; c < channels; ++c) {
239 for (int j = 0; j < blockSize; ++j) {
240 plugbuf[c][j] = 0.0f;
241 }
242 }
243
244 for (int c = 0; c < sfinfo.channels; ++c) {
245 int tc = c;
246 if (mix) tc = 0;
247 for (int j = 0; j < blockSize && j < count; ++j) {
248 plugbuf[tc][j] += filebuf[j * channels + c];
249 }
250
251 if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
252 transformInput(plugbuf[tc], blockSize);
253 }
254 }
255
256 printFeatures
257 (i, sfinfo.samplerate, output, plugin->process
258 (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
259 }
260
261 printFeatures(sfinfo.frames, sfinfo.samplerate, output,
262 plugin->getRemainingFeatures());
263
264 done:
265 delete plugin;
266
267 DLCLOSE(libraryHandle);
268 sf_close(sndfile);
269 return 0;
270 }
271
272 void
273 printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
274 {
275 for (unsigned int i = 0; i < features[output].size(); ++i) {
276 Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr);
277 if (features[output][i].hasTimestamp) {
278 rt = features[output][i].timestamp;
279 }
280 cout << rt.toString() << ":";
281 for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
282 cout << " " << features[output][i].values[j];
283 }
284 cout << endl;
285 }
286 }
287
288 void
289 transformInput(float *buffer, size_t size)
290 {
291 double *inbuf = new double[size * 2];
292 double *outbuf = new double[size * 2];
293
294 // Copy across with Hanning window
295 for (size_t i = 0; i < size; ++i) {
296 inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size));
297 inbuf[i + size] = 0.0;
298 }
299
300 for (size_t i = 0; i < size/2; ++i) {
301 double temp = inbuf[i];
302 inbuf[i] = inbuf[i + size/2];
303 inbuf[i + size/2] = temp;
304 }
305
306 fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size);
307
308 for (size_t i = 0; i < size/2; ++i) {
309 buffer[i * 2] = outbuf[i];
310 buffer[i * 2 + 1] = outbuf[i + size];
311 }
312
313 delete inbuf;
314 delete outbuf;
315 }
316
317 void
318 fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io)
319 {
320 if (!ri || !ro || !io) return;
321
322 unsigned int bits;
323 unsigned int i, j, k, m;
324 unsigned int blockSize, blockEnd;
325
326 double tr, ti;
327
328 if (n < 2) return;
329 if (n & (n-1)) return;
330
331 double angle = 2.0 * M_PI;
332 if (inverse) angle = -angle;
333
334 for (i = 0; ; ++i) {
335 if (n & (1 << i)) {
336 bits = i;
337 break;
338 }
339 }
340
341 static unsigned int tableSize = 0;
342 static int *table = 0;
343
344 if (tableSize != n) {
345
346 delete[] table;
347
348 table = new int[n];
349
350 for (i = 0; i < n; ++i) {
351
352 m = i;
353
354 for (j = k = 0; j < bits; ++j) {
355 k = (k << 1) | (m & 1);
356 m >>= 1;
357 }
358
359 table[i] = k;
360 }
361
362 tableSize = n;
363 }
364
365 if (ii) {
366 for (i = 0; i < n; ++i) {
367 ro[table[i]] = ri[i];
368 io[table[i]] = ii[i];
369 }
370 } else {
371 for (i = 0; i < n; ++i) {
372 ro[table[i]] = ri[i];
373 io[table[i]] = 0.0;
374 }
375 }
376
377 blockEnd = 1;
378
379 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
380
381 double delta = angle / (double)blockSize;
382 double sm2 = -sin(-2 * delta);
383 double sm1 = -sin(-delta);
384 double cm2 = cos(-2 * delta);
385 double cm1 = cos(-delta);
386 double w = 2 * cm1;
387 double ar[3], ai[3];
388
389 for (i = 0; i < n; i += blockSize) {
390
391 ar[2] = cm2;
392 ar[1] = cm1;
393
394 ai[2] = sm2;
395 ai[1] = sm1;
396
397 for (j = i, m = 0; m < blockEnd; j++, m++) {
398
399 ar[0] = w * ar[1] - ar[2];
400 ar[2] = ar[1];
401 ar[1] = ar[0];
402
403 ai[0] = w * ai[1] - ai[2];
404 ai[2] = ai[1];
405 ai[1] = ai[0];
406
407 k = j + blockEnd;
408 tr = ar[0] * ro[k] - ai[0] * io[k];
409 ti = ar[0] * io[k] + ai[0] * ro[k];
410
411 ro[k] = ro[j] - tr;
412 io[k] = io[j] - ti;
413
414 ro[j] += tr;
415 io[j] += ti;
416 }
417 }
418
419 blockEnd = blockSize;
420 }
421
422 if (inverse) {
423
424 double denom = (double)n;
425
426 for (i = 0; i < n; i++) {
427 ro[i] /= denom;
428 io[i] /= denom;
429 }
430 }
431 }
432
433