To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / host / vamp-simple-host.cpp

History | View | Annotate | Download (29.5 KB)

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, copyright 2007-2008 QMUL.
10
  
11
    Permission is hereby granted, free of charge, to any person
12
    obtaining a copy of this software and associated documentation
13
    files (the "Software"), to deal in the Software without
14
    restriction, including without limitation the rights to use, copy,
15
    modify, merge, publish, distribute, sublicense, and/or sell copies
16
    of the Software, and to permit persons to whom the Software is
17
    furnished to do so, subject to the following conditions:
18

19
    The above copyright notice and this permission notice shall be
20
    included in all copies or substantial portions of the Software.
21

22
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
26
    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27
    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29

30
    Except as contained in this notice, the names of the Centre for
31
    Digital Music; Queen Mary, University of London; and Chris Cannam
32
    shall not be used in advertising or otherwise to promote the sale,
33
    use or other dealings in this Software without prior written
34
    authorization.
35
*/
36

    
37

    
38
/*
39
 * This "simple" Vamp plugin host is no longer as simple as it was; it
40
 * now has a lot of options and includes a lot of code to handle the
41
 * various useful listing modes it supports.
42
 *
43
 * However, the runPlugin function still contains a reasonable
44
 * implementation of a fairly generic Vamp plugin host capable of
45
 * evaluating a given output on a given plugin for a sound file read
46
 * via libsndfile.
47
 */
48

    
49
#include <vamp-hostsdk/PluginHostAdapter.h>
50
#include <vamp-hostsdk/PluginInputDomainAdapter.h>
51
#include <vamp-hostsdk/PluginLoader.h>
52

    
53
#include <iostream>
54
#include <fstream>
55
#include <set>
56
#include <sndfile.h>
57

    
58
#include <cstring>
59
#include <cstdlib>
60

    
61
#include "system.h"
62

    
63
#include <cmath>
64

    
65
using namespace std;
66

    
67
using Vamp::Plugin;
68
using Vamp::PluginHostAdapter;
69
using Vamp::RealTime;
70
using Vamp::HostExt::PluginLoader;
71
using Vamp::HostExt::PluginWrapper;
72
using Vamp::HostExt::PluginInputDomainAdapter;
73

    
74
#define HOST_VERSION "1.5"
75

    
76
enum Verbosity {
77
    PluginIds,
78
    PluginOutputIds,
79
    PluginInformation,
80
    PluginInformationDetailed
81
};
82

    
83
void printFeatures(int, int,
84
                   const Plugin::OutputDescriptor &, int,
85
                   const Plugin::FeatureSet &, ofstream *, bool frames);
86
void transformInput(float *, size_t);
87
void fft(unsigned int, bool, double *, double *, double *, double *);
88
void printPluginPath(bool verbose);
89
void printPluginCategoryList();
90
void enumeratePlugins(Verbosity);
91
void listPluginsInLibrary(string soname);
92
int runPlugin(string myname, string soname, string id, string output,
93
              int outputNo, string inputFile, string outfilename, bool frames);
