1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
6
An API for audio analysis and feature extraction plugins.
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.
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:
20
The above copyright notice and this permission notice shall be
21
included in all copies or substantial portions of the Software.
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.
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
38
#include "vamp-sdk/PluginHostAdapter.h"
39
#include "vamp-sdk/hostext/PluginChannelAdapter.h"
40
#include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
41
#include "vamp-sdk/hostext/PluginLoader.h"
42
#include "vamp/vamp.h"
57
using Vamp::HostExt::PluginLoader;
59
#define HOST_VERSION "1.1"
61
void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
62
void transformInput(float *, size_t);
63
void fft(unsigned int, bool, double *, double *, double *, double *);
64
void printPluginPath(bool verbose);
65
void enumeratePlugins();
66
void listPluginsInLibrary(string soname);
67
int runPlugin(string myname, string soname, string id, string output,
68
int outputNo, string inputFile);
70
void usage(const char *name)
73
<< name << ": A simple Vamp plugin host.\n\n"
74
"Centre for Digital Music, Queen Mary, University of London.\n"
75
"Copyright 2006-2007 Chris Cannam and QMUL.\n"
76
"Freely redistributable; published under a BSD-style license.\n\n"
78
" " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav\n"
79
" " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno]\n\n"
80
" -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
81
" audio data in \"file.wav\", retrieving the named \"output\", or output\n"
82
" number \"outputno\" (the first output by default) and dumping it to\n"
83
" standard output.\n\n"
84
" \"pluginlibrary\" should be a library name, not a file path; the\n"
85
" standard Vamp library search path will be used to locate it. If\n"
86
" a file path is supplied, the directory part(s) will be ignored.\n\n"
87
" " << name << " -l\n\n"
88
" -- List the plugin libraries and Vamp plugins in the library search path.\n\n"
89
" " << name << " -p\n\n"
90
" -- Print out the Vamp library search path.\n\n"
91
" " << name << " -v\n\n"
92
" -- Display version information only.\n\n"
97
int main(int argc, char **argv)
99
char *scooter = argv[0];
101
while (scooter && *scooter) {
102
if (*scooter == '/' || *scooter == '\\') name = ++scooter;
105
if (!name || !*name) name = argv[0];
107
if (argc < 2 || argc > 4 ||
109
(!strcmp(argv[1], "-?") ||
110
!strcmp(argv[1], "-h") ||
111
!strcmp(argv[1], "--help")))) {
113
usage(name); // does not return
116
if (argc == 2 && !strcmp(argv[1], "-v")) {
117
cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
118
<< "Vamp API version: " << VAMP_API_VERSION << endl
119
<< "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
123
if (argc == 2 && !strcmp(argv[1], "-l")) {
124
printPluginPath(true);
128
if (argc == 2 && !strcmp(argv[1], "-p")) {
129
printPluginPath(false);
133
cerr << endl << name << ": Running..." << endl;
135
string soname = argv[1];
140
if (argc >= 3) wavname = argv[2];
142
string::size_type sep = soname.find(':');
144
if (sep != string::npos) {
145
plugid = soname.substr(sep + 1);
146
soname = soname.substr(0, sep);
148
sep = plugid.find(':');
149
if (sep != string::npos) {
150
output = plugid.substr(sep + 1);
151
plugid = plugid.substr(0, sep);
159
if (argc == 4) outputNo = atoi(argv[3]);
161
if (output != "" && outputNo != -1) {
165
return runPlugin(name, soname, plugid, output, outputNo, wavname);
169
int runPlugin(string myname, string soname, string id,
170
string output, int outputNo, string wavname)
172
PluginLoader *loader = PluginLoader::getInstance();
174
PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
178
memset(&sfinfo, 0, sizeof(SF_INFO));
180
sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
182
cerr << myname << ": ERROR: Failed to open input file \""
183
<< wavname << "\": " << sf_strerror(sndfile) << endl;
187
Vamp::Plugin *plugin = loader->loadPlugin
188
(key, sfinfo.samplerate, PluginLoader::ADAPT_ALL);
190
cerr << myname << ": ERROR: Failed to load plugin \"" << id
191
<< "\" from library \"" << soname << "\"" << endl;
196
cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
198
int blockSize = plugin->getPreferredBlockSize();
199
int stepSize = plugin->getPreferredStepSize();
201
int channels = sfinfo.channels;
203
float *filebuf = new float[blockSize * channels];
204
float **plugbuf = new float*[channels];
205
for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
207
cerr << "Using block size = " << blockSize << ", step size = "
210
int minch = plugin->getMinChannelCount();
211
int maxch = plugin->getMaxChannelCount();
212
cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
213
cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
215
Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
216
Vamp::Plugin::OutputDescriptor od;
220
if (outputs.empty()) {
221
cerr << "ERROR: Plugin has no outputs!" << endl;
227
for (size_t oi = 0; oi < outputs.size(); ++oi) {
228
if (outputs[oi].identifier == output) {
235
cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
241
if (int(outputs.size()) <= outputNo) {
242
cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
247
od = outputs[outputNo];
248
cerr << "Output is: \"" << od.identifier << "\"" << endl;
250
if (!plugin->initialise(channels, stepSize, blockSize)) {
251
cerr << "ERROR: Plugin initialise (channels = " << channels
252
<< ", stepSize = " << stepSize << ", blockSize = "
253
<< blockSize << ") failed." << endl;
257
for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
261
if (sf_seek(sndfile, i, SEEK_SET) < 0) {
262
cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
266
if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
267
cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
271
for (int c = 0; c < channels; ++c) {
274
plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
277
while (j < blockSize) {
278
plugbuf[c][j] = 0.0f;
284
(i, sfinfo.samplerate, outputNo, plugin->process
285
(plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
288
printFeatures(sfinfo.frames, sfinfo.samplerate, outputNo,
289
plugin->getRemainingFeatures());
300
printPluginPath(bool verbose)
303
cout << "\nVamp plugin search path: ";
306
vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
307
for (size_t i = 0; i < path.size(); ++i) {
309
cout << "[" << path[i] << "]";
311
cout << path[i] << endl;
315
if (verbose) cout << endl;
321
PluginLoader *loader = PluginLoader::getInstance();
323
cout << "\nVamp plugin libraries found in search path:" << endl;
325
std::vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
326
typedef std::multimap<std::string, PluginLoader::PluginKey>
328
LibraryMap libraryMap;
330
for (size_t i = 0; i < plugins.size(); ++i) {
331
std::string path = loader->getLibraryPathForPlugin(plugins[i]);
332
libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
335
std::string prevPath = "";
338
for (LibraryMap::iterator i = libraryMap.begin();
339
i != libraryMap.end(); ++i) {
341
std::string path = i->first;
342
PluginLoader::PluginKey key = i->second;
344
if (path != prevPath) {
347
cout << "\n " << path << ":" << endl;
350
Vamp::Plugin *plugin = loader->loadPlugin(key, 48000);
353
char c = char('A' + index);
354
if (c > 'Z') c = char('a' + (index - 26));
356
cout << " [" << c << "] [v"
357
<< plugin->getVampApiVersion() << "] "
358
<< plugin->getName() << ", \""
359
<< plugin->getIdentifier() << "\"" << " ["
360
<< plugin->getMaker() << "]" << endl;
362
PluginLoader::PluginCategoryHierarchy category =
363
loader->getPluginCategory(key);
364
if (!category.empty()) {
366
for (size_t ci = 0; ci < category.size(); ++ci) {
367
cout << " > " << category[ci];
372
if (plugin->getDescription() != "") {
373
cout << " - " << plugin->getDescription() << endl;
376
Vamp::Plugin::OutputList outputs =
377
plugin->getOutputDescriptors();
379
if (outputs.size() > 1) {
380
for (size_t j = 0; j < outputs.size(); ++j) {
381
cout << " (" << j << ") "
382
<< outputs[j].name << ", \""
383
<< outputs[j].identifier << "\"" << endl;
384
if (outputs[j].description != "") {
386
<< outputs[j].description << endl;
401
printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
403
for (unsigned int i = 0; i < features[output].size(); ++i) {
404
Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr);
405
if (features[output][i].hasTimestamp) {
406
rt = features[output][i].timestamp;
408
cout << rt.toString() << ":";
409
for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
410
cout << " " << features[output][i].values[j];