74 #define HOST_VERSION "1.4"
83 void printFeatures(
int,
int,
int, Plugin::FeatureSet, ofstream *,
bool frames);
85 void fft(
unsigned int,
bool,
double *,
double *,
double *,
double *);
90 int runPlugin(
string myname,
string soname,
string id,
string output,
91 int outputNo,
string inputFile,
string outfilename,
bool frames);
96 << name <<
": A command-line host for Vamp audio analysis plugins.\n\n"
97 "Centre for Digital Music, Queen Mary, University of London.\n"
98 "Copyright 2006-2009 Chris Cannam and QMUL.\n"
99 "Freely redistributable; published under a BSD-style license.\n\n"
101 " " << name <<
" [-s] pluginlibrary[." <<
PLUGIN_SUFFIX <<
"]:plugin[:output] file.wav [-o out.txt]\n"
102 " " << name <<
" [-s] pluginlibrary[." <<
PLUGIN_SUFFIX <<
"]:plugin file.wav [outputno] [-o out.txt]\n\n"
103 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
104 " audio data in \"file.wav\", retrieving the named \"output\", or output\n"
105 " number \"outputno\" (the first output by default) and dumping it to\n"
106 " standard output, or to \"out.txt\" if the -o option is given.\n\n"
107 " \"pluginlibrary\" should be a library name, not a file path; the\n"
108 " standard Vamp library search path will be used to locate it. If\n"
109 " a file path is supplied, the directory part(s) will be ignored.\n\n"
110 " If the -s option is given, results will be labelled with the audio\n"
111 " sample frame at which they occur. Otherwise, they will be labelled\n"
112 " with time in seconds.\n\n"
113 " " << name <<
" -l\n"
114 " " << name <<
" --list\n\n"
115 " -- List the plugin libraries and Vamp plugins in the library search path\n"
116 " in a verbose human-readable format.\n\n"
117 " " << name <<
" --list-full\n\n"
118 " -- List all data reported by all the Vamp plugins in the library search\n"
119 " path in a very verbose human-readable format.\n\n"
120 " " << name <<
" --list-ids\n\n"
121 " -- List the plugins in the search path in a terse machine-readable format,\n"
122 " in the form vamp:soname:identifier.\n\n"
123 " " << name <<
" --list-outputs\n\n"
124 " -- List the outputs for plugins in the search path in a machine-readable\n"
125 " format, in the form vamp:soname:identifier:output.\n\n"
126 " " << name <<
" --list-by-category\n\n"
127 " -- List the plugins as a plugin index by category, in a machine-readable\n"
128 " format. The format may change in future releases.\n\n"
129 " " << name <<
" -p\n\n"
130 " -- Print out the Vamp library search path.\n\n"
131 " " << name <<
" -v\n\n"
132 " -- Display version information only.\n"
137 int main(
int argc,
char **argv)
139 char *scooter = argv[0];
141 while (scooter && *scooter) {
142 if (*scooter ==
'/' || *scooter ==
'\\') name = ++scooter;
145 if (!name || !*name) name = argv[0];
147 if (argc < 2)
usage(name);
151 if (!strcmp(argv[1],
"-v")) {
153 cout <<
"Simple Vamp plugin host version: " <<
HOST_VERSION << endl
158 }
else if (!strcmp(argv[1],
"-l") || !strcmp(argv[1],
"--list")) {
164 }
else if (!strcmp(argv[1],
"--list-full")) {
169 }
else if (!strcmp(argv[1],
"-p")) {
174 }
else if (!strcmp(argv[1],
"--list-ids")) {
179 }
else if (!strcmp(argv[1],
"--list-outputs")) {
184 }
else if (!strcmp(argv[1],
"--list-by-category")) {
192 if (argc < 3)
usage(name);
194 bool useFrames =
false;
197 if (!strcmp(argv[1],
"-s")) {
202 string soname = argv[base];
203 string wavname = argv[base+1];
209 if (argc >= base+3) {
213 if (isdigit(*argv[idx])) {
214 outputNo = atoi(argv[idx++]);
217 if (argc == idx + 2) {
218 if (!strcmp(argv[idx],
"-o")) {
219 outfilename = argv[idx+1];
221 }
else if (argc != idx) {
226 cerr << endl << name <<
": Running..." << endl;
228 cerr <<
"Reading file: \"" << wavname <<
"\", writing to ";
229 if (outfilename ==
"") {
230 cerr <<
"standard output" << endl;
232 cerr <<
"\"" << outfilename <<
"\"" << endl;
235 string::size_type sep = soname.find(
':');
237 if (sep != string::npos) {
238 plugid = soname.substr(sep + 1);
239 soname = soname.substr(0, sep);
241 sep = plugid.find(
':');
242 if (sep != string::npos) {
243 output = plugid.substr(sep + 1);
244 plugid = plugid.substr(0, sep);
252 if (output !=
"" && outputNo != -1) {
256 if (output ==
"" && outputNo == -1) {
260 return runPlugin(name, soname, plugid, output, outputNo,
261 wavname, outfilename, useFrames);
266 string output,
int outputNo,
string wavname,
267 string outfilename,
bool useFrames)
275 memset(&sfinfo, 0,
sizeof(SF_INFO));
277 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
279 cerr << myname <<
": ERROR: Failed to open input file \""
280 << wavname <<
"\": " << sf_strerror(sndfile) << endl;
285 if (outfilename !=
"") {
286 out =
new ofstream(outfilename.c_str(), ios::out);
288 cerr << myname <<
": ERROR: Failed to open output file \""
289 << outfilename <<
"\" for writing" << endl;
296 (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
298 cerr << myname <<
": ERROR: Failed to load plugin \"" <<
id
299 <<
"\" from library \"" << soname <<
"\"" << endl;
308 cerr <<
"Running plugin: \"" << plugin->
getIdentifier() <<
"\"..." << endl;
324 if (blockSize == 0) {
329 stepSize = blockSize/2;
331 stepSize = blockSize;
333 }
else if (stepSize > blockSize) {
334 cerr <<
"WARNING: stepSize " << stepSize <<
" > blockSize " << blockSize <<
", resetting blockSize to ";
336 blockSize = stepSize * 2;
338 blockSize = stepSize;
340 cerr << blockSize << endl;
343 int channels = sfinfo.channels;
345 float *filebuf =
new float[blockSize * channels];
346 float **plugbuf =
new float*[channels];
347 for (
int c = 0; c < channels; ++c) plugbuf[c] =
new float[blockSize + 2];
349 cerr <<
"Using block size = " << blockSize <<
", step size = "
358 cerr <<
"Plugin accepts " << minch <<
" -> " << maxch <<
" channel(s)" << endl;
359 cerr <<
"Sound file has " << channels <<
" (will mix/augment if necessary)" << endl;
362 Plugin::OutputDescriptor od;
369 RealTime adjustment = RealTime::zeroTime;
371 if (outputs.empty()) {
372 cerr <<
"ERROR: Plugin has no outputs!" << endl;
378 for (
size_t oi = 0; oi < outputs.size(); ++oi) {
379 if (outputs[oi].identifier == output) {
386 cerr <<
"ERROR: Non-existent output \"" << output <<
"\" requested" << endl;
392 if (
int(outputs.size()) <= outputNo) {
393 cerr <<
"ERROR: Output " << outputNo <<
" requested, but plugin has only " << outputs.size() <<
" output(s)" << endl;
398 od = outputs[outputNo];
399 cerr <<
"Output is: \"" << od.identifier <<
"\"" << endl;
401 if (!plugin->
initialise(channels, stepSize, blockSize)) {
402 cerr <<
"ERROR: Plugin initialise (channels = " << channels
403 <<
", stepSize = " << stepSize <<
", blockSize = "
404 << blockSize <<
") failed." << endl;
417 for (sf_count_t i = 0; i < sfinfo.frames; i += stepSize) {
421 if (sf_seek(sndfile, i, SEEK_SET) < 0) {
422 cerr <<
"ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
426 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
427 cerr <<
"ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
431 for (
int c = 0; c < channels; ++c) {
434 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
437 while (j < blockSize) {
438 plugbuf[c][j] = 0.0f;
443 rt = RealTime::frame2RealTime(i, sfinfo.samplerate);
446 (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
447 sfinfo.samplerate, outputNo, plugin->
process(plugbuf, rt),
451 progress = lrintf((
float(i) / sfinfo.frames) * 100.f);
452 if (progress != pp && out) {
453 cerr <<
"\r" << progress <<
"%";
456 if (out) cerr <<
"\rDone" << endl;
458 rt = RealTime::frame2RealTime(sfinfo.frames, sfinfo.samplerate);
460 printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
461 sfinfo.samplerate, outputNo,
478 Plugin::FeatureSet features, ofstream *out,
bool useFrames)
480 for (
unsigned int i = 0; i < features[output].size(); ++i) {
484 int displayFrame = frame;
486 if (features[output][i].hasTimestamp) {
487 displayFrame = RealTime::realTime2Frame
488 (features[output][i].timestamp, sr);
491 (out ? *out : cout) << displayFrame;
493 if (features[output][i].hasDuration) {
494 displayFrame = RealTime::realTime2Frame
495 (features[output][i].duration, sr);
496 (out ? *out : cout) <<
"," << displayFrame;
499 (out ? *out : cout) <<
":";
503 RealTime rt = RealTime::frame2RealTime(frame, sr);
505 if (features[output][i].hasTimestamp) {
506 rt = features[output][i].timestamp;
509 (out ? *out : cout) << rt.
toString();
511 if (features[output][i].hasDuration) {
512 rt = features[output][i].duration;
513 (out ? *out : cout) <<
"," << rt.
toString();
516 (out ? *out : cout) <<
":";
519 for (
unsigned int j = 0; j < features[output][i].values.size(); ++j) {
520 (out ? *out : cout) <<
" " << features[output][i].values[j];
523 (out ? *out : cout) << endl;
531 cout <<
"\nVamp plugin search path: ";
534 vector<string> path = PluginHostAdapter::getPluginPath();
535 for (
size_t i = 0; i < path.size(); ++i) {
537 cout <<
"[" << path[i] <<
"]";
539 cout << path[i] << endl;
543 if (verbose) cout << endl;
550 string out =
'\n' + text +
'\n';
551 for (
size_t i = 0; i < text.length(); ++i) {
552 out += (level == 1 ?
'=' : level == 2 ?
'-' :
'~');
564 cout <<
"\nVamp plugin libraries found in search path:" << endl;
567 vector<PluginLoader::PluginKey> plugins = loader->
listPlugins();
568 typedef multimap<string, PluginLoader::PluginKey>
570 LibraryMap libraryMap;
572 for (
size_t i = 0; i < plugins.size(); ++i) {
574 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
577 string prevPath =
"";
580 for (LibraryMap::iterator i = libraryMap.begin();
581 i != libraryMap.end(); ++i) {
583 string path = i->first;
584 PluginLoader::PluginKey key = i->second;
586 if (path != prevPath) {
590 cout <<
"\n " << path <<
":" << endl;
592 string::size_type ki = i->second.find(
':');
593 string text =
"Library \"" + i->second.substr(0, ki) +
"\"";
594 cout <<
"\n" <<
header(text, 1);
601 char c = char(
'A' + index);
602 if (c >
'Z') c = char(
'a' + (index - 26));
604 PluginLoader::PluginCategoryHierarchy category =
607 if (!category.empty()) {
608 for (
size_t ci = 0; ci < category.size(); ++ci) {
609 if (ci > 0) catstr +=
" > ";
610 catstr += category[ci];
616 cout <<
" [" << c <<
"] [v"
620 << plugin->
getMaker() <<
"]" << endl;
623 cout <<
" > " << catstr << endl;
633 cout <<
" - Identifier: "
635 cout <<
" - Plugin Version: "
637 cout <<
" - Vamp API Version: "
639 cout <<
" - Maker: \""
640 << plugin->
getMaker() <<
"\"" << endl;
641 cout <<
" - Copyright: \""
643 cout <<
" - Description: \""
645 cout <<
" - Input Domain: "
647 "Time Domain" :
"Frequency Domain") << endl;
648 cout <<
" - Default Step Size: "
650 cout <<
" - Default Block Size: "
652 cout <<
" - Minimum Channels: "
654 cout <<
" - Maximum Channels: "
658 cout <<
"vamp:" << key << endl;
661 Plugin::OutputList outputs =
667 for (
size_t j = 0; j < params.size(); ++j) {
668 Plugin::ParameterDescriptor &pd(params[j]);
669 cout <<
"\nParameter " << j+1 <<
": \"" << pd.name <<
"\"" << endl;
670 cout <<
" - Identifier: " << pd.identifier << endl;
671 cout <<
" - Description: \"" << pd.description <<
"\"" << endl;
673 cout <<
" - Unit: " << pd.unit << endl;
675 cout <<
" - Range: ";
676 cout << pd.minValue <<
" -> " << pd.maxValue << endl;
677 cout <<
" - Default: ";
678 cout << pd.defaultValue << endl;
679 if (pd.isQuantized) {
680 cout <<
" - Quantize Step: "
681 << pd.quantizeStep << endl;
683 if (!pd.valueNames.empty()) {
684 cout <<
" - Value Names: ";
685 for (
size_t k = 0; k < pd.valueNames.size(); ++k) {
686 if (k > 0) cout <<
", ";
687 cout <<
"\"" << pd.valueNames[k] <<
"\"";
693 if (outputs.empty()) {
694 cout <<
"\n** Note: This plugin reports no outputs!" << endl;
696 for (
size_t j = 0; j < outputs.size(); ++j) {
697 Plugin::OutputDescriptor &od(outputs[j]);
698 cout <<
"\nOutput " << j+1 <<
": \"" << od.name <<
"\"" << endl;
699 cout <<
" - Identifier: " << od.identifier << endl;
700 cout <<
" - Description: \"" << od.description <<
"\"" << endl;
702 cout <<
" - Unit: " << od.unit << endl;
704 if (od.hasFixedBinCount) {
705 cout <<
" - Default Bin Count: " << od.binCount << endl;
707 if (!od.binNames.empty()) {
709 for (
size_t k = 0; k < od.binNames.size(); ++k) {
710 if (od.binNames[k] !=
"") {
715 cout <<
" - Bin Names: ";
716 for (
size_t k = 0; k < od.binNames.size(); ++k) {
717 if (k > 0) cout <<
", ";
718 cout <<
"\"" << od.binNames[k] <<
"\"";
723 if (od.hasKnownExtents) {
724 cout <<
" - Default Extents: ";
725 cout << od.minValue <<
" -> " << od.maxValue << endl;
727 if (od.isQuantized) {
728 cout <<
" - Quantize Step: "
729 << od.quantizeStep << endl;
731 cout <<
" - Sample Type: "
733 Plugin::OutputDescriptor::OneSamplePerStep ?
734 "One Sample Per Step" :
736 Plugin::OutputDescriptor::FixedSampleRate ?
737 "Fixed Sample Rate" :
738 "Variable Sample Rate") << endl;
740 Plugin::OutputDescriptor::OneSamplePerStep) {
741 cout <<
" - Default Rate: "
742 << od.sampleRate << endl;
744 cout <<
" - Has Duration: "
745 << (od.hasDuration ?
"Yes" :
"No") << endl;
750 for (
size_t j = 0; j < outputs.size(); ++j) {
752 cout <<
" (" << j <<
") "
753 << outputs[j].name <<
", \""
754 << outputs[j].identifier <<
"\"" << endl;
755 if (outputs[j].description !=
"") {
757 << outputs[j].description << endl;
760 cout <<
"vamp:" << key <<
":" << outputs[j].identifier << endl;
782 vector<PluginLoader::PluginKey> plugins = loader->
listPlugins();
784 set<string> printedcats;
786 for (
size_t i = 0; i < plugins.size(); ++i) {
788 PluginLoader::PluginKey key = plugins[i];
790 PluginLoader::PluginCategoryHierarchy category =
794 if (!plugin)
continue;
798 if (category.empty()) catstr =
'|';
800 for (
size_t j = 0; j < category.size(); ++j) {
801 catstr += category[j];
803 if (printedcats.find(catstr) == printedcats.end()) {
804 std::cout << catstr << std::endl;
805 printedcats.insert(catstr);