94

    
95
void usage(const char *name)
96
{
97
    cerr << "\n"
98
         << name << ": A command-line host for Vamp audio analysis plugins.\n\n"
99
        "Centre for Digital Music, Queen Mary, University of London.\n"
100
        "Copyright 2006-2009 Chris Cannam and QMUL.\n"
101
        "Freely redistributable; published under a BSD-style license.\n\n"
102
        "Usage:\n\n"
103
        "  " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
104
        "  " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
105
        "    -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
106
        "       audio data in \"file.wav\", retrieving the named \"output\", or output\n"
107
        "       number \"outputno\" (the first output by default) and dumping it to\n"
108
        "       standard output, or to \"out.txt\" if the -o option is given.\n\n"
109
        "       \"pluginlibrary\" should be a library name, not a file path; the\n"
110
        "       standard Vamp library search path will be used to locate it.  If\n"
111
        "       a file path is supplied, the directory part(s) will be ignored.\n\n"
112
        "       If the -s option is given, results will be labelled with the audio\n"
113
        "       sample frame at which they occur. Otherwise, they will be labelled\n"
114
        "       with time in seconds.\n\n"
115
        "  " << name << " -l\n"
116
        "  " << name << " --list\n\n"
117
        "    -- List the plugin libraries and Vamp plugins in the library search path\n"
118
        "       in a verbose human-readable format.\n\n"
119
        "  " << name << " -L\n"
120
        "  " << name << " --list-full\n\n"
121
        "    -- List all data reported by all the Vamp plugins in the library search\n"
122
        "       path in a very verbose human-readable format.\n\n"
123
        "  " << name << " --list-ids\n\n"
124
        "    -- List the plugins in the search path in a terse machine-readable format,\n"
125
        "       in the form vamp:soname:identifier.\n\n"
126
        "  " << name << " --list-outputs\n\n"
127
        "    -- List the outputs for plugins in the search path in a machine-readable\n"
128
        "       format, in the form vamp:soname:identifier:output.\n\n"
129
        "  " << name << " --list-by-category\n\n"
130
        "    -- List the plugins as a plugin index by category, in a machine-readable\n"
131
        "       format.  The format may change in future releases.\n\n"
132
        "  " << name << " -p\n\n"
133
        "    -- Print out the Vamp library search path.\n\n"
134
        "  " << name << " -v\n\n"
135
        "    -- Display version information only.\n"
136
         << endl;
137
    exit(2);
138
}
139

    
140
int main(int argc, char **argv)
141
{
142
    char *scooter = argv[0];
143
    char *name = 0;
144
    while (scooter && *scooter) {
145
        if (*scooter == '/' || *scooter == '\\') name = ++scooter;
146
        else ++scooter;
147
    }
148
    if (!name || !*name) name = argv[0];
149
    
150
    if (argc < 2) usage(name);
151

    
152
    if (argc == 2) {
153

    
154
        if (!strcmp(argv[1], "-v")) {
155

    
156
            cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
157
                 << "Vamp API version: " << VAMP_API_VERSION << endl
158
                 << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
159
            return 0;
160

    
161
        } else if (!strcmp(argv[1], "-l") || !strcmp(argv[1], "--list")) {
162

    
163
            printPluginPath(true);
164
            enumeratePlugins(PluginInformation);
165
            return 0;
166

    
167
        } else if (!strcmp(argv[1], "-L") || !strcmp(argv[1], "--list-full")) {
168

    
169
            enumeratePlugins(PluginInformationDetailed);
170
            return 0;
171

    
172
        } else if (!strcmp(argv[1], "-p")) {
173

    
174
            printPluginPath(false);
175
            return 0;
176

    
177
        } else if (!strcmp(argv[1], "--list-ids")) {
178

    
179
            enumeratePlugins(PluginIds);
180
            return 0;
181

    
182
        } else if (!strcmp(argv[1], "--list-outputs")) {
183

    
184
            enumeratePlugins(PluginOutputIds);
185
            return 0;
186

    
187
        } else if (!strcmp(argv[1], "--list-by-category")) {
188

    
189
            printPluginCategoryList();
190
            return 0;
191

    
192
        } else usage(name);
193
    }
194

    
195
    if (argc < 3) usage(name);
196

    
197
    bool useFrames = false;
198
    
199
    int base = 1;
200
    if (!strcmp(argv[1], "-s")) {
201
        useFrames = true;
202
        base = 2;
203
    }
204

    
205
    string soname = argv[base];
206
    string wavname = argv[base+1];
207
    string plugid = "";
208
    string output = "";
209
    int outputNo = -1;
210
    string outfilename;
211

    
212
    if (argc >= base+3) {
213

    
214
        int idx = base+2;
215

    
216
        if (isdigit(*argv[idx])) {
217
            outputNo = atoi(argv[idx++]);
218
        }
219

    
220
        if (argc == idx + 2) {
221
            if (!strcmp(argv[idx], "-o")) {
222
                outfilename = argv[idx+1];
223
            } else usage(name);
224
        } else if (argc != idx) {
225
            (usage(name));
226
        }
227
    }
228

    
229
    cerr << endl << name << ": Running..." << endl;
230

    
231
    cerr << "Reading file: \"" << wavname << "\", writing to ";
232
    if (outfilename == "") {
233
        cerr << "standard output" << endl;
234
    } else {
235
        cerr << "\"" << outfilename << "\"" << endl;
236
    }
237

    
238
    string::size_type sep = soname.find(':');
239

    
240
    if (sep != string::npos) {
241
        plugid = soname.substr(sep + 1);
242
        soname = soname.substr(0, sep);
243

    
244
        sep = plugid.find(':');
245
        if (sep != string::npos) {
246
            output = plugid.substr(sep + 1);
247
            plugid = plugid.substr(0, sep);
248
        }
249
    }
250

    
251
    if (plugid == "") {
252
        usage(name);
253
    }
254

    
255
    if (output != "" && outputNo != -1) {
256
        usage(name);
257
    }
258

    
259
    if (output == "" && outputNo == -1) {
260
        outputNo = 0;
261
    }
262

    
263
    return runPlugin(name, soname, plugid, output, outputNo,
264
                     wavname, outfilename, useFrames);
265
}
266

    
267

    
268
int runPlugin(string myname, string soname, string id,
269
              string output, int outputNo, string wavname,
270
              string outfilename, bool useFrames)
