view rdf/generator/vamp-rdf-template-generator.cpp @ 525:8c18bdaad04f c++11-mutex

Avoid simple static allocation of mutex, as it could lead to mutex being destroyed before last adapter that needs to use it (since adapters are usually also static)
author Chris Cannam
date Mon, 09 Sep 2019 10:24:13 +0100
parents aede6e90a6b8
children
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

#include <vamp-hostsdk/PluginHostAdapter.h>
#include <vamp-hostsdk/PluginChannelAdapter.h>
#include <vamp-hostsdk/PluginInputDomainAdapter.h>
#include <vamp-hostsdk/PluginLoader.h>
#include <vamp/vamp.h>

#include <iostream>
#include <fstream>
#include <sstream>

#include <cmath>
#include <cstdlib>
#include <cstring>

#include <cstdlib>
#include <cstring>

using std::cout;
using std::cin;
using std::cerr;
using std::getline;
using std::endl;
using std::string;
using std::vector;
using std::ofstream;
using std::ios;

using Vamp::HostExt::PluginLoader;
using Vamp::Plugin;

string programURI = "http://vamp-plugins.org/rdf/template-generator";

void usage()
{
    cerr << endl;
    cerr << "vamp-rdf-template-generator: Create a skeleton RDF description file describing" << endl;
    cerr << "a Vamp plugin library using the Vamp ontology." << endl;
    cerr << endl;
    cerr << "Usage:" << endl;
    cerr << "   vamp-rdf-template-generator -i vamp:soname[:plugin] [vamp:soname[:plugin] ...]" << endl;
    cerr << "   vamp-rdf-template-generator PLUGIN_BASE_URI [ -m YOUR_URI ] [vamp:]soname[:plugin] [[vamp:]soname[:plugin] ...]" << endl;
    cerr << endl;
    cerr << "Example:" << endl;
    cerr << "   vamp-rdf-template-generator http://vamp-plugins.org/rdf/plugins/ vamp-example-plugins" << endl;
    cerr << endl;
    exit(2);
}

template <class T>
inline string to_string (const T& t)
{
    std::stringstream ss;
    ss << t;
    return ss.str();
}

string describe_namespaces(string pluginBundleBaseURI, string libname)
{
    string res=\
        "@prefix rdfs:     <http://www.w3.org/2000/01/rdf-schema#> .\n\
@prefix xsd:      <http://www.w3.org/2001/XMLSchema#> .\n\
@prefix vamp:     <http://purl.org/ontology/vamp/> .\n\
@prefix plugbase: <"+pluginBundleBaseURI+libname+"#> .\n\
@prefix owl:      <http://www.w3.org/2002/07/owl#> .\n\
@prefix dc:       <http://purl.org/dc/elements/1.1/> .\n\
@prefix af:       <http://purl.org/ontology/af/> .\n\
@prefix foaf:     <http://xmlns.com/foaf/0.1/> .\n\
@prefix doap:     <http://usefulinc.com/ns/doap#> .\n\
@prefix cc:       <http://web.resource.org/cc/> .\n\
@prefix :         <#> .\n\n";
	
    return res;
}

string describe_doc(string describerURI, string pluginBundleBaseURI,
                    string libname)
{
    string res = "\n## Properties of this document\n\n\
<> a vamp:PluginDescription ;\n";

    if (describerURI != "") {
        res += "    foaf:maker          <"+describerURI+"> ;\n";
    }

    res += "\
    foaf:maker         <"+programURI+"> ;\n\
    foaf:primaryTopic  <"+pluginBundleBaseURI+libname+"> .\n\n";
    return res;
}

bool have_multiple_makers(vector<Plugin *> plugins)
{
    string firstMaker = "";
    for (size_t i = 0; i < plugins.size(); ++i) {
        if (i == 0) {
            firstMaker = plugins[i]->getMaker();
        } else if (plugins[i]->getMaker() != firstMaker) {
            return true;
        }
    }
    return false;
}

string describe_maker(vector<Plugin *> plugins, bool multipleMakers)
{
    string res = "\n## Maker of the whole plugin library\n\n\
:library_maker\n";

    if (!multipleMakers) {
        string name;
        if (!plugins.empty()) {
            name = plugins[0]->getMaker();
        }
        res += "\
    foaf:name  \"" + name + "\" ;\n\
#   foaf:page  <> ;  # Place maker's homepage URL in here and uncomment\n\
#   foaf:logo  <> ;  # URL of an image here, if you happen to have a logo\n";

    } else {
        res += "\
    foaf:name  \"Multiple makers\" ;\n";
    }

    res += "    .\n\n";
    return res;
}

