annotate plugin/PiperVampPluginFactory.cpp @ 1482:c014839f49c7 plugin-path-config

Record path associated with first helper found for a plugin, not last (so native helper in preference to 32-bit helper). Also more debug output
author Chris Cannam
date Mon, 11 Jun 2018 11:34:11 +0100
parents cee1be4fb8c1
children c55de1488b93
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@1240 61
Chris@1246 62 HelperExecPath hep(HelperExecPath::AllInstalled);
Chris@1246 63 m_servers = hep.getHelperExecutables(serverName);
Chris@1240 64
Chris@1246 65 for (auto n: m_servers) {
Chris@1247 66 SVDEBUG << "NOTE: PiperVampPluginFactory: Found server: "
Chris@1247 67 << n.executable << endl;
Chris@1246 68 }
Chris@1246 69
Chris@1240 70 if (m_servers.empty()) {
Chris@1247 71 SVDEBUG << "NOTE: No Piper Vamp servers found in installation;"
Chris@1247 72 << " found none of the following:" << endl;
Chris@1246 73 for (auto d: hep.getHelperCandidatePaths(serverName)) {
Chris@1247 74 SVDEBUG << "NOTE: " << d << endl;
Chris@1241 75 }
Chris@1240 76 }
Chris@1240 77 }
Chris@1240 78
Chris@1264 79 PiperVampPluginFactory::~PiperVampPluginFactory()
Chris@1264 80 {
Chris@1264 81 delete m_logger;
Chris@1264 82 }
Chris@1264 83
Chris@1164 84 vector<QString>
Chris@1227 85 PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage)
Chris@0 86 {
Chris@1225 87 Profiler profiler("PiperVampPluginFactory::getPluginIdentifiers");
Chris@408 88
Chris@1209 89 QMutexLocker locker(&m_mutex);
Chris@1209 90
Chris@1240 91 if (m_servers.empty()) {
Chris@1227 92 errorMessage = QObject::tr("External plugin host executable does not appear to be installed");
Chris@1227 93 return {};
Chris@1227 94 }
Chris@1227 95
Chris@1209 96 if (m_pluginData.empty()) {
Chris@1227 97 populate(errorMessage);
Chris@1209 98 }
Chris@1209 99
Chris@1164 100 vector<QString> rv;
Chris@1179 101
Chris@1209 102 for (const auto &d: m_pluginData) {
Chris@1225 103 rv.push_back(QString("vamp:") + QString::fromStdString(d.second.pluginKey));
Chris@66 104 }
Chris@66 105
Chris@0 106 return rv;
Chris@0 107 }
Chris@0 108
Chris@66 109 Vamp::Plugin *
Chris@1225 110 PiperVampPluginFactory::instantiatePlugin(QString identifier,
Chris@1225 111 sv_samplerate_t inputSampleRate)
Chris@0 112 {
Chris@1225 113 Profiler profiler("PiperVampPluginFactory::instantiatePlugin");
Chris@1225 114
Chris@1240 115 if (m_origins.find(identifier) == m_origins.end()) {
Chris@1428 116 SVCERR << "ERROR: No known server for identifier " << identifier << endl;
Chris@1240 117 return 0;
Chris@1240 118 }
Chris@1240 119
Chris@1225 120 auto psd = getPluginStaticData(identifier);
Chris@1225 121 if (psd.pluginKey == "") {
Chris@1225 122 return 0;
Chris@1225 123 }
Chris@1264 124
Chris@1378 125 SVDEBUG << "PiperVampPluginFactory: Creating PiperAutoPlugin for server "
Chris@1264 126 << m_origins[identifier] << ", identifier " << identifier << endl;
Chris@1210 127
Chris@1378 128 auto ap = new piper_vamp::client::PiperAutoPlugin
Chris@1240 129 (m_origins[identifier].toStdString(),
Chris@1264 130 psd.pluginKey,
Chris@1264 131 float(inputSampleRate),
Chris@1264 132 0,
Chris@1264 133 m_logger);
Chris@1240 134
Chris@1210 135 if (!ap->isOK()) {
Chris@1210 136 delete ap;
Chris@1210 137 return 0;
Chris@1225 138 }
Chris@1225 139
Chris@1225 140 return ap;
Chris@1225 141 }
Chris@1225 142
Chris@1225 143 piper_vamp::PluginStaticData
Chris@1225 144 PiperVampPluginFactory::getPluginStaticData(QString identifier)
Chris@1225 145 {
Chris@1225 146 if (m_pluginData.find(identifier) != m_pluginData.end()) {
Chris@1225 147 return m_pluginData[identifier];
Chris@1210 148 } else {
Chris@1225 149 return {};
Chris@1210 150 }
Chris@298 151 }
Chris@298 152
Chris@165 153 QString
Chris@1225 154 PiperVampPluginFactory::getPluginCategory(QString identifier)
Chris@165 155 {
Chris@1223 156 if (m_taxonomy.find(identifier) != m_taxonomy.end()) {
Chris@1223 157 return m_taxonomy[identifier];
Chris@1223 158 } else {
Chris@1223 159 return {};
Chris@1223 160 }
Chris@165 161 }
Chris@165 162
Chris@1464 163 QString
Chris@1464 164 PiperVampPluginFactory::getPluginLibraryPath(QString identifier)
Chris@1464 165 {
Chris@1464 166 // What we want to return here is the file path of the library in
Chris@1464 167 // which the plugin was actually found -- we want to be paranoid
Chris@1464 168 // about that and not just query
Chris@1464 169 // Vamp::HostExt::PluginLoader::getLibraryPathForPlugin to return
Chris@1464 170 // what the SDK thinks the likely location would be (in case our
Chris@1464 171 // search order turns out to have been different)
Chris@1464 172
Chris@1464 173 QStringList bits = identifier.split(':');
Chris@1464 174 if (bits.size() > 1) {
Chris@1464 175 QString soname = bits[bits.size() - 2];
Chris@1464 176 auto i = m_libraries.find(soname);
Chris@1464 177 if (i != m_libraries.end()) {
Chris@1464 178 return i->second;
Chris@1464 179 }
Chris@1464 180 }
Chris@1464 181 return QString();
Chris@1464 182 }
Chris@1464 183
Chris@165 184 void
Chris@1227 185 PiperVampPluginFactory::populate(QString &errorMessage)
Chris@165 186 {
Chris@1240 187 QString someError;
Chris@1227 188
Chris@1246 189 for (auto s: m_servers) {
Chris@1240 190
Chris@1240 191 populateFrom(s, someError);
Chris@1240 192
Chris@1240 193 if (someError != "" && errorMessage == "") {
Chris@1240 194 errorMessage = someError;
Chris@1240 195 }
Chris@1240 196 }
Chris@1240 197 }
Chris@1240 198
Chris@1240 199 void
Chris@1246 200 PiperVampPluginFactory::populateFrom(const HelperExecPath::HelperExec &server,
Chris@1246 201 QString &errorMessage)
Chris@1240 202 {
Chris@1246 203 QString tag = server.tag;
Chris@1246 204 string executable = server.executable.toStdString();
Chris@1246 205
Chris@1246 206 PluginScan *scan = PluginScan::getInstance();
Chris@1246 207 auto candidateLibraries =
Chris@1246 208 scan->getCandidateLibrariesFor(PluginScan::VampPlugin);
Chris@1246 209
Chris@1264 210 SVDEBUG << "PiperVampPluginFactory: Populating from " << executable << endl;
Chris@1250 211 SVDEBUG << "INFO: Have " << candidateLibraries.size()
Chris@1264 212 << " candidate Vamp plugin libraries from scanner" << endl;
Chris@1249 213
Chris@1246 214 vector<string> from;
Chris@1246 215 for (const auto &c: candidateLibraries) {
Chris@1246 216 if (c.helperTag == tag) {
Chris@1246 217 string soname = QFileInfo(c.libraryPath).baseName().toStdString();
Chris@1247 218 SVDEBUG << "INFO: For tag \"" << tag << "\" giving library " << soname << endl;
Chris@1246 219 from.push_back(soname);
Chris@1482 220 QString qsoname = QString::fromStdString(soname);
Chris@1482 221 if (m_libraries.find(qsoname) == m_libraries.end()) {
Chris@1482 222 m_libraries[qsoname] = c.libraryPath;
Chris@1482 223 }
Chris@1246 224 }
Chris@1246 225 }
Chris@1246 226
Chris@1246 227 if (from.empty()) {
Chris@1247 228 SVDEBUG << "PiperVampPluginFactory: No candidate libraries for tag \""
Chris@1246 229 << tag << "\"";
Chris@1246 230 if (scan->scanSucceeded()) {
Chris@1246 231 // we have to assume that they all failed to load (i.e. we
Chris@1246 232 // exclude them all) rather than sending an empty list
Chris@1246 233 // (which would mean no exclusions)
Chris@1247 234 SVDEBUG << ", skipping" << endl;
Chris@1246 235 return;
Chris@1246 236 } else {
Chris@1247 237 SVDEBUG << ", but it seems the scan failed, so bumbling on anyway" << endl;
Chris@1246 238 }
Chris@1246 239 }
Chris@1246 240
Chris@1264 241 piper_vamp::client::ProcessQtTransport transport(executable, "capnp", m_logger);
Chris@1227 242 if (!transport.isOK()) {
Chris@1264 243 SVDEBUG << "PiperVampPluginFactory: Failed to start Piper process transport" << endl;
Chris@1227 244 errorMessage = QObject::tr("Could not start external plugin host");
Chris@1227 245 return;
Chris@1227 246 }
Chris@1234 247
Chris@1264 248 piper_vamp::client::CapnpRRClient client(&transport, m_logger);
Chris@1248 249
Chris@1248 250 piper_vamp::ListRequest req;
Chris@1248 251 req.from = from;
Chris@1248 252
Chris@1248 253 piper_vamp::ListResponse resp;
Chris@1234 254
Chris@1234 255 try {
Chris@1378 256 resp = client.list(req);
Chris@1465 257 } catch (const piper_vamp::client::ServerCrashed &) {
Chris@1264 258 SVDEBUG << "PiperVampPluginFactory: Piper server crashed" << endl;
Chris@1234 259 errorMessage = QObject::tr
Chris@1234 260 ("External plugin host exited unexpectedly while listing plugins");
Chris@1234 261 return;
Chris@1235 262 } catch (const std::exception &e) {
Chris@1264 263 SVDEBUG << "PiperVampPluginFactory: Exception caught: " << e.what() << endl;
Chris@1235 264 errorMessage = QObject::tr("External plugin host invocation failed: %1")
Chris@1235 265 .arg(e.what());
Chris@1235 266 return;
Chris@1234 267 }
Chris@1213 268
Chris@1247 269 SVDEBUG << "PiperVampPluginFactory: server \"" << executable << "\" lists "
Chris@1248 270 << resp.available.size() << " plugin(s)" << endl;
Chris@1244 271
Chris@1248 272 for (const auto &pd: resp.available) {
Chris@1240 273
Chris@1213 274 QString identifier =
Chris@1213 275 QString("vamp:") + QString::fromStdString(pd.pluginKey);
Chris@1213 276
Chris@1240 277 if (m_origins.find(identifier) != m_origins.end()) {
Chris@1240 278 // have it already, from a higher-priority server
Chris@1240 279 // (e.g. 64-bit instead of 32-bit)
Chris@1240 280 continue;
Chris@1240 281 }
Chris@1240 282
Chris@1246 283 m_origins[identifier] = server.executable;
Chris@1240 284
Chris@1225 285 m_pluginData[identifier] = pd;
Chris@1225 286
Chris@1213 287 QStringList catlist;
Chris@1213 288 for (const auto &cs: pd.category) {
Chris@1213 289 catlist.push_back(QString::fromStdString(cs));
Chris@1213 290 }
Chris@1223 291
Chris@1213 292 m_taxonomy[identifier] = catlist.join(" > ");
Chris@1213 293 }
Chris@1209 294 }
Chris@165 295
Chris@1249 296 #endif