comparison src/vamp-simple-host.cpp @ 226:14029eb08472 distinct-libraries

* start moving things about
author cannam
date Thu, 06 Nov 2008 14:05:33 +0000
parents host/vamp-simple-host.cpp@1982246a3902
children
comparison
equal deleted inserted replaced
225:f8b38a017d9a 226:14029eb08472
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 "vamp-sdk/PluginHostAdapter.h"
39 #include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
40 #include "vamp-sdk/hostext/PluginLoader.h"
41 #include "vamp/vamp.h"
42
43 #include <iostream>
44 #include <fstream>
45 #include <set>
46 #include <sndfile.h>
47
48 #include <cstring>
49 #include <cstdlib>
50
51 #include "system.h"
52
53 #include <cmath>
54
55 using namespace std;
56
57 using Vamp::Plugin;
58 using Vamp::PluginHostAdapter;
59 using Vamp::RealTime;
60 using Vamp::HostExt::PluginLoader;
61 using Vamp::HostExt::PluginWrapper;
62 using Vamp::HostExt::PluginInputDomainAdapter;
63
64 #define HOST_VERSION "1.3"
65
66 enum Verbosity {
67 PluginIds,
68 PluginOutputIds,
69 PluginInformation
70 };
71
72 void printFeatures(int, int, int, Plugin::FeatureSet, ofstream *, bool frames);
73 void transformInput(float *, size_t);
74 void fft(unsigned int, bool, double *, double *, double *, double *);
75 void printPluginPath(bool verbose);
76 void printPluginCategoryList();
77 void enumeratePlugins(Verbosity);
78 void listPluginsInLibrary(string soname);
79 int runPlugin(string myname, string soname, string id, string output,
80 int outputNo, string inputFile, string outfilename, bool frames);
81
82 void usage(const char *name)
83 {
84 cerr << "\n"
85 << name << ": A simple Vamp plugin host.\n\n"
86 "Centre for Digital Music, Queen Mary, University of London.\n"
87 "Copyright 2006-2007 Chris Cannam and QMUL.\n"
88 "Freely redistributable; published under a BSD-style license.\n\n"
89 "Usage:\n\n"
90 " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
91 " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
92 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
93 " audio data in \"file.wav\", retrieving the named \"output\", or output\n"
94 " number \"outputno\" (the first output by default) and dumping it to\n"
95 " standard output, or to \"out.txt\" if the -o option is given.\n\n"
96 " \"pluginlibrary\" should be a library name, not a file path; the\n"
97 " standard Vamp library search path will be used to locate it. If\n"
98 " a file path is supplied, the directory part(s) will be ignored.\n\n"
99 " If the -s option is given, results will be labelled with the audio\n"
100 " sample frame at which they occur. Otherwise, they will be labelled\n"
101 " with time in seconds.\n\n"
102 " " << name << " -l\n\n"
103 " -- List the plugin libraries and Vamp plugins in the library search path\n"
104 " in a verbose human-readable format.\n\n"
105 " " << name << " --list-ids\n\n"
106 " -- List the plugins in the search path in a terse machine-readable format,\n"
107 " in the form vamp:soname:identifier.\n\n"
108 " " << name << " --list-outputs\n\n"
109 " -- List the outputs for plugins in the search path in a machine-readable\n"
110 " format, in the form vamp:soname:identifier:output.\n\n"
111 " " << name << " --list-by-category\n\n"
112 " -- List the plugins as a plugin index by category, in a machine-readable\n"
113 " format. The format may change in future releases.\n\n"
114 " " << name << " -p\n\n"
115 " -- Print out the Vamp library search path.\n\n"
116 " " << name << " -v\n\n"
117 " -- Display version information only.\n"
118 << endl;
119 exit(2);
120 }
121
122 int main(int argc, char **argv)
123 {
124 char *scooter = argv[0];
125 char *name = 0;
126 while (scooter && *scooter) {
127 if (*scooter == '/' || *scooter == '\\') name = ++scooter;
128 else ++scooter;
129 }
130 if (!name || !*name) name = argv[0];
131
132 if (argc < 2) usage(name);
133
134 if (argc == 2) {
135
136 if (!strcmp(argv[1], "-v")) {
137
138 cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
139 << "Vamp API version: " << VAMP_API_VERSION << endl
140 << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
141 return 0;
142
143 } else if (!strcmp(argv[1], "-l")) {
144
145 printPluginPath(true);
146 enumeratePlugins(PluginInformation);
147 return 0;
148
149 } else if (!strcmp(argv[1], "-p")) {
150
151 printPluginPath(false);
152 return 0;
153
154 } else if (!strcmp(argv[1], "--list-ids")) {
155
156 enumeratePlugins(PluginIds);
157 return 0;
158
159 } else if (!strcmp(argv[1], "--list-outputs")) {
160
161 enumeratePlugins(PluginOutputIds);
162 return 0;
163
164 } else if (!strcmp(argv[1], "--list-by-category")) {
165
166 printPluginCategoryList();
167 return 0;
168
169 } else usage(name);
170 }
171
172 if (argc < 3) usage(name);
173
174 bool useFrames = false;
175
176 int base = 1;
177 if (!strcmp(argv[1], "-s")) {
178 useFrames = true;
179 base = 2;
180 }
181
182 string soname = argv[base];
183 string wavname = argv[base+1];
184 string plugid = "";
185 string output = "";
186 int outputNo = -1;
187 string outfilename;
188
189 if (argc >= base+3) {
190
191 int idx = base+2;
192
193 if (isdigit(*argv[idx])) {
194 outputNo = atoi(argv[idx++]);
195 }
196
197 if (argc == idx + 2) {
198 if (!strcmp(argv[idx], "-o")) {
199 outfilename = argv[idx+1];
200 } else usage(name);
201 } else if (argc != idx) {
202 (usage(name));
203 }
204 }
205
206 cerr << endl << name << ": Running..." << endl;
207
208 cerr << "Reading file: \"" << wavname << "\", writing to ";
209 if (outfilename == "") {
210 cerr << "standard output" << endl;
211 } else {
212 cerr << "\"" << outfilename << "\"" << endl;
213 }
214
215 string::size_type sep = soname.find(':');
216
217 if (sep != string::npos) {
218 plugid = soname.substr(sep + 1);
219 soname = soname.substr(0, sep);
220
221 sep = plugid.find(':');
222 if (sep != string::npos) {
223 output = plugid.substr(sep + 1);
224 plugid = plugid.substr(0, sep);
225 }
226 }
227
228 if (plugid == "") {
229 usage(name);
230 }
231
232 if (output != "" && outputNo != -1) {
233 usage(name);
234 }
235
236 if (output == "" && outputNo == -1) {
237 outputNo = 0;
238 }
239
240 return runPlugin(name, soname, plugid, output, outputNo,
241 wavname, outfilename, useFrames);
242 }
243
244
245 int runPlugin(string myname, string soname, string id,
246 string output, int outputNo, string wavname,
247 string outfilename, bool useFrames)
248 {
249 PluginLoader *loader = PluginLoader::getInstance();
250
251 PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
252
253 SNDFILE *sndfile;
254 SF_INFO sfinfo;
255 memset(&sfinfo, 0, sizeof(SF_INFO));
256
257 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
258 if (!sndfile) {
259 cerr << myname << ": ERROR: Failed to open input file \""
260 << wavname << "\": " << sf_strerror(sndfile) << endl;
261 return 1;
262 }
263
264 ofstream *out = 0;
265 if (outfilename != "") {
266 out = new ofstream(outfilename.c_str(), ios::out);
267 if (!*out) {
268 cerr << myname << ": ERROR: Failed to open output file \""
269 << outfilename << "\" for writing" << endl;
270 delete out;
271 return 1;
272 }
273 }
274
275 Plugin *plugin = loader->loadPlugin
276 (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
277 if (!plugin) {
278 cerr << myname << ": ERROR: Failed to load plugin \"" << id
279 << "\" from library \"" << soname << "\"" << endl;
280 sf_close(sndfile);
281 if (out) {
282 out->close();
283 delete out;
284 }
285 return 1;
286 }
287
288 cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
289
290 int blockSize = plugin->getPreferredBlockSize();
291 int stepSize = plugin->getPreferredStepSize();
292
293 if (blockSize == 0) {
294 blockSize = 1024;
295 }
296 if (stepSize == 0) {
297 if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
298 stepSize = blockSize/2;
299 } else {
300 stepSize = blockSize;
301 }
302 } else if (stepSize > blockSize) {
303 cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
304 if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
305 blockSize = stepSize * 2;
306 } else {
307 blockSize = stepSize;
308 }
309 cerr << blockSize << endl;
310 }
311
312 int channels = sfinfo.channels;
313
314 float *filebuf = new float[blockSize * channels];
315 float **plugbuf = new float*[channels];
316 for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
317
318 cerr << "Using block size = " << blockSize << ", step size = "
319 << stepSize << endl;
320
321 int minch = plugin->getMinChannelCount();
322 int maxch = plugin->getMaxChannelCount();
323 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
324 cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
325
326 Plugin::OutputList outputs = plugin->getOutputDescriptors();
327 Plugin::OutputDescriptor od;
328
329 int returnValue = 1;
330 int progress = 0;
331
332 RealTime rt;
333 PluginWrapper *wrapper = 0;
334 RealTime adjustment = RealTime::zeroTime;
335
336 if (outputs.empty()) {
337 cerr << "ERROR: Plugin has no outputs!" << endl;
338 goto done;
339 }
340
341 if (outputNo < 0) {
342
343 for (size_t oi = 0; oi < outputs.size(); ++oi) {
344 if (outputs[oi].identifier == output) {
345 outputNo = oi;
346 break;
347 }
348 }
349
350 if (outputNo < 0) {
351 cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
352 goto done;
353 }
354
355 } else {
356
357 if (int(outputs.size()) <= outputNo) {
358 cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
359 goto done;
360 }
361 }
362
363 od = outputs[outputNo];
364 cerr << "Output is: \"" << od.identifier << "\"" << endl;
365
366 if (!plugin->initialise(channels, stepSize, blockSize)) {
367 cerr << "ERROR: Plugin initialise (channels = " << channels
368 << ", stepSize = " << stepSize << ", blockSize = "
369 << blockSize << ") failed." << endl;
370 goto done;
371 }
372
373 wrapper = dynamic_cast<PluginWrapper *>(plugin);
374 if (wrapper) {
375 PluginInputDomainAdapter *ida =
376 wrapper->getWrapper<PluginInputDomainAdapter>();
377 if (ida) adjustment = ida->getTimestampAdjustment();
378 }
379
380 for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
381
382 int count;
383
384 if (sf_seek(sndfile, i, SEEK_SET) < 0) {
385 cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
386 break;
387 }
388
389 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
390 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
391 break;
392 }
393
394 for (int c = 0; c < channels; ++c) {
395 int j = 0;
396 while (j < count) {
397 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
398 ++j;
399 }
400 while (j < blockSize) {
401 plugbuf[c][j] = 0.0f;
402 ++j;
403 }
404 }
405
406 rt = RealTime::frame2RealTime(i, sfinfo.samplerate);
407
408 printFeatures
409 (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
410 sfinfo.samplerate, outputNo, plugin->process(plugbuf, rt),
411 out, useFrames);
412
413 int pp = progress;
414 progress = lrintf((float(i) / sfinfo.frames) * 100.f);
415 if (progress != pp && out) {
416 cerr << "\r" << progress << "%";
417 }
418 }
419 if (out) cerr << "\rDone" << endl;
420
421 rt = RealTime::frame2RealTime(sfinfo.frames, sfinfo.samplerate);
422
423 printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
424 sfinfo.samplerate, outputNo,
425 plugin->getRemainingFeatures(), out, useFrames);
426
427 returnValue = 0;
428
429 done:
430 delete plugin;
431 if (out) {
432 out->close();
433 delete out;
434 }
435 sf_close(sndfile);
436 return returnValue;
437 }
438
439 void
440 printFeatures(int frame, int sr, int output,
441 Plugin::FeatureSet features, ofstream *out, bool useFrames)
442 {
443 for (unsigned int i = 0; i < features[output].size(); ++i) {
444
445 if (useFrames) {
446
447 int displayFrame = frame;
448
449 if (features[output][i].hasTimestamp) {
450 displayFrame = RealTime::realTime2Frame
451 (features[output][i].timestamp, sr);
452 }
453
454 (out ? *out : cout) << displayFrame;
455
456 if (features[output][i].hasDuration) {
457 displayFrame = RealTime::realTime2Frame
458 (features[output][i].duration, sr);
459 (out ? *out : cout) << "," << displayFrame;
460 }
461
462 (out ? *out : cout) << ":";
463
464 } else {
465
466 RealTime rt = RealTime::frame2RealTime(frame, sr);
467
468 if (features[output][i].hasTimestamp) {
469 rt = features[output][i].timestamp;
470 }
471
472 (out ? *out : cout) << rt.toString();
473
474 if (features[output][i].hasDuration) {
475 rt = features[output][i].duration;
476 (out ? *out : cout) << "," << rt.toString();
477 }
478
479 (out ? *out : cout) << ":";
480 }
481
482 for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
483 (out ? *out : cout) << " " << features[output][i].values[j];
484 }
485
486 (out ? *out : cout) << endl;
487 }
488 }
489
490 void
491 printPluginPath(bool verbose)
492 {
493 if (verbose) {
494 cout << "\nVamp plugin search path: ";
495 }
496
497 vector<string> path = PluginHostAdapter::getPluginPath();
498 for (size_t i = 0; i < path.size(); ++i) {
499 if (verbose) {
500 cout << "[" << path[i] << "]";
501 } else {
502 cout << path[i] << endl;
503 }
504 }
505
506 if (verbose) cout << endl;
507 }
508
509 void
510 enumeratePlugins(Verbosity verbosity)
511 {
512 PluginLoader *loader = PluginLoader::getInstance();
513
514 if (verbosity == PluginInformation) {
515 cout << "\nVamp plugin libraries found in search path:" << endl;
516 }
517
518 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
519 typedef multimap<string, PluginLoader::PluginKey>
520 LibraryMap;
521 LibraryMap libraryMap;
522
523 for (size_t i = 0; i < plugins.size(); ++i) {
524 string path = loader->getLibraryPathForPlugin(plugins[i]);
525 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
526 }
527
528 string prevPath = "";
529 int index = 0;
530
531 for (LibraryMap::iterator i = libraryMap.begin();
532 i != libraryMap.end(); ++i) {
533
534 string path = i->first;
535 PluginLoader::PluginKey key = i->second;
536
537 if (path != prevPath) {
538 prevPath = path;
539 index = 0;
540 if (verbosity == PluginInformation) {
541 cout << "\n " << path << ":" << endl;
542 }
543 }
544
545 Plugin *plugin = loader->loadPlugin(key, 48000);
546 if (plugin) {
547
548 char c = char('A' + index);
549 if (c > 'Z') c = char('a' + (index - 26));
550
551 if (verbosity == PluginInformation) {
552
553 cout << " [" << c << "] [v"
554 << plugin->getVampApiVersion() << "] "
555 << plugin->getName() << ", \""
556 << plugin->getIdentifier() << "\"" << " ["
557 << plugin->getMaker() << "]" << endl;
558
559 PluginLoader::PluginCategoryHierarchy category =
560 loader->getPluginCategory(key);
561
562 if (!category.empty()) {
563 cout << " ";
564 for (size_t ci = 0; ci < category.size(); ++ci) {
565 cout << " > " << category[ci];
566 }
567 cout << endl;
568 }
569
570 if (plugin->getDescription() != "") {
571 cout << " - " << plugin->getDescription() << endl;
572 }
573
574 } else if (verbosity == PluginIds) {
575 cout << "vamp:" << key << endl;
576 }
577
578 Plugin::OutputList outputs =
579 plugin->getOutputDescriptors();
580
581 if (outputs.size() > 1 || verbosity == PluginOutputIds) {
582 for (size_t j = 0; j < outputs.size(); ++j) {
583 if (verbosity == PluginInformation) {
584 cout << " (" << j << ") "
585 << outputs[j].name << ", \""
586 << outputs[j].identifier << "\"" << endl;
587 if (outputs[j].description != "") {
588 cout << " - "
589 << outputs[j].description << endl;
590 }
591 } else if (verbosity == PluginOutputIds) {
592 cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
593 }
594 }
595 }
596
597 ++index;
598
599 delete plugin;
600 }
601 }
602
603 if (verbosity == PluginInformation) {
604 cout << endl;
605 }
606 }
607
608 void
609 printPluginCategoryList()
610 {
611 PluginLoader *loader = PluginLoader::getInstance();
612
613 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
614
615 set<string> printedcats;
616
617 for (size_t i = 0; i < plugins.size(); ++i) {
618
619 PluginLoader::PluginKey key = plugins[i];
620
621 PluginLoader::PluginCategoryHierarchy category =
622 loader->getPluginCategory(key);
623
624 Plugin *plugin = loader->loadPlugin(key, 48000);
625 if (!plugin) continue;
626
627 string catstr = "";
628
629 if (category.empty()) catstr = '|';
630 else {
631 for (size_t j = 0; j < category.size(); ++j) {
632 catstr += category[j];
633 catstr += '|';
634 if (printedcats.find(catstr) == printedcats.end()) {
635 std::cout << catstr << std::endl;
636 printedcats.insert(catstr);
637 }
638 }
639 }
640
641 std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
642 }
643 }
644