diff generator/generator.cpp @ 153:38675dcea44f

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