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