annotate plugin/PiperVampPluginFactory.cpp @ 1377:f1c4ca3ccaa3 dev/refactor-piper-related

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