string describe_library(string libname, vector<Plugin *> plugins)
{
    string res = "\n## Properties of the plugin library, and references to the plugins it contains\n\n\
plugbase:library a vamp:PluginLibrary ;\n\
    vamp:identifier        \""+libname+"\" ;\n\
    foaf:maker             :library_maker";

    for (size_t i = 0; i < plugins.size(); ++i) {
        res += " ; \n\
    vamp:available_plugin  plugbase:"+plugins[i]->getIdentifier();
    }

    res += " ; \n\
#   dc:title               \"\" ;  # Place library name here and uncomment\n\
#   dc:description         \"\" ;  # Place library description here and uncomment\n\
#   foaf:page              <> ;  # Place more-info HTML page URL here and uncomment\n\
#   doap:download-page     <> ;  # Place download HTML page URL here and uncomment\n\
    .\n\n";
    return res;
}

string describe_plugin(Plugin* plugin, bool multipleMakers)
{
    string res = "\n## Properties of the " + plugin->getName() + " plugin\n\n\
plugbase:"+plugin->getIdentifier()+" a vamp:Plugin ;\n\
    dc:title              \""+plugin->getName()+"\" ;\n\
    vamp:name             \""+plugin->getName()+"\" ;\n\
    dc:description        \"\"\""+plugin->getDescription()+"\"\"\" ;\n\
    foaf:maker            ";

    if (multipleMakers) {
        res += "[ foaf:name \""+plugin->getMaker()+"\" ] ;\n";
    } else {
        res += ":library_maker ;\n";
    }

    res += "\
    dc:rights             \"\"\""+plugin->getCopyright()+"\"\"\" ;\n\
#   cc:license            <Place plugin license URI here and uncomment> ; \n\
    vamp:identifier       \""+plugin->getIdentifier()+"\" ;\n\
    vamp:vamp_API_version vamp:api_version_"+to_string(plugin->getVampApiVersion())+" ;\n\
    owl:versionInfo       \""+to_string(plugin->getPluginVersion())+"\" ;\n";
    if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain)
        res+="    vamp:input_domain     vamp:FrequencyDomain ;\n\n";
    else
        res+="    vamp:input_domain     vamp:TimeDomain ;\n";
	
    Plugin::ParameterList params = plugin->getParameterDescriptors();
    for (Plugin::ParameterList::const_iterator i = params.begin(); i != params.end(); i++)
        res+="    vamp:parameter   plugbase:"+plugin->getIdentifier()+"_param_"+(*i).identifier+" ;\n";
    if (!params.empty()) res+="\n";

    Plugin::OutputList outputs = plugin->getOutputDescriptors();
    for (Plugin::OutputList::const_iterator i = outputs.begin(); i!= outputs.end(); i++)
        res+="    vamp:output      plugbase:"+plugin->getIdentifier()+"_output_"+(*i).identifier+" ;\n";
    res+="    .\n";
	
    return res;
}

string describe_param(Plugin *plugin, Plugin::ParameterDescriptor p)
{

    //FIXME: dc:format and vamp:unit are the same???
    //Should be a QUantizedParameter also a Parameter??
    if(p.isQuantized){
     string res=\
        "plugbase:"+plugin->getIdentifier()+"_param_"+p.identifier+" a  vamp:QuantizedParameter ;\n\
    vamp:identifier     \""+p.identifier+"\" ;\n\
    dc:title            \""+p.name+"\" ;\n\
    dc:format           \""+p.unit+"\" ;\n\
    vamp:min_value       "+to_string(p.minValue)+" ;\n\
    vamp:max_value       "+to_string(p.maxValue)+" ;\n\
    vamp:unit           \""+p.unit+"\" ;\n\
    vamp:quantize_step   "+to_string(p.quantizeStep)+"  ;\n\
    vamp:default_value   "+to_string(p.defaultValue)+" ;\n\
    vamp:value_names     (";

            unsigned int i;
            for (i=0; i+1 < p.valueNames.size(); i++)
                res+=" \""+p.valueNames[i]+"\"";
            if (i < p.valueNames.size())
                res+=" \""+p.valueNames[i]+"\"";
            res+=");\n";
    
    res+="    .\n";

    return res;
            
    }else{
    string res=\
        "plugbase:"+plugin->getIdentifier()+"_param_"+p.identifier+" a  vamp:Parameter ;\n\
    vamp:identifier     \""+p.identifier+"\" ;\n\
    dc:title            \""+p.name+"\" ;\n\
    dc:format           \""+p.unit+"\" ;\n\
    vamp:min_value       "+to_string(p.minValue)+" ;\n\
    vamp:max_value       "+to_string(p.maxValue)+" ;\n\
    vamp:unit           \""+p.unit+"\"  ;\n\
    vamp:default_value   "+to_string(p.defaultValue)+" ;\n\
    vamp:value_names     (";

            unsigned int i;
            for (i=0; i+1 < p.valueNames.size(); i++)
                res+=" \""+p.valueNames[i]+"\"";
            if (i < p.valueNames.size())
                res+=" \""+p.valueNames[i]+"\"";
            res+=");\n";
    
    res+="    .\n";

    return res;

    }  
}

