Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@52: Sonic Visualiser Chris@52: An audio file viewer and annotation editor. Chris@52: Centre for Digital Music, Queen Mary, University of London. Chris@1241: This file copyright 2006-2016 Chris Cannam and QMUL. Chris@0: Chris@52: This program is free software; you can redistribute it and/or Chris@52: modify it under the terms of the GNU General Public License as Chris@52: published by the Free Software Foundation; either version 2 of the Chris@52: License, or (at your option) any later version. See the file Chris@52: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@1249: #ifdef HAVE_PIPER Chris@1249: Chris@1225: #include "PiperVampPluginFactory.h" Chris@0: #include "PluginIdentifier.h" Chris@0: Chris@150: #include "system/System.h" Chris@66: Chris@1179: #include "PluginScan.h" Chris@1179: Chris@1224: #ifdef _WIN32 Chris@1224: #undef VOID Chris@1224: #undef ERROR Chris@1224: #define CAPNP_LITE 1 Chris@1224: #endif Chris@1225: Chris@1378: #include "vamp-client/qt/PiperAutoPlugin.h" Chris@1370: #include "vamp-client/qt/ProcessQtTransport.h" Chris@1370: #include "vamp-client/CapnpRRClient.h" Chris@1210: Chris@66: #include Chris@66: #include Chris@66: #include Chris@165: #include Chris@1227: #include Chris@66: Chris@0: #include Chris@0: Chris@408: #include "base/Profiler.h" Chris@1241: #include "base/HelperExecPath.h" Chris@408: Chris@1164: using namespace std; Chris@1164: Chris@249: //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 Chris@249: Chris@1264: class PiperVampPluginFactory::Logger : public piper_vamp::client::LogCallback { Chris@1264: protected: Chris@1264: void log(std::string message) const override { Chris@1264: SVDEBUG << "PiperVampPluginFactory: " << message << endl; Chris@1264: } Chris@1264: }; Chris@1264: Chris@1264: PiperVampPluginFactory::PiperVampPluginFactory() : Chris@1264: m_logger(new Logger) Chris@66: { Chris@1241: QString serverName = "piper-vamp-simple-server"; Chris@1240: Chris@1246: HelperExecPath hep(HelperExecPath::AllInstalled); Chris@1246: m_servers = hep.getHelperExecutables(serverName); Chris@1240: Chris@1246: for (auto n: m_servers) { Chris@1247: SVDEBUG << "NOTE: PiperVampPluginFactory: Found server: " Chris@1247: << n.executable << endl; Chris@1246: } Chris@1246: Chris@1240: if (m_servers.empty()) { Chris@1247: SVDEBUG << "NOTE: No Piper Vamp servers found in installation;" Chris@1247: << " found none of the following:" << endl; Chris@1246: for (auto d: hep.getHelperCandidatePaths(serverName)) { Chris@1247: SVDEBUG << "NOTE: " << d << endl; Chris@1241: } Chris@1240: } Chris@1240: } Chris@1240: Chris@1264: PiperVampPluginFactory::~PiperVampPluginFactory() Chris@1264: { Chris@1264: delete m_logger; Chris@1264: } Chris@1264: Chris@1164: vector Chris@1227: PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage) Chris@0: { Chris@1225: Profiler profiler("PiperVampPluginFactory::getPluginIdentifiers"); Chris@408: Chris@1209: QMutexLocker locker(&m_mutex); Chris@1209: Chris@1240: if (m_servers.empty()) { Chris@1227: errorMessage = QObject::tr("External plugin host executable does not appear to be installed"); Chris@1227: return {}; Chris@1227: } Chris@1227: Chris@1209: if (m_pluginData.empty()) { Chris@1227: populate(errorMessage); Chris@1209: } Chris@1209: Chris@1164: vector rv; Chris@1179: Chris@1209: for (const auto &d: m_pluginData) { Chris@1225: rv.push_back(QString("vamp:") + QString::fromStdString(d.second.pluginKey)); Chris@66: } Chris@66: Chris@0: return rv; Chris@0: } Chris@0: Chris@66: Vamp::Plugin * Chris@1225: PiperVampPluginFactory::instantiatePlugin(QString identifier, Chris@1225: sv_samplerate_t inputSampleRate) Chris@0: { Chris@1225: Profiler profiler("PiperVampPluginFactory::instantiatePlugin"); Chris@1225: Chris@1240: if (m_origins.find(identifier) == m_origins.end()) { Chris@1428: SVCERR << "ERROR: No known server for identifier " << identifier << endl; Chris@1240: return 0; Chris@1240: } Chris@1240: Chris@1225: auto psd = getPluginStaticData(identifier); Chris@1225: if (psd.pluginKey == "") { Chris@1225: return 0; Chris@1225: } Chris@1264: Chris@1378: SVDEBUG << "PiperVampPluginFactory: Creating PiperAutoPlugin for server " Chris@1264: << m_origins[identifier] << ", identifier " << identifier << endl; Chris@1210: Chris@1378: auto ap = new piper_vamp::client::PiperAutoPlugin Chris@1240: (m_origins[identifier].toStdString(), Chris@1264: psd.pluginKey, Chris@1264: float(inputSampleRate), Chris@1264: 0, Chris@1264: m_logger); Chris@1240: Chris@1210: if (!ap->isOK()) { Chris@1210: delete ap; Chris@1210: return 0; Chris@1225: } Chris@1225: Chris@1225: return ap; Chris@1225: } Chris@1225: Chris@1225: piper_vamp::PluginStaticData Chris@1225: PiperVampPluginFactory::getPluginStaticData(QString identifier) Chris@1225: { Chris@1225: if (m_pluginData.find(identifier) != m_pluginData.end()) { Chris@1225: return m_pluginData[identifier]; Chris@1210: } else { Chris@1225: return {}; Chris@1210: } Chris@298: } Chris@298: Chris@165: QString Chris@1225: PiperVampPluginFactory::getPluginCategory(QString identifier) Chris@165: { Chris@1223: if (m_taxonomy.find(identifier) != m_taxonomy.end()) { Chris@1223: return m_taxonomy[identifier]; Chris@1223: } else { Chris@1223: return {}; Chris@1223: } Chris@165: } Chris@165: Chris@1464: QString Chris@1464: PiperVampPluginFactory::getPluginLibraryPath(QString identifier) Chris@1464: { Chris@1464: // What we want to return here is the file path of the library in Chris@1464: // which the plugin was actually found -- we want to be paranoid Chris@1464: // about that and not just query Chris@1464: // Vamp::HostExt::PluginLoader::getLibraryPathForPlugin to return Chris@1464: // what the SDK thinks the likely location would be (in case our Chris@1464: // search order turns out to have been different) Chris@1464: Chris@1464: QStringList bits = identifier.split(':'); Chris@1464: if (bits.size() > 1) { Chris@1464: QString soname = bits[bits.size() - 2]; Chris@1464: auto i = m_libraries.find(soname); Chris@1464: if (i != m_libraries.end()) { Chris@1464: return i->second; Chris@1464: } Chris@1464: } Chris@1464: return QString(); Chris@1464: } Chris@1464: Chris@165: void Chris@1227: PiperVampPluginFactory::populate(QString &errorMessage) Chris@165: { Chris@1240: QString someError; Chris@1227: Chris@1246: for (auto s: m_servers) { Chris@1240: Chris@1240: populateFrom(s, someError); Chris@1240: Chris@1240: if (someError != "" && errorMessage == "") { Chris@1240: errorMessage = someError; Chris@1240: } Chris@1240: } Chris@1240: } Chris@1240: Chris@1240: void Chris@1246: PiperVampPluginFactory::populateFrom(const HelperExecPath::HelperExec &server, Chris@1246: QString &errorMessage) Chris@1240: { Chris@1246: QString tag = server.tag; Chris@1246: string executable = server.executable.toStdString(); Chris@1246: Chris@1246: PluginScan *scan = PluginScan::getInstance(); Chris@1246: auto candidateLibraries = Chris@1246: scan->getCandidateLibrariesFor(PluginScan::VampPlugin); Chris@1246: Chris@1264: SVDEBUG << "PiperVampPluginFactory: Populating from " << executable << endl; Chris@1250: SVDEBUG << "INFO: Have " << candidateLibraries.size() Chris@1264: << " candidate Vamp plugin libraries from scanner" << endl; Chris@1249: Chris@1246: vector from; Chris@1246: for (const auto &c: candidateLibraries) { Chris@1246: if (c.helperTag == tag) { Chris@1246: string soname = QFileInfo(c.libraryPath).baseName().toStdString(); Chris@1247: SVDEBUG << "INFO: For tag \"" << tag << "\" giving library " << soname << endl; Chris@1246: from.push_back(soname); Chris@1464: m_libraries[QString::fromStdString(soname)] = c.libraryPath; Chris@1246: } Chris@1246: } Chris@1246: Chris@1246: if (from.empty()) { Chris@1247: SVDEBUG << "PiperVampPluginFactory: No candidate libraries for tag \"" Chris@1246: << tag << "\""; Chris@1246: if (scan->scanSucceeded()) { Chris@1246: // we have to assume that they all failed to load (i.e. we Chris@1246: // exclude them all) rather than sending an empty list Chris@1246: // (which would mean no exclusions) Chris@1247: SVDEBUG << ", skipping" << endl; Chris@1246: return; Chris@1246: } else { Chris@1247: SVDEBUG << ", but it seems the scan failed, so bumbling on anyway" << endl; Chris@1246: } Chris@1246: } Chris@1246: Chris@1264: piper_vamp::client::ProcessQtTransport transport(executable, "capnp", m_logger); Chris@1227: if (!transport.isOK()) { Chris@1264: SVDEBUG << "PiperVampPluginFactory: Failed to start Piper process transport" << endl; Chris@1227: errorMessage = QObject::tr("Could not start external plugin host"); Chris@1227: return; Chris@1227: } Chris@1234: Chris@1264: piper_vamp::client::CapnpRRClient client(&transport, m_logger); Chris@1248: Chris@1248: piper_vamp::ListRequest req; Chris@1248: req.from = from; Chris@1248: Chris@1248: piper_vamp::ListResponse resp; Chris@1234: Chris@1234: try { Chris@1378: resp = client.list(req); Chris@1465: } catch (const piper_vamp::client::ServerCrashed &) { Chris@1264: SVDEBUG << "PiperVampPluginFactory: Piper server crashed" << endl; Chris@1234: errorMessage = QObject::tr Chris@1234: ("External plugin host exited unexpectedly while listing plugins"); Chris@1234: return; Chris@1235: } catch (const std::exception &e) { Chris@1264: SVDEBUG << "PiperVampPluginFactory: Exception caught: " << e.what() << endl; Chris@1235: errorMessage = QObject::tr("External plugin host invocation failed: %1") Chris@1235: .arg(e.what()); Chris@1235: return; Chris@1234: } Chris@1213: Chris@1247: SVDEBUG << "PiperVampPluginFactory: server \"" << executable << "\" lists " Chris@1248: << resp.available.size() << " plugin(s)" << endl; Chris@1244: Chris@1248: for (const auto &pd: resp.available) { Chris@1240: Chris@1213: QString identifier = Chris@1213: QString("vamp:") + QString::fromStdString(pd.pluginKey); Chris@1213: Chris@1240: if (m_origins.find(identifier) != m_origins.end()) { Chris@1240: // have it already, from a higher-priority server Chris@1240: // (e.g. 64-bit instead of 32-bit) Chris@1240: continue; Chris@1240: } Chris@1240: Chris@1246: m_origins[identifier] = server.executable; Chris@1240: Chris@1225: m_pluginData[identifier] = pd; Chris@1225: Chris@1213: QStringList catlist; Chris@1213: for (const auto &cs: pd.category) { Chris@1213: catlist.push_back(QString::fromStdString(cs)); Chris@1213: } Chris@1223: Chris@1213: m_taxonomy[identifier] = catlist.join(" > "); Chris@1213: } Chris@1209: } Chris@165: Chris@1249: #endif