cannam@153: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@153: /* cannam@153: Piper C++ cannam@153: cannam@153: An API for audio analysis and feature extraction plugins. cannam@153: cannam@153: Centre for Digital Music, Queen Mary, University of London. cannam@153: Copyright 2006-2017 Chris Cannam and QMUL. cannam@153: cannam@153: Permission is hereby granted, free of charge, to any person cannam@153: obtaining a copy of this software and associated documentation cannam@153: files (the "Software"), to deal in the Software without cannam@153: restriction, including without limitation the rights to use, copy, cannam@153: modify, merge, publish, distribute, sublicense, and/or sell copies cannam@153: of the Software, and to permit persons to whom the Software is cannam@153: furnished to do so, subject to the following conditions: cannam@153: cannam@153: The above copyright notice and this permission notice shall be cannam@153: included in all copies or substantial portions of the Software. cannam@153: cannam@153: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, cannam@153: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF cannam@153: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND cannam@153: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR cannam@153: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF cannam@153: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION cannam@153: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cannam@153: cannam@153: Except as contained in this notice, the names of the Centre for cannam@153: Digital Music; Queen Mary, University of London; and Chris Cannam cannam@153: shall not be used in advertising or otherwise to promote the sale, cannam@153: use or other dealings in this Software without prior written cannam@153: authorization. cannam@153: */ cannam@153: cannam@153: #include "vamp-json/VampJson.h" cannam@153: #include "vamp-support/RequestOrResponse.h" cannam@153: #include "vamp-support/LoaderRequests.h" cannam@153: cannam@153: #include cannam@153: #include cannam@153: #include cannam@153: cannam@153: #include cannam@153: #include cannam@153: cannam@153: // for _setmode stuff and _dup cannam@153: #ifdef _WIN32 cannam@153: #include cannam@153: #include cannam@153: #endif cannam@153: cannam@153: // for dup, open etc cannam@153: #ifndef _WIN32 cannam@153: #include cannam@153: #include cannam@153: #endif cannam@153: cannam@153: using namespace std; cannam@153: using namespace json11; cannam@153: using namespace piper_vamp; cannam@153: using namespace Vamp; cannam@153: cannam@153: static string myname = "piper-vamp-stub-generator"; cannam@153: cannam@153: static void version() cannam@153: { cannam@153: cout << "1.0" << endl; cannam@153: exit(0); cannam@153: } cannam@153: cannam@153: static void usage(bool successful = false) cannam@153: { cannam@153: cerr << "\n" << myname << cannam@153: ": Emit a preliminary version of the main code\nfor a Piper Adapter implementation of a Vamp plugin library\n\n" cannam@156: " Usage: " << myname << " \n" cannam@153: " " << myname << " -v\n" cannam@153: " " << myname << " -h\n\n" cannam@153: " where\n" cannam@153: " : the Vamp plugin library name to emit code for\n" cannam@153: " -v: print version number to stdout and exit\n" cannam@153: " -h: print this text to stderr and exit\n\n"; cannam@153: if (successful) exit(0); cannam@153: else exit(2); cannam@153: } cannam@153: cannam@153: // We write our output to stdout, but want to ensure that the plugin cannam@153: // doesn't write anything itself. To do this we open a null file cannam@153: // descriptor and dup2() it into place of stdout in the gaps between cannam@153: // our own output activity. cannam@153: cannam@153: static int normalFd = -1; cannam@153: static int suspendedFd = -1; cannam@153: cannam@153: static void initFds(bool binary) cannam@153: { cannam@153: #ifdef _WIN32 cannam@153: if (binary) { cannam@153: int result = _setmode(0, _O_BINARY); cannam@153: if (result == -1) { cannam@153: throw runtime_error("Failed to set binary mode on stdin"); cannam@153: } cannam@153: result = _setmode(1, _O_BINARY); cannam@153: if (result == -1) { cannam@153: throw runtime_error("Failed to set binary mode on stdout"); cannam@153: } cannam@153: } cannam@153: normalFd = _dup(1); cannam@153: suspendedFd = _open("NUL", _O_WRONLY); cannam@153: #else cannam@153: (void)binary; cannam@153: normalFd = dup(1); cannam@153: suspendedFd = open("/dev/null", O_WRONLY); cannam@153: #endif cannam@153: cannam@153: if (normalFd < 0 || suspendedFd < 0) { cannam@153: throw runtime_error("Failed to initialise fds for stdio suspend/resume"); cannam@153: } cannam@153: } cannam@153: cannam@153: static void suspendOutput() cannam@153: { cannam@153: #ifdef _WIN32 cannam@153: _dup2(suspendedFd, 1); cannam@153: #else cannam@153: dup2(suspendedFd, 1); cannam@153: #endif cannam@153: } cannam@153: cannam@153: static void resumeOutput() cannam@153: { cannam@153: #ifdef _WIN32 cannam@153: _dup2(normalFd, 1); cannam@153: #else cannam@153: dup2(normalFd, 1); cannam@153: #endif cannam@153: } cannam@153: cannam@153: ListResponse cannam@153: makeRequest(string libname) cannam@153: { cannam@153: ListRequest req; cannam@153: req.from.push_back(libname); cannam@153: return LoaderRequests().listPluginData(req); cannam@153: } cannam@153: cannam@153: struct PlausibleMetadata cannam@153: { cannam@153: string className; cannam@153: string adapterName; cannam@153: }; cannam@153: cannam@153: PlausibleMetadata cannam@153: inventPlausibleMetadata(string key) cannam@153: { cannam@153: string className, adapterName; cannam@153: bool initial = true, interCap = false; cannam@153: cannam@153: int idIdx = 0; cannam@153: for (int i = 0; i < int(key.size()); ++i) { cannam@153: char c = key[i]; cannam@153: if (c == ':') { cannam@153: idIdx = i + 1; cannam@153: break; cannam@153: } cannam@153: } cannam@153: cannam@153: for (int i = idIdx; i < int(key.size()); ++i) { cannam@153: char c = key[i]; cannam@153: if (isalpha(c)) { cannam@153: if (initial || interCap) { cannam@153: className += toupper(c); cannam@153: } else { cannam@153: className += c; cannam@153: } cannam@153: if (interCap) { cannam@153: adapterName += toupper(c); cannam@153: } else { cannam@153: adapterName += c; cannam@153: } cannam@153: interCap = false; cannam@153: } else { cannam@153: interCap = true; cannam@153: } cannam@153: initial = false; cannam@153: } cannam@153: adapterName += "Adapter"; cannam@153: cannam@153: PlausibleMetadata pm; cannam@153: pm.className = className; cannam@153: pm.adapterName = adapterName; cannam@153: return pm; cannam@153: } cannam@153: cannam@153: void cannam@153: emitFor(string libname, const ListResponse &resp) cannam@153: { cannam@153: // The same plugin key may appear more than once in the available cannam@153: // list, if the same plugin is installed in more than one location cannam@153: // on the Vamp path. To avoid emitting its wrapper definition cannam@153: // multiple times, we need to keep a record of which ones we've cannam@153: // emitted for each stage cannam@153: set emitted; cannam@153: cannam@153: cout << cannam@153: "\n#include \"PiperExport.h\"\n" cannam@153: "\n" cannam@153: "// !!! The following #include(s) are guesses. Replace them with the\n" cannam@153: "// real header files in which your plugin classes are defined.\n" cannam@153: "//\n"; cannam@153: cannam@153: for (const auto &plugin: resp.available) { cannam@153: cannam@153: string key = plugin.pluginKey; cannam@153: if (emitted.find(key) != emitted.end()) { cannam@153: continue; cannam@153: } cannam@153: cannam@153: PlausibleMetadata pm = inventPlausibleMetadata(key); cannam@153: cannam@153: cout << "#include \"" << pm.className << ".h\"\n"; cannam@153: cannam@153: emitted.insert(key); cannam@153: } cannam@153: cannam@153: cout << cannam@153: "\n" cannam@153: "using piper_vamp_js::PiperAdapter;\n" cannam@153: "using piper_vamp_js::PiperPluginLibrary;\n" cannam@153: "\n" cannam@153: "static std::string libname(\"" << libname << "\");\n" cannam@153: "\n"; cannam@153: cannam@153: emitted.clear(); cannam@153: for (const auto &plugin: resp.available) { cannam@153: cannam@153: string key = plugin.pluginKey; cannam@153: if (emitted.find(key) != emitted.end()) { cannam@153: continue; cannam@153: } cannam@153: cannam@153: PlausibleMetadata pm = inventPlausibleMetadata(key); cannam@153: cout << "// !!! Replace " << pm.className << " in the following line with the real\n" cannam@153: "// name of the class implementing the \"" << plugin.basic.identifier << "\" plugin.\n" cannam@153: "//\n"; cannam@153: cout << "static PiperAdapter<" cannam@153: << pm.className cannam@153: << ">\n" << pm.adapterName cannam@153: << "(\n libname,\n "; cannam@153: cannam@153: string catString = "{ "; cannam@153: bool first = true; cannam@153: for (auto c: plugin.category) { cannam@153: if (!first) catString += ", "; cannam@153: catString += "\"" + c + "\""; cannam@153: first = false; cannam@153: } cannam@153: catString += " }"; cannam@153: cout << catString << ",\n "; cannam@153: cannam@153: cout << "{\n "; cannam@153: first = true; cannam@153: for (auto o: plugin.basicOutputInfo) { cannam@153: if (!first) { cannam@153: cout << ",\n "; cannam@153: } cannam@153: cout << " "; cannam@153: string outputId = o.identifier; cannam@153: cout << "{ \"" << outputId << "\",\n { \""; cannam@153: if (plugin.staticOutputInfo.find(outputId) != cannam@153: plugin.staticOutputInfo.end()) { cannam@153: cout << plugin.staticOutputInfo.at(outputId).typeURI; cannam@153: } cannam@153: cout << "\" }\n }"; cannam@153: first = false; cannam@153: } cannam@153: cout << "\n }\n"; cannam@153: cout << " );\n\n"; cannam@153: emitted.insert(key); cannam@153: } cannam@153: cannam@153: cout << "static PiperPluginLibrary library({\n "; cannam@153: emitted.clear(); cannam@153: for (const auto &plugin: resp.available) { cannam@153: cannam@153: string key = plugin.pluginKey; cannam@153: if (emitted.find(key) != emitted.end()) { cannam@153: continue; cannam@153: } cannam@153: cannam@153: PlausibleMetadata pm = inventPlausibleMetadata(key); cannam@153: if (!emitted.empty()) { cannam@153: cout << ",\n "; cannam@153: } cannam@153: cout << "&" << pm.adapterName; cannam@153: emitted.insert(key); cannam@153: } cannam@153: cout << "\n});\n\n"; cannam@153: cout << "PIPER_EXPORT_LIBRARY(library);\n" << endl; cannam@153: } cannam@153: cannam@153: int main(int argc, char **argv) cannam@153: { cannam@153: if (argc != 2 && argc != 3) { cannam@153: usage(); cannam@153: } cannam@153: cannam@153: string arg = argv[1]; cannam@153: if (arg == "-h") { cannam@153: if (argc == 2) { cannam@153: usage(true); cannam@153: } else { cannam@153: usage(); cannam@153: } cannam@153: } else if (arg == "-v") { cannam@153: if (argc == 2) { cannam@153: version(); cannam@153: } else { cannam@153: usage(); cannam@153: } cannam@153: } cannam@153: cannam@153: string libname = arg; cannam@153: cannam@153: auto li = libname.rfind('.'); cannam@153: if (li != std::string::npos) { cannam@153: cerr << "NOTE: library name is to be specified without extension, dropping \"" << libname.substr(li) << "\"" << endl; cannam@153: libname = libname.substr(0, li); cannam@153: } cannam@153: cannam@153: try { cannam@153: initFds(false); cannam@153: } catch (exception &e) { cannam@153: cerr << "ERROR: " << e.what() << endl; cannam@153: exit(1); cannam@153: } cannam@153: cannam@153: suspendOutput(); cannam@153: cannam@153: ListResponse resp = makeRequest(libname); cannam@153: cannam@153: resumeOutput(); cannam@153: cannam@153: if (resp.available.empty()) { cannam@153: cerr << "ERROR: No plugin(s) found in library named \"" cannam@153: << libname << "\"\nCheck that the library name is correct and the right files are in $VAMP_PATH.\nThat would mean either " cannam@153: << libname << ".so, " << libname << ".dll, or " << libname cannam@153: << ".dylib (depending on your platform), as well as " cannam@153: << libname << ".n3 and " << libname << ".cat." cannam@153: << endl; cannam@153: exit(1); cannam@153: } cannam@153: cannam@153: emitFor(libname, resp); cannam@153: cannam@153: exit(0); cannam@153: }