string describe_output(Plugin *plugin, Plugin::OutputDescriptor o)
{

    //we need to distinguish here between different output types:

//Quantize or not
//KnownExtents or not
//Data output classification:
    //DenseOutput
    //SparseOutput
    //TrackLevelOutput


    // SparseOutput: variable sample rate. Events are not evenly
    // spaced so we need to record the time associated with the event
    // as it its not ensured that we have an event after the next one
    // (but there is not time to set the duration, it has to be
    // calculated as the different between 2 different events). The
    // timestamp must be read.

    string res;

    if (o.sampleType == Plugin::OutputDescriptor::VariableSampleRate ||
        !o.hasFixedBinCount)
    {

        res=\
            "plugbase:"+plugin->getIdentifier()+"_output_"+o.identifier+" a  vamp:SparseOutput ;\n\
    vamp:identifier       \""+o.identifier+"\" ;\n\
    dc:title              \""+o.name+"\" ;\n\
    dc:description        \"\"\""+o.description+"\"\"\"  ;\n\
    vamp:fixed_bin_count  \""+(o.hasFixedBinCount == 1 ? "true" : "false")+"\" ;\n\
    vamp:unit             \""+(o.unit)+"\" ;\n";
                          
             
        //another type of output           
        if(o.isQuantized){

            res+="    a                     vamp:QuantizedOutput ;\n";
            res+="    vamp:quantize_step    "+to_string(o.quantizeStep)+"  ;\n";    
        }
        
        //and yet another type
        if(o.hasKnownExtents){

            res+="    a                 vamp:KnownExtentsOutput ;\n";
            res+="    vamp:min_value    "+to_string(o.minValue)+"  ;\n"; 
            res+="    vamp:max_value    "+to_string(o.maxValue)+"  ;\n";    
        }

        // FIXME ? Bin names may vary based on plugin setup, so including them here might be misleading...
        if (o.hasFixedBinCount)
        {
            res+="    vamp:bin_count        "+to_string(o.binCount)+" ;\n";

            bool haveBinNames = false;
            for (unsigned int i=0; i < o.binNames.size(); i++) {
                if (o.binNames[i] != "") {
                    haveBinNames = true;
                    break;
                }
            }
            
            if (haveBinNames) {
                res+="    vamp:bin_names        (";

                unsigned int i;
                for (i=0; i+1 < o.binNames.size(); i++)
                    res+=" \""+o.binNames[i]+"\"";
                if (i < o.binNames.size())
                    res+=" \""+o.binNames[i]+"\"";
                res+=");\n";
            }
        }
       
        res+="    vamp:sample_type      vamp:VariableSampleRate ;\n";
        if (o.sampleRate > 0.0f)
            res+="    vamp:sample_rate      "+to_string(o.sampleRate)+" ;\n";
	        
    }

    //If we do not have SparseOutput, then we have DenseOutput. TrackLevelOutput can not be inferred from the plugin directly without actually
    //running the plugin.
    else{

        res=\
            "plugbase:"+plugin->getIdentifier()+"_output_"+o.identifier+" a  vamp:DenseOutput ;\n\
    vamp:identifier       \""+o.identifier+"\" ;\n\
    dc:title              \""+o.name+"\" ;\n\
    dc:description        \"\"\""+o.description+"\"\"\"  ;\n\
    vamp:fixed_bin_count  \""+(o.hasFixedBinCount == 1 ? "true" : "false")+"\" ;\n\
    vamp:unit             \""+(o.unit)+"\" ;\n";


        //another type of output           
        if(o.isQuantized){

            res+="    a                     vamp:QuantizedOutput ;\n";
            res+="    vamp:quantize_step    "+to_string(o.quantizeStep)+"  ;\n";    
        }
        
        //and yet another type
        if(o.hasKnownExtents){

            res+="    a                 vamp:KnownExtentsOutput ;\n";
            res+="    vamp:min_value    "+to_string(o.minValue)+"  ;\n"; 
            res+="    vamp:max_value    "+to_string(o.maxValue)+"  ;\n";    
        }

        // FIXME ? Bin names may vary based on plugin setup, so including them here might be misleading...
        if (o.hasFixedBinCount)
        {
            res+="    vamp:bin_count        "+to_string(o.binCount)+" ;\n";

            bool haveBinNames = false;
            for (unsigned int i=0; i < o.binNames.size(); i++) {
                if (o.binNames[i] != "") {
                    haveBinNames = true;
                    break;
                }
            }

            if (haveBinNames) {
                res+="    vamp:bin_names        (";

                unsigned int i;
                for (i=0; i+1 < o.binNames.size(); i++)
                    res+=" \""+o.binNames[i]+"\"";
                if (i < o.binNames.size())
                    res+=" \""+o.binNames[i]+"\"";
                res+=");\n";
            }
        }

        else if (o.sampleType == Plugin::OutputDescriptor::FixedSampleRate)
        {
            res+="    vamp:sample_type      vamp:FixedSampleRate ;\n";
            res+="    vamp:sample_rate      "+to_string(o.sampleRate)+" ;\n";
        }
        else if (o.sampleType == Plugin::OutputDescriptor::OneSamplePerStep)
            res+="    vamp:sample_type      vamp:OneSamplePerStep ;\n";
        else
        {
            cerr<<"Incomprehensible sampleType for output descriptor "+o.identifier<<" !"<<endl;
            exit(1);
        }
    }

    //There is no way to know this in advance, but we can use the km a bit for this.
    res+="#   vamp:computes_event_type   <Place event type URI here and uncomment> ;\n";
    res+="#   vamp:computes_feature      <Place feature attribute URI here and uncomment> ;\n";
    res+="#   vamp:computes_signal_type  <Place signal type URI here and uncomment> ;\n";
    res+="    .\n";

    return res;
}

