annotate plugin/PiperVampPluginFactory.cpp @ 1773:fadd9f8aaa27

This output is too annoying, in the perfectly innocuous case of reading from an aggregate model whose components are different lengths
author Chris Cannam
date Wed, 14 Aug 2019 13:54:23 +0100
parents 70e172e6cc59
children 5f8fbbde08ff
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@1241 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@1249 16 #ifdef HAVE_PIPER
Chris@1249 17
Chris@1225 18 #include "PiperVampPluginFactory.h"
Chris@0 19 #include "PluginIdentifier.h"
Chris@0 20
Chris@150 21 #include "system/System.h"
Chris@66 22
Chris@1179 23 #include "PluginScan.h"
Chris@1179 24
Chris@1224 25 #ifdef _WIN32
Chris@1224 26 #undef VOID
Chris@1224 27 #undef ERROR
Chris@1224 28 #define CAPNP_LITE 1
Chris@1224 29 #endif
Chris@1225 30
Chris@1378 31 #include "vamp-client/qt/PiperAutoPlugin.h"
Chris@1370 32 #include "vamp-client/qt/ProcessQtTransport.h"
Chris@1370 33 #include "vamp-client/CapnpRRClient.h"
Chris@1210 34
Chris@66 35 #include <QDir>
Chris@66 36 #include <QFile>
Chris@66 37 #include <QFileInfo>
Chris@165 38 #include <QTextStream>
Chris@1227 39 #include <QCoreApplication>
Chris@66 40
Chris@0 41 #include <iostream>
Chris@0 42
Chris@408 43 #include "base/Profiler.h"
Chris@1241 44 #include "base/HelperExecPath.h"
Chris@408 45
Chris@1164 46 using namespace std;
Chris@1164 47
Chris@249 48 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
Chris@249 49
Chris@1264 50 class PiperVampPluginFactory::Logger : public piper_vamp::client::LogCallback {
Chris@1264 51 protected:
Chris@1264 52 void log(std::string message) const override {
Chris@1264 53 SVDEBUG << "PiperVampPluginFactory: " << message << endl;
Chris@1264 54 }
Chris@1264 55 };
Chris@1264 56
Chris@1264 57 PiperVampPluginFactory::PiperVampPluginFactory() :
Chris@1264 58 m_logger(new Logger)
Chris@66 59 {
Chris@1241 60 QString serverName = "piper-vamp-simple-server";
Chris@1559 61 float minimumVersion = 2.0;
Chris@1240 62
Chris@1246 63 HelperExecPath hep(HelperExecPath::AllInstalled);
Chris@1240 64
Chris@1559 65 auto servers = hep.getHelperExecutables(serverName);
Chris@1559 66
Chris@1559 67 for (auto n: servers) {
Chris@1247 68 SVDEBUG << "NOTE: PiperVampPluginFactory: Found server: "
Chris@1247 69 << n.executable << endl;
Chris@1559 70 if (serverMeetsMinimumVersion(n, minimumVersion)) {
Chris@1559 71 m_servers.push_back(n);
Chris@1559 72 } else {
Chris@1559 73 SVCERR << "WARNING: PiperVampPluginFactory: Server at "
Chris@1559 74 << n.executable
Chris@1559 75 << " does not meet minimum version requirement (version >= "
Chris@1559 76 << minimumVersion << ")" << endl;
Chris@1559 77 }
Chris@1246 78 }
Chris@1246 79
Chris@1240 80 if (m_servers.empty()) {
Chris@1247 81 SVDEBUG << "NOTE: No Piper Vamp servers found in installation;"
Chris@1559 82 << " the following paths are either absent or fail "
Chris@1559 83 << "minimum-version check:" << endl;
Chris@1246 84 for (auto d: hep.getHelperCandidatePaths(serverName)) {
Chris@1247 85 SVDEBUG << "NOTE: " << d << endl;
Chris@1241 86 }
Chris@1240 87 }
Chris@1240 88 }
Chris@1240 89
Chris@1264 90 PiperVampPluginFactory::~PiperVampPluginFactory()
Chris@1264 91 {
Chris@1264 92 delete m_logger;
Chris@1264 93 }
Chris@1264 94
Chris@1559 95 bool
Chris@1559 96 PiperVampPluginFactory::serverMeetsMinimumVersion(const HelperExecPath::HelperExec &server,
Chris@1559 97 float minimumVersion)
Chris@1559 98 {
Chris@1559 99 QProcess process;
Chris@1559 100 QString executable = server.executable;
Chris@1559 101 process.setReadChannel(QProcess::StandardOutput);
Chris@1559 102 process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
Chris@1559 103 process.start(executable, { "--version" });
Chris@1559 104
Chris@1559 105 if (!process.waitForStarted()) {
Chris@1559 106 QProcess::ProcessError err = process.error();
Chris@1559 107 if (err == QProcess::FailedToStart) {
Chris@1559 108 SVCERR << "WARNING: Unable to start server " << executable
Chris@1559 109 << " for version check" << endl;
Chris@1559 110 } else if (err == QProcess::Crashed) {
Chris@1559 111 SVCERR << "WARNING: Server " << executable
Chris@1559 112 << " crashed on version check" << endl;
Chris@1559 113 } else {
Chris@1559 114 SVCERR << "WARNING: Server " << executable
Chris@1559 115 << " failed on version check with error code "
Chris@1559 116 << err << endl;
Chris@1559 117 }
Chris@1559 118 return false;
Chris@1559 119 }
Chris@1559 120 process.waitForFinished();
Chris@1559 121
Chris@1559 122 QByteArray output = process.readAllStandardOutput();
Chris@1559 123 while (output.endsWith('\n') || output.endsWith('\r')) {
Chris@1559 124 output.chop(1);
Chris@1559 125 }
Chris@1559 126
Chris@1559 127 QString outputString(output);
Chris@1559 128 bool ok = false;
Chris@1559 129 float version = outputString.toFloat(&ok);
Chris@1559 130 if (!ok) {
Chris@1559 131 SVCERR << "WARNING: Failed to convert server version response \""
Chris@1559 132 << outputString << "\" into one- or two-part version number"
Chris@1559 133 << endl;
Chris@1559 134 }
Chris@1559 135
Chris@1559 136 SVDEBUG << "Server " << executable << " reports version number "
Chris@1559 137 << version << endl;
Chris@1559 138
Chris@1568 139 float eps = 1e-6f;
Chris@1559 140 return (version >= minimumVersion ||
Chris@1559 141 fabsf(version - minimumVersion) < eps); // arf
Chris@1559 142 }
Chris@1559 143
Chris@1164 144 vector<QString>
Chris@1227 145 PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage)
Chris@0 146 {
Chris@1225 147 Profiler profiler("PiperVampPluginFactory::getPluginIdentifiers");
Chris@408 148
Chris@1209 149 QMutexLocker locker(&m_mutex);
Chris@1209 150
Chris@1240 151 if (m_servers.empty()) {
Chris@1227 152 errorMessage = QObject::tr("External plugin host executable does not appear to be installed");
Chris@1227 153 return {};
Chris@1227 154 }
Chris@1227 155
Chris@1209 156 if (m_pluginData.empty()) {
Chris@1227 157 populate(errorMessage);
Chris@1209 158 }
Chris@1209 159
Chris@1164 160 vector<QString> rv;
Chris@1179 161
Chris@1209 162 for (const auto &d: m_pluginData) {
Chris@1225 163 rv.push_back(QString("vamp:") + QString::fromStdString(d.second.pluginKey));
Chris@66 164 }
Chris@66 165
Chris@0 166 return rv;
Chris@0 167 }
Chris@0 168
Chris@66 169 Vamp::Plugin *
Chris@1225 170 PiperVampPluginFactory::instantiatePlugin(QString identifier,
Chris@1225 171 sv_samplerate_t inputSampleRate)
Chris@0 172 {
Chris@1225 173 Profiler profiler("PiperVampPluginFactory::instantiatePlugin");
Chris@1225 174
Chris@1240 175 if (m_origins.find(identifier) == m_origins.end()) {
Chris@1428 176 SVCERR << "ERROR: No known server for identifier " << identifier << endl;
Chris@1582 177 return nullptr;
Chris@1240 178 }
Chris@1240 179
Chris@1225 180 auto psd = getPluginStaticData(identifier);
Chris@1225 181 if (psd.pluginKey == "") {
Chris@1582 182 return nullptr;
Chris@1225 183 }
Chris@1264 184
Chris@1378 185 SVDEBUG << "PiperVampPluginFactory: Creating PiperAutoPlugin for server "
Chris@1264 186 << m_origins[identifier] << ", identifier " << identifier << endl;
Chris@1210 187
Chris@1378 188 auto ap = new piper_vamp::client::PiperAutoPlugin
Chris@1240 189 (m_origins[identifier].toStdString(),
Chris@1264 190 psd.pluginKey,
Chris@1264 191 float(inputSampleRate),
Chris@1264 192 0,
Chris@1264 193 m_logger);
Chris@1240 194
Chris@1210 195 if (!ap->isOK()) {
Chris@1210 196 delete ap;
Chris@1582 197 return nullptr;
Chris@1225 198 }
Chris@1225 199
Chris@1225 200 return ap;
Chris@1225 201 }
Chris@1225 202
Chris@1225 203 piper_vamp::PluginStaticData
Chris@1225 204 PiperVampPluginFactory::getPluginStaticData(QString identifier)
Chris@1225 205 {
Chris@1225 206 if (m_pluginData.find(identifier) != m_pluginData.end()) {
Chris@1225 207 return m_pluginData[identifier];
Chris@1210 208 } else {
Chris@1225 209 return {};
Chris@1210 210 }
Chris@298 211 }
Chris@298 212
Chris@165 213 QString
Chris@1225 214 PiperVampPluginFactory::getPluginCategory(QString identifier)
Chris@165 215 {
Chris@1223 216 if (m_taxonomy.find(identifier) != m_taxonomy.end()) {
Chris@1223 217 return m_taxonomy[identifier];
Chris@1223 218 } else {
Chris@1223 219 return {};
Chris@1223 220 }
Chris@165 221 }
Chris@165 222
Chris@1464 223 QString
Chris@1464 224 PiperVampPluginFactory::getPluginLibraryPath(QString identifier)
Chris@1464 225 {
Chris@1464 226 // What we want to return here is the file path of the library in
Chris@1464 227 // which the plugin was actually found -- we want to be paranoid
Chris@1464 228 // about that and not just query
Chris@1464 229 // Vamp::HostExt::PluginLoader::getLibraryPathForPlugin to return
Chris@1464 230 // what the SDK thinks the likely location would be (in case our
Chris@1464 231 // search order turns out to have been different)
Chris@1464 232
Chris@1464 233 QStringList bits = identifier.split(':');
Chris@1464 234 if (bits.size() > 1) {
Chris@1464 235 QString soname = bits[bits.size() - 2];
Chris@1464 236 auto i = m_libraries.find(soname);
Chris@1464 237 if (i != m_libraries.end()) {
Chris@1464 238 return i->second;
Chris@1464 239 }
Chris@1464 240 }
Chris@1464 241 return QString();
Chris@1464 242 }
Chris@1464 243
Chris@165 244 void
Chris@1227 245 PiperVampPluginFactory::populate(QString &errorMessage)
Chris@165 246 {
Chris@1240 247 QString someError;
Chris@1227 248
Chris@1246 249 for (auto s: m_servers) {
Chris@1240 250
Chris@1240 251 populateFrom(s, someError);
Chris@1240 252
Chris@1240 253 if (someError != "" && errorMessage == "") {
Chris@1240 254 errorMessage = someError;
Chris@1240 255 }
Chris@1240 256 }
Chris@1240 257 }
Chris@1240 258
Chris@1240 259 void
Chris@1246 260 PiperVampPluginFactory::populateFrom(const HelperExecPath::HelperExec &server,
Chris@1246 261 QString &errorMessage)
Chris@1240 262 {
Chris@1246 263 QString tag = server.tag;
Chris@1246 264 string executable = server.executable.toStdString();
Chris@1246 265
Chris@1246 266 PluginScan *scan = PluginScan::getInstance();
Chris@1246 267 auto candidateLibraries =
Chris@1246 268 scan->getCandidateLibrariesFor(PluginScan::VampPlugin);
Chris@1246 269
Chris@1264 270 SVDEBUG << "PiperVampPluginFactory: Populating from " << executable << endl;
Chris@1250 271 SVDEBUG << "INFO: Have " << candidateLibraries.size()
Chris@1264 272 << " candidate Vamp plugin libraries from scanner" << endl;
Chris@1249 273
Chris@1246 274 vector<string> from;
Chris@1246 275 for (const auto &c: candidateLibraries) {
Chris@1246 276 if (c.helperTag == tag) {
Chris@1246 277 string soname = QFileInfo(c.libraryPath).baseName().toStdString();
Chris@1247 278 SVDEBUG << "INFO: For tag \"" << tag << "\" giving library " << soname << endl;
Chris@1246 279 from.push_back(soname);
Chris@1482 280 QString qsoname = QString::fromStdString(soname);
Chris@1482 281 if (m_libraries.find(qsoname) == m_libraries.end()) {
Chris@1482 282 m_libraries[qsoname] = c.libraryPath;
Chris@1482 283 }
Chris@1246 284 }
Chris@1246 285 }
Chris@1246 286
Chris@1246 287 if (from.empty()) {
Chris@1247 288 SVDEBUG << "PiperVampPluginFactory: No candidate libraries for tag \""
Chris@1246 289 << tag << "\"";
Chris@1246 290 if (scan->scanSucceeded()) {
Chris@1246 291 // we have to assume that they all failed to load (i.e. we
Chris@1246 292 // exclude them all) rather than sending an empty list
Chris@1246 293 // (which would mean no exclusions)
Chris@1247 294 SVDEBUG << ", skipping" << endl;
Chris@1246 295 return;
Chris@1246 296 } else {
Chris@1247 297 SVDEBUG << ", but it seems the scan failed, so bumbling on anyway" << endl;
Chris@1246 298 }
Chris@1246 299 }
Chris@1246 300
Chris@1264 301 piper_vamp::client::ProcessQtTransport transport(executable, "capnp", m_logger);
Chris@1227 302 if (!transport.isOK()) {
Chris@1264 303 SVDEBUG << "PiperVampPluginFactory: Failed to start Piper process transport" << endl;
Chris@1227 304 errorMessage = QObject::tr("Could not start external plugin host");
Chris@1227 305 return;
Chris@1227 306 }
Chris@1234 307
Chris@1264 308 piper_vamp::client::CapnpRRClient client(&transport, m_logger);
Chris@1248 309
Chris@1248 310 piper_vamp::ListRequest req;
Chris@1248 311 req.from = from;
Chris@1248 312
Chris@1248 313 piper_vamp::ListResponse resp;
Chris@1234 314
Chris@1234 315 try {
Chris@1378 316 resp = client.list(req);
Chris@1465 317 } catch (const piper_vamp::client::ServerCrashed &) {
Chris@1264 318 SVDEBUG << "PiperVampPluginFactory: Piper server crashed" << endl;
Chris@1234 319 errorMessage = QObject::tr
Chris@1234 320 ("External plugin host exited unexpectedly while listing plugins");
Chris@1234 321 return;
Chris@1235 322 } catch (const std::exception &e) {
Chris@1264 323 SVDEBUG << "PiperVampPluginFactory: Exception caught: " << e.what() << endl;
Chris@1235 324 errorMessage = QObject::tr("External plugin host invocation failed: %1")
Chris@1235 325 .arg(e.what());
Chris@1235 326 return;
Chris@1234 327 }
Chris@1213 328
Chris@1247 329 SVDEBUG << "PiperVampPluginFactory: server \"" << executable << "\" lists "
Chris@1248 330 << resp.available.size() << " plugin(s)" << endl;
Chris@1244 331
Chris@1248 332 for (const auto &pd: resp.available) {
Chris@1240 333
Chris@1213 334 QString identifier =
Chris@1213 335 QString("vamp:") + QString::fromStdString(pd.pluginKey);
Chris@1213 336
Chris@1240 337 if (m_origins.find(identifier) != m_origins.end()) {
Chris@1240 338 // have it already, from a higher-priority server
Chris@1240 339 // (e.g. 64-bit instead of 32-bit)
Chris@1240 340 continue;
Chris@1240 341 }
Chris@1240 342
Chris@1246 343 m_origins[identifier] = server.executable;
Chris@1240 344
Chris@1225 345 m_pluginData[identifier] = pd;
Chris@1225 346
Chris@1213 347 QStringList catlist;
Chris@1213 348 for (const auto &cs: pd.category) {
Chris@1213 349 catlist.push_back(QString::fromStdString(cs));
Chris@1213 350 }
Chris@1223 351
Chris@1213 352 m_taxonomy[identifier] = catlist.join(" > ");
Chris@1213 353 }
Chris@1209 354 }
Chris@165 355
Chris@1249 356 #endif