annotate plugin/PiperVampPluginFactory.cpp @ 1520:954d0cf29ca7 import-audio-data

Switch the normalisation option in WritableWaveFileModel from normalising on read to normalising on write, so that the saved file is already normalised and therefore can be read again without having to remember to normalise it
author Chris Cannam
date Wed, 12 Sep 2018 13:56:56 +0100
parents c014839f49c7
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