string describe(vector<Plugin *> plugins, string pluginBundleBaseURI,
                string describerURI, string libname)
{
    bool multipleMakers = have_multiple_makers(plugins);

    string res = describe_namespaces(pluginBundleBaseURI, libname);
	
    res += describe_doc(describerURI, pluginBundleBaseURI, libname);
	
    res += describe_maker(plugins, multipleMakers);
	
    res += describe_library(libname, plugins);

    for (size_t i = 0; i < plugins.size(); ++i) {

        Plugin *plugin = plugins[i];

        res += describe_plugin(plugin, multipleMakers);
	
        Plugin::ParameterList params = plugin->getParameterDescriptors();
        for (Plugin::ParameterList::const_iterator i = params.begin(); i != params.end(); i++)
            res += describe_param(plugin, *i);
	
        Plugin::OutputList outputs = plugin->getOutputDescriptors();
        for (Plugin::OutputList::const_iterator i = outputs.begin(); i!= outputs.end(); i++)
            res += describe_output(plugin, *i);
    }
	
    return res;
}

int main(int argc, char **argv)
{
    if (argc < 3) usage();

    bool interactive = false;
    if (!strcmp(argv[1], "-i")) interactive = true;

    if (!interactive && argc < 3) usage();

    string pluginBundleBaseURI, describerURI;

    int argidx = 2;
	
    if (!interactive) {
        pluginBundleBaseURI = argv[1];
        if (!strcmp(argv[2], "-m")) {
            if (argc < 5) usage();
            describerURI = argv[3];
            argidx = 4;
        }
    } else {
        cerr << "Please enter the base URI for the plugin bundle : ";
        getline(cin, pluginBundleBaseURI);
        cerr << "Please enter your URI (empty to omit) : ";
        getline(cin, describerURI);
    }

    vector<Plugin *> plugins;
    string libname;

    PluginLoader *loader = PluginLoader::getInstance();

    while (argidx < argc) {

        string pluginName = argv[argidx];

        if (pluginName.substr(0, 5) == "vamp:") {
            pluginName = pluginName.substr(5);
        }

        string mylibname = pluginName.substr(0, pluginName.find(':'));

        if (libname == "") libname = mylibname;
        else if (libname != mylibname) {
            cerr << "ERROR: All plugins specified on command line must originate in the same library" << endl;
            exit(1);
        }

        if (mylibname == pluginName) { // pluginName is a library, not a plugin

            PluginLoader::PluginKeyList list = loader->listPlugins();
            for (size_t i = 0; i < list.size(); ++i) {
                string thislibname = list[i].substr(0, list[i].find(':'));
                if (thislibname != mylibname) continue;
                Plugin *plugin = loader->loadPlugin(list[i], 44100);
                if (!plugin) {
                    cerr << "ERROR: Plugin \"" << list[i] << "\" could not be loaded" << endl;
                    exit(1);
                }
                plugins.push_back(plugin);
            }

            if (plugins.empty()) {
                cerr << "ERROR: Plugin library \"" << mylibname << "\" does not exist, could not be opened, or contains no plugins" << endl;
                exit(1);
            }

        } else { // pluginName is a plugin

            Plugin *plugin = loader->loadPlugin(pluginName, size_t(44100));
            if (!plugin) {
                cerr << "ERROR: Plugin \"" << pluginName << "\" could not be loaded" << endl;
                exit(1);
            }
            plugins.push_back(plugin);
        }

        ++argidx;
    }
	
    cout << describe(plugins, pluginBundleBaseURI, describerURI, libname) << endl;

    return 0;
}