| cannam@138 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| cannam@138 | 2 | 
| cannam@138 | 3 #include "vamp-sdk/PluginHostAdapter.h" | 
| cannam@138 | 4 #include "vamp-sdk/hostext/PluginChannelAdapter.h" | 
| cannam@138 | 5 #include "vamp-sdk/hostext/PluginInputDomainAdapter.h" | 
| cannam@138 | 6 #include "vamp-sdk/hostext/PluginLoader.h" | 
| cannam@138 | 7 #include "vamp/vamp.h" | 
| cannam@138 | 8 | 
| cannam@138 | 9 #include <iostream> | 
| cannam@138 | 10 #include <fstream> | 
| cannam@138 | 11 #include <sstream> | 
| cannam@138 | 12 #include <sndfile.h> | 
| cannam@138 | 13 | 
| cannam@138 | 14 #include <cmath> | 
| cannam@138 | 15 | 
| cannam@138 | 16 using std::cout; | 
| cannam@138 | 17 using std::cin; | 
| cannam@138 | 18 using std::cerr; | 
| cannam@138 | 19 using std::getline; | 
| cannam@138 | 20 using std::endl; | 
| cannam@138 | 21 using std::string; | 
| cannam@138 | 22 using std::vector; | 
| cannam@138 | 23 using std::ofstream; | 
| cannam@138 | 24 using std::ios; | 
| cannam@138 | 25 | 
| cannam@138 | 26 using Vamp::HostExt::PluginLoader; | 
| cannam@138 | 27 using Vamp::Plugin; | 
| cannam@138 | 28 | 
| cannam@138 | 29 string programURI = "http://www.vamp-plugins.org/doap.rdf#template-generator"; | 
| cannam@138 | 30 | 
| cannam@138 | 31 void usage() | 
| cannam@138 | 32 { | 
| cannam@144 | 33     cerr << "usage: template-generator -i vamp:soname[:plugin] [vamp:soname[:plugin] ...]" << endl; | 
| cannam@144 | 34     cerr << "usage: template-generator PLUGIN_BASE_URI YOUR_URI vamp:soname[:plugin] [vamp:soname[:plugin] ...]" << endl; | 
| cannam@138 | 35     exit(2); | 
| cannam@138 | 36 } | 
| cannam@138 | 37 | 
| cannam@138 | 38 template <class T> | 
| cannam@138 | 39 inline string to_string (const T& t) | 
| cannam@138 | 40 { | 
| cannam@138 | 41     std::stringstream ss; | 
| cannam@138 | 42     ss << t; | 
| cannam@138 | 43     return ss.str(); | 
| cannam@138 | 44 } | 
| cannam@138 | 45 | 
| cannam@144 | 46 string describe_namespaces(string pluginBundleBaseURI, string libname) | 
| cannam@138 | 47 { | 
| cannam@142 | 48     string res=\ | 
| cannam@142 | 49         "@prefix rdfs:     <http://www.w3.org/2000/01/rdf-schema#> .\n\ | 
| cannam@138 | 50 @prefix xsd:      <http://www.w3.org/2001/XMLSchema#> .\n\ | 
| cannam@147 | 51 @prefix vamp:     <http://purl.org/ontology/vamp/> .\n\ | 
| cannam@144 | 52 @prefix plugbase: <"+pluginBundleBaseURI+libname+"#> .\n\ | 
| cannam@138 | 53 @prefix owl:      <http://www.w3.org/2002/07/owl#> .\n\ | 
| cannam@138 | 54 @prefix dc:       <http://purl.org/dc/elements/1.1/> .\n\ | 
| cannam@138 | 55 @prefix af:       <http://purl.org/ontology/af/> .\n\ | 
| cannam@138 | 56 @prefix foaf:     <http://xmlns.com/foaf/0.1/> .\n\ | 
| cannam@138 | 57 @prefix cc:       <http://web.resource.org/cc/> .\n\ | 
| cannam@138 | 58 @prefix :         <> .\n\n"; | 
| cannam@138 | 59 | 
| cannam@142 | 60     return res; | 
| cannam@138 | 61 } | 
| cannam@138 | 62 | 
| cannam@144 | 63 string describe_doc(string describerURI, string pluginBundleBaseURI, | 
| cannam@144 | 64                     string libname) | 
| cannam@138 | 65 { | 
| cannam@142 | 66     string res=\ | 
| cannam@142 | 67         "<>  a   vamp:PluginDescription ;\n\ | 
| cannam@144 | 68     foaf:maker          <"+describerURI+"> ;\n\ | 
| cannam@144 | 69     foaf:maker          <"+programURI+"> ;\n\ | 
| cannam@144 | 70     foaf:primaryTopic   <"+pluginBundleBaseURI+libname+"> .\n\n"; | 
| cannam@142 | 71     return res; | 
| cannam@138 | 72 } | 
| cannam@138 | 73 | 
| cannam@138 | 74 | 
| cannam@144 | 75 string describe_library(string libname, vector<Plugin *> plugins) | 
| cannam@144 | 76 { | 
| cannam@144 | 77     string res=\ | 
| cannam@144 | 78         ":"+libname+" a  vamp:PluginLibrary ;\n\ | 
| cannam@144 | 79     vamp:identifier \""+libname+"\" "; | 
| cannam@144 | 80 | 
| cannam@144 | 81     for (size_t i = 0; i < plugins.size(); ++i) { | 
| cannam@144 | 82         res += "; \n\ | 
| cannam@145 | 83     vamp:available_plugin plugbase:"+plugins[i]->getIdentifier(); | 
| cannam@144 | 84     } | 
| cannam@144 | 85 | 
| cannam@144 | 86     res += " .\n\n"; | 
| cannam@144 | 87     return res; | 
| cannam@144 | 88 } | 
| cannam@144 | 89 | 
| cannam@138 | 90 string describe_plugin(Plugin* plugin) | 
| cannam@138 | 91 { | 
| cannam@142 | 92     string res=\ | 
| cannam@142 | 93         "plugbase:"+plugin->getIdentifier()+" a   vamp:Plugin ;\n\ | 
| cannam@138 | 94     dc:title              \""+plugin->getName()+"\" ;\n\ | 
| cannam@139 | 95     vamp:name             \""+plugin->getName()+"\" ;\n\ | 
| cannam@138 | 96     dc:description        \""+plugin->getDescription()+"\" ;\n\ | 
| cannam@138 | 97     foaf:maker            [ foaf:name \""+plugin->getMaker()+"\"] ; # FIXME could give plugin author's URI here\n\ | 
| cannam@138 | 98     cc:license            <FIXME license for the plugin> ; \n\ | 
| cannam@138 | 99     vamp:identifier       \""+plugin->getIdentifier()+"\" ;\n\ | 
| cannam@138 | 100     vamp:vamp_API_version vamp:api_version_"+to_string(plugin->getVampApiVersion())+" ;\n\ | 
| cannam@138 | 101     owl:versionInfo       \""+to_string(plugin->getPluginVersion())+"\" ;\n"; | 
| cannam@142 | 102     if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) | 
| cannam@142 | 103         res+="    vamp:input_domain     vamp:FrequencyDomain ;\n\n"; | 
| cannam@142 | 104     else | 
| cannam@142 | 105         res+="    vamp:input_domain     vamp:TimeDomain ;\n\n"; | 
| cannam@138 | 106 | 
| cannam@138 | 107 | 
| cannam@142 | 108     Plugin::ParameterList params = plugin->getParameterDescriptors(); | 
| cannam@142 | 109     for (Plugin::ParameterList::const_iterator i = params.begin(); i != params.end(); i++) | 
| cannam@144 | 110         res+="    vamp:parameter_descriptor   plugbase:"+plugin->getIdentifier()+"_param_"+(*i).identifier+" ;\n"; | 
| cannam@142 | 111     res+="\n"; | 
| cannam@138 | 112 | 
| cannam@142 | 113     Plugin::OutputList outputs = plugin->getOutputDescriptors(); | 
| cannam@142 | 114     for (Plugin::OutputList::const_iterator i = outputs.begin(); i!= outputs.end(); i++) | 
| cannam@144 | 115         res+="    vamp:output_descriptor      plugbase:"+plugin->getIdentifier()+"_output_"+(*i).identifier+" ;\n"; | 
| cannam@142 | 116     res+="    .\n"; | 
| cannam@138 | 117 | 
| cannam@142 | 118     return res; | 
| cannam@138 | 119 } | 
| cannam@138 | 120 | 
| cannam@144 | 121 string describe_param(Plugin *plugin, Plugin::ParameterDescriptor p) | 
| cannam@138 | 122 { | 
| cannam@148 | 123 | 
| cannam@148 | 124     //FIXME: dc:format and vamp:unit are the same??? | 
| cannam@148 | 125     if(p.isQuantized){ | 
| cannam@148 | 126      string res=\ | 
| cannam@148 | 127         "plugbase:"+plugin->getIdentifier()+"_param_"+p.identifier+" a  vamp:QuantizedParameterDescriptor ;\n\ | 
| cannam@148 | 128     vamp:identifier     \""+p.identifier+"\" ;\n\ | 
| cannam@148 | 129     dc:title            \""+p.name+"\" ;\n\ | 
| cannam@148 | 130     dc:format           \""+p.unit+"\" ;\n\ | 
| cannam@148 | 131     vamp:min_value       "+to_string(p.minValue)+" ;\n\ | 
| cannam@148 | 132     vamp:max_value       "+to_string(p.maxValue)+" ;\n\ | 
| cannam@148 | 133     vamp:unit           \""+p.unit+"\" ;\n\ | 
| cannam@148 | 134     vamo:quantize_step   "+to_string(p.quantizeStep)+"  ;\n\ | 
| cannam@148 | 135     vamp:default_value   "+to_string(p.defaultValue)+" ;\n\ | 
| cannam@148 | 136     vamp:value_names     ("; | 
| cannam@148 | 137 | 
| cannam@148 | 138             unsigned int i; | 
| cannam@148 | 139             for (i=0; i+1 < p.valueNames.size(); i++) | 
| cannam@148 | 140                 res+=" \""+p.valueNames[i]+"\""; | 
| cannam@148 | 141             if (i < p.valueNames.size()) | 
| cannam@148 | 142                 res+=" \""+p.valueNames[i]+"\""; | 
| cannam@148 | 143             res+=");\n"; | 
| cannam@148 | 144 | 
| cannam@148 | 145     res+="    .\n"; | 
| cannam@148 | 146 | 
| cannam@148 | 147     return res; | 
| cannam@148 | 148 | 
| cannam@148 | 149     }else{ | 
| cannam@142 | 150     string res=\ | 
| cannam@144 | 151         "plugbase:"+plugin->getIdentifier()+"_param_"+p.identifier+" a  vamp:ParameterDescriptor ;\n\ | 
| cannam@138 | 152     vamp:identifier     \""+p.identifier+"\" ;\n\ | 
| cannam@138 | 153     dc:title            \""+p.name+"\" ;\n\ | 
| cannam@138 | 154     dc:format           \""+p.unit+"\" ;\n\ | 
| cannam@139 | 155     vamp:min_value       "+to_string(p.minValue)+" ;\n\ | 
| cannam@139 | 156     vamp:max_value       "+to_string(p.maxValue)+" ;\n\ | 
| cannam@148 | 157     vamp:unit           \""+p.unit+"\"  ;\n\ | 
| cannam@148 | 158     vamp:default_value   "+to_string(p.defaultValue)+" ;\n\ | 
| cannam@148 | 159     vamp:value_names     ("; | 
| cannam@148 | 160 | 
| cannam@148 | 161             unsigned int i; | 
| cannam@148 | 162             for (i=0; i+1 < p.valueNames.size(); i++) | 
| cannam@148 | 163                 res+=" \""+p.valueNames[i]+"\""; | 
| cannam@148 | 164             if (i < p.valueNames.size()) | 
| cannam@148 | 165                 res+=" \""+p.valueNames[i]+"\""; | 
| cannam@148 | 166             res+=");\n"; | 
| cannam@148 | 167 | 
| cannam@148 | 168     res+="    .\n"; | 
| cannam@148 | 169 | 
| cannam@142 | 170     return res; | 
| cannam@148 | 171 | 
| cannam@148 | 172     } | 
| cannam@138 | 173 } | 
| cannam@138 | 174 | 
| cannam@144 | 175 string describe_output(Plugin *plugin, Plugin::OutputDescriptor o) | 
| cannam@138 | 176 { | 
| cannam@138 | 177 | 
| cannam@142 | 178     //we need to distinguish here between different output types: | 
| cannam@148 | 179 | 
| cannam@148 | 180 //Quantize or not | 
| cannam@148 | 181 //KnownExtents or not | 
| cannam@148 | 182 //Data output classification: | 
| cannam@142 | 183     //DenseOutput | 
| cannam@142 | 184     //SparseOutput | 
| cannam@142 | 185     //TrackLevelOutput | 
| cannam@139 | 186 | 
| cannam@139 | 187 | 
| cannam@142 | 188     //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. | 
| cannam@139 | 189 | 
| cannam@142 | 190     string res; | 
| cannam@139 | 191 | 
| cannam@142 | 192     if (o.sampleType == Plugin::OutputDescriptor::VariableSampleRate) | 
| cannam@142 | 193     { | 
| cannam@139 | 194 | 
| cannam@142 | 195         res=\ | 
| cannam@144 | 196             "plugbase:"+plugin->getIdentifier()+"_output_"+o.identifier+" a  vamp:SparseOutput ;\n\ | 
| cannam@139 | 197     vamp:identifier       \""+o.identifier+"\" ;\n\ | 
| cannam@139 | 198     dc:title              \""+o.name+"\" ;\n\ | 
| cannam@139 | 199     dc:description        \""+o.description+"\"  ;\n\ | 
| cannam@139 | 200     vamp:fixed_bin_count  \""+(o.hasFixedBinCount == 1 ? "true" : "false")+"\" ;\n\ | 
| cannam@139 | 201     vamp:unit             \""+(o.unit)+"\" ;\n"; | 
| cannam@139 | 202 | 
| cannam@148 | 203 | 
| cannam@148 | 204         //another type of output | 
| cannam@148 | 205         if(o.isQuantized){ | 
| cannam@148 | 206 | 
| cannam@148 | 207             res+="    a                     vamp:QuantizedOutput ;\n"; | 
| cannam@148 | 208             res+="    vamp:quantize_step    "+to_string(o.quantizeStep)+"  ;\n"; | 
| cannam@148 | 209         } | 
| cannam@148 | 210 | 
| cannam@148 | 211         //and yet another type | 
| cannam@148 | 212         if(o.hasKnownExtents){ | 
| cannam@148 | 213 | 
| cannam@148 | 214             res+="    a                 vamp:KnownExtentsOutput ;\n"; | 
| cannam@148 | 215             res+="    vamp:min_value    "+to_string(o.minValue)+"  ;\n"; | 
| cannam@148 | 216             res+="    vamp:max_value    "+to_string(o.maxValue)+"  ;\n"; | 
| cannam@148 | 217         } | 
| cannam@139 | 218 | 
| cannam@142 | 219         // FIXME ? Bin names may vary based on plugin setup, so including them here might be misleading... | 
| cannam@142 | 220         if (o.hasFixedBinCount) | 
| cannam@142 | 221         { | 
| cannam@144 | 222             res+="    vamp:bin_count        "+to_string(o.binCount)+" ;\n"; | 
| cannam@144 | 223             res+="    vamp:bin_names        ("; | 
| cannam@138 | 224 | 
| cannam@142 | 225             unsigned int i; | 
| cannam@142 | 226             for (i=0; i+1 < o.binNames.size(); i++) | 
| cannam@142 | 227                 res+=" \""+o.binNames[i]+"\""; | 
| cannam@142 | 228             if (i < o.binNames.size()) | 
| cannam@142 | 229                 res+=" \""+o.binNames[i]+"\""; | 
| cannam@142 | 230             res+=");\n"; | 
| cannam@142 | 231         } | 
| cannam@148 | 232 | 
| cannam@144 | 233         res+="    vamp:sample_type      vamp:VariableSampleRate ;\n"; | 
| cannam@142 | 234         if (o.sampleRate > 0.0f) | 
| cannam@144 | 235             res+="    vamp:sample_rate      "+to_string(o.sampleRate)+" ;\n"; | 
| cannam@142 | 236 | 
| cannam@142 | 237     } | 
| cannam@139 | 238 | 
| cannam@142 | 239     //If we do not have SparseOutput, then we have DenseOutput. TrackLevelOutput can not be inferred from the plugin directly without actually | 
| cannam@142 | 240     //running the plugin. | 
| cannam@142 | 241     else{ | 
| cannam@142 | 242 | 
| cannam@142 | 243         res=\ | 
| cannam@144 | 244             "plugbase:"+plugin->getIdentifier()+"_output_"+o.identifier+" a  vamp:DenseOutput ;\n\ | 
| cannam@139 | 245     vamp:identifier       \""+o.identifier+"\" ;\n\ | 
| cannam@139 | 246     dc:title              \""+o.name+"\" ;\n\ | 
| cannam@139 | 247     dc:description        \""+o.description+"\"  ;\n\ | 
| cannam@139 | 248     vamp:fixed_bin_count  \""+(o.hasFixedBinCount == 1 ? "true" : "false")+"\" ;\n\ | 
| cannam@139 | 249     vamp:unit             \""+(o.unit)+"\" ;\n"; | 
| cannam@139 | 250 | 
| cannam@139 | 251 | 
| cannam@148 | 252         //another type of output | 
| cannam@148 | 253         if(o.isQuantized){ | 
| cannam@148 | 254 | 
| cannam@148 | 255             res+="    a                     vamp:QuantizedOutput ;\n"; | 
| cannam@148 | 256             res+="    vamp:quantize_step    "+to_string(o.quantizeStep)+"  ;\n"; | 
| cannam@148 | 257         } | 
| cannam@148 | 258 | 
| cannam@148 | 259         //and yet another type | 
| cannam@148 | 260         if(o.hasKnownExtents){ | 
| cannam@148 | 261 | 
| cannam@148 | 262             res+="    a                 vamp:KnownExtentsOutput ;\n"; | 
| cannam@148 | 263             res+="    vamp:min_value    "+to_string(o.minValue)+"  ;\n"; | 
| cannam@148 | 264             res+="    vamp:max_value    "+to_string(o.maxValue)+"  ;\n"; | 
| cannam@148 | 265         } | 
| cannam@139 | 266 | 
| cannam@142 | 267         // FIXME ? Bin names may vary based on plugin setup, so including them here might be misleading... | 
| cannam@142 | 268         if (o.hasFixedBinCount) | 
| cannam@142 | 269         { | 
| cannam@144 | 270             res+="    vamp:bin_count        "+to_string(o.binCount)+" ;\n"; | 
| cannam@144 | 271             res+="    vamp:bin_names        ("; | 
| cannam@139 | 272 | 
| cannam@142 | 273             unsigned int i; | 
| cannam@142 | 274             for (i=0; i+1 < o.binNames.size(); i++) | 
| cannam@142 | 275                 res+=" \""+o.binNames[i]+"\""; | 
| cannam@142 | 276             if (i < o.binNames.size()) | 
| cannam@142 | 277                 res+=" \""+o.binNames[i]+"\""; | 
| cannam@142 | 278             res+=");\n"; | 
| cannam@139 | 279         } | 
| cannam@139 | 280 | 
| cannam@142 | 281         else if (o.sampleType == Plugin::OutputDescriptor::FixedSampleRate) | 
| cannam@142 | 282         { | 
| cannam@144 | 283             res+="    vamp:sample_type      vamp:FixedSampleRate ;\n"; | 
| cannam@144 | 284             res+="    vamp:sample_rate      "+to_string(o.sampleRate)+" ;\n"; | 
| cannam@142 | 285         } | 
| cannam@142 | 286         else if (o.sampleType == Plugin::OutputDescriptor::OneSamplePerStep) | 
| cannam@144 | 287             res+="    vamp:sample_type      vamp:OneSamplePerStep ;\n"; | 
| cannam@142 | 288         else | 
| cannam@142 | 289         { | 
| cannam@142 | 290             cerr<<"Incomprehensible sampleType for output descriptor "+o.identifier<<" !"<<endl; | 
| cannam@142 | 291             exit(1); | 
| cannam@142 | 292         } | 
| cannam@142 | 293     } | 
| cannam@142 | 294 | 
| cannam@142 | 295     //There is no way to know this in advance, but we can use the km a bit for this. | 
| cannam@142 | 296     res+="    vamp:computes_feature_type  <FIXME feature type URI> ;\n"; | 
| cannam@142 | 297     res+="    vamp:computes_event_type    <FIXME event type URI> ;\n"; | 
| cannam@142 | 298     res+="    .\n"; | 
| cannam@142 | 299 | 
| cannam@142 | 300     return res; | 
| cannam@138 | 301 } | 
| cannam@139 | 302 | 
| cannam@144 | 303 string describe(vector<Plugin *> plugins, string pluginBundleBaseURI, | 
| cannam@144 | 304                 string describerURI, string libname) | 
| cannam@138 | 305 { | 
| cannam@144 | 306     string res = describe_namespaces(pluginBundleBaseURI, libname); | 
| cannam@138 | 307 | 
| cannam@144 | 308     res += describe_doc(describerURI, pluginBundleBaseURI, libname); | 
| cannam@138 | 309 | 
| cannam@144 | 310     res += describe_library(libname, plugins); | 
| cannam@144 | 311 | 
| cannam@144 | 312     for (size_t i = 0; i < plugins.size(); ++i) { | 
| cannam@144 | 313 | 
| cannam@144 | 314         Plugin *plugin = plugins[i]; | 
| cannam@144 | 315 | 
| cannam@144 | 316         res += describe_plugin(plugin); | 
| cannam@138 | 317 | 
| cannam@144 | 318         Plugin::ParameterList params = plugin->getParameterDescriptors(); | 
| cannam@144 | 319         for (Plugin::ParameterList::const_iterator i = params.begin(); i != params.end(); i++) | 
| cannam@144 | 320             res += describe_param(plugin, *i); | 
| cannam@138 | 321 | 
| cannam@144 | 322         Plugin::OutputList outputs = plugin->getOutputDescriptors(); | 
| cannam@144 | 323         for (Plugin::OutputList::const_iterator i = outputs.begin(); i!= outputs.end(); i++) | 
| cannam@144 | 324             res += describe_output(plugin, *i); | 
| cannam@144 | 325     } | 
| cannam@138 | 326 | 
| cannam@142 | 327     return res; | 
| cannam@138 | 328 } | 
| cannam@138 | 329 | 
| cannam@138 | 330 int main(int argc, char **argv) | 
| cannam@138 | 331 { | 
| cannam@144 | 332     if (argc < 3) usage(); | 
| cannam@138 | 333 | 
| cannam@144 | 334     bool interactive = false; | 
| cannam@144 | 335     if (!strcmp(argv[1], "-i")) interactive = true; | 
| cannam@138 | 336 | 
| cannam@144 | 337     if (!interactive && argc < 4) usage(); | 
| cannam@138 | 338 | 
| cannam@138 | 339     string pluginBundleBaseURI, describerURI; | 
| cannam@144 | 340 | 
| cannam@144 | 341     int argidx = 2; | 
| cannam@138 | 342 | 
| cannam@144 | 343     if (!interactive) { | 
| cannam@138 | 344         pluginBundleBaseURI = argv[1]; | 
| cannam@138 | 345         describerURI = argv[2]; | 
| cannam@144 | 346         argidx = 3; | 
| cannam@144 | 347     } else { | 
| cannam@138 | 348         cerr << "Please enter the base URI for the plugin bundle : "; | 
| cannam@138 | 349         getline(cin, pluginBundleBaseURI); | 
| cannam@138 | 350         cerr << "Please enter your URI : "; | 
| cannam@138 | 351         getline(cin, describerURI); | 
| cannam@138 | 352     } | 
| cannam@144 | 353 | 
| cannam@144 | 354     vector<Plugin *> plugins; | 
| cannam@144 | 355     string libname; | 
| cannam@144 | 356 | 
| cannam@144 | 357     PluginLoader *loader = PluginLoader::getInstance(); | 
| cannam@144 | 358 | 
| cannam@144 | 359     while (argidx < argc) { | 
| cannam@144 | 360 | 
| cannam@144 | 361         string pluginName = argv[argidx]; | 
| cannam@144 | 362 | 
| cannam@144 | 363         if (pluginName.substr(0, 5) == "vamp:") { | 
| cannam@144 | 364             pluginName = pluginName.substr(5); | 
| cannam@144 | 365         } | 
| cannam@144 | 366 | 
| cannam@144 | 367         string mylibname = pluginName.substr(0, pluginName.find(':')); | 
| cannam@144 | 368 | 
| cannam@144 | 369         if (libname == "") libname = mylibname; | 
| cannam@144 | 370         else if (libname != mylibname) { | 
| cannam@144 | 371             cerr << "ERROR: All plugins specified on command line must originate in the same library" << endl; | 
| cannam@144 | 372             exit(1); | 
| cannam@144 | 373         } | 
| cannam@144 | 374 | 
| cannam@144 | 375         if (mylibname == pluginName) { // pluginName is a library, not a plugin | 
| cannam@144 | 376 | 
| cannam@144 | 377             PluginLoader::PluginKeyList list = loader->listPlugins(); | 
| cannam@144 | 378             for (size_t i = 0; i < list.size(); ++i) { | 
| cannam@144 | 379                 string thislibname = list[i].substr(0, list[i].find(':')); | 
| cannam@144 | 380                 if (thislibname != mylibname) continue; | 
| cannam@144 | 381                 Plugin *plugin = loader->loadPlugin(list[i], 44100); | 
| cannam@144 | 382                 if (!plugin) { | 
| cannam@144 | 383                     cerr << "ERROR: Plugin \"" << list[i] << "\" could not be loaded" << endl; | 
| cannam@144 | 384                     exit(1); | 
| cannam@144 | 385                 } | 
| cannam@144 | 386                 plugins.push_back(plugin); | 
| cannam@144 | 387             } | 
| cannam@144 | 388         } else { // pluginName is a plugin | 
| cannam@144 | 389 | 
| cannam@144 | 390             Plugin *plugin = loader->loadPlugin(pluginName, size_t(44100)); | 
| cannam@144 | 391             if (!plugin) { | 
| cannam@144 | 392                 cerr << "ERROR: Plugin \"" << pluginName << "\" could not be loaded" << endl; | 
| cannam@144 | 393                 exit(1); | 
| cannam@144 | 394             } | 
| cannam@144 | 395             plugins.push_back(plugin); | 
| cannam@144 | 396         } | 
| cannam@144 | 397 | 
| cannam@144 | 398         ++argidx; | 
| cannam@144 | 399     } | 
| cannam@138 | 400 | 
| cannam@144 | 401     cout << describe(plugins, pluginBundleBaseURI, describerURI, libname) << endl; | 
| cannam@138 | 402 | 
| cannam@138 | 403     return 0; | 
| cannam@138 | 404 } | 
| cannam@138 | 405 | 
| cannam@138 | 406 |