comparison src/vamp-hostsdk/PluginLoader.cpp @ 233:521734d2b498 distinct-libraries

* Flatten directory tree a bit, update doxygen
author cannam
date Fri, 07 Nov 2008 15:28:33 +0000
parents
children 4454843ff384
comparison
equal deleted inserted replaced
232:71ea10a3cbe7 233:521734d2b498
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-2007 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/PluginHostAdapter.h>
38 #include <vamp-hostsdk/PluginLoader.h>
39 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
40 #include <vamp-hostsdk/PluginChannelAdapter.h>
41 #include <vamp-hostsdk/PluginBufferingAdapter.h>
42
43 #include <fstream>
44 #include <cctype> // tolower
45
46 #include <cstring>
47
48 #ifdef _WIN32
49
50 #include <windows.h>
51 #include <tchar.h>
52 #define PLUGIN_SUFFIX "dll"
53
54 #else /* ! _WIN32 */
55
56 #include <dirent.h>
57 #include <dlfcn.h>
58
59 #ifdef __APPLE__
60 #define PLUGIN_SUFFIX "dylib"
61 #else /* ! __APPLE__ */
62 #define PLUGIN_SUFFIX "so"
63 #endif /* ! __APPLE__ */
64
65 #endif /* ! _WIN32 */
66
67 using namespace std;
68
69 namespace Vamp {
70
71 namespace HostExt {
72
73 class PluginLoader::Impl
74 {
75 public:
76 Impl();
77 virtual ~Impl();
78
79 PluginKeyList listPlugins();
80
81 Plugin *loadPlugin(PluginKey key,
82 float inputSampleRate,
83 int adapterFlags);
84
85 PluginKey composePluginKey(string libraryName, string identifier);
86
87 PluginCategoryHierarchy getPluginCategory(PluginKey key);
88
89 string getLibraryPathForPlugin(PluginKey key);
90
91 static void setInstanceToClean(PluginLoader *instance);
92
93 protected:
94 class PluginDeletionNotifyAdapter : public PluginWrapper {
95 public:
96 PluginDeletionNotifyAdapter(Plugin *plugin, Impl *loader);
97 virtual ~PluginDeletionNotifyAdapter();
98 protected:
99 Impl *m_loader;
100 };
101
102 class InstanceCleaner {
103 public:
104 InstanceCleaner() : m_instance(0) { }
105 ~InstanceCleaner() { delete m_instance; }
106 void setInstance(PluginLoader *instance) { m_instance = instance; }
107 protected:
108 PluginLoader *m_instance;
109 };
110
111 virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
112
113 map<PluginKey, string> m_pluginLibraryNameMap;
114 bool m_allPluginsEnumerated;
115 void enumeratePlugins(PluginKey forPlugin = "");
116
117 map<PluginKey, PluginCategoryHierarchy> m_taxonomy;
118 void generateTaxonomy();
119
120 map<Plugin *, void *> m_pluginLibraryHandleMap;
121
122 bool decomposePluginKey(PluginKey key,
123 string &libraryName, string &identifier);
124
125 void *loadLibrary(string path);
126 void unloadLibrary(void *handle);
127 void *lookupInLibrary(void *handle, const char *symbol);
128
129 string splicePath(string a, string b);
130 vector<string> listFiles(string dir, string ext);
131
132 static InstanceCleaner m_cleaner;
133 };
134
135 PluginLoader *
136 PluginLoader::m_instance = 0;
137
138 PluginLoader::Impl::InstanceCleaner
139 PluginLoader::Impl::m_cleaner;
140
141 PluginLoader::PluginLoader()
142 {
143 m_impl = new Impl();
144 }
145
146 PluginLoader::~PluginLoader()
147 {
148 delete m_impl;
149 }
150
151 PluginLoader *
152 PluginLoader::getInstance()
153 {
154 if (!m_instance) {
155 // The cleaner doesn't own the instance, because we leave the
156 // instance pointer in the base class for binary backwards
157 // compatibility reasons and to avoid waste
158 m_instance = new PluginLoader();
159 Impl::setInstanceToClean(m_instance);
160 }
161 return m_instance;
162 }
163
164 vector<PluginLoader::PluginKey>
165 PluginLoader::listPlugins()
166 {
167 return m_impl->listPlugins();
168 }
169
170 Plugin *
171 PluginLoader::loadPlugin(PluginKey key,
172 float inputSampleRate,
173 int adapterFlags)
174 {
175 return m_impl->loadPlugin(key, inputSampleRate, adapterFlags);
176 }
177
178 PluginLoader::PluginKey
179 PluginLoader::composePluginKey(string libraryName, string identifier)
180 {
181 return m_impl->composePluginKey(libraryName, identifier);
182 }
183
184 PluginLoader::PluginCategoryHierarchy
185 PluginLoader::getPluginCategory(PluginKey key)
186 {
187 return m_impl->getPluginCategory(key);
188 }
189
190 string
191 PluginLoader::getLibraryPathForPlugin(PluginKey key)
192 {
193 return m_impl->getLibraryPathForPlugin(key);
194 }
195
196 PluginLoader::Impl::Impl() :
197 m_allPluginsEnumerated(false)
198 {
199 }
200
201 PluginLoader::Impl::~Impl()
202 {
203 }
204
205 void
206 PluginLoader::Impl::setInstanceToClean(PluginLoader *instance)
207 {
208 m_cleaner.setInstance(instance);
209 }
210
211 vector<PluginLoader::PluginKey>
212 PluginLoader::Impl::listPlugins()
213 {
214 if (!m_allPluginsEnumerated) enumeratePlugins();
215
216 vector<PluginKey> plugins;
217 for (map<PluginKey, string>::iterator mi = m_pluginLibraryNameMap.begin();
218 mi != m_pluginLibraryNameMap.end(); ++mi) {
219 plugins.push_back(mi->first);
220 }
221
222 return plugins;
223 }
224
225 void
226 PluginLoader::Impl::enumeratePlugins(PluginKey forPlugin)
227 {
228 vector<string> path = PluginHostAdapter::getPluginPath();
229
230 string libraryName, identifier;
231 if (forPlugin != "") {
232 if (!decomposePluginKey(forPlugin, libraryName, identifier)) {
233 std::cerr << "WARNING: Vamp::HostExt::PluginLoader: Invalid plugin key \""
234 << forPlugin << "\" in enumerate" << std::endl;
235 return;
236 }
237 }
238
239 for (size_t i = 0; i < path.size(); ++i) {
240
241 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
242
243 for (vector<string>::iterator fi = files.begin();
244 fi != files.end(); ++fi) {
245
246 if (libraryName != "") {
247 // libraryName is lowercased and lacking an extension,
248 // as it came from the plugin key
249 string temp = *fi;
250 for (size_t i = 0; i < temp.length(); ++i) {
251 temp[i] = tolower(temp[i]);
252 }
253 string::size_type pi = temp.find('.');
254 if (pi == string::npos) {
255 if (libraryName != temp) continue;
256 } else {
257 if (libraryName != temp.substr(0, pi)) continue;
258 }
259 }
260
261 string fullPath = path[i];
262 fullPath = splicePath(fullPath, *fi);
263 void *handle = loadLibrary(fullPath);
264 if (!handle) continue;
265
266 VampGetPluginDescriptorFunction fn =
267 (VampGetPluginDescriptorFunction)lookupInLibrary
268 (handle, "vampGetPluginDescriptor");
269
270 if (!fn) {
271 unloadLibrary(handle);
272 continue;
273 }
274
275 int index = 0;
276 const VampPluginDescriptor *descriptor = 0;
277
278 while ((descriptor = fn(VAMP_API_VERSION, index))) {
279 ++index;
280 if (identifier != "") {
281 if (descriptor->identifier != identifier) continue;
282 }
283 PluginKey key = composePluginKey(*fi, descriptor->identifier);
284 // std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
285 if (m_pluginLibraryNameMap.find(key) ==
286 m_pluginLibraryNameMap.end()) {
287 m_pluginLibraryNameMap[key] = fullPath;
288 }
289 }
290
291 unloadLibrary(handle);
292 }
293 }
294
295 if (forPlugin == "") m_allPluginsEnumerated = true;
296 }
297
298 PluginLoader::PluginKey
299 PluginLoader::Impl::composePluginKey(string libraryName, string identifier)
300 {
301 string basename = libraryName;
302
303 string::size_type li = basename.rfind('/');
304 if (li != string::npos) basename = basename.substr(li + 1);
305
306 li = basename.find('.');
307 if (li != string::npos) basename = basename.substr(0, li);
308
309 for (size_t i = 0; i < basename.length(); ++i) {
310 basename[i] = tolower(basename[i]);
311 }
312
313 return basename + ":" + identifier;
314 }
315
316 bool
317 PluginLoader::Impl::decomposePluginKey(PluginKey key,
318 string &libraryName,
319 string &identifier)
320 {
321 string::size_type ki = key.find(':');
322 if (ki == string::npos) {
323 return false;
324 }
325
326 libraryName = key.substr(0, ki);
327 identifier = key.substr(ki + 1);
328 return true;
329 }
330
331 PluginLoader::PluginCategoryHierarchy
332 PluginLoader::Impl::getPluginCategory(PluginKey plugin)
333 {
334 if (m_taxonomy.empty()) generateTaxonomy();
335 if (m_taxonomy.find(plugin) == m_taxonomy.end()) {
336 return PluginCategoryHierarchy();
337 }
338 return m_taxonomy[plugin];
339 }
340
341 string
342 PluginLoader::Impl::getLibraryPathForPlugin(PluginKey plugin)
343 {
344 if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
345 if (m_allPluginsEnumerated) return "";
346 enumeratePlugins(plugin);
347 }
348 if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
349 return "";
350 }
351 return m_pluginLibraryNameMap[plugin];
352 }
353
354 Plugin *
355 PluginLoader::Impl::loadPlugin(PluginKey key,
356 float inputSampleRate, int adapterFlags)
357 {
358 string libname, identifier;
359 if (!decomposePluginKey(key, libname, identifier)) {
360 std::cerr << "Vamp::HostExt::PluginLoader: Invalid plugin key \""
361 << key << "\" in loadPlugin" << std::endl;
362 return 0;
363 }
364
365 string fullPath = getLibraryPathForPlugin(key);
366 if (fullPath == "") return 0;
367
368 void *handle = loadLibrary(fullPath);
369 if (!handle) return 0;
370
371 VampGetPluginDescriptorFunction fn =
372 (VampGetPluginDescriptorFunction)lookupInLibrary
373 (handle, "vampGetPluginDescriptor");
374
375 if (!fn) {
376 unloadLibrary(handle);
377 return 0;
378 }
379
380 int index = 0;
381 const VampPluginDescriptor *descriptor = 0;
382
383 while ((descriptor = fn(VAMP_API_VERSION, index))) {
384
385 if (string(descriptor->identifier) == identifier) {
386
387 Vamp::PluginHostAdapter *plugin =
388 new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
389
390 Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
391
392 m_pluginLibraryHandleMap[adapter] = handle;
393
394 if (adapterFlags & ADAPT_INPUT_DOMAIN) {
395 if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
396 adapter = new PluginInputDomainAdapter(adapter);
397 }
398 }
399
400 if (adapterFlags & ADAPT_BUFFER_SIZE) {
401 adapter = new PluginBufferingAdapter(adapter);
402 }
403
404 if (adapterFlags & ADAPT_CHANNEL_COUNT) {
405 adapter = new PluginChannelAdapter(adapter);
406 }
407
408 return adapter;
409 }
410
411 ++index;
412 }
413
414 cerr << "Vamp::HostExt::PluginLoader: Plugin \""
415 << identifier << "\" not found in library \""
416 << fullPath << "\"" << endl;
417
418 return 0;
419 }
420
421 void
422 PluginLoader::Impl::generateTaxonomy()
423 {
424 // cerr << "PluginLoader::Impl::generateTaxonomy" << endl;
425
426 vector<string> path = PluginHostAdapter::getPluginPath();
427 string libfragment = "/lib/";
428 vector<string> catpath;
429
430 string suffix = "cat";
431
432 for (vector<string>::iterator i = path.begin();
433 i != path.end(); ++i) {
434
435 // It doesn't matter that we're using literal forward-slash in
436 // this bit, as it's only relevant if the path contains
437 // "/lib/", which is only meaningful and only plausible on
438 // systems with forward-slash delimiters
439
440 string dir = *i;
441 string::size_type li = dir.find(libfragment);
442
443 if (li != string::npos) {
444 catpath.push_back
445 (dir.substr(0, li)
446 + "/share/"
447 + dir.substr(li + libfragment.length()));
448 }
449
450 catpath.push_back(dir);
451 }
452
453 char buffer[1024];
454
455 for (vector<string>::iterator i = catpath.begin();
456 i != catpath.end(); ++i) {
457
458 vector<string> files = listFiles(*i, suffix);
459
460 for (vector<string>::iterator fi = files.begin();
461 fi != files.end(); ++fi) {
462
463 string filepath = splicePath(*i, *fi);
464 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
465
466 if (is.fail()) {
467 // cerr << "failed to open: " << filepath << endl;
468 continue;
469 }
470
471 // cerr << "opened: " << filepath << endl;
472
473 while (!!is.getline(buffer, 1024)) {
474
475 string line(buffer);
476
477 // cerr << "line = " << line << endl;
478
479 string::size_type di = line.find("::");
480 if (di == string::npos) continue;
481
482 string id = line.substr(0, di);
483 string encodedCat = line.substr(di + 2);
484
485 if (id.substr(0, 5) != "vamp:") continue;
486 id = id.substr(5);
487
488 while (encodedCat.length() >= 1 &&
489 encodedCat[encodedCat.length()-1] == '\r') {
490 encodedCat = encodedCat.substr(0, encodedCat.length()-1);
491 }
492
493 // cerr << "id = " << id << ", cat = " << encodedCat << endl;
494
495 PluginCategoryHierarchy category;
496 string::size_type ai;
497 while ((ai = encodedCat.find(" > ")) != string::npos) {
498 category.push_back(encodedCat.substr(0, ai));
499 encodedCat = encodedCat.substr(ai + 3);
500 }
501 if (encodedCat != "") category.push_back(encodedCat);
502
503 m_taxonomy[id] = category;
504 }
505 }
506 }
507 }
508
509 void *
510 PluginLoader::Impl::loadLibrary(string path)
511 {
512 void *handle = 0;
513 #ifdef _WIN32
514 handle = LoadLibrary(path.c_str());
515 if (!handle) {
516 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
517 << path << "\"" << endl;
518 }
519 #else
520 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
521 if (!handle) {
522 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
523 << path << "\": " << dlerror() << endl;
524 }
525 #endif
526 return handle;
527 }
528
529 void
530 PluginLoader::Impl::unloadLibrary(void *handle)
531 {
532 #ifdef _WIN32
533 FreeLibrary((HINSTANCE)handle);
534 #else
535 dlclose(handle);
536 #endif
537 }
538
539 void *
540 PluginLoader::Impl::lookupInLibrary(void *handle, const char *symbol)
541 {
542 #ifdef _WIN32
543 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
544 #else
545 return (void *)dlsym(handle, symbol);
546 #endif
547 }
548
549 string
550 PluginLoader::Impl::splicePath(string a, string b)
551 {
552 #ifdef _WIN32
553 return a + "\\" + b;
554 #else
555 return a + "/" + b;
556 #endif
557 }
558
559 vector<string>
560 PluginLoader::Impl::listFiles(string dir, string extension)
561 {
562 vector<string> files;
563
564 #ifdef _WIN32
565
566 string expression = dir + "\\*." + extension;
567 WIN32_FIND_DATA data;
568 HANDLE fh = FindFirstFile(expression.c_str(), &data);
569 if (fh == INVALID_HANDLE_VALUE) return files;
570
571 bool ok = true;
572 while (ok) {
573 files.push_back(data.cFileName);
574 ok = FindNextFile(fh, &data);
575 }
576
577 FindClose(fh);
578
579 #else
580
581 size_t extlen = extension.length();
582 DIR *d = opendir(dir.c_str());
583 if (!d) return files;
584
585 struct dirent *e = 0;
586 while ((e = readdir(d))) {
587
588 if (!e->d_name) continue;
589
590 size_t len = strlen(e->d_name);
591 if (len < extlen + 2 ||
592 e->d_name + len - extlen - 1 != "." + extension) {
593 continue;
594 }
595
596 files.push_back(e->d_name);
597 }
598
599 closedir(d);
600 #endif
601
602 return files;
603 }
604
605 void
606 PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
607 {
608 void *handle = m_pluginLibraryHandleMap[adapter];
609 if (handle) unloadLibrary(handle);
610 m_pluginLibraryHandleMap.erase(adapter);
611 }
612
613 PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
614 Impl *loader) :
615 PluginWrapper(plugin),
616 m_loader(loader)
617 {
618 }
619
620 PluginLoader::Impl::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
621 {
622 // We need to delete the plugin before calling pluginDeleted, as
623 // the delete call may require calling through to the descriptor
624 // (for e.g. cleanup) but pluginDeleted may unload the required
625 // library for the call. To prevent a double deletion when our
626 // parent's destructor runs (after this one), be sure to set
627 // m_plugin to 0 after deletion.
628 delete m_plugin;
629 m_plugin = 0;
630
631 if (m_loader) m_loader->pluginDeleted(this);
632 }
633
634 }
635
636 }