annotate plugin/PiperVampPluginFactory.cpp @ 1375:da5f4d37988d dev/refactor-piper-related

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