271
{
272
    PluginLoader *loader = PluginLoader::getInstance();
273

    
274
    PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
275
    
276
    SNDFILE *sndfile;
277
    SF_INFO sfinfo;
278
    memset(&sfinfo, 0, sizeof(SF_INFO));
279

    
280
    sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
281
    if (!sndfile) {
282
        cerr << myname << ": ERROR: Failed to open input file \""
283
             << wavname << "\": " << sf_strerror(sndfile) << endl;
284
        return 1;
285
    }
286

    
287
    ofstream *out = 0;
288
    if (outfilename != "") {
289
        out = new ofstream(outfilename.c_str(), ios::out);
290
        if (!*out) {
291
            cerr << myname << ": ERROR: Failed to open output file \""
292
                 << outfilename << "\" for writing" << endl;
293
            delete out;
294
            return 1;
295
        }
296
    }
297

    
298
    Plugin *plugin = loader->loadPlugin
299
        (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
300
    if (!plugin) {
301
        cerr << myname << ": ERROR: Failed to load plugin \"" << id
302
             << "\" from library \"" << soname << "\"" << endl;
303
        sf_close(sndfile);
304
        if (out) {
305
            out->close();
306
            delete out;
307
        }
308
        return 1;
309
    }
310

    
311
    cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
312

    
313
    // Note that the following would be much simpler if we used a
314
    // PluginBufferingAdapter as well -- i.e. if we had passed
315
    // PluginLoader::ADAPT_ALL to loader->loadPlugin() above, instead
316
    // of ADAPT_ALL_SAFE.  Then we could simply specify our own block
317
    // size, keep the step size equal to the block size, and ignore
318
    // the plugin's bleatings.  However, there are some issues with
319
    // using a PluginBufferingAdapter that make the results sometimes
320
    // technically different from (if effectively the same as) the
321
    // un-adapted plugin, so we aren't doing that here.  See the
322
    // PluginBufferingAdapter documentation for details.
323

    
324
    int blockSize = plugin->getPreferredBlockSize();
325
    int stepSize = plugin->getPreferredStepSize();
326

    
327
    if (blockSize == 0) {
328
        blockSize = 1024;
329
    }
330
    if (stepSize == 0) {
331
        if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
332
            stepSize = blockSize/2;
333
        } else {
334
            stepSize = blockSize;
335
        }
336
    } else if (stepSize > blockSize) {
337
        cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
338
        if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
339
            blockSize = stepSize * 2;
340
        } else {
341
            blockSize = stepSize;
342
        }
343
        cerr << blockSize << endl;
344
    }
345
    int overlapSize = blockSize - stepSize;
346
    sf_count_t currentStep = 0;
347
    int finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF
348

    
349
    int channels = sfinfo.channels;
350

    
351
    float *filebuf = new float[blockSize * channels];
352
    float **plugbuf = new float*[channels];
353
    for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
354

    
355
    cerr << "Using block size = " << blockSize << ", step size = "
356
              << stepSize << endl;
357

    
358
    // The channel queries here are for informational purposes only --
359
    // a PluginChannelAdapter is being used automatically behind the
360
    // scenes, and it will take case of any channel mismatch
361

    
362
    int minch = plugin->getMinChannelCount();
363
    int maxch = plugin->getMaxChannelCount();
364
    cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
365
    cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
366

    
367
    Plugin::OutputList outputs = plugin->getOutputDescriptors();
368
    Plugin::OutputDescriptor od;
369
    Plugin::FeatureSet features;
370

    
371
    int returnValue = 1;
