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
|