annotate src/vamp-plugin-sdk-2.5/rdf/generator/vamp-rdf-template-generator.cpp @ 168:ceec0dd9ec9c

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