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 / src / vamp-hostsdk / PluginLoader.cpp @ 532:569fc23fa37a

History | View | Annotate | Download (17.3 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-2016 Chris Cannam and 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
#include <vamp-hostsdk/PluginLoader.h>
38
#include <vamp-hostsdk/PluginInputDomainAdapter.h>
39
#include <vamp-hostsdk/PluginChannelAdapter.h>
40
#include <vamp-hostsdk/PluginBufferingAdapter.h>
41
#include <vamp-hostsdk/PluginHostAdapter.h>
42

    
43
#include <vamp/vamp.h>
44

    
45
#include "Files.h"
46

    
47
#include <fstream>
48

    
49
using namespace std;
50

    
51
_VAMP_SDK_HOSTSPACE_BEGIN(PluginLoader.cpp)
52

    
53
namespace Vamp {
54
        
55
namespace HostExt {
56

    
57
class PluginLoader::Impl
58
{
59
public:
60
    Impl();
61
    virtual ~Impl();
62

    
63
    PluginKeyList listPlugins();
64
    PluginKeyList listPluginsIn(vector<string>);
65
    PluginKeyList listPluginsNotIn(vector<string>);
66

    
67
    Plugin *loadPlugin(PluginKey key,
68
                       float inputSampleRate,
69
                       int adapterFlags);
70
    
71
    PluginKey composePluginKey(string libraryName, string identifier);
72

    
73
    PluginCategoryHierarchy getPluginCategory(PluginKey key);
74

    
75
    string getLibraryPathForPlugin(PluginKey key);
76

    
77
    static void setInstanceToClean(PluginLoader *instance);
78

    
79
protected:
80
    class PluginDeletionNotifyAdapter : public PluginWrapper {
81
    public:
82
        PluginDeletionNotifyAdapter(Plugin *plugin, Impl *loader);
83
        virtual ~PluginDeletionNotifyAdapter();
84
    protected:
85
        Impl *m_loader;
86
    };
87

    
88
    class InstanceCleaner {
89
    public:
90
        InstanceCleaner() : m_instance(0) { }
91
        ~InstanceCleaner() { delete m_instance; }
92
        void setInstance(PluginLoader *instance) { m_instance = instance; }
93
    protected:
94
        PluginLoader *m_instance;
95
    };
96

    
97
    virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
98

    
99
    map<PluginKey, string> m_pluginLibraryNameMap;
100
    bool m_allPluginsEnumerated;
101

    
102
    struct Enumeration {
103
        enum { All, SinglePlugin, InLibraries, NotInLibraries } type;
104
        PluginKey key;
105
        vector<string> libraryNames;
106
        Enumeration() : type(All) { }
107
    };
108
    vector<string> listLibraryFilesFor(Enumeration);
109

    
110
    /// Populate m_pluginLibraryNameMap and return a list of the keys
111
    /// that were added to it
112
    vector<PluginKey> enumeratePlugins(Enumeration);
113

    
114
    map<PluginKey, PluginCategoryHierarchy> m_taxonomy;
115
    void generateTaxonomy();
116

    
117
    map<Plugin *, void *> m_pluginLibraryHandleMap;
118

    
119
    bool decomposePluginKey(PluginKey key,
120
                            string &libraryName, string &identifier);
121

    
122
    static InstanceCleaner m_cleaner;
123
};
124

    
125
PluginLoader *
126
PluginLoader::m_instance = 0;
127

    
128
PluginLoader::Impl::InstanceCleaner
129
PluginLoader::Impl::m_cleaner;
130

    
131
PluginLoader::PluginLoader()
132
{
133
    m_impl = new Impl();
134
}
135

    
136
PluginLoader::~PluginLoader()
137
{
138
    delete m_impl;
139
}
140

    
141
PluginLoader *
142
PluginLoader::getInstance()
143
{
144
    if (!m_instance) {
145
        // The cleaner doesn't own the instance, because we leave the
146
        // instance pointer in the base class for binary backwards
147
        // compatibility reasons and to avoid waste
148
        m_instance = new PluginLoader();
149
        Impl::setInstanceToClean(m_instance);
150
    }
151
    return m_instance;
152
}
153

    
154
PluginLoader::PluginKeyList
155
PluginLoader::listPlugins() 
156
{
157
    return m_impl->listPlugins();
158
}
159

    
160
PluginLoader::PluginKeyList
161
PluginLoader::listPluginsIn(vector<string> libs) 
162
{
163
    return m_impl->listPluginsIn(libs);
164
}
165

    
166
PluginLoader::PluginKeyList
167
PluginLoader::listPluginsNotIn(vector<string> libs) 
168
{
169
    return m_impl->listPluginsNotIn(libs);
170
}
171

    
172
Plugin *
173
PluginLoader::loadPlugin(PluginKey key,
174
                         float inputSampleRate,
175
                         int adapterFlags)
176
{
177
    return m_impl->loadPlugin(key, inputSampleRate, adapterFlags);
178
}
179

    
180
PluginLoader::PluginKey
181
PluginLoader::composePluginKey(string libraryName, string identifier) 
182
{
183
    return m_impl->composePluginKey(libraryName, identifier);
184
}
185

    
186
PluginLoader::PluginCategoryHierarchy
187
PluginLoader::getPluginCategory(PluginKey key)
188
{
189
    return m_impl->getPluginCategory(key);
190
}
191

    
192
string
193
PluginLoader::getLibraryPathForPlugin(PluginKey key)
194
{
195
    return m_impl->getLibraryPathForPlugin(key);
196
}
197
 
198
PluginLoader::Impl::Impl() :
199
    m_allPluginsEnumerated(false)
200
{
201
}
202

    
203
PluginLoader::Impl::~Impl()
204
{
205
}
206

    
207
void
208
PluginLoader::Impl::setInstanceToClean(PluginLoader *instance)
209
{
210
    m_cleaner.setInstance(instance);
211
}
212

    
213
PluginLoader::PluginKeyList
214
PluginLoader::Impl::listPlugins() 
215
{
216
    if (!m_allPluginsEnumerated) enumeratePlugins(Enumeration());
217

    
218
    vector<PluginKey> plugins;
219
    for (map<PluginKey, string>::const_iterator i =
220
             m_pluginLibraryNameMap.begin();
221
         i != m_pluginLibraryNameMap.end(); ++i) {
222
        plugins.push_back(i->first);
223
    }
224

    
225
    return plugins;
226
}
227

    
228
PluginLoader::PluginKeyList
229
PluginLoader::Impl::listPluginsIn(vector<string> libs) 
230
{
231
    Enumeration enumeration;
232
    enumeration.type = Enumeration::InLibraries;
233
    enumeration.libraryNames = libs;
234
    return enumeratePlugins(enumeration);
235
}
236

    
237
PluginLoader::PluginKeyList
238
PluginLoader::Impl::listPluginsNotIn(vector<string> libs) 
239
{
240
    Enumeration enumeration;
241
    enumeration.type = Enumeration::NotInLibraries;
242
    enumeration.libraryNames = libs;
243
    return enumeratePlugins(enumeration);
244
}
245

    
246
vector<string>
247
PluginLoader::Impl::listLibraryFilesFor(Enumeration enumeration)
248
{
249
    Files::Filter filter;
250
    
251
    switch (enumeration.type) {
252

    
253
    case Enumeration::All:
254
        filter.type = Files::Filter::All;
255
        break;
256

    
257
    case Enumeration::SinglePlugin:
258
    {
259
        string libraryName, identifier;
260
        if (!decomposePluginKey(enumeration.key, libraryName, identifier)) {
261
            std::cerr << "WARNING: Vamp::HostExt::PluginLoader: "
262
                      << "Invalid plugin key \"" << enumeration.key
263
                      << "\" in enumerate" << std::endl;
264
            return vector<string>();
265
        }
266
        filter.type = Files::Filter::Matching;
267
        filter.libraryNames.clear();
268
        filter.libraryNames.push_back(libraryName);
269
        break;
270
    }
271

    
272
    case Enumeration::InLibraries:
273
        filter.type = Files::Filter::Matching;
274
        filter.libraryNames = enumeration.libraryNames;
275
        break;
276

    
277
    case Enumeration::NotInLibraries:
278
        filter.type = Files::Filter::NotMatching;
279
        filter.libraryNames = enumeration.libraryNames;
280
        break;
281
    }
282
        
283
    return Files::listLibraryFilesMatching(filter);
284
}
285

    
286
vector<PluginLoader::PluginKey>
287
PluginLoader::Impl::enumeratePlugins(Enumeration enumeration)
288
{
289
    string libraryName, identifier;
290
    if (enumeration.type == Enumeration::SinglePlugin) {
291
        decomposePluginKey(enumeration.key, libraryName, identifier);
292
    }
293
    
294
    vector<string> fullPaths = listLibraryFilesFor(enumeration);
295

    
296
    // For these we should warn if a plugin can be loaded from a library
297
    bool specific = (enumeration.type == Enumeration::SinglePlugin ||
298
                     enumeration.type == Enumeration::InLibraries);
299

    
300
    vector<PluginKey> added;
301
    
302
    for (size_t i = 0; i < fullPaths.size(); ++i) {
303

    
304
        string fullPath = fullPaths[i];
305
        void *handle = Files::loadLibrary(fullPath);
306
        if (!handle) continue;
307
            
308
        VampGetPluginDescriptorFunction fn =
309
            (VampGetPluginDescriptorFunction)Files::lookupInLibrary
310
            (handle, "vampGetPluginDescriptor");
311
            
312
        if (!fn) {
313
            if (specific) {
314
                cerr << "Vamp::HostExt::PluginLoader: "
315
                    << "No vampGetPluginDescriptor function found in library \""
316
                     << fullPath << "\"" << endl;
317
            }
318
            Files::unloadLibrary(handle);
319
            continue;
320
        }
321
            
322
        int index = 0;
323
        const VampPluginDescriptor *descriptor = 0;
324
        bool found = false;
325
            
326
        while ((descriptor = fn(VAMP_API_VERSION, index))) {
327
            ++index;
328
            if (identifier != "") {
329
                if (descriptor->identifier != identifier) {
330
                    continue;
331
                }
332
            }
333
            found = true;
334
            PluginKey key = composePluginKey(fullPath, descriptor->identifier);
335
            if (m_pluginLibraryNameMap.find(key) ==
336
                m_pluginLibraryNameMap.end()) {
337
                m_pluginLibraryNameMap[key] = fullPath;
338
            }
339
            added.push_back(key);
340
        }
341

    
342
        if (!found && specific) {
343
            cerr << "Vamp::HostExt::PluginLoader: Plugin \""
344
                 << identifier << "\" not found in library \""
345
                 << fullPath << "\"" << endl;
346
        }
347
            
348
        Files::unloadLibrary(handle);
349
    }
350

    
351
    if (enumeration.type == Enumeration::All) {
352
        m_allPluginsEnumerated = true;
353
    }
354

    
355
    return added;
356
}
357

    
358
PluginLoader::PluginKey
359
PluginLoader::Impl::composePluginKey(string libraryName, string identifier)
360
{
361
    string basename = Files::lcBasename(libraryName);
362
    return basename + ":" + identifier;
363
}
364

    
365
bool
366
PluginLoader::Impl::decomposePluginKey(PluginKey key,
367
                                       string &libraryName,
368
                                       string &identifier)
369
{
370
    string::size_type ki = key.find(':');
371
    if (ki == string::npos) {
372
        return false;
373
    }
374

    
375
    libraryName = key.substr(0, ki);
376
    identifier = key.substr(ki + 1);
377
    return true;
378
}
379

    
380
PluginLoader::PluginCategoryHierarchy
381
PluginLoader::Impl::getPluginCategory(PluginKey plugin)
382
{
383
    if (m_taxonomy.empty()) generateTaxonomy();
384
    if (m_taxonomy.find(plugin) == m_taxonomy.end()) {
385
        return PluginCategoryHierarchy();
386
    }
387
    return m_taxonomy[plugin];
388
}
389

    
390
string
391
PluginLoader::Impl::getLibraryPathForPlugin(PluginKey plugin)
392
{
393
    if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
394
        if (m_allPluginsEnumerated) return "";
395
        Enumeration enumeration;
396
        enumeration.type = Enumeration::SinglePlugin;
397
        enumeration.key = plugin;
398
        enumeratePlugins(enumeration);
399
    }
400
    if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
401
        return "";
402
    }
403
    return m_pluginLibraryNameMap[plugin];
404
}    
405

    
406
Plugin *
407
PluginLoader::Impl::loadPlugin(PluginKey key,
408
                               float inputSampleRate, int adapterFlags)
409
{
410
    string libname, identifier;
411
    if (!decomposePluginKey(key, libname, identifier)) {
412
        std::cerr << "Vamp::HostExt::PluginLoader: Invalid plugin key \""
413
                  << key << "\" in loadPlugin" << std::endl;
414
        return 0;
415
    }
416
        
417
    string fullPath = getLibraryPathForPlugin(key);
418
    if (fullPath == "") {
419
        std::cerr << "Vamp::HostExt::PluginLoader: No library found in Vamp path for plugin \"" << key << "\"" << std::endl;
420
        return 0;
421
    }
422
    
423
    void *handle = Files::loadLibrary(fullPath);
424
    if (!handle) return 0;
425
    
426
    VampGetPluginDescriptorFunction fn =
427
        (VampGetPluginDescriptorFunction)Files::lookupInLibrary
428
        (handle, "vampGetPluginDescriptor");
429

    
430
    if (!fn) {
431
        cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
432
             << fullPath << "\"" << endl;
433
        Files::unloadLibrary(handle);
434
        return 0;
435
    }
436

    
437
    int index = 0;
438
    const VampPluginDescriptor *descriptor = 0;
439

    
440
    while ((descriptor = fn(VAMP_API_VERSION, index))) {
441

    
442
        if (string(descriptor->identifier) == identifier) {
443

    
444
            Vamp::PluginHostAdapter *plugin =
445
                new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
446

    
447
            Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
448

    
449
            m_pluginLibraryHandleMap[adapter] = handle;
450

    
451
            if (adapterFlags & ADAPT_INPUT_DOMAIN) {
452
                if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
453
                    adapter = new PluginInputDomainAdapter(adapter);
454
                }
455
            }
456

    
457
            if (adapterFlags & ADAPT_BUFFER_SIZE) {
458
                adapter = new PluginBufferingAdapter(adapter);
459
            }
460

    
461
            if (adapterFlags & ADAPT_CHANNEL_COUNT) {
462
                adapter = new PluginChannelAdapter(adapter);
463
            }
464

    
465
            return adapter;
466
        }
467

    
468
        ++index;
469
    }
470

    
471
    cerr << "Vamp::HostExt::PluginLoader: Plugin \""
472
         << identifier << "\" not found in library \""
473
         << fullPath << "\"" << endl;
474

    
475
    return 0;
476
}
477

    
478
void
479
PluginLoader::Impl::generateTaxonomy()
480
{
481
//    cerr << "PluginLoader::Impl::generateTaxonomy" << endl;
482

    
483
    vector<string> path = PluginHostAdapter::getPluginPath();
484
    string libfragment = "/lib/";
485
    vector<string> catpath;
486

    
487
    string suffix = "cat";
488

    
489
    for (vector<string>::iterator i = path.begin();
490
         i != path.end(); ++i) {
491

    
492
        // It doesn't matter that we're using literal forward-slash in
493
        // this bit, as it's only relevant if the path contains
494
        // "/lib/", which is only meaningful and only plausible on
495
        // systems with forward-slash delimiters
496
        
497
        string dir = *i;
498
        string::size_type li = dir.find(libfragment);
499

    
500
        if (li != string::npos) {
501
            catpath.push_back
502
                (dir.substr(0, li)
503
                 + "/share/"
504
                 + dir.substr(li + libfragment.length()));
505
        }
506

    
507
        catpath.push_back(dir);
508
    }
509

    
510
    char buffer[1024];
511

    
512
    for (vector<string>::iterator i = catpath.begin();
513
         i != catpath.end(); ++i) {
514
        
515
        vector<string> files = Files::listFiles(*i, suffix);
516

    
517
        for (vector<string>::iterator fi = files.begin();
518
             fi != files.end(); ++fi) {
519

    
520
            string filepath = Files::splicePath(*i, *fi);
521
            ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
522

    
523
            if (is.fail()) {
524
//                cerr << "failed to open: " << filepath << endl;
525
                continue;
526
            }
527

    
528
//            cerr << "opened: " << filepath << endl;
529

    
530
            while (!!is.getline(buffer, 1024)) {
531

    
532
                string line(buffer);
533

    
534
//                cerr << "line = " << line << endl;
535

    
536
                string::size_type di = line.find("::");
537
                if (di == string::npos) continue;
538

    
539
                string id = line.substr(0, di);
540
                string encodedCat = line.substr(di + 2);
541

    
542
                if (id.substr(0, 5) != "vamp:") continue;
543
                id = id.substr(5);
544

    
545
                while (encodedCat.length() >= 1 &&
546
                       encodedCat[encodedCat.length()-1] == '\r') {
547
                    encodedCat = encodedCat.substr(0, encodedCat.length()-1);
548
                }
549

    
550
//                cerr << "id = " << id << ", cat = " << encodedCat << endl;
551

    
552
                PluginCategoryHierarchy category;
553
                string::size_type ai;
554
                while ((ai = encodedCat.find(" > ")) != string::npos) {
555
                    category.push_back(encodedCat.substr(0, ai));
556
                    encodedCat = encodedCat.substr(ai + 3);
557
                }
558
                if (encodedCat != "") category.push_back(encodedCat);
559

    
560
                m_taxonomy[id] = category;
561
            }
562
        }
563
    }
564
}    
565

    
566
void
567
PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
568
{
569
    void *handle = m_pluginLibraryHandleMap[adapter];
570
    if (!handle) return;
571

    
572
    m_pluginLibraryHandleMap.erase(adapter);
573

    
574
    for (auto h: m_pluginLibraryHandleMap) {
575
        if (h.second == handle) {
576
            // still in use
577
            return;
578
        }
579
    }
580
    
581
    Files::unloadLibrary(handle);
582
}
583

    
584
PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
585
                                                                             Impl *loader) :
586
    PluginWrapper(plugin),
587
    m_loader(loader)
588
{
589
}
590

    
591
PluginLoader::Impl::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
592
{
593
    // We need to delete the plugin before calling pluginDeleted, as
594
    // the delete call may require calling through to the descriptor
595
    // (for e.g. cleanup) but pluginDeleted may unload the required
596
    // library for the call.  To prevent a double deletion when our
597
    // parent's destructor runs (after this one), be sure to set
598
    // m_plugin to 0 after deletion.
599
    delete m_plugin;
600
    m_plugin = 0;
601

    
602
    if (m_loader) m_loader->pluginDeleted(this);
603
}
604

    
605
}
606

    
607
}
608

    
609
_VAMP_SDK_HOSTSPACE_END(PluginLoader.cpp)
610