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