annotate plugin/PiperVampPluginFactory.cpp @ 1376:d9511f9e04d7 dev/refactor-piper-related

Introduce some POD structs for describing an external server application and the desired libraries to load from it, and disambiguating between empty list request and invalid list request. This allows for overriding PiperVampPluginFactory behaviour for using a PluginScan to populate the list request.
author Lucas Thompson <lucas.thompson@qmul.ac.uk>
date Fri, 10 Feb 2017 11:15:19 +0000
parents da5f4d37988d
children f1c4ca3ccaa3
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@1370 31 #include "vamp-client/qt/AutoPlugin.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>
lucas@1376 42 #include <algorithm>
Chris@0 43
Chris@408 44 #include "base/Profiler.h"
Chris@1241 45 #include "base/HelperExecPath.h"
Chris@408 46
Chris@1164 47 using namespace std;
Chris@1164 48
Chris@249 49 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
Chris@249 50
Chris@1264 51 class PiperVampPluginFactory::Logger : public piper_vamp::client::LogCallback {
Chris@1264 52 protected:
Chris@1264 53 void log(std::string message) const override {
Chris@1264 54 SVDEBUG << "PiperVampPluginFactory: " << message << endl;
Chris@1264 55 }
Chris@1264 56 };
Chris@1264 57
lucas@1376 58 PiperVampPluginFactory::PiperVampPluginFactory(std::initializer_list<ServerDescription> servers) :
Chris@1264 59 m_logger(new Logger)
Chris@66 60 {
lucas@1375 61 HelperExecPath hep(HelperExecPath::AllInstalled);
Chris@1240 62
lucas@1375 63 for (auto server: servers) {
lucas@1376 64 for (auto platformHelper: hep.getHelperExecutables(QString::fromStdString(server.name)))
lucas@1375 65 m_servers.push_back(platformHelper);
lucas@1376 66 if (server.hasDesiredExtractors) {
lucas@1376 67 setDesiredExtractors(server.name, server.extractors);
lucas@1376 68 }
lucas@1375 69 }
Chris@1240 70
Chris@1246 71 for (auto n: m_servers) {
lucas@1375 72 SVDEBUG << "NOTE: PiperVampPluginFactory: Found server: "
Chris@1247 73 << n.executable << endl;
Chris@1246 74 }
lucas@1375 75
Chris@1240 76 if (m_servers.empty()) {
lucas@1375 77 SVDEBUG << "NOTE: No Piper Vamp servers found in installation;"
Chris@1247 78 << " found none of the following:" << endl;
lucas@1376 79 for (auto server: servers)
lucas@1376 80 for (auto d: hep.getHelperCandidatePaths(QString::fromStdString(server.name))) {
lucas@1375 81 SVDEBUG << "NOTE: " << d << endl;
lucas@1375 82 }
Chris@1240 83 }
Chris@1240 84 }
Chris@1240 85
lucas@1375 86 PiperVampPluginFactory::PiperVampPluginFactory() :
lucas@1376 87 PiperVampPluginFactory({{"piper-vamp-simple-server"}}) {}
lucas@1375 88
Chris@1264 89 PiperVampPluginFactory::~PiperVampPluginFactory()
Chris@1264 90 {
Chris@1264 91 delete m_logger;
Chris@1264 92 }
Chris@1264 93
lucas@1376 94 void
lucas@1376 95 PiperVampPluginFactory::setDesiredExtractors(ServerName name,
lucas@1376 96 DesiredExtractors extractors)
lucas@1376 97 {
lucas@1376 98 const bool isValidServerName = std::find_if(
lucas@1376 99 m_servers.begin(),
lucas@1376 100 m_servers.end(),
lucas@1376 101 [&name](const HelperExecPath::HelperExec &h) -> bool {
lucas@1376 102 return QFileInfo(h.executable).fileName().toStdString() == name;
lucas@1376 103 }) != m_servers.end();
lucas@1376 104 if ( isValidServerName ) {
lucas@1376 105 m_overrideDesiredExtractors[name] = extractors;
lucas@1376 106 }
lucas@1376 107 }
lucas@1376 108
Chris@1164 109 vector<QString>
Chris@1227 110 PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage)
Chris@0 111 {
Chris@1225 112 Profiler profiler("PiperVampPluginFactory::getPluginIdentifiers");
Chris@408 113
Chris@1209 114 QMutexLocker locker(&m_mutex);
Chris@1209 115
Chris@1240 116 if (m_servers.empty()) {
Chris@1227 117 errorMessage = QObject::tr("External plugin host executable does not appear to be installed");
Chris@1227 118 return {};
Chris@1227 119 }
Chris@1227 120
Chris@1209 121 if (m_pluginData.empty()) {
Chris@1227 122 populate(errorMessage);
Chris@1209 123 }
Chris@1209 124
Chris@1164 125 vector<QString> rv;
Chris@1179 126
Chris@1209 127 for (const auto &d: m_pluginData) {
Chris@1225 128 rv.push_back(QString("vamp:") + QString::fromStdString(d.second.pluginKey));
Chris@66 129 }
Chris@66 130
Chris@0 131 return rv;
Chris@0 132 }
Chris@0 133
Chris@66 134 Vamp::Plugin *
Chris@1225 135 PiperVampPluginFactory::instantiatePlugin(QString identifier,
Chris@1225 136 sv_samplerate_t inputSampleRate)
Chris@0 137 {
Chris@1225 138 Profiler profiler("PiperVampPluginFactory::instantiatePlugin");
Chris@1225 139
Chris@1240 140 if (m_origins.find(identifier) == m_origins.end()) {
Chris@1240 141 cerr << "ERROR: No known server for identifier " << identifier << endl;
Chris@1247 142 SVDEBUG << "ERROR: No known server for identifier " << identifier << endl;
Chris@1240 143 return 0;
Chris@1240 144 }
Chris@1240 145
Chris@1225 146 auto psd = getPluginStaticData(identifier);
Chris@1225 147 if (psd.pluginKey == "") {
Chris@1225 148 return 0;
Chris@1225 149 }
Chris@1264 150
Chris@1264 151 SVDEBUG << "PiperVampPluginFactory: Creating Piper AutoPlugin for server "
Chris@1264 152 << m_origins[identifier] << ", identifier " << identifier << endl;
Chris@1210 153
Chris@1210 154 auto ap = new piper_vamp::client::AutoPlugin
Chris@1240 155 (m_origins[identifier].toStdString(),
Chris@1264 156 psd.pluginKey,
Chris@1264 157 float(inputSampleRate),
Chris@1264 158 0,
Chris@1264 159 m_logger);
Chris@1240 160
Chris@1210 161 if (!ap->isOK()) {
Chris@1210 162 delete ap;
Chris@1210 163 return 0;
Chris@1225 164 }
Chris@1225 165
Chris@1225 166 return ap;
Chris@1225 167 }
Chris@1225 168
Chris@1225 169 piper_vamp::PluginStaticData
Chris@1225 170 PiperVampPluginFactory::getPluginStaticData(QString identifier)
Chris@1225 171 {
Chris@1225 172 if (m_pluginData.find(identifier) != m_pluginData.end()) {
Chris@1225 173 return m_pluginData[identifier];
Chris@1210 174 } else {
Chris@1225 175 return {};
Chris@1210 176 }
Chris@298 177 }
Chris@298 178
Chris@165 179 QString
Chris@1225 180 PiperVampPluginFactory::getPluginCategory(QString identifier)
Chris@165 181 {
Chris@1223 182 if (m_taxonomy.find(identifier) != m_taxonomy.end()) {
Chris@1223 183 return m_taxonomy[identifier];
Chris@1223 184 } else {
Chris@1223 185 return {};
Chris@1223 186 }
Chris@165 187 }
Chris@165 188
Chris@165 189 void
Chris@1227 190 PiperVampPluginFactory::populate(QString &errorMessage)
Chris@165 191 {
Chris@1240 192 QString someError;
Chris@1227 193
Chris@1246 194 for (auto s: m_servers) {
Chris@1240 195
Chris@1240 196 populateFrom(s, someError);
Chris@1240 197
Chris@1240 198 if (someError != "" && errorMessage == "") {
Chris@1240 199 errorMessage = someError;
Chris@1240 200 }
Chris@1240 201 }
Chris@1240 202 }
Chris@1240 203
Chris@1240 204 void
Chris@1246 205 PiperVampPluginFactory::populateFrom(const HelperExecPath::HelperExec &server,
Chris@1246 206 QString &errorMessage)
Chris@1240 207 {
Chris@1246 208 QString tag = server.tag;
Chris@1246 209 string executable = server.executable.toStdString();
lucas@1376 210 const string serverName = QFileInfo(server.executable).fileName().toStdString();
lucas@1376 211
lucas@1376 212 DesiredSubset from;
lucas@1376 213
lucas@1376 214 if (m_overrideDesiredExtractors.find(serverName) != m_overrideDesiredExtractors.end()) {
lucas@1376 215 const auto desired = m_overrideDesiredExtractors.at(serverName);
lucas@1376 216 if (desired.allAvailable) {
lucas@1376 217 if (desired.from.empty()) {
lucas@1376 218 from = {};
lucas@1376 219 } else {
lucas@1376 220 // ambiguous struct
lucas@1376 221 return;
lucas@1376 222 }
lucas@1376 223 } else {
lucas@1376 224 if (desired.from.empty()) {
lucas@1376 225 // ambigous
lucas@1376 226 return;
lucas@1376 227 } else {
lucas@1376 228 from = desired.from;
lucas@1376 229 }
lucas@1376 230 }
lucas@1376 231 } else {
lucas@1376 232 PluginScan *scan = PluginScan::getInstance();
lucas@1376 233 auto candidateLibraries =
lucas@1376 234 scan->getCandidateLibrariesFor(PluginScan::VampPlugin);
Chris@1246 235
lucas@1376 236 SVDEBUG << "PiperVampPluginFactory: Populating from " << executable << endl;
lucas@1376 237 SVDEBUG << "INFO: Have " << candidateLibraries.size()
lucas@1376 238 << " candidate Vamp plugin libraries from scanner" << endl;
lucas@1376 239 for (const auto &c: candidateLibraries) {
lucas@1376 240 if (c.helperTag == tag) {
lucas@1376 241 string soname = QFileInfo(c.libraryPath).baseName().toStdString();
lucas@1376 242 SVDEBUG << "INFO: For tag \"" << tag << "\" giving library " << soname << endl;
lucas@1376 243 from.push_back(soname);
lucas@1376 244 }
lucas@1376 245 }
Chris@1246 246
lucas@1376 247 if (from.empty()) {
lucas@1376 248 SVDEBUG << "PiperVampPluginFactory: No candidate libraries for tag \""
lucas@1376 249 << tag << "\"";
lucas@1376 250 if (scan->scanSucceeded()) {
lucas@1376 251 // we have to assume that they all failed to load (i.e. we
lucas@1376 252 // exclude them all) rather than sending an empty list
lucas@1376 253 // (which would mean no exclusions)
lucas@1376 254 SVDEBUG << ", skipping" << endl;
lucas@1376 255 return;
lucas@1376 256 } else {
lucas@1376 257 SVDEBUG << ", but it seems the scan failed, so bumbling on anyway" << endl;
lucas@1376 258 }
Chris@1246 259 }
Chris@1246 260 }
Chris@1246 261
Chris@1264 262 piper_vamp::client::ProcessQtTransport transport(executable, "capnp", m_logger);
Chris@1227 263 if (!transport.isOK()) {
Chris@1264 264 SVDEBUG << "PiperVampPluginFactory: Failed to start Piper process transport" << endl;
Chris@1227 265 errorMessage = QObject::tr("Could not start external plugin host");
Chris@1227 266 return;
Chris@1227 267 }
Chris@1234 268
Chris@1264 269 piper_vamp::client::CapnpRRClient client(&transport, m_logger);
Chris@1248 270
Chris@1248 271 piper_vamp::ListRequest req;
Chris@1248 272 req.from = from;
Chris@1248 273
Chris@1248 274 piper_vamp::ListResponse resp;
Chris@1234 275
Chris@1234 276 try {
Chris@1248 277 resp = client.listPluginData(req);
Chris@1234 278 } catch (piper_vamp::client::ServerCrashed) {
Chris@1264 279 SVDEBUG << "PiperVampPluginFactory: Piper server crashed" << endl;
Chris@1234 280 errorMessage = QObject::tr
Chris@1234 281 ("External plugin host exited unexpectedly while listing plugins");
Chris@1234 282 return;
Chris@1235 283 } catch (const std::exception &e) {
Chris@1264 284 SVDEBUG << "PiperVampPluginFactory: Exception caught: " << e.what() << endl;
Chris@1235 285 errorMessage = QObject::tr("External plugin host invocation failed: %1")
Chris@1235 286 .arg(e.what());
Chris@1235 287 return;
Chris@1234 288 }
Chris@1213 289
Chris@1247 290 SVDEBUG << "PiperVampPluginFactory: server \"" << executable << "\" lists "
Chris@1248 291 << resp.available.size() << " plugin(s)" << endl;
Chris@1244 292
Chris@1248 293 for (const auto &pd: resp.available) {
Chris@1240 294
Chris@1213 295 QString identifier =
Chris@1213 296 QString("vamp:") + QString::fromStdString(pd.pluginKey);
Chris@1213 297
Chris@1240 298 if (m_origins.find(identifier) != m_origins.end()) {
Chris@1240 299 // have it already, from a higher-priority server
Chris@1240 300 // (e.g. 64-bit instead of 32-bit)
Chris@1240 301 continue;
Chris@1240 302 }
Chris@1240 303
Chris@1246 304 m_origins[identifier] = server.executable;
Chris@1240 305
Chris@1225 306 m_pluginData[identifier] = pd;
Chris@1225 307
Chris@1213 308 QStringList catlist;
Chris@1213 309 for (const auto &cs: pd.category) {
Chris@1213 310 catlist.push_back(QString::fromStdString(cs));
Chris@1213 311 }
Chris@1223 312
Chris@1213 313 m_taxonomy[identifier] = catlist.join(" > ");
Chris@1213 314 }
Chris@1209 315 }
Chris@165 316
Chris@1249 317 #endif