annotate generator/generator.cpp @ 176:eaf46e7647a0 tip master

Update for latest Emscripten - Pointer_stringify has apparently been deprecated for a while, and was removed in v1.38.27
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 27 Feb 2019 11:29:53 +0000
parents 205f9a6240f5
children
rev   line source
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 }