372
    int progress = 0;
373

    
374
    RealTime rt;
375
    PluginWrapper *wrapper = 0;
376
    RealTime adjustment = RealTime::zeroTime;
377

    
378
    if (outputs.empty()) {
379
        cerr << "ERROR: Plugin has no outputs!" << endl;
380
        goto done;
381
    }
382

    
383
    if (outputNo < 0) {
384

    
385
        for (size_t oi = 0; oi < outputs.size(); ++oi) {
386
            if (outputs[oi].identifier == output) {
387
                outputNo = oi;
388
                break;
389
            }
390
        }
391

    
392
        if (outputNo < 0) {
393
            cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
394
            goto done;
395
        }
396

    
397
    } else {
398

    
399
        if (int(outputs.size()) <= outputNo) {
400
            cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
401
            goto done;
402
        }        
403
    }
404

    
405
    od = outputs[outputNo];
406
    cerr << "Output is: \"" << od.identifier << "\"" << endl;
407

    
408
    if (!plugin->initialise(channels, stepSize, blockSize)) {
409
        cerr << "ERROR: Plugin initialise (channels = " << channels
410
             << ", stepSize = " << stepSize << ", blockSize = "
411
             << blockSize << ") failed." << endl;
412
        goto done;
413
    }
414

    
415
    wrapper = dynamic_cast<PluginWrapper *>(plugin);
416
    if (wrapper) {
417
        // See documentation for
418
        // PluginInputDomainAdapter::getTimestampAdjustment
419
        PluginInputDomainAdapter *ida =
420
            wrapper->getWrapper<PluginInputDomainAdapter>();
421
        if (ida) adjustment = ida->getTimestampAdjustment();
422
    }
423
    
424
    // Here we iterate over the frames, avoiding asking the numframes in case it's streaming input.
425
    do {
426

    
427
        int count;
428

    
429
        if ((blockSize==stepSize) || (currentStep==0)) {
430
            // read a full fresh block
431
            if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
432
                cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
433
                break;
434
            }
435
            if (count != blockSize) --finalStepsRemaining;
436
        } else {
437
            //  otherwise shunt the existing data down and read the remainder.
438
            memmove(filebuf, filebuf + (stepSize * channels), overlapSize * channels * sizeof(float));
439
            if ((count = sf_readf_float(sndfile, filebuf + (overlapSize * channels), stepSize)) < 0) {
440
                cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
441
                break;
442
            }
443
            if (count != stepSize) --finalStepsRemaining;
444
            count += overlapSize;
445
        }
446

    
447
        for (int c = 0; c < channels; ++c) {
448
            int j = 0;
449
            while (j < count) {
450
                plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
451
                ++j;
452
            }
453
            while (j < blockSize) {
454
                plugbuf[c][j] = 0.0f;
455
                ++j;
456
            }
457
        }
458

    
459
        rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
460

    
461
        features = plugin->process(plugbuf, rt);
462
        
463
        printFeatures
464
            (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
465
             sfinfo.samplerate, od, outputNo, features, out, useFrames);
466

    
467
        if (sfinfo.frames > 0){
468
            int pp = progress;
469
            progress = (int)((float(currentStep * stepSize) / sfinfo.frames) * 100.f + 0.5f);
470
            if (progress != pp && out) {
471
                cerr << "\r" << progress << "%";
472
            }
473
        }
474

    
475
        ++currentStep;
476

    
477
    } while (finalStepsRemaining > 0);
478

    
479
    if (out) cerr << "\rDone" << endl;
480

    
481
    rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
482

    
483
    features = plugin->getRemainingFeatures();
484
    
485
    printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
486
                  sfinfo.samplerate, od, outputNo, features, out, useFrames);
487

    
488
    returnValue = 0;
489

    
490
done:
491
    delete plugin;
492
    if (out) {
493
        out->close();
494
        delete out;
495
    }
496
    sf_close(sndfile);
497
    return returnValue;
498
}
499

    
500
static double
501
toSeconds(const RealTime &time)
502
{
503
    return time.sec + double(time.nsec + 1) / 1000000000.0;
504
}
505

    
506
void
507
printFeatures(int frame, int sr,
508
              const Plugin::OutputDescriptor &output, int outputNo,
509
              const Plugin::FeatureSet &features, ofstream *out, bool useFrames)
