annotate plugin/NativeVampPluginFactory.cpp @ 1394:9ef1cc26024c

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