annotate vamp-stubber/stubber.cpp @ 238:0664784566d8

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