Mercurial > hg > svcore
comparison plugin/NativeVampPluginFactory.cpp @ 1230:9ae2ce9190e6 project-file-rework
Merge
author | Chris Cannam |
---|---|
date | Tue, 25 Oct 2016 11:04:26 +0100 |
parents | 5d886b7b4029 |
children | 75aefcc9f07d |
comparison
equal
deleted
inserted
replaced
1228:a2091d148d7f | 1230:9ae2ce9190e6 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 This file copyright 2006-2016 Chris Cannam and QMUL. | |
8 | |
9 This program is free software; you can redistribute it and/or | |
10 modify it under the terms of the GNU General Public License as | |
11 published by the Free Software Foundation; either version 2 of the | |
12 License, or (at your option) any later version. See the file | |
13 COPYING included with this distribution for more information. | |
14 */ | |
15 | |
16 #include "NativeVampPluginFactory.h" | |
17 #include "PluginIdentifier.h" | |
18 | |
19 #include <vamp-hostsdk/PluginHostAdapter.h> | |
20 #include <vamp-hostsdk/PluginWrapper.h> | |
21 | |
22 #include "system/System.h" | |
23 | |
24 #include "PluginScan.h" | |
25 | |
26 #include <QDir> | |
27 #include <QFile> | |
28 #include <QFileInfo> | |
29 #include <QTextStream> | |
30 | |
31 #include <iostream> | |
32 | |
33 #include "base/Profiler.h" | |
34 | |
35 #include <QMutex> | |
36 #include <QMutexLocker> | |
37 | |
38 using namespace std; | |
39 | |
40 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 | |
41 | |
42 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper { | |
43 public: | |
44 PluginDeletionNotifyAdapter(Vamp::Plugin *plugin, | |
45 NativeVampPluginFactory *factory) : | |
46 PluginWrapper(plugin), m_factory(factory) { } | |
47 virtual ~PluginDeletionNotifyAdapter(); | |
48 protected: | |
49 NativeVampPluginFactory *m_factory; | |
50 }; | |
51 | |
52 PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter() | |
53 { | |
54 // see notes in vamp-sdk/hostext/PluginLoader.cpp from which this is drawn | |
55 Vamp::Plugin *p = m_plugin; | |
56 delete m_plugin; | |
57 m_plugin = 0; | |
58 // acceptable use after free here, as pluginDeleted uses p only as | |
59 // pointer key and does not deref it | |
60 if (m_factory) m_factory->pluginDeleted(p); | |
61 } | |
62 | |
63 vector<QString> | |
64 NativeVampPluginFactory::getPluginPath() | |
65 { | |
66 if (!m_pluginPath.empty()) return m_pluginPath; | |
67 | |
68 vector<string> p = Vamp::PluginHostAdapter::getPluginPath(); | |
69 for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str()); | |
70 return m_pluginPath; | |
71 } | |
72 | |
73 vector<QString> | |
74 NativeVampPluginFactory::getPluginIdentifiers(QString &) | |
75 { | |
76 Profiler profiler("NativeVampPluginFactory::getPluginIdentifiers"); | |
77 | |
78 QMutexLocker locker(&m_mutex); | |
79 | |
80 if (!m_identifiers.empty()) { | |
81 return m_identifiers; | |
82 } | |
83 | |
84 QStringList candidates = PluginScan::getInstance()->getCandidateLibrariesFor | |
85 (PluginScan::VampPlugin); | |
86 | |
87 for (QString soname : candidates) { | |
88 | |
89 void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); | |
90 | |
91 if (!libraryHandle) { | |
92 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; | |
93 continue; | |
94 } | |
95 | |
96 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) | |
97 DLSYM(libraryHandle, "vampGetPluginDescriptor"); | |
98 | |
99 if (!fn) { | |
100 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; | |
101 if (DLCLOSE(libraryHandle) != 0) { | |
102 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; | |
103 } | |
104 continue; | |
105 } | |
106 | |
107 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
108 cerr << "NativeVampPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl; | |
109 #endif | |
110 | |
111 const VampPluginDescriptor *descriptor = 0; | |
112 int index = 0; | |
113 | |
114 map<string, int> known; | |
115 bool ok = true; | |
116 | |
117 while ((descriptor = fn(VAMP_API_VERSION, index))) { | |
118 | |
119 if (known.find(descriptor->identifier) != known.end()) { | |
120 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Plugin library " | |
121 << soname | |
122 << " returns the same plugin identifier \"" | |
123 << descriptor->identifier << "\" at indices " | |
124 << known[descriptor->identifier] << " and " | |
125 << index << endl; | |
126 cerr << "NativeVampPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; | |
127 ok = false; | |
128 break; | |
129 } else { | |
130 known[descriptor->identifier] = index; | |
131 } | |
132 | |
133 ++index; | |
134 } | |
135 | |
136 if (ok) { | |
137 | |
138 index = 0; | |
139 | |
140 while ((descriptor = fn(VAMP_API_VERSION, index))) { | |
141 | |
142 QString id = PluginIdentifier::createIdentifier | |
143 ("vamp", soname, descriptor->identifier); | |
144 m_identifiers.push_back(id); | |
145 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
146 cerr << "NativeVampPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; | |
147 #endif | |
148 ++index; | |
149 } | |
150 } | |
151 | |
152 if (DLCLOSE(libraryHandle) != 0) { | |
153 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; | |
154 } | |
155 } | |
156 | |
157 generateTaxonomy(); | |
158 | |
159 // Plugins can change the locale, revert it to default. | |
160 RestoreStartupLocale(); | |
161 | |
162 return m_identifiers; | |
163 } | |
164 | |
165 QString | |
166 NativeVampPluginFactory::findPluginFile(QString soname, QString inDir) | |
167 { | |
168 QString file = ""; | |
169 | |
170 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
171 cerr << "NativeVampPluginFactory::findPluginFile(\"" | |
172 << soname << "\", \"" << inDir << "\")" | |
173 << endl; | |
174 #endif | |
175 | |
176 if (inDir != "") { | |
177 | |
178 QDir dir(inDir, PLUGIN_GLOB, | |
179 QDir::Name | QDir::IgnoreCase, | |
180 QDir::Files | QDir::Readable); | |
181 if (!dir.exists()) return ""; | |
182 | |
183 file = dir.filePath(QFileInfo(soname).fileName()); | |
184 | |
185 if (QFileInfo(file).exists() && QFileInfo(file).isFile()) { | |
186 | |
187 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
188 cerr << "NativeVampPluginFactory::findPluginFile: " | |
189 << "found trivially at " << file << endl; | |
190 #endif | |
191 | |
192 return file; | |
193 } | |
194 | |
195 for (unsigned int j = 0; j < dir.count(); ++j) { | |
196 file = dir.filePath(dir[j]); | |
197 if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) { | |
198 | |
199 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
200 cerr << "NativeVampPluginFactory::findPluginFile: " | |
201 << "found \"" << soname << "\" at " << file << endl; | |
202 #endif | |
203 | |
204 return file; | |
205 } | |
206 } | |
207 | |
208 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
209 cerr << "NativeVampPluginFactory::findPluginFile (with dir): " | |
210 << "not found" << endl; | |
211 #endif | |
212 | |
213 return ""; | |
214 | |
215 } else { | |
216 | |
217 QFileInfo fi(soname); | |
218 | |
219 if (fi.isAbsolute() && fi.exists() && fi.isFile()) { | |
220 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
221 cerr << "NativeVampPluginFactory::findPluginFile: " | |
222 << "found trivially at " << soname << endl; | |
223 #endif | |
224 return soname; | |
225 } | |
226 | |
227 if (fi.isAbsolute() && fi.absolutePath() != "") { | |
228 file = findPluginFile(soname, fi.absolutePath()); | |
229 if (file != "") return file; | |
230 } | |
231 | |
232 vector<QString> path = getPluginPath(); | |
233 for (vector<QString>::iterator i = path.begin(); | |
234 i != path.end(); ++i) { | |
235 if (*i != "") { | |
236 file = findPluginFile(soname, *i); | |
237 if (file != "") return file; | |
238 } | |
239 } | |
240 | |
241 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
242 cerr << "NativeVampPluginFactory::findPluginFile: " | |
243 << "not found" << endl; | |
244 #endif | |
245 | |
246 return ""; | |
247 } | |
248 } | |
249 | |
250 Vamp::Plugin * | |
251 NativeVampPluginFactory::instantiatePlugin(QString identifier, | |
252 sv_samplerate_t inputSampleRate) | |
253 { | |
254 Profiler profiler("NativeVampPluginFactory::instantiatePlugin"); | |
255 | |
256 Vamp::Plugin *rv = 0; | |
257 Vamp::PluginHostAdapter *plugin = 0; | |
258 | |
259 const VampPluginDescriptor *descriptor = 0; | |
260 int index = 0; | |
261 | |
262 QString type, soname, label; | |
263 PluginIdentifier::parseIdentifier(identifier, type, soname, label); | |
264 if (type != "vamp") { | |
265 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
266 cerr << "NativeVampPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; | |
267 #endif | |
268 return 0; | |
269 } | |
270 | |
271 QString found = findPluginFile(soname); | |
272 | |
273 if (found == "") { | |
274 cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to find library file " << soname << endl; | |
275 return 0; | |
276 } else if (found != soname) { | |
277 | |
278 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
279 cerr << "NativeVampPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl; | |
280 cerr << soname << " -> " << found << endl; | |
281 #endif | |
282 | |
283 } | |
284 | |
285 soname = found; | |
286 | |
287 void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); | |
288 | |
289 if (!libraryHandle) { | |
290 cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to load library " << soname << ": " << DLERROR() << endl; | |
291 return 0; | |
292 } | |
293 | |
294 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) | |
295 DLSYM(libraryHandle, "vampGetPluginDescriptor"); | |
296 | |
297 if (!fn) { | |
298 cerr << "NativeVampPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl; | |
299 goto done; | |
300 } | |
301 | |
302 while ((descriptor = fn(VAMP_API_VERSION, index))) { | |
303 if (label == descriptor->identifier) break; | |
304 ++index; | |
305 } | |
306 | |
307 if (!descriptor) { | |
308 cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to find plugin \"" << label << "\" in library " << soname << endl; | |
309 goto done; | |
310 } | |
311 | |
312 plugin = new Vamp::PluginHostAdapter(descriptor, float(inputSampleRate)); | |
313 | |
314 if (plugin) { | |
315 m_handleMap[plugin] = libraryHandle; | |
316 rv = new PluginDeletionNotifyAdapter(plugin, this); | |
317 } | |
318 | |
319 // SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << endl; | |
320 | |
321 //!!! need to dlclose() when plugins from a given library are unloaded | |
322 | |
323 done: | |
324 if (!rv) { | |
325 if (DLCLOSE(libraryHandle) != 0) { | |
326 cerr << "WARNING: NativeVampPluginFactory::instantiatePlugin: Failed to unload library " << soname << endl; | |
327 } | |
328 } | |
329 | |
330 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
331 cerr << "NativeVampPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl; | |
332 #endif | |
333 | |
334 return rv; | |
335 } | |
336 | |
337 void | |
338 NativeVampPluginFactory::pluginDeleted(Vamp::Plugin *plugin) | |
339 { | |
340 void *handle = m_handleMap[plugin]; | |
341 if (handle) { | |
342 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE | |
343 cerr << "unloading library " << handle << " for plugin " << plugin << endl; | |
344 #endif | |
345 DLCLOSE(handle); | |
346 } | |
347 m_handleMap.erase(plugin); | |
348 } | |
349 | |
350 QString | |
351 NativeVampPluginFactory::getPluginCategory(QString identifier) | |
352 { | |
353 return m_taxonomy[identifier]; | |
354 } | |
355 | |
356 void | |
357 NativeVampPluginFactory::generateTaxonomy() | |
358 { | |
359 vector<QString> pluginPath = getPluginPath(); | |
360 vector<QString> path; | |
361 | |
362 for (size_t i = 0; i < pluginPath.size(); ++i) { | |
363 if (pluginPath[i].contains("/lib/")) { | |
364 QString p(pluginPath[i]); | |
365 path.push_back(p); | |
366 p.replace("/lib/", "/share/"); | |
367 path.push_back(p); | |
368 } | |
369 path.push_back(pluginPath[i]); | |
370 } | |
371 | |
372 for (size_t i = 0; i < path.size(); ++i) { | |
373 | |
374 QDir dir(path[i], "*.cat"); | |
375 | |
376 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl; | |
377 for (unsigned int j = 0; j < dir.count(); ++j) { | |
378 | |
379 QFile file(path[i] + "/" + dir[j]); | |
380 | |
381 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl; | |
382 | |
383 if (file.open(QIODevice::ReadOnly)) { | |
384 // cerr << "...opened" << endl; | |
385 QTextStream stream(&file); | |
386 QString line; | |
387 | |
388 while (!stream.atEnd()) { | |
389 line = stream.readLine(); | |
390 // cerr << "line is: \"" << line << "\"" << endl; | |
391 QString id = PluginIdentifier::canonicalise | |
392 (line.section("::", 0, 0)); | |
393 QString cat = line.section("::", 1, 1); | |
394 m_taxonomy[id] = cat; | |
395 // cerr << "NativeVampPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl; | |
396 } | |
397 } | |
398 } | |
399 } | |
400 } | |
401 | |
402 piper_vamp::PluginStaticData | |
403 NativeVampPluginFactory::getPluginStaticData(QString identifier) | |
404 { | |
405 QMutexLocker locker(&m_mutex); | |
406 | |
407 if (m_pluginData.find(identifier) != m_pluginData.end()) { | |
408 return m_pluginData[identifier]; | |
409 } | |
410 | |
411 QString type, soname, label; | |
412 PluginIdentifier::parseIdentifier(identifier, type, soname, label); | |
413 std::string pluginKey = (soname + ":" + label).toStdString(); | |
414 | |
415 std::vector<std::string> catlist; | |
416 for (auto s: getPluginCategory(identifier).split(" > ")) { | |
417 catlist.push_back(s.toStdString()); | |
418 } | |
419 | |
420 Vamp::Plugin *p = instantiatePlugin(identifier, 44100); | |
421 if (!p) return {}; | |
422 | |
423 auto psd = piper_vamp::PluginStaticData::fromPlugin(pluginKey, | |
424 catlist, | |
425 p); | |
426 | |
427 delete p; | |
428 | |
429 m_pluginData[identifier] = psd; | |
430 return psd; | |
431 } | |
432 |