00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "vamp-sdk/PluginHostAdapter.h"
00039 #include "vamp-sdk/hostext/PluginChannelAdapter.h"
00040 #include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
00041 #include "vamp-sdk/hostext/PluginLoader.h"
00042 #include "vamp/vamp.h"
00043
00044 #include <iostream>
00045 #include <fstream>
00046 #include <set>
00047 #include <sndfile.h>
00048
00049 #include <cstring>
00050 #include <cstdlib>
00051
00052 #include "system.h"
00053
00054 #include <cmath>
00055
00056 using namespace std;
00057
00058 using Vamp::Plugin;
00059 using Vamp::PluginHostAdapter;
00060 using Vamp::RealTime;
00061 using Vamp::HostExt::PluginLoader;
00062
00063 #define HOST_VERSION "1.1"
00064
00065 enum Verbosity {
00066 PluginIds,
00067 PluginOutputIds,
00068 PluginInformation
00069 };
00070
00071 void printFeatures(int, int, int, Plugin::FeatureSet, ofstream *, bool frames);
00072 void transformInput(float *, size_t);
00073 void fft(unsigned int, bool, double *, double *, double *, double *);
00074 void printPluginPath(bool verbose);
00075 void printPluginCategoryList();
00076 void enumeratePlugins(Verbosity);
00077 void listPluginsInLibrary(string soname);
00078 int runPlugin(string myname, string soname, string id, string output,
00079 int outputNo, string inputFile, string outfilename, bool frames);
00080
00081 void usage(const char *name)
00082 {
00083 cerr << "\n"
00084 << name << ": A simple Vamp plugin host.\n\n"
00085 "Centre for Digital Music, Queen Mary, University of London.\n"
00086 "Copyright 2006-2007 Chris Cannam and QMUL.\n"
00087 "Freely redistributable; published under a BSD-style license.\n\n"
00088 "Usage:\n\n"
00089 " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
00090 " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
00091 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
00092 " audio data in \"file.wav\", retrieving the named \"output\", or output\n"
00093 " number \"outputno\" (the first output by default) and dumping it to\n"
00094 " standard output, or to \"out.txt\" if the -o option is given.\n\n"
00095 " \"pluginlibrary\" should be a library name, not a file path; the\n"
00096 " standard Vamp library search path will be used to locate it. If\n"
00097 " a file path is supplied, the directory part(s) will be ignored.\n\n"
00098 " If the -s option is given, results will be labelled with the audio\n"
00099 " sample frame at which they occur. Otherwise, they will be labelled\n"
00100 " with time in seconds.\n\n"
00101 " " << name << " -l\n\n"
00102 " -- List the plugin libraries and Vamp plugins in the library search path\n"
00103 " in a verbose human-readable format.\n\n"
00104 " " << name << " --list-ids\n\n"
00105 " -- List the plugins in the search path in a terse machine-readable format,\n"
00106 " in the form vamp:soname:identifier.\n\n"
00107 " " << name << " --list-outputs\n\n"
00108 " -- List the outputs for plugins in the search path in a machine-readable\n"
00109 " format, in the form vamp:soname:identifier:output.\n\n"
00110 " " << name << " --list-by-category\n\n"
00111 " -- List the plugins as a plugin index by category, in a machine-readable\n"
00112 " format. The format may change in future releases.\n\n"
00113 " " << name << " -p\n\n"
00114 " -- Print out the Vamp library search path.\n\n"
00115 " " << name << " -v\n\n"
00116 " -- Display version information only.\n"
00117 << endl;
00118 exit(2);
00119 }
00120
00121 int main(int argc, char **argv)
00122 {
00123 char *scooter = argv[0];
00124 char *name = 0;
00125 while (scooter && *scooter) {
00126 if (*scooter == '/' || *scooter == '\\') name = ++scooter;
00127 else ++scooter;
00128 }
00129 if (!name || !*name) name = argv[0];
00130
00131 if (argc < 2) usage(name);
00132
00133 if (argc == 2) {
00134
00135 if (!strcmp(argv[1], "-v")) {
00136
00137 cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
00138 << "Vamp API version: " << VAMP_API_VERSION << endl
00139 << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
00140 return 0;
00141
00142 } else if (!strcmp(argv[1], "-l")) {
00143
00144 printPluginPath(true);
00145 enumeratePlugins(PluginInformation);
00146 return 0;
00147
00148 } else if (!strcmp(argv[1], "-p")) {
00149
00150 printPluginPath(false);
00151 return 0;
00152
00153 } else if (!strcmp(argv[1], "--list-ids")) {
00154
00155 enumeratePlugins(PluginIds);
00156 return 0;
00157
00158 } else if (!strcmp(argv[1], "--list-outputs")) {
00159
00160 enumeratePlugins(PluginOutputIds);
00161 return 0;
00162
00163 } else if (!strcmp(argv[1], "--list-by-category")) {
00164
00165 printPluginCategoryList();
00166 return 0;
00167
00168 } else usage(name);
00169 }
00170
00171 if (argc < 3) usage(name);
00172
00173 bool useFrames = false;
00174
00175 int base = 1;
00176 if (!strcmp(argv[1], "-s")) {
00177 useFrames = true;
00178 base = 2;
00179 }
00180
00181 string soname = argv[base];
00182 string wavname = argv[base+1];
00183 string plugid = "";
00184 string output = "";
00185 int outputNo = -1;
00186 string outfilename;
00187
00188 if (argc >= base+3) {
00189
00190 int idx = base+2;
00191
00192 if (isdigit(*argv[idx])) {
00193 outputNo = atoi(argv[idx++]);
00194 }
00195
00196 if (argc == idx + 2) {
00197 if (!strcmp(argv[idx], "-o")) {
00198 outfilename = argv[idx+1];
00199 } else usage(name);
00200 } else if (argc != idx) {
00201 (usage(name));
00202 }
00203 }
00204
00205 cerr << endl << name << ": Running..." << endl;
00206
00207 cerr << "Reading file: \"" << wavname << "\", writing to ";
00208 if (outfilename == "") {
00209 cerr << "standard output" << endl;
00210 } else {
00211 cerr << "\"" << outfilename << "\"" << endl;
00212 }
00213
00214 string::size_type sep = soname.find(':');
00215
00216 if (sep != string::npos) {
00217 plugid = soname.substr(sep + 1);
00218 soname = soname.substr(0, sep);
00219
00220 sep = plugid.find(':');
00221 if (sep != string::npos) {
00222 output = plugid.substr(sep + 1);
00223 plugid = plugid.substr(0, sep);
00224 }
00225 }
00226
00227 if (plugid == "") {
00228 usage(name);
00229 }
00230
00231 if (output != "" && outputNo != -1) {
00232 usage(name);
00233 }
00234
00235 if (output == "" && outputNo == -1) {
00236 outputNo = 0;
00237 }
00238
00239 return runPlugin(name, soname, plugid, output, outputNo,
00240 wavname, outfilename, useFrames);
00241 }
00242
00243
00244 int runPlugin(string myname, string soname, string id,
00245 string output, int outputNo, string wavname,
00246 string outfilename, bool useFrames)
00247 {
00248 PluginLoader *loader = PluginLoader::getInstance();
00249
00250 PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
00251
00252 SNDFILE *sndfile;
00253 SF_INFO sfinfo;
00254 memset(&sfinfo, 0, sizeof(SF_INFO));
00255
00256 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
00257 if (!sndfile) {
00258 cerr << myname << ": ERROR: Failed to open input file \""
00259 << wavname << "\": " << sf_strerror(sndfile) << endl;
00260 return 1;
00261 }
00262
00263 ofstream *out = 0;
00264 if (outfilename != "") {
00265 out = new ofstream(outfilename.c_str(), ios::out);
00266 if (!*out) {
00267 cerr << myname << ": ERROR: Failed to open output file \""
00268 << outfilename << "\" for writing" << endl;
00269 delete out;
00270 return 1;
00271 }
00272 }
00273
00274 Plugin *plugin = loader->loadPlugin
00275 (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
00276 if (!plugin) {
00277 cerr << myname << ": ERROR: Failed to load plugin \"" << id
00278 << "\" from library \"" << soname << "\"" << endl;
00279 sf_close(sndfile);
00280 if (out) {
00281 out->close();
00282 delete out;
00283 }
00284 return 1;
00285 }
00286
00287 cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
00288
00289 int blockSize = plugin->getPreferredBlockSize();
00290 int stepSize = plugin->getPreferredStepSize();
00291
00292 if (blockSize == 0) {
00293 blockSize = 1024;
00294 }
00295 if (stepSize == 0) {
00296 if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
00297 stepSize = blockSize/2;
00298 } else {
00299 stepSize = blockSize;
00300 }
00301 } else if (stepSize > blockSize) {
00302 cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
00303 if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
00304 blockSize = stepSize * 2;
00305 } else {
00306 blockSize = stepSize;
00307 }
00308 cerr << blockSize << endl;
00309 }
00310
00311 int channels = sfinfo.channels;
00312
00313 float *filebuf = new float[blockSize * channels];
00314 float **plugbuf = new float*[channels];
00315 for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
00316
00317 cerr << "Using block size = " << blockSize << ", step size = "
00318 << stepSize << endl;
00319
00320 int minch = plugin->getMinChannelCount();
00321 int maxch = plugin->getMaxChannelCount();
00322 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
00323 cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
00324
00325 Plugin::OutputList outputs = plugin->getOutputDescriptors();
00326 Plugin::OutputDescriptor od;
00327
00328 int returnValue = 1;
00329 int progress = 0;
00330
00331 if (outputs.empty()) {
00332 cerr << "ERROR: Plugin has no outputs!" << endl;
00333 goto done;
00334 }
00335
00336 if (outputNo < 0) {
00337
00338 for (size_t oi = 0; oi < outputs.size(); ++oi) {
00339 if (outputs[oi].identifier == output) {
00340 outputNo = oi;
00341 break;
00342 }
00343 }
00344
00345 if (outputNo < 0) {
00346 cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
00347 goto done;
00348 }
00349
00350 } else {
00351
00352 if (int(outputs.size()) <= outputNo) {
00353 cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
00354 goto done;
00355 }
00356 }
00357
00358 od = outputs[outputNo];
00359 cerr << "Output is: \"" << od.identifier << "\"" << endl;
00360
00361 if (!plugin->initialise(channels, stepSize, blockSize)) {
00362 cerr << "ERROR: Plugin initialise (channels = " << channels
00363 << ", stepSize = " << stepSize << ", blockSize = "
00364 << blockSize << ") failed." << endl;
00365 goto done;
00366 }
00367
00368 for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
00369
00370 int count;
00371
00372 if (sf_seek(sndfile, i, SEEK_SET) < 0) {
00373 cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
00374 break;
00375 }
00376
00377 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
00378 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
00379 break;
00380 }
00381
00382 for (int c = 0; c < channels; ++c) {
00383 int j = 0;
00384 while (j < count) {
00385 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
00386 ++j;
00387 }
00388 while (j < blockSize) {
00389 plugbuf[c][j] = 0.0f;
00390 ++j;
00391 }
00392 }
00393
00394 printFeatures
00395 (i, sfinfo.samplerate, outputNo, plugin->process
00396 (plugbuf, RealTime::frame2RealTime(i, sfinfo.samplerate)),
00397 out, useFrames);
00398
00399 int pp = progress;
00400 progress = lrintf((float(i) / sfinfo.frames) * 100.f);
00401 if (progress != pp && out) {
00402 cerr << "\r" << progress << "%";
00403 }
00404 }
00405 if (out) cerr << "\rDone" << endl;
00406
00407 printFeatures(sfinfo.frames, sfinfo.samplerate, outputNo,
00408 plugin->getRemainingFeatures(), out, useFrames);
00409
00410 returnValue = 0;
00411
00412 done:
00413 delete plugin;
00414 if (out) {
00415 out->close();
00416 delete out;
00417 }
00418 sf_close(sndfile);
00419 return returnValue;
00420 }
00421
00422 void
00423 printFeatures(int frame, int sr, int output,
00424 Plugin::FeatureSet features, ofstream *out, bool useFrames)
00425 {
00426 for (unsigned int i = 0; i < features[output].size(); ++i) {
00427
00428 if (useFrames) {
00429
00430 int displayFrame = frame;
00431
00432 if (features[output][i].hasTimestamp) {
00433 displayFrame = RealTime::realTime2Frame
00434 (features[output][i].timestamp, sr);
00435 }
00436
00437 (out ? *out : cout) << displayFrame << ":";
00438
00439 } else {
00440
00441 RealTime rt = RealTime::frame2RealTime(frame, sr);
00442
00443 if (features[output][i].hasTimestamp) {
00444 rt = features[output][i].timestamp;
00445 }
00446
00447 (out ? *out : cout) << rt.toString() << ":";
00448 }
00449
00450 for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
00451 (out ? *out : cout) << " " << features[output][i].values[j];
00452 }
00453
00454 (out ? *out : cout) << endl;
00455 }
00456 }
00457
00458 void
00459 printPluginPath(bool verbose)
00460 {
00461 if (verbose) {
00462 cout << "\nVamp plugin search path: ";
00463 }
00464
00465 vector<string> path = PluginHostAdapter::getPluginPath();
00466 for (size_t i = 0; i < path.size(); ++i) {
00467 if (verbose) {
00468 cout << "[" << path[i] << "]";
00469 } else {
00470 cout << path[i] << endl;
00471 }
00472 }
00473
00474 if (verbose) cout << endl;
00475 }
00476
00477 void
00478 enumeratePlugins(Verbosity verbosity)
00479 {
00480 PluginLoader *loader = PluginLoader::getInstance();
00481
00482 if (verbosity == PluginInformation) {
00483 cout << "\nVamp plugin libraries found in search path:" << endl;
00484 }
00485
00486 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
00487 typedef multimap<string, PluginLoader::PluginKey>
00488 LibraryMap;
00489 LibraryMap libraryMap;
00490
00491 for (size_t i = 0; i < plugins.size(); ++i) {
00492 string path = loader->getLibraryPathForPlugin(plugins[i]);
00493 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
00494 }
00495
00496 string prevPath = "";
00497 int index = 0;
00498
00499 for (LibraryMap::iterator i = libraryMap.begin();
00500 i != libraryMap.end(); ++i) {
00501
00502 string path = i->first;
00503 PluginLoader::PluginKey key = i->second;
00504
00505 if (path != prevPath) {
00506 prevPath = path;
00507 index = 0;
00508 if (verbosity == PluginInformation) {
00509 cout << "\n " << path << ":" << endl;
00510 }
00511 }
00512
00513 Plugin *plugin = loader->loadPlugin(key, 48000);
00514 if (plugin) {
00515
00516 char c = char('A' + index);
00517 if (c > 'Z') c = char('a' + (index - 26));
00518
00519 if (verbosity == PluginInformation) {
00520
00521 cout << " [" << c << "] [v"
00522 << plugin->getVampApiVersion() << "] "
00523 << plugin->getName() << ", \""
00524 << plugin->getIdentifier() << "\"" << " ["
00525 << plugin->getMaker() << "]" << endl;
00526
00527 PluginLoader::PluginCategoryHierarchy category =
00528 loader->getPluginCategory(key);
00529
00530 if (!category.empty()) {
00531 cout << " ";
00532 for (size_t ci = 0; ci < category.size(); ++ci) {
00533 cout << " > " << category[ci];
00534 }
00535 cout << endl;
00536 }
00537
00538 if (plugin->getDescription() != "") {
00539 cout << " - " << plugin->getDescription() << endl;
00540 }
00541
00542 } else if (verbosity == PluginIds) {
00543 cout << "vamp:" << key << endl;
00544 }
00545
00546 Plugin::OutputList outputs =
00547 plugin->getOutputDescriptors();
00548
00549 if (outputs.size() > 1 || verbosity == PluginOutputIds) {
00550 for (size_t j = 0; j < outputs.size(); ++j) {
00551 if (verbosity == PluginInformation) {
00552 cout << " (" << j << ") "
00553 << outputs[j].name << ", \""
00554 << outputs[j].identifier << "\"" << endl;
00555 if (outputs[j].description != "") {
00556 cout << " - "
00557 << outputs[j].description << endl;
00558 }
00559 } else if (verbosity == PluginOutputIds) {
00560 cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
00561 }
00562 }
00563 }
00564
00565 ++index;
00566
00567 delete plugin;
00568 }
00569 }
00570
00571 if (verbosity == PluginInformation) {
00572 cout << endl;
00573 }
00574 }
00575
00576 void
00577 printPluginCategoryList()
00578 {
00579 PluginLoader *loader = PluginLoader::getInstance();
00580
00581 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
00582
00583 set<string> printedcats;
00584
00585 for (size_t i = 0; i < plugins.size(); ++i) {
00586
00587 PluginLoader::PluginKey key = plugins[i];
00588
00589 PluginLoader::PluginCategoryHierarchy category =
00590 loader->getPluginCategory(key);
00591
00592 Plugin *plugin = loader->loadPlugin(key, 48000);
00593 if (!plugin) continue;
00594
00595 string catstr = "";
00596
00597 if (category.empty()) catstr = '|';
00598 else {
00599 for (size_t j = 0; j < category.size(); ++j) {
00600 catstr += category[j];
00601 catstr += '|';
00602 if (printedcats.find(catstr) == printedcats.end()) {
00603 std::cout << catstr << std::endl;
00604 printedcats.insert(catstr);
00605 }
00606 }
00607 }
00608
00609 std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
00610 }
00611 }
00612