cannam@153
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@153
|
2 /*
|
cannam@153
|
3 Piper C++
|
cannam@153
|
4
|
cannam@153
|
5 An API for audio analysis and feature extraction plugins.
|
cannam@153
|
6
|
cannam@153
|
7 Centre for Digital Music, Queen Mary, University of London.
|
cannam@153
|
8 Copyright 2006-2017 Chris Cannam and QMUL.
|
cannam@153
|
9
|
cannam@153
|
10 Permission is hereby granted, free of charge, to any person
|
cannam@153
|
11 obtaining a copy of this software and associated documentation
|
cannam@153
|
12 files (the "Software"), to deal in the Software without
|
cannam@153
|
13 restriction, including without limitation the rights to use, copy,
|
cannam@153
|
14 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@153
|
15 of the Software, and to permit persons to whom the Software is
|
cannam@153
|
16 furnished to do so, subject to the following conditions:
|
cannam@153
|
17
|
cannam@153
|
18 The above copyright notice and this permission notice shall be
|
cannam@153
|
19 included in all copies or substantial portions of the Software.
|
cannam@153
|
20
|
cannam@153
|
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@153
|
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@153
|
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@153
|
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@153
|
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@153
|
26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@153
|
27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@153
|
28
|
cannam@153
|
29 Except as contained in this notice, the names of the Centre for
|
cannam@153
|
30 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@153
|
31 shall not be used in advertising or otherwise to promote the sale,
|
cannam@153
|
32 use or other dealings in this Software without prior written
|
cannam@153
|
33 authorization.
|
cannam@153
|
34 */
|
cannam@153
|
35
|
cannam@153
|
36 #include "vamp-json/VampJson.h"
|
cannam@153
|
37 #include "vamp-support/RequestOrResponse.h"
|
cannam@153
|
38 #include "vamp-support/LoaderRequests.h"
|
cannam@153
|
39
|
cannam@153
|
40 #include <iostream>
|
cannam@153
|
41 #include <sstream>
|
cannam@153
|
42 #include <stdexcept>
|
cannam@153
|
43
|
cannam@153
|
44 #include <map>
|
cannam@153
|
45 #include <set>
|
cannam@153
|
46
|
cannam@153
|
47 // for _setmode stuff and _dup
|
cannam@153
|
48 #ifdef _WIN32
|
cannam@153
|
49 #include <io.h>
|
cannam@153
|
50 #include <fcntl.h>
|
cannam@153
|
51 #endif
|
cannam@153
|
52
|
cannam@153
|
53 // for dup, open etc
|
cannam@153
|
54 #ifndef _WIN32
|
cannam@153
|
55 #include <fcntl.h>
|
cannam@153
|
56 #include <unistd.h>
|
cannam@153
|
57 #endif
|
cannam@153
|
58
|
cannam@153
|
59 using namespace std;
|
cannam@153
|
60 using namespace json11;
|
cannam@153
|
61 using namespace piper_vamp;
|
cannam@153
|
62 using namespace Vamp;
|
cannam@153
|
63
|
cannam@153
|
64 static string myname = "piper-vamp-stub-generator";
|
cannam@153
|
65
|
cannam@153
|
66 static void version()
|
cannam@153
|
67 {
|
cannam@153
|
68 cout << "1.0" << endl;
|
cannam@153
|
69 exit(0);
|
cannam@153
|
70 }
|
cannam@153
|
71
|
cannam@153
|
72 static void usage(bool successful = false)
|
cannam@153
|
73 {
|
cannam@153
|
74 cerr << "\n" << myname <<
|
cannam@153
|
75 ": Emit a preliminary version of the main code\nfor a Piper Adapter implementation of a Vamp plugin library\n\n"
|
cannam@156
|
76 " Usage: " << myname << " <libname>\n"
|
cannam@153
|
77 " " << myname << " -v\n"
|
cannam@153
|
78 " " << myname << " -h\n\n"
|
cannam@153
|
79 " where\n"
|
cannam@153
|
80 " <libname>: the Vamp plugin library name to emit code for\n"
|
cannam@153
|
81 " -v: print version number to stdout and exit\n"
|
cannam@153
|
82 " -h: print this text to stderr and exit\n\n";
|
cannam@153
|
83 if (successful) exit(0);
|
cannam@153
|
84 else exit(2);
|
cannam@153
|
85 }
|
cannam@153
|
86
|
cannam@153
|
87 // We write our output to stdout, but want to ensure that the plugin
|
cannam@153
|
88 // doesn't write anything itself. To do this we open a null file
|
cannam@153
|
89 // descriptor and dup2() it into place of stdout in the gaps between
|
cannam@153
|
90 // our own output activity.
|
cannam@153
|
91
|
cannam@153
|
92 static int normalFd = -1;
|
cannam@153
|
93 static int suspendedFd = -1;
|
cannam@153
|
94
|
cannam@153
|
95 static void initFds(bool binary)
|
cannam@153
|
96 {
|
cannam@153
|
97 #ifdef _WIN32
|
cannam@153
|
98 if (binary) {
|
cannam@153
|
99 int result = _setmode(0, _O_BINARY);
|
cannam@153
|
100 if (result == -1) {
|
cannam@153
|
101 throw runtime_error("Failed to set binary mode on stdin");
|
cannam@153
|
102 }
|
cannam@153
|
103 result = _setmode(1, _O_BINARY);
|
cannam@153
|
104 if (result == -1) {
|
cannam@153
|
105 throw runtime_error("Failed to set binary mode on stdout");
|
cannam@153
|
106 }
|
cannam@153
|
107 }
|
cannam@153
|
108 normalFd = _dup(1);
|
cannam@153
|
109 suspendedFd = _open("NUL", _O_WRONLY);
|
cannam@153
|
110 #else
|
cannam@153
|
111 (void)binary;
|
cannam@153
|
112 normalFd = dup(1);
|
cannam@153
|
113 suspendedFd = open("/dev/null", O_WRONLY);
|
cannam@153
|
114 #endif
|
cannam@153
|
115
|
cannam@153
|
116 if (normalFd < 0 || suspendedFd < 0) {
|
cannam@153
|
117 throw runtime_error("Failed to initialise fds for stdio suspend/resume");
|
cannam@153
|
118 }
|
cannam@153
|
119 }
|
cannam@153
|
120
|
cannam@153
|
121 static void suspendOutput()
|
cannam@153
|
122 {
|
cannam@153
|
123 #ifdef _WIN32
|
cannam@153
|
124 _dup2(suspendedFd, 1);
|
cannam@153
|
125 #else
|
cannam@153
|
126 dup2(suspendedFd, 1);
|
cannam@153
|
127 #endif
|
cannam@153
|
128 }
|
cannam@153
|
129
|
cannam@153
|
130 static void resumeOutput()
|
cannam@153
|
131 {
|
cannam@153
|
132 #ifdef _WIN32
|
cannam@153
|
133 _dup2(normalFd, 1);
|
cannam@153
|
134 #else
|
cannam@153
|
135 dup2(normalFd, 1);
|
cannam@153
|
136 #endif
|
cannam@153
|
137 }
|
cannam@153
|
138
|
cannam@153
|
139 ListResponse
|
cannam@153
|
140 makeRequest(string libname)
|
cannam@153
|
141 {
|
cannam@153
|
142 ListRequest req;
|
cannam@153
|
143 req.from.push_back(libname);
|
cannam@153
|
144 return LoaderRequests().listPluginData(req);
|
cannam@153
|
145 }
|
cannam@153
|
146
|
cannam@153
|
147 struct PlausibleMetadata
|
cannam@153
|
148 {
|
cannam@153
|
149 string className;
|
cannam@153
|
150 string adapterName;
|
cannam@153
|
151 };
|
cannam@153
|
152
|
cannam@153
|
153 PlausibleMetadata
|
cannam@153
|
154 inventPlausibleMetadata(string key)
|
cannam@153
|
155 {
|
cannam@153
|
156 string className, adapterName;
|
cannam@153
|
157 bool initial = true, interCap = false;
|
cannam@153
|
158
|
cannam@153
|
159 int idIdx = 0;
|
cannam@153
|
160 for (int i = 0; i < int(key.size()); ++i) {
|
cannam@153
|
161 char c = key[i];
|
cannam@153
|
162 if (c == ':') {
|
cannam@153
|
163 idIdx = i + 1;
|
cannam@153
|
164 break;
|
cannam@153
|
165 }
|
cannam@153
|
166 }
|
cannam@153
|
167
|
cannam@153
|
168 for (int i = idIdx; i < int(key.size()); ++i) {
|
cannam@153
|
169 char c = key[i];
|
cannam@153
|
170 if (isalpha(c)) {
|
cannam@153
|
171 if (initial || interCap) {
|
cannam@153
|
172 className += toupper(c);
|
cannam@153
|
173 } else {
|
cannam@153
|
174 className += c;
|
cannam@153
|
175 }
|
cannam@153
|
176 if (interCap) {
|
cannam@153
|
177 adapterName += toupper(c);
|
cannam@153
|
178 } else {
|
cannam@153
|
179 adapterName += c;
|
cannam@153
|
180 }
|
cannam@153
|
181 interCap = false;
|
cannam@153
|
182 } else {
|
cannam@153
|
183 interCap = true;
|
cannam@153
|
184 }
|
cannam@153
|
185 initial = false;
|
cannam@153
|
186 }
|
cannam@153
|
187 adapterName += "Adapter";
|
cannam@153
|
188
|
cannam@153
|
189 PlausibleMetadata pm;
|
cannam@153
|
190 pm.className = className;
|
cannam@153
|
191 pm.adapterName = adapterName;
|
cannam@153
|
192 return pm;
|
cannam@153
|
193 }
|
cannam@153
|
194
|
cannam@153
|
195 void
|
cannam@153
|
196 emitFor(string libname, const ListResponse &resp)
|
cannam@153
|
197 {
|
cannam@153
|
198 // The same plugin key may appear more than once in the available
|
cannam@153
|
199 // list, if the same plugin is installed in more than one location
|
cannam@153
|
200 // on the Vamp path. To avoid emitting its wrapper definition
|
cannam@153
|
201 // multiple times, we need to keep a record of which ones we've
|
cannam@153
|
202 // emitted for each stage
|
cannam@153
|
203 set<string> emitted;
|
cannam@153
|
204
|
cannam@153
|
205 cout <<
|
cannam@153
|
206 "\n#include \"PiperExport.h\"\n"
|
cannam@153
|
207 "\n"
|
cannam@153
|
208 "// !!! The following #include(s) are guesses. Replace them with the\n"
|
cannam@153
|
209 "// real header files in which your plugin classes are defined.\n"
|
cannam@153
|
210 "//\n";
|
cannam@153
|
211
|
cannam@153
|
212 for (const auto &plugin: resp.available) {
|
cannam@153
|
213
|
cannam@153
|
214 string key = plugin.pluginKey;
|
cannam@153
|
215 if (emitted.find(key) != emitted.end()) {
|
cannam@153
|
216 continue;
|
cannam@153
|
217 }
|
cannam@153
|
218
|
cannam@153
|
219 PlausibleMetadata pm = inventPlausibleMetadata(key);
|
cannam@153
|
220
|
cannam@153
|
221 cout << "#include \"" << pm.className << ".h\"\n";
|
cannam@153
|
222
|
cannam@153
|
223 emitted.insert(key);
|
cannam@153
|
224 }
|
cannam@153
|
225
|
cannam@153
|
226 cout <<
|
cannam@153
|
227 "\n"
|
cannam@153
|
228 "using piper_vamp_js::PiperAdapter;\n"
|
cannam@153
|
229 "using piper_vamp_js::PiperPluginLibrary;\n"
|
cannam@153
|
230 "\n"
|
cannam@153
|
231 "static std::string libname(\"" << libname << "\");\n"
|
cannam@153
|
232 "\n";
|
cannam@153
|
233
|
cannam@153
|
234 emitted.clear();
|
cannam@153
|
235 for (const auto &plugin: resp.available) {
|
cannam@153
|
236
|
cannam@153
|
237 string key = plugin.pluginKey;
|
cannam@153
|
238 if (emitted.find(key) != emitted.end()) {
|
cannam@153
|
239 continue;
|
cannam@153
|
240 }
|
cannam@153
|
241
|
cannam@153
|
242 PlausibleMetadata pm = inventPlausibleMetadata(key);
|
cannam@153
|
243 cout << "// !!! Replace " << pm.className << " in the following line with the real\n"
|
cannam@153
|
244 "// name of the class implementing the \"" << plugin.basic.identifier << "\" plugin.\n"
|
cannam@153
|
245 "//\n";
|
cannam@153
|
246 cout << "static PiperAdapter<"
|
cannam@153
|
247 << pm.className
|
cannam@153
|
248 << ">\n" << pm.adapterName
|
cannam@153
|
249 << "(\n libname,\n ";
|
cannam@153
|
250
|
cannam@153
|
251 string catString = "{ ";
|
cannam@153
|
252 bool first = true;
|
cannam@153
|
253 for (auto c: plugin.category) {
|
cannam@153
|
254 if (!first) catString += ", ";
|
cannam@153
|
255 catString += "\"" + c + "\"";
|
cannam@153
|
256 first = false;
|
cannam@153
|
257 }
|
cannam@153
|
258 catString += " }";
|
cannam@153
|
259 cout << catString << ",\n ";
|
cannam@153
|
260
|
cannam@153
|
261 cout << "{\n ";
|
cannam@153
|
262 first = true;
|
cannam@153
|
263 for (auto o: plugin.basicOutputInfo) {
|
cannam@153
|
264 if (!first) {
|
cannam@153
|
265 cout << ",\n ";
|
cannam@153
|
266 }
|
cannam@153
|
267 cout << " ";
|
cannam@153
|
268 string outputId = o.identifier;
|
cannam@153
|
269 cout << "{ \"" << outputId << "\",\n { \"";
|
cannam@153
|
270 if (plugin.staticOutputInfo.find(outputId) !=
|
cannam@153
|
271 plugin.staticOutputInfo.end()) {
|
cannam@153
|
272 cout << plugin.staticOutputInfo.at(outputId).typeURI;
|
cannam@153
|
273 }
|
cannam@153
|
274 cout << "\" }\n }";
|
cannam@153
|
275 first = false;
|
cannam@153
|
276 }
|
cannam@153
|
277 cout << "\n }\n";
|
cannam@153
|
278 cout << " );\n\n";
|
cannam@153
|
279 emitted.insert(key);
|
cannam@153
|
280 }
|
cannam@153
|
281
|
cannam@153
|
282 cout << "static PiperPluginLibrary library({\n ";
|
cannam@153
|
283 emitted.clear();
|
cannam@153
|
284 for (const auto &plugin: resp.available) {
|
cannam@153
|
285
|
cannam@153
|
286 string key = plugin.pluginKey;
|
cannam@153
|
287 if (emitted.find(key) != emitted.end()) {
|
cannam@153
|
288 continue;
|
cannam@153
|
289 }
|
cannam@153
|
290
|
cannam@153
|
291 PlausibleMetadata pm = inventPlausibleMetadata(key);
|
cannam@153
|
292 if (!emitted.empty()) {
|
cannam@153
|
293 cout << ",\n ";
|
cannam@153
|
294 }
|
cannam@153
|
295 cout << "&" << pm.adapterName;
|
cannam@153
|
296 emitted.insert(key);
|
cannam@153
|
297 }
|
cannam@153
|
298 cout << "\n});\n\n";
|
cannam@153
|
299 cout << "PIPER_EXPORT_LIBRARY(library);\n" << endl;
|
cannam@153
|
300 }
|
cannam@153
|
301
|
cannam@153
|
302 int main(int argc, char **argv)
|
cannam@153
|
303 {
|
cannam@153
|
304 if (argc != 2 && argc != 3) {
|
cannam@153
|
305 usage();
|
cannam@153
|
306 }
|
cannam@153
|
307
|
cannam@153
|
308 string arg = argv[1];
|
cannam@153
|
309 if (arg == "-h") {
|
cannam@153
|
310 if (argc == 2) {
|
cannam@153
|
311 usage(true);
|
cannam@153
|
312 } else {
|
cannam@153
|
313 usage();
|
cannam@153
|
314 }
|
cannam@153
|
315 } else if (arg == "-v") {
|
cannam@153
|
316 if (argc == 2) {
|
cannam@153
|
317 version();
|
cannam@153
|
318 } else {
|
cannam@153
|
319 usage();
|
cannam@153
|
320 }
|
cannam@153
|
321 }
|
cannam@153
|
322
|
cannam@153
|
323 string libname = arg;
|
cannam@153
|
324
|
cannam@153
|
325 auto li = libname.rfind('.');
|
cannam@153
|
326 if (li != std::string::npos) {
|
cannam@153
|
327 cerr << "NOTE: library name is to be specified without extension, dropping \"" << libname.substr(li) << "\"" << endl;
|
cannam@153
|
328 libname = libname.substr(0, li);
|
cannam@153
|
329 }
|
cannam@153
|
330
|
cannam@153
|
331 try {
|
cannam@153
|
332 initFds(false);
|
cannam@153
|
333 } catch (exception &e) {
|
cannam@153
|
334 cerr << "ERROR: " << e.what() << endl;
|
cannam@153
|
335 exit(1);
|
cannam@153
|
336 }
|
cannam@153
|
337
|
cannam@153
|
338 suspendOutput();
|
cannam@153
|
339
|
cannam@153
|
340 ListResponse resp = makeRequest(libname);
|
cannam@153
|
341
|
cannam@153
|
342 resumeOutput();
|
cannam@153
|
343
|
cannam@153
|
344 if (resp.available.empty()) {
|
cannam@153
|
345 cerr << "ERROR: No plugin(s) found in library named \""
|
cannam@153
|
346 << libname << "\"\nCheck that the library name is correct and the right files are in $VAMP_PATH.\nThat would mean either "
|
cannam@153
|
347 << libname << ".so, " << libname << ".dll, or " << libname
|
cannam@153
|
348 << ".dylib (depending on your platform), as well as "
|
cannam@153
|
349 << libname << ".n3 and " << libname << ".cat."
|
cannam@153
|
350 << endl;
|
cannam@153
|
351 exit(1);
|
cannam@153
|
352 }
|
cannam@153
|
353
|
cannam@153
|
354 emitFor(libname, resp);
|
cannam@153
|
355
|
cannam@153
|
356 exit(0);
|
cannam@153
|
357 }
|