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