510
{
511
    static int featureCount = -1;
512
    
513
    if (features.find(outputNo) == features.end()) return;
514
    
515
    for (size_t i = 0; i < features.at(outputNo).size(); ++i) {
516

    
517
        const Plugin::Feature &f = features.at(outputNo).at(i);
518

    
519
        bool haveRt = false;
520
        RealTime rt;
521

    
522
        if (output.sampleType == Plugin::OutputDescriptor::VariableSampleRate) {
523
            rt = f.timestamp;
524
            haveRt = true;
525
        } else if (output.sampleType == Plugin::OutputDescriptor::FixedSampleRate) {
526
            int n = featureCount + 1;
527
            if (f.hasTimestamp) {
528
                n = int(round(toSeconds(f.timestamp) * output.sampleRate));
529
            }
530
            rt = RealTime::fromSeconds(double(n) / output.sampleRate);
531
            haveRt = true;
532
            featureCount = n;
533
        }
534
        
535
        if (useFrames) {
536

    
537
            int displayFrame = frame;
538

    
539
            if (haveRt) {
540
                displayFrame = RealTime::realTime2Frame(rt, sr);
541
            }
542

    
543
            (out ? *out : cout) << displayFrame;
544

    
545
            if (f.hasDuration) {
546
                displayFrame = RealTime::realTime2Frame(f.duration, sr);
547
                (out ? *out : cout) << "," << displayFrame;
548
            }
549

    
550
            (out ? *out : cout)  << ":";
551

    
552
        } else {
553

    
554
            if (!haveRt) {
555
                rt = RealTime::frame2RealTime(frame, sr);
556
            }
557

    
558
            (out ? *out : cout) << rt.toString();
559

    
560
            if (f.hasDuration) {
561
                rt = f.duration;
562
                (out ? *out : cout) << "," << rt.toString();
563
            }
564

    
565
            (out ? *out : cout) << ":";
566
        }
567

    
568
        for (unsigned int j = 0; j < f.values.size(); ++j) {
569
            (out ? *out : cout) << " " << f.values[j];
570
        }
571
        (out ? *out : cout) << " " << f.label;
572

    
573
        (out ? *out : cout) << endl;
574
    }
575
}
576

    
577
void
578
printPluginPath(bool verbose)
579
{
580
    if (verbose) {
581
        cout << "\nVamp plugin search path: ";
582
    }
583

    
584
    vector<string> path = PluginHostAdapter::getPluginPath();
585
    for (size_t i = 0; i < path.size(); ++i) {
586
        if (verbose) {
587
            cout << "[" << path[i] << "]";
588
        } else {
589
            cout << path[i] << endl;
590
        }
591
    }
592

    
593
    if (verbose) cout << endl;
594
}
595

    
596
static
597
string
598
header(string text, int level)
599
{
600
    string out = '\n' + text + '\n';
601
    for (size_t i = 0; i < text.length(); ++i) {
602
        out += (level == 1 ? '=' : level == 2 ? '-' : '~');
603
    }
604
    out += '\n';
605
    return out;
606
}
607

    
608
void
609
enumeratePlugins(Verbosity verbosity)
610
{
611
    PluginLoader *loader = PluginLoader::getInstance();
612

    
613
    if (verbosity == PluginInformation) {
614
        cout << "\nVamp plugin libraries found in search path:" << endl;
615
    }
616

    
617
    vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
618
    typedef multimap<string, PluginLoader::PluginKey>
619
        LibraryMap;
620
    LibraryMap libraryMap;
621

    
622
    for (size_t i = 0; i < plugins.size(); ++i) {
623
        string path = loader->getLibraryPathForPlugin(plugins[i]);
624
        libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
625
    }
626

    
627
    string prevPath = "";
628
    int index = 0;
629

    
630
    for (LibraryMap::iterator i = libraryMap.begin();
631
         i != libraryMap.end(); ++i) {
632
        
633
        string path = i->first;
634
        PluginLoader::PluginKey key = i->second;
635

    
636
        if (path != prevPath) {
637
            prevPath = path;
638
            index = 0;
639
            if (verbosity == PluginInformation) {
640
                cout << "\n  " << path << ":" << endl;
641
            } else if (verbosity == PluginInformationDetailed) {
642
                string::size_type ki = i->second.find(':');
643
                string text = "Library \"" + i->second.substr(0, ki) + "\"";
644
                cout << "\n" << header(text, 1);
645
            }
646
        }
647

    
648
        Plugin *plugin = loader->loadPlugin(key, 48000);
649
        if (plugin) {
650

    
651
            char c = char('A' + index);
652
            if (c > 'Z') c = char('a' + (index - 26));
653

    
654
            PluginLoader::PluginCategoryHierarchy category =
655
                loader->getPluginCategory(key);
656
            string catstr;
657
            if (!category.empty()) {
658
                for (size_t ci = 0; ci < category.size(); ++ci) {
659
                    if (ci > 0) catstr += " > ";
660
                        catstr += category[ci];
661
                }
662
            }
663

    
664
            if (verbosity == PluginInformation) {
665

    
666
                cout << "    [" << c << "] [v"
667
                     << plugin->getVampApiVersion() << "] "
668
                     << plugin->getName() << ", \""
669
                     << plugin->getIdentifier() << "\"" << " ["
670
                     << plugin->getMaker() << "]" << endl;
671
                
672
                if (catstr != "") {
673
                    cout << "       > " << catstr << endl;
674
                }
675

    
676
                if (plugin->getDescription() != "") {
677
                    cout << "        - " << plugin->getDescription() << endl;
678
                }
679

    
680
            } else if (verbosity == PluginInformationDetailed) {
681

    
682
                cout << header(plugin->getName(), 2);
683
                cout << " - Identifier:         "
684
                     << key << endl;
685
                cout << " - Plugin Version:     " 
686
                     << plugin->getPluginVersion() << endl;
687
                cout << " - Vamp API Version:   "
688
                     << plugin->getVampApiVersion() << endl;
689
                cout << " - Maker:              \""
690
                     << plugin->getMaker() << "\"" << endl;
691
                cout << " - Copyright:          \""
692
                     << plugin->getCopyright() << "\"" << endl;
693
                cout << " - Description:        \""
694
                     << plugin->getDescription() << "\"" << endl;
695
                cout << " - Input Domain:       "
696
                     << (plugin->getInputDomain() == Vamp::Plugin::TimeDomain ?
697
                         "Time Domain" : "Frequency Domain") << endl;
698
                cout << " - Default Step Size:  " 
699
                     << plugin->getPreferredStepSize() << endl;
700
                cout << " - Default Block Size: " 
701
                     << plugin->getPreferredBlockSize() << endl;
702
                cout << " - Minimum Channels:   " 
703
                     << plugin->getMinChannelCount() << endl;
704
                cout << " - Maximum Channels:   " 
705
                     << plugin->getMaxChannelCount() << endl;
706

    
707
            } else if (verbosity == PluginIds) {
708
                cout << "vamp:" << key << endl;
709
            }
710
            
711
            Plugin::OutputList outputs =
712
                plugin->getOutputDescriptors();
713

    
714
            if (verbosity == PluginInformationDetailed) {
715

    
716
                Plugin::ParameterList params = plugin->getParameterDescriptors();
717
                for (size_t j = 0; j < params.size(); ++j) {
718
                    Plugin::ParameterDescriptor &pd(params[j]);
719
                    cout << "\nParameter " << j+1 << ": \"" << pd.name << "\"" << endl;
720
                    cout << " - Identifier:         " << pd.identifier << endl;
721
                    cout << " - Description:        \"" << pd.description << "\"" << endl;
722
                    if (pd.unit != "") {
723
                        cout << " - Unit:               " << pd.unit << endl;
724
                    }
725
                    cout << " - Range:              ";
726
                    cout << pd.minValue << " -> " << pd.maxValue << endl;
727
                    cout << " - Default:            ";
728
                    cout << pd.defaultValue << endl;
729
                    if (pd.isQuantized) {
730
                        cout << " - Quantize Step:      "
731
                             << pd.quantizeStep << endl;
732
                    }
733
                    if (!pd.valueNames.empty()) {
734
                        cout << " - Value Names:        ";
735
                        for (size_t k = 0; k < pd.valueNames.size(); ++k) {
736
                            if (k > 0) cout << ", ";
737
                            cout << "\"" << pd.valueNames[k] << "\"";
738
                        }
739
                        cout << endl;
740
                    }
741
                }
742

    
743
                if (outputs.empty()) {
744
                    cout << "\n** Note: This plugin reports no outputs!" << endl;
745
                }
746
                for (size_t j = 0; j < outputs.size(); ++j) {
747
                    Plugin::OutputDescriptor &od(outputs[j]);
748
                    cout << "\nOutput " << j+1 << ": \"" << od.name << "\"" << endl;
749
                    cout << " - Identifier:         " << od.identifier << endl;
750
                    cout << " - Description:        \"" << od.description << "\"" << endl;
751
                    if (od.unit != "") {
752
                        cout << " - Unit:               " << od.unit << endl;
753
                    }
754
                    if (od.hasFixedBinCount) {
755
                        cout << " - Default Bin Count:  " << od.binCount << endl;
756
                    }
757
                    if (!od.binNames.empty()) {
758
                        bool have = false;
759
                        for (size_t k = 0; k < od.binNames.size(); ++k) {
760
                            if (od.binNames[k] != "") {
761
                                have = true; break;
762
                            }
763
                        }
764
                        if (have) {
765
                            cout << " - Bin Names:          ";
766
                            for (size_t k = 0; k < od.binNames.size(); ++k) {
767
                                if (k > 0) cout << ", ";
768
                                cout << "\"" << od.binNames[k] << "\"";
769
                            }
770
                            cout << endl;
771
                        }
772
                    }
773
                    if (od.hasKnownExtents) {
774
                        cout << " - Default Extents:    ";
775
                        cout << od.minValue << " -> " << od.maxValue << endl;
776
                    }
777
                    if (od.isQuantized) {
778
                        cout << " - Quantize Step:      "
779
                             << od.quantizeStep << endl;
780
                    }
781
                    cout << " - Sample Type:        "
782
                         << (od.sampleType ==
783
                             Plugin::OutputDescriptor::OneSamplePerStep ?
784
                             "One Sample Per Step" :
785
                             od.sampleType ==
786
                             Plugin::OutputDescriptor::FixedSampleRate ?
787
                             "Fixed Sample Rate" :
788
                             "Variable Sample Rate") << endl;
789
                    if (od.sampleType !=
790
                        Plugin::OutputDescriptor::OneSamplePerStep) {
791
                        cout << " - Default Rate:       "
792
                             << od.sampleRate << endl;
793
                    }
794
                    cout << " - Has Duration:       "
795
                         << (od.hasDuration ? "Yes" : "No") << endl;
796
                }
797
            }
798

    
799
            if (outputs.size() > 1 || verbosity == PluginOutputIds) {
800
                for (size_t j = 0; j < outputs.size(); ++j) {
801
                    if (verbosity == PluginInformation) {
802
                        cout << "         (" << j << ") "
803
                             << outputs[j].name << ", \""
804
                             << outputs[j].identifier << "\"" << endl;
805
                        if (outputs[j].description != "") {
806
                            cout << "             - " 
807
                                 << outputs[j].description << endl;
808
                        }
809
                    } else if (verbosity == PluginOutputIds) {
810
                        cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
811
                    }
812
                }
813
            }
814

    
815
            ++index;
816

    
817
            delete plugin;
818
        }
819
    }
820

    
821
    if (verbosity == PluginInformation ||
822
        verbosity == PluginInformationDetailed) {
823
        cout << endl;
824
    }
825
}
826

    
827
void
828
printPluginCategoryList()
829
{
830
    PluginLoader *loader = PluginLoader::getInstance();
831

    
832
    vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
833

    
834
    set<string> printedcats;
835

    
836
    for (size_t i = 0; i < plugins.size(); ++i) {
837

    
838
        PluginLoader::PluginKey key = plugins[i];
839
        
840
        PluginLoader::PluginCategoryHierarchy category =
841
            loader->getPluginCategory(key);
842

    
843
        Plugin *plugin = loader->loadPlugin(key, 48000);
844
        if (!plugin) continue;
845

    
846
        string catstr = "";
847

    
848
        if (category.empty()) catstr = '|';
849
        else {
850
            for (size_t j = 0; j < category.size(); ++j) {
851
                catstr += category[j];
852
                catstr += '|';
853
                if (printedcats.find(catstr) == printedcats.end()) {
854
                    std::cout << catstr << std::endl;
855
                    printedcats.insert(catstr);
856
                }
857
            }
858
        }
859

    
860
        std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
861
    }
862
}
863