annotate plugin/PluginScan.cpp @ 1520:954d0cf29ca7 import-audio-data

Switch the normalisation option in WritableWaveFileModel from normalising on read to normalising on write, so that the saved file is already normalised and therefore can be read again without having to remember to normalise it
author Chris Cannam
date Wed, 12 Sep 2018 13:56:56 +0100
parents d7fdc77252c6
children 830972646ccd
rev   line source
Chris@1178 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1178 2
Chris@1178 3 /*
Chris@1178 4 Sonic Visualiser
Chris@1178 5 An audio file viewer and annotation editor.
Chris@1178 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1178 7
Chris@1178 8 This program is free software; you can redistribute it and/or
Chris@1178 9 modify it under the terms of the GNU General Public License as
Chris@1178 10 published by the Free Software Foundation; either version 2 of the
Chris@1178 11 License, or (at your option) any later version. See the file
Chris@1178 12 COPYING included with this distribution for more information.
Chris@1178 13 */
Chris@1178 14
Chris@1178 15 #include "PluginScan.h"
Chris@1178 16
Chris@1178 17 #include "base/Debug.h"
Chris@1246 18 #include "base/Preferences.h"
Chris@1241 19 #include "base/HelperExecPath.h"
Chris@1178 20
Chris@1501 21 #include <sstream>
Chris@1180 22
Chris@1178 23 #include <QMutex>
Chris@1181 24 #include <QCoreApplication>
Chris@1178 25
Chris@1178 26 using std::string;
Chris@1178 27
Chris@1249 28 class PluginScan::Logger
Chris@1249 29 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 30 : public PluginCandidates::LogCallback
Chris@1249 31 #endif
Chris@1180 32 {
Chris@1180 33 protected:
Chris@1180 34 void log(std::string message) {
Chris@1264 35 SVDEBUG << "PluginScan: " << message << endl;
Chris@1180 36 }
Chris@1180 37 };
Chris@1180 38
Chris@1180 39 PluginScan *PluginScan::getInstance()
Chris@1180 40 {
Chris@1178 41 static QMutex mutex;
Chris@1178 42 static PluginScan *m_instance = 0;
Chris@1178 43 mutex.lock();
Chris@1178 44 if (!m_instance) m_instance = new PluginScan();
Chris@1178 45 mutex.unlock();
Chris@1178 46 return m_instance;
Chris@1178 47 }
Chris@1178 48
Chris@1246 49 PluginScan::PluginScan() : m_succeeded(false), m_logger(new Logger) {
Chris@1178 50 }
Chris@1178 51
Chris@1178 52 PluginScan::~PluginScan() {
Chris@1246 53 QMutexLocker locker(&m_mutex);
Chris@1241 54 clear();
Chris@1180 55 delete m_logger;
Chris@1396 56 SVDEBUG << "PluginScan::~PluginScan completed" << endl;
Chris@1178 57 }
Chris@1178 58
Chris@1178 59 void
Chris@1241 60 PluginScan::scan()
Chris@1178 61 {
Chris@1249 62 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 63
Chris@1246 64 QMutexLocker locker(&m_mutex);
Chris@1246 65
Chris@1246 66 bool inProcess = Preferences::getInstance()->getRunPluginsInProcess();
Chris@1246 67
Chris@1246 68 HelperExecPath hep(inProcess ?
Chris@1246 69 HelperExecPath::NativeArchitectureOnly :
Chris@1246 70 HelperExecPath::AllInstalled);
Chris@1246 71
Chris@1352 72 QString helperName("vamp-plugin-load-checker");
Chris@1246 73 auto helpers = hep.getHelperExecutables(helperName);
Chris@1241 74
Chris@1241 75 clear();
Chris@1241 76
Chris@1246 77 for (auto p: helpers) {
Chris@1247 78 SVDEBUG << "NOTE: PluginScan: Found helper: " << p.executable << endl;
Chris@1246 79 }
Chris@1246 80
Chris@1246 81 if (helpers.empty()) {
Chris@1247 82 SVDEBUG << "NOTE: No plugin checker helpers found in installation;"
Chris@1246 83 << " found none of the following:" << endl;
Chris@1246 84 for (auto d: hep.getHelperCandidatePaths(helperName)) {
Chris@1247 85 SVDEBUG << "NOTE: " << d << endl;
Chris@1246 86 }
Chris@1246 87 }
Chris@1246 88
Chris@1246 89 for (auto p: helpers) {
Chris@1241 90 try {
Chris@1474 91 KnownPluginCandidates *kp = new KnownPluginCandidates
Chris@1246 92 (p.executable.toStdString(), m_logger);
Chris@1246 93 if (m_kp.find(p.tag) != m_kp.end()) {
Chris@1247 94 SVDEBUG << "WARNING: PluginScan::scan: Duplicate tag " << p.tag
Chris@1246 95 << " for helpers" << endl;
Chris@1246 96 continue;
Chris@1246 97 }
Chris@1246 98 m_kp[p.tag] = kp;
Chris@1241 99 m_succeeded = true;
Chris@1241 100 } catch (const std::exception &e) {
Chris@1247 101 SVDEBUG << "ERROR: PluginScan::scan: " << e.what()
Chris@1246 102 << " (with helper path = " << p.executable << ")" << endl;
Chris@1241 103 }
Chris@1241 104 }
Chris@1249 105
Chris@1396 106 SVDEBUG << "PluginScan::scan complete" << endl;
Chris@1249 107 #endif
Chris@1241 108 }
Chris@1241 109
Chris@1246 110 bool
Chris@1246 111 PluginScan::scanSucceeded() const
Chris@1246 112 {
Chris@1246 113 QMutexLocker locker(&m_mutex);
Chris@1246 114 return m_succeeded;
Chris@1246 115 }
Chris@1246 116
Chris@1241 117 void
Chris@1241 118 PluginScan::clear()
Chris@1241 119 {
Chris@1246 120 for (auto &p: m_kp) {
Chris@1246 121 delete p.second;
Chris@1246 122 }
Chris@1241 123 m_kp.clear();
Chris@1179 124 m_succeeded = false;
Chris@1179 125 }
Chris@1179 126
Chris@1246 127 QList<PluginScan::Candidate>
Chris@1278 128 PluginScan::getCandidateLibrariesFor(PluginType
Chris@1278 129 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1278 130 type
Chris@1278 131 #endif
Chris@1278 132 ) const
Chris@1179 133 {
Chris@1249 134 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 135
Chris@1246 136 QMutexLocker locker(&m_mutex);
Chris@1246 137
Chris@1180 138 KnownPlugins::PluginType kpt;
Chris@1180 139 switch (type) {
Chris@1180 140 case VampPlugin: kpt = KnownPlugins::VampPlugin; break;
Chris@1180 141 case LADSPAPlugin: kpt = KnownPlugins::LADSPAPlugin; break;
Chris@1180 142 case DSSIPlugin: kpt = KnownPlugins::DSSIPlugin; break;
Chris@1180 143 default: throw std::logic_error("Inconsistency in plugin type enums");
Chris@1180 144 }
Chris@1180 145
Chris@1246 146 QList<Candidate> candidates;
Chris@1245 147
Chris@1246 148 for (auto rec: m_kp) {
Chris@1245 149
Chris@1474 150 KnownPluginCandidates *kp = rec.second;
Chris@1246 151
Chris@1241 152 auto c = kp->getCandidateLibrariesFor(kpt);
Chris@1245 153
Chris@1247 154 SVDEBUG << "PluginScan: helper \"" << kp->getHelperExecutableName()
Chris@1247 155 << "\" likes " << c.size() << " libraries of type "
Chris@1247 156 << kp->getTagFor(kpt) << endl;
Chris@1245 157
Chris@1246 158 for (auto s: c) {
Chris@1246 159 candidates.push_back({ s.c_str(), rec.first });
Chris@1246 160 }
Chris@1245 161
Chris@1245 162 if (type != VampPlugin) {
Chris@1245 163 // We are only interested in querying multiple helpers
Chris@1245 164 // when dealing with Vamp plugins, for which we can use
Chris@1245 165 // external servers and so in some cases can support
Chris@1245 166 // additional architectures. Other plugin formats are
Chris@1245 167 // loaded directly and so must match the host, which is
Chris@1245 168 // what the first helper is supposed to handle -- so
Chris@1245 169 // break after the first one if not querying Vamp
Chris@1245 170 break;
Chris@1245 171 }
Chris@1241 172 }
Chris@1245 173
Chris@1179 174 return candidates;
Chris@1249 175
Chris@1249 176 #else
Chris@1249 177 return {};
Chris@1249 178 #endif
Chris@1178 179 }
Chris@1178 180
Chris@1501 181 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1501 182 QString
Chris@1501 183 PluginScan::formatFailureReport(QString tag,
Chris@1501 184 std::vector<PluginCandidates::FailureRec> failures) const
Chris@1501 185 {
Chris@1501 186 int n = int(failures.size());
Chris@1501 187 int i = 0;
Chris@1501 188
Chris@1501 189 std::ostringstream os;
Chris@1501 190
Chris@1501 191 os << "<ul>";
Chris@1501 192 for (auto f: failures) {
Chris@1501 193 os << "<li>" + f.library;
Chris@1501 194
Chris@1501 195 SVDEBUG << "PluginScan::formatFailureReport: tag is \"" << tag
Chris@1501 196 << "\", failure code is " << int(f.code) << ", message is \""
Chris@1501 197 << f.message << "\"" << endl;
Chris@1501 198
Chris@1501 199 QString userMessage = QString::fromStdString(f.message);
Chris@1501 200
Chris@1501 201 switch (f.code) {
Chris@1501 202
Chris@1501 203 case PluginCheckCode::FAIL_LIBRARY_NOT_FOUND:
Chris@1501 204 userMessage = QObject::tr("Library file could not be opened");
Chris@1501 205 break;
Chris@1501 206
Chris@1501 207 case PluginCheckCode::FAIL_WRONG_ARCHITECTURE:
Chris@1501 208 if (tag == "64" || (sizeof(void *) == 8 && tag == "")) {
Chris@1501 209 userMessage = QObject::tr
Chris@1505 210 ("Library has wrong architecture - possibly a 32-bit plugin installed in a 64-bit plugin folder");
Chris@1501 211 } else if (tag == "32" || (sizeof(void *) == 4 && tag == "")) {
Chris@1501 212 userMessage = QObject::tr
Chris@1505 213 ("Library has wrong architecture - possibly a 64-bit plugin installed in a 32-bit plugin folder");
Chris@1501 214 }
Chris@1501 215 break;
Chris@1501 216
Chris@1501 217 case PluginCheckCode::FAIL_DEPENDENCY_MISSING:
Chris@1501 218 userMessage = QObject::tr
Chris@1501 219 ("Library depends on another library that cannot be found: %1")
Chris@1501 220 .arg(userMessage);
Chris@1501 221 break;
Chris@1501 222
Chris@1501 223 case PluginCheckCode::FAIL_NOT_LOADABLE:
Chris@1501 224 userMessage = QObject::tr
Chris@1501 225 ("Library cannot be loaded: %1").arg(userMessage);
Chris@1501 226 break;
Chris@1501 227
Chris@1501 228 case PluginCheckCode::FAIL_DESCRIPTOR_MISSING:
Chris@1501 229 userMessage = QObject::tr
Chris@1501 230 ("Not a valid plugin library (no descriptor found)");
Chris@1501 231 break;
Chris@1501 232
Chris@1501 233 case PluginCheckCode::FAIL_NO_PLUGINS:
Chris@1501 234 userMessage = QObject::tr
Chris@1501 235 ("Library contains no plugins");
Chris@1501 236 break;
Chris@1501 237
Chris@1501 238 case PluginCheckCode::FAIL_OTHER:
Chris@1501 239 if (userMessage == "") {
Chris@1501 240 userMessage = QObject::tr
Chris@1501 241 ("Unknown error");
Chris@1501 242 }
Chris@1501 243 break;
Chris@1501 244
Chris@1501 245 case PluginCheckCode::SUCCESS:
Chris@1501 246 // success shouldn't happen here!
Chris@1501 247 break;
Chris@1501 248 }
Chris@1501 249
Chris@1501 250 os << "<br><i>" + userMessage.toStdString() + "</i>";
Chris@1501 251 os << "</li>";
Chris@1501 252
Chris@1501 253 if (n > 10) {
Chris@1501 254 if (++i == 5) {
Chris@1501 255 os << "<li>";
Chris@1501 256 os << QObject::tr("... and %n further failure(s)",
Chris@1501 257 "", n - i)
Chris@1501 258 .toStdString();
Chris@1501 259 os << "</li>";
Chris@1501 260 break;
Chris@1501 261 }
Chris@1501 262 }
Chris@1501 263 }
Chris@1501 264 os << "</ul>";
Chris@1501 265
Chris@1501 266 return QString::fromStdString(os.str());
Chris@1501 267 }
Chris@1501 268 #endif
Chris@1501 269
Chris@1178 270 QString
Chris@1178 271 PluginScan::getStartupFailureReport() const
Chris@1178 272 {
Chris@1249 273 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 274
Chris@1246 275 QMutexLocker locker(&m_mutex);
Chris@1246 276
Chris@1179 277 if (!m_succeeded) {
Chris@1429 278 return QObject::tr("<b>Failed to scan for plugins</b>"
Chris@1429 279 "<p>Failed to scan for plugins at startup. Possibly "
Chris@1352 280 "the plugin checker program was not correctly "
Chris@1181 281 "installed alongside %1?</p>")
Chris@1181 282 .arg(QCoreApplication::applicationName());
Chris@1179 283 }
Chris@1241 284 if (m_kp.empty()) {
Chris@1429 285 return QObject::tr("<b>Did not scan for plugins</b>"
Chris@1429 286 "<p>Apparently no scan for plugins was attempted "
Chris@1429 287 "(internal error?)</p>");
Chris@1179 288 }
Chris@1179 289
Chris@1241 290 QString report;
Chris@1241 291 for (auto kp: m_kp) {
Chris@1501 292 auto failures = kp.second->getFailures();
Chris@1502 293 if (!failures.empty()) {
Chris@1502 294 report += formatFailureReport(kp.first, failures);
Chris@1502 295 }
Chris@1241 296 }
Chris@1179 297 if (report == "") {
Chris@1429 298 return report;
Chris@1179 299 }
Chris@1179 300
Chris@1179 301 return QObject::tr("<b>Failed to load plugins</b>"
Chris@1429 302 "<p>Failed to load one or more plugin libraries:</p>")
Chris@1429 303 + report
Chris@1181 304 + QObject::tr("<p>These plugins may be incompatible with the system, "
Chris@1181 305 "and will be ignored during this run of %1.</p>")
Chris@1181 306 .arg(QCoreApplication::applicationName());
Chris@1249 307
Chris@1249 308 #else
Chris@1249 309 return "";
Chris@1249 310 #endif
Chris@1178 311 }
Chris@1178 312