annotate transform/TransformFactory.cpp @ 451:3c5252e7cefd

* Fix (kinda) display of fixed-sample-rate features with sample rate different from the input model's rate
author Chris Cannam
date Wed, 08 Oct 2008 15:27:31 +0000
parents 804601e50fd5
children ef14acd6d102
rev   line source
Chris@330 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@330 2
Chris@330 3 /*
Chris@330 4 Sonic Visualiser
Chris@330 5 An audio file viewer and annotation editor.
Chris@330 6 Centre for Digital Music, Queen Mary, University of London.
Chris@330 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@330 8
Chris@330 9 This program is free software; you can redistribute it and/or
Chris@330 10 modify it under the terms of the GNU General Public License as
Chris@330 11 published by the Free Software Foundation; either version 2 of the
Chris@330 12 License, or (at your option) any later version. See the file
Chris@330 13 COPYING included with this distribution for more information.
Chris@330 14 */
Chris@330 15
Chris@330 16 #include "TransformFactory.h"
Chris@330 17
Chris@330 18 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@330 19 #include "plugin/RealTimePluginFactory.h"
Chris@332 20 #include "plugin/RealTimePluginInstance.h"
Chris@330 21 #include "plugin/PluginXml.h"
Chris@330 22
Chris@332 23 #include "vamp-sdk/Plugin.h"
Chris@330 24 #include "vamp-sdk/PluginHostAdapter.h"
Chris@332 25 #include "vamp-sdk/hostext/PluginWrapper.h"
Chris@330 26
Chris@446 27 #include "base/XmlExportable.h"
Chris@446 28
Chris@330 29 #include <iostream>
Chris@330 30 #include <set>
Chris@330 31
Chris@330 32 #include <QRegExp>
Chris@350 33 #include <QTextStream>
Chris@330 34
Chris@443 35 using std::cerr;
Chris@443 36 using std::endl;
Chris@443 37
Chris@330 38 TransformFactory *
Chris@330 39 TransformFactory::m_instance = new TransformFactory;
Chris@330 40
Chris@330 41 TransformFactory *
Chris@330 42 TransformFactory::getInstance()
Chris@330 43 {
Chris@330 44 return m_instance;
Chris@330 45 }
Chris@330 46
Chris@330 47 TransformFactory::~TransformFactory()
Chris@330 48 {
Chris@330 49 }
Chris@330 50
Chris@330 51 TransformList
Chris@350 52 TransformFactory::getAllTransformDescriptions()
Chris@330 53 {
Chris@330 54 if (m_transforms.empty()) populateTransforms();
Chris@330 55
Chris@330 56 std::set<TransformDescription> dset;
Chris@330 57 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 58 i != m_transforms.end(); ++i) {
Chris@443 59 // cerr << "inserting transform into set: id = " << i->second.identifier.toStdString() << endl;
Chris@330 60 dset.insert(i->second);
Chris@330 61 }
Chris@330 62
Chris@330 63 TransformList list;
Chris@330 64 for (std::set<TransformDescription>::const_iterator i = dset.begin();
Chris@330 65 i != dset.end(); ++i) {
Chris@443 66 // cerr << "inserting transform into list: id = " << i->identifier.toStdString() << endl;
Chris@330 67 list.push_back(*i);
Chris@330 68 }
Chris@330 69
Chris@330 70 return list;
Chris@330 71 }
Chris@330 72
Chris@350 73 TransformDescription
Chris@350 74 TransformFactory::getTransformDescription(TransformId id)
Chris@350 75 {
Chris@350 76 if (m_transforms.empty()) populateTransforms();
Chris@350 77
Chris@350 78 if (m_transforms.find(id) == m_transforms.end()) {
Chris@350 79 return TransformDescription();
Chris@350 80 }
Chris@350 81
Chris@350 82 return m_transforms[id];
Chris@350 83 }
Chris@350 84
Chris@330 85 std::vector<QString>
Chris@330 86 TransformFactory::getAllTransformTypes()
Chris@330 87 {
Chris@330 88 if (m_transforms.empty()) populateTransforms();
Chris@330 89
Chris@330 90 std::set<QString> types;
Chris@330 91 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 92 i != m_transforms.end(); ++i) {
Chris@330 93 types.insert(i->second.type);
Chris@330 94 }
Chris@330 95
Chris@330 96 std::vector<QString> rv;
Chris@330 97 for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@330 98 rv.push_back(*i);
Chris@330 99 }
Chris@330 100
Chris@330 101 return rv;
Chris@330 102 }
Chris@330 103
Chris@330 104 std::vector<QString>
Chris@330 105 TransformFactory::getTransformCategories(QString transformType)
Chris@330 106 {
Chris@330 107 if (m_transforms.empty()) populateTransforms();
Chris@330 108
Chris@330 109 std::set<QString> categories;
Chris@330 110 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 111 i != m_transforms.end(); ++i) {
Chris@330 112 if (i->second.type == transformType) {
Chris@330 113 categories.insert(i->second.category);
Chris@330 114 }
Chris@330 115 }
Chris@330 116
Chris@330 117 bool haveEmpty = false;
Chris@330 118
Chris@330 119 std::vector<QString> rv;
Chris@330 120 for (std::set<QString>::iterator i = categories.begin();
Chris@330 121 i != categories.end(); ++i) {
Chris@330 122 if (*i != "") rv.push_back(*i);
Chris@330 123 else haveEmpty = true;
Chris@330 124 }
Chris@330 125
Chris@330 126 if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@330 127
Chris@330 128 return rv;
Chris@330 129 }
Chris@330 130
Chris@330 131 std::vector<QString>
Chris@330 132 TransformFactory::getTransformMakers(QString transformType)
Chris@330 133 {
Chris@330 134 if (m_transforms.empty()) populateTransforms();
Chris@330 135
Chris@330 136 std::set<QString> makers;
Chris@330 137 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 138 i != m_transforms.end(); ++i) {
Chris@330 139 if (i->second.type == transformType) {
Chris@330 140 makers.insert(i->second.maker);
Chris@330 141 }
Chris@330 142 }
Chris@330 143
Chris@330 144 bool haveEmpty = false;
Chris@330 145
Chris@330 146 std::vector<QString> rv;
Chris@330 147 for (std::set<QString>::iterator i = makers.begin();
Chris@330 148 i != makers.end(); ++i) {
Chris@330 149 if (*i != "") rv.push_back(*i);
Chris@330 150 else haveEmpty = true;
Chris@330 151 }
Chris@330 152
Chris@330 153 if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@330 154
Chris@330 155 return rv;
Chris@330 156 }
Chris@330 157
Chris@330 158 void
Chris@330 159 TransformFactory::populateTransforms()
Chris@330 160 {
Chris@330 161 TransformDescriptionMap transforms;
Chris@330 162
Chris@330 163 populateFeatureExtractionPlugins(transforms);
Chris@330 164 populateRealTimePlugins(transforms);
Chris@330 165
Chris@330 166 // disambiguate plugins with similar names
Chris@330 167
Chris@330 168 std::map<QString, int> names;
Chris@330 169 std::map<QString, QString> pluginSources;
Chris@330 170 std::map<QString, QString> pluginMakers;
Chris@330 171
Chris@330 172 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@330 173 i != transforms.end(); ++i) {
Chris@330 174
Chris@330 175 TransformDescription desc = i->second;
Chris@330 176
Chris@330 177 QString td = desc.name;
Chris@330 178 QString tn = td.section(": ", 0, 0);
Chris@330 179 QString pn = desc.identifier.section(":", 1, 1);
Chris@330 180
Chris@330 181 if (pluginSources.find(tn) != pluginSources.end()) {
Chris@330 182 if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) {
Chris@330 183 ++names[tn];
Chris@330 184 }
Chris@330 185 } else {
Chris@330 186 ++names[tn];
Chris@330 187 pluginSources[tn] = pn;
Chris@330 188 pluginMakers[tn] = desc.maker;
Chris@330 189 }
Chris@330 190 }
Chris@330 191
Chris@330 192 std::map<QString, int> counts;
Chris@330 193 m_transforms.clear();
Chris@330 194
Chris@330 195 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@330 196 i != transforms.end(); ++i) {
Chris@330 197
Chris@330 198 TransformDescription desc = i->second;
Chris@330 199 QString identifier = desc.identifier;
Chris@330 200 QString maker = desc.maker;
Chris@330 201
Chris@330 202 QString td = desc.name;
Chris@330 203 QString tn = td.section(": ", 0, 0);
Chris@330 204 QString to = td.section(": ", 1);
Chris@330 205
Chris@330 206 if (names[tn] > 1) {
Chris@330 207 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@330 208 tn = QString("%1 [%2]").arg(tn).arg(maker);
Chris@330 209 }
Chris@330 210
Chris@330 211 if (to != "") {
Chris@330 212 desc.name = QString("%1: %2").arg(tn).arg(to);
Chris@330 213 } else {
Chris@330 214 desc.name = tn;
Chris@330 215 }
Chris@330 216
Chris@330 217 m_transforms[identifier] = desc;
Chris@330 218 }
Chris@330 219 }
Chris@330 220
Chris@330 221 void
Chris@330 222 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
Chris@330 223 {
Chris@330 224 std::vector<QString> plugs =
Chris@330 225 FeatureExtractionPluginFactory::getAllPluginIdentifiers();
Chris@330 226
Chris@330 227 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@330 228
Chris@330 229 QString pluginId = plugs[i];
Chris@330 230
Chris@330 231 FeatureExtractionPluginFactory *factory =
Chris@330 232 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@330 233
Chris@330 234 if (!factory) {
Chris@443 235 cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << endl;
Chris@330 236 continue;
Chris@330 237 }
Chris@330 238
Chris@330 239 Vamp::Plugin *plugin =
Chris@350 240 factory->instantiatePlugin(pluginId, 44100);
Chris@330 241
Chris@330 242 if (!plugin) {
Chris@443 243 cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << endl;
Chris@330 244 continue;
Chris@330 245 }
Chris@330 246
Chris@330 247 QString pluginName = plugin->getName().c_str();
Chris@330 248 QString category = factory->getPluginCategory(pluginId);
Chris@330 249
Chris@330 250 Vamp::Plugin::OutputList outputs =
Chris@330 251 plugin->getOutputDescriptors();
Chris@330 252
Chris@330 253 for (size_t j = 0; j < outputs.size(); ++j) {
Chris@330 254
Chris@330 255 QString transformId = QString("%1:%2")
Chris@330 256 .arg(pluginId).arg(outputs[j].identifier.c_str());
Chris@330 257
Chris@330 258 QString userName;
Chris@330 259 QString friendlyName;
Chris@330 260 QString units = outputs[j].unit.c_str();
Chris@330 261 QString description = plugin->getDescription().c_str();
Chris@330 262 QString maker = plugin->getMaker().c_str();
Chris@330 263 if (maker == "") maker = tr("<unknown maker>");
Chris@330 264
Chris@443 265 QString longDescription = description;
Chris@443 266
Chris@443 267 if (longDescription == "") {
Chris@330 268 if (outputs.size() == 1) {
Chris@443 269 longDescription = tr("Extract features using \"%1\" plugin (from %2)")
Chris@330 270 .arg(pluginName).arg(maker);
Chris@330 271 } else {
Chris@443 272 longDescription = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
Chris@330 273 .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@330 274 }
Chris@330 275 } else {
Chris@330 276 if (outputs.size() == 1) {
Chris@443 277 longDescription = tr("%1 using \"%2\" plugin (from %3)")
Chris@443 278 .arg(longDescription).arg(pluginName).arg(maker);
Chris@330 279 } else {
Chris@443 280 longDescription = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
Chris@443 281 .arg(longDescription).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@330 282 }
Chris@330 283 }
Chris@330 284
Chris@330 285 if (outputs.size() == 1) {
Chris@330 286 userName = pluginName;
Chris@330 287 friendlyName = pluginName;
Chris@330 288 } else {
Chris@330 289 userName = QString("%1: %2")
Chris@330 290 .arg(pluginName)
Chris@330 291 .arg(outputs[j].name.c_str());
Chris@330 292 friendlyName = outputs[j].name.c_str();
Chris@330 293 }
Chris@330 294
Chris@330 295 bool configurable = (!plugin->getPrograms().empty() ||
Chris@330 296 !plugin->getParameterDescriptors().empty());
Chris@330 297
Chris@443 298 // cerr << "Feature extraction plugin transform: " << transformId.toStdString() << " friendly name: " << friendlyName.toStdString() << endl;
Chris@330 299
Chris@330 300 transforms[transformId] =
Chris@330 301 TransformDescription(tr("Analysis"),
Chris@332 302 category,
Chris@332 303 transformId,
Chris@332 304 userName,
Chris@332 305 friendlyName,
Chris@332 306 description,
Chris@443 307 longDescription,
Chris@332 308 maker,
Chris@332 309 units,
Chris@332 310 configurable);
Chris@330 311 }
Chris@330 312
Chris@330 313 delete plugin;
Chris@330 314 }
Chris@330 315 }
Chris@330 316
Chris@330 317 void
Chris@330 318 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
Chris@330 319 {
Chris@330 320 std::vector<QString> plugs =
Chris@330 321 RealTimePluginFactory::getAllPluginIdentifiers();
Chris@330 322
Chris@330 323 static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
Chris@330 324
Chris@330 325 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@330 326
Chris@330 327 QString pluginId = plugs[i];
Chris@330 328
Chris@330 329 RealTimePluginFactory *factory =
Chris@330 330 RealTimePluginFactory::instanceFor(pluginId);
Chris@330 331
Chris@330 332 if (!factory) {
Chris@443 333 cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << endl;
Chris@330 334 continue;
Chris@330 335 }
Chris@330 336
Chris@330 337 const RealTimePluginDescriptor *descriptor =
Chris@330 338 factory->getPluginDescriptor(pluginId);
Chris@330 339
Chris@330 340 if (!descriptor) {
Chris@443 341 cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << endl;
Chris@330 342 continue;
Chris@330 343 }
Chris@330 344
Chris@330 345 //!!! if (descriptor->controlOutputPortCount == 0 ||
Chris@330 346 // descriptor->audioInputPortCount == 0) continue;
Chris@330 347
Chris@443 348 // std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << endl;
Chris@330 349
Chris@330 350 QString pluginName = descriptor->name.c_str();
Chris@330 351 QString category = factory->getPluginCategory(pluginId);
Chris@330 352 bool configurable = (descriptor->parameterCount > 0);
Chris@330 353 QString maker = descriptor->maker.c_str();
Chris@330 354 if (maker == "") maker = tr("<unknown maker>");
Chris@330 355
Chris@330 356 if (descriptor->audioInputPortCount > 0) {
Chris@330 357
Chris@330 358 for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
Chris@330 359
Chris@330 360 QString transformId = QString("%1:%2").arg(pluginId).arg(j);
Chris@330 361 QString userName;
Chris@330 362 QString units;
Chris@330 363 QString portName;
Chris@330 364
Chris@330 365 if (j < descriptor->controlOutputPortNames.size() &&
Chris@330 366 descriptor->controlOutputPortNames[j] != "") {
Chris@330 367
Chris@330 368 portName = descriptor->controlOutputPortNames[j].c_str();
Chris@330 369
Chris@330 370 userName = tr("%1: %2")
Chris@330 371 .arg(pluginName)
Chris@330 372 .arg(portName);
Chris@330 373
Chris@330 374 if (unitRE.indexIn(portName) >= 0) {
Chris@330 375 units = unitRE.cap(1);
Chris@330 376 }
Chris@330 377
Chris@330 378 } else if (descriptor->controlOutputPortCount > 1) {
Chris@330 379
Chris@330 380 userName = tr("%1: Output %2")
Chris@330 381 .arg(pluginName)
Chris@330 382 .arg(j + 1);
Chris@330 383
Chris@330 384 } else {
Chris@330 385
Chris@330 386 userName = pluginName;
Chris@330 387 }
Chris@330 388
Chris@330 389 QString description;
Chris@330 390
Chris@330 391 if (portName != "") {
Chris@330 392 description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)")
Chris@330 393 .arg(portName)
Chris@330 394 .arg(pluginName)
Chris@330 395 .arg(maker);
Chris@330 396 } else {
Chris@330 397 description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)")
Chris@330 398 .arg(j + 1)
Chris@330 399 .arg(pluginName)
Chris@330 400 .arg(maker);
Chris@330 401 }
Chris@330 402
Chris@330 403 transforms[transformId] =
Chris@330 404 TransformDescription(tr("Effects Data"),
Chris@332 405 category,
Chris@332 406 transformId,
Chris@332 407 userName,
Chris@332 408 userName,
Chris@443 409 "",
Chris@332 410 description,
Chris@332 411 maker,
Chris@332 412 units,
Chris@332 413 configurable);
Chris@330 414 }
Chris@330 415 }
Chris@330 416
Chris@330 417 if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) {
Chris@330 418
Chris@330 419 if (descriptor->audioOutputPortCount > 0) {
Chris@330 420
Chris@330 421 QString transformId = QString("%1:A").arg(pluginId);
Chris@330 422 QString type = tr("Effects");
Chris@330 423
Chris@330 424 QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)")
Chris@330 425 .arg(pluginName)
Chris@330 426 .arg(maker);
Chris@330 427
Chris@330 428 if (descriptor->audioInputPortCount == 0) {
Chris@330 429 type = tr("Generators");
Chris@330 430 QString description = tr("Generate audio signal using \"%1\" plugin (from %2)")
Chris@330 431 .arg(pluginName)
Chris@330 432 .arg(maker);
Chris@330 433 }
Chris@330 434
Chris@330 435 transforms[transformId] =
Chris@330 436 TransformDescription(type,
Chris@332 437 category,
Chris@332 438 transformId,
Chris@332 439 pluginName,
Chris@332 440 pluginName,
Chris@443 441 "",
Chris@332 442 description,
Chris@332 443 maker,
Chris@332 444 "",
Chris@332 445 configurable);
Chris@330 446 }
Chris@330 447 }
Chris@330 448 }
Chris@330 449 }
Chris@330 450
Chris@350 451
Chris@350 452 Transform
Chris@350 453 TransformFactory::getDefaultTransformFor(TransformId id, size_t rate)
Chris@350 454 {
Chris@350 455 Transform t;
Chris@350 456 t.setIdentifier(id);
Chris@350 457 if (rate != 0) t.setSampleRate(rate);
Chris@350 458
Chris@351 459 Vamp::PluginBase *plugin = instantiateDefaultPluginFor(id, rate);
Chris@350 460
Chris@350 461 if (plugin) {
Chris@366 462 t.setPluginVersion(QString("%1").arg(plugin->getPluginVersion()));
Chris@350 463 setParametersFromPlugin(t, plugin);
Chris@350 464 makeContextConsistentWithPlugin(t, plugin);
Chris@350 465 delete plugin;
Chris@350 466 }
Chris@350 467
Chris@350 468 return t;
Chris@350 469 }
Chris@350 470
Chris@350 471 Vamp::PluginBase *
Chris@351 472 TransformFactory::instantiatePluginFor(const Transform &transform)
Chris@351 473 {
Chris@351 474 Vamp::PluginBase *plugin = instantiateDefaultPluginFor
Chris@351 475 (transform.getIdentifier(), transform.getSampleRate());
Chris@351 476 if (plugin) {
Chris@351 477 setPluginParameters(transform, plugin);
Chris@351 478 }
Chris@351 479 return plugin;
Chris@351 480 }
Chris@351 481
Chris@351 482 Vamp::PluginBase *
Chris@351 483 TransformFactory::instantiateDefaultPluginFor(TransformId identifier, size_t rate)
Chris@350 484 {
Chris@350 485 Transform t;
Chris@350 486 t.setIdentifier(identifier);
Chris@350 487 if (rate == 0) rate = 44100;
Chris@350 488 QString pluginId = t.getPluginIdentifier();
Chris@350 489
Chris@350 490 Vamp::PluginBase *plugin = 0;
Chris@350 491
Chris@350 492 if (t.getType() == Transform::FeatureExtraction) {
Chris@350 493
Chris@350 494 FeatureExtractionPluginFactory *factory =
Chris@350 495 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@350 496
Chris@439 497 if (factory) {
Chris@439 498 plugin = factory->instantiatePlugin(pluginId, rate);
Chris@439 499 }
Chris@350 500
Chris@350 501 } else {
Chris@350 502
Chris@350 503 RealTimePluginFactory *factory =
Chris@350 504 RealTimePluginFactory::instanceFor(pluginId);
Chris@439 505
Chris@439 506 if (factory) {
Chris@439 507 plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1);
Chris@439 508 }
Chris@350 509 }
Chris@350 510
Chris@350 511 return plugin;
Chris@350 512 }
Chris@350 513
Chris@350 514 Vamp::Plugin *
Chris@350 515 TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin)
Chris@350 516 {
Chris@350 517 Vamp::Plugin *vp = dynamic_cast<Vamp::Plugin *>(plugin);
Chris@350 518 if (!vp) {
Chris@443 519 // cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << endl;
Chris@350 520 vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin); //!!! why?
Chris@350 521 }
Chris@350 522 if (!vp) {
Chris@443 523 // cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << endl;
Chris@350 524 vp = dynamic_cast<Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why?
Chris@350 525 }
Chris@350 526 if (!vp) {
Chris@443 527 // cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << endl;
Chris@350 528 }
Chris@350 529 return vp;
Chris@350 530 }
Chris@350 531
Chris@330 532 bool
Chris@330 533 TransformFactory::haveTransform(TransformId identifier)
Chris@330 534 {
Chris@333 535 if (m_transforms.empty()) populateTransforms();
Chris@330 536 return (m_transforms.find(identifier) != m_transforms.end());
Chris@330 537 }
Chris@330 538
Chris@330 539 QString
Chris@330 540 TransformFactory::getTransformName(TransformId identifier)
Chris@330 541 {
Chris@330 542 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 543 return m_transforms[identifier].name;
Chris@330 544 } else return "";
Chris@330 545 }
Chris@330 546
Chris@330 547 QString
Chris@330 548 TransformFactory::getTransformFriendlyName(TransformId identifier)
Chris@330 549 {
Chris@330 550 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 551 return m_transforms[identifier].friendlyName;
Chris@330 552 } else return "";
Chris@330 553 }
Chris@330 554
Chris@330 555 QString
Chris@330 556 TransformFactory::getTransformUnits(TransformId identifier)
Chris@330 557 {
Chris@330 558 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 559 return m_transforms[identifier].units;
Chris@330 560 } else return "";
Chris@330 561 }
Chris@330 562
Chris@350 563 Vamp::Plugin::InputDomain
Chris@350 564 TransformFactory::getTransformInputDomain(TransformId identifier)
Chris@350 565 {
Chris@350 566 Transform transform;
Chris@350 567 transform.setIdentifier(identifier);
Chris@350 568
Chris@350 569 if (transform.getType() != Transform::FeatureExtraction) {
Chris@350 570 return Vamp::Plugin::TimeDomain;
Chris@350 571 }
Chris@350 572
Chris@350 573 Vamp::Plugin *plugin =
Chris@351 574 downcastVampPlugin(instantiateDefaultPluginFor(identifier, 0));
Chris@350 575
Chris@350 576 if (plugin) {
Chris@350 577 Vamp::Plugin::InputDomain d = plugin->getInputDomain();
Chris@350 578 delete plugin;
Chris@350 579 return d;
Chris@350 580 }
Chris@350 581
Chris@350 582 return Vamp::Plugin::TimeDomain;
Chris@350 583 }
Chris@350 584
Chris@330 585 bool
Chris@330 586 TransformFactory::isTransformConfigurable(TransformId identifier)
Chris@330 587 {
Chris@330 588 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 589 return m_transforms[identifier].configurable;
Chris@330 590 } else return false;
Chris@330 591 }
Chris@330 592
Chris@330 593 bool
Chris@330 594 TransformFactory::getTransformChannelRange(TransformId identifier,
Chris@330 595 int &min, int &max)
Chris@330 596 {
Chris@330 597 QString id = identifier.section(':', 0, 2);
Chris@330 598
Chris@330 599 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@330 600
Chris@330 601 Vamp::Plugin *plugin =
Chris@330 602 FeatureExtractionPluginFactory::instanceFor(id)->
Chris@350 603 instantiatePlugin(id, 44100);
Chris@330 604 if (!plugin) return false;
Chris@330 605
Chris@330 606 min = plugin->getMinChannelCount();
Chris@330 607 max = plugin->getMaxChannelCount();
Chris@330 608 delete plugin;
Chris@330 609
Chris@330 610 return true;
Chris@330 611
Chris@330 612 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@330 613
Chris@350 614 // don't need to instantiate
Chris@350 615
Chris@330 616 const RealTimePluginDescriptor *descriptor =
Chris@330 617 RealTimePluginFactory::instanceFor(id)->
Chris@330 618 getPluginDescriptor(id);
Chris@330 619 if (!descriptor) return false;
Chris@330 620
Chris@330 621 min = descriptor->audioInputPortCount;
Chris@330 622 max = descriptor->audioInputPortCount;
Chris@330 623
Chris@330 624 return true;
Chris@330 625 }
Chris@330 626
Chris@330 627 return false;
Chris@330 628 }
Chris@332 629
Chris@332 630 void
Chris@332 631 TransformFactory::setParametersFromPlugin(Transform &transform,
Chris@332 632 Vamp::PluginBase *plugin)
Chris@332 633 {
Chris@332 634 Transform::ParameterMap pmap;
Chris@332 635
Chris@350 636 //!!! record plugin & API version
Chris@350 637
Chris@350 638 //!!! check that this is the right plugin!
Chris@350 639
Chris@332 640 Vamp::PluginBase::ParameterList parameters =
Chris@332 641 plugin->getParameterDescriptors();
Chris@332 642
Chris@332 643 for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
Chris@332 644 i != parameters.end(); ++i) {
Chris@332 645 pmap[i->identifier.c_str()] = plugin->getParameter(i->identifier);
Chris@332 646 }
Chris@332 647
Chris@332 648 transform.setParameters(pmap);
Chris@332 649
Chris@332 650 if (plugin->getPrograms().empty()) {
Chris@332 651 transform.setProgram("");
Chris@332 652 } else {
Chris@332 653 transform.setProgram(plugin->getCurrentProgram().c_str());
Chris@332 654 }
Chris@332 655
Chris@332 656 RealTimePluginInstance *rtpi =
Chris@332 657 dynamic_cast<RealTimePluginInstance *>(plugin);
Chris@332 658
Chris@332 659 Transform::ConfigurationMap cmap;
Chris@332 660
Chris@332 661 if (rtpi) {
Chris@332 662
Chris@332 663 RealTimePluginInstance::ConfigurationPairMap configurePairs =
Chris@332 664 rtpi->getConfigurePairs();
Chris@332 665
Chris@332 666 for (RealTimePluginInstance::ConfigurationPairMap::const_iterator i
Chris@332 667 = configurePairs.begin(); i != configurePairs.end(); ++i) {
Chris@332 668 cmap[i->first.c_str()] = i->second.c_str();
Chris@332 669 }
Chris@332 670 }
Chris@332 671
Chris@332 672 transform.setConfiguration(cmap);
Chris@332 673 }
Chris@332 674
Chris@332 675 void
Chris@350 676 TransformFactory::setPluginParameters(const Transform &transform,
Chris@350 677 Vamp::PluginBase *plugin)
Chris@350 678 {
Chris@350 679 //!!! check plugin & API version (see e.g. PluginXml::setParameters)
Chris@350 680
Chris@350 681 //!!! check that this is the right plugin!
Chris@350 682
Chris@350 683 RealTimePluginInstance *rtpi =
Chris@350 684 dynamic_cast<RealTimePluginInstance *>(plugin);
Chris@350 685
Chris@350 686 if (rtpi) {
Chris@350 687 const Transform::ConfigurationMap &cmap = transform.getConfiguration();
Chris@350 688 for (Transform::ConfigurationMap::const_iterator i = cmap.begin();
Chris@350 689 i != cmap.end(); ++i) {
Chris@350 690 rtpi->configure(i->first.toStdString(), i->second.toStdString());
Chris@350 691 }
Chris@350 692 }
Chris@350 693
Chris@350 694 if (transform.getProgram() != "") {
Chris@350 695 plugin->selectProgram(transform.getProgram().toStdString());
Chris@350 696 }
Chris@350 697
Chris@350 698 const Transform::ParameterMap &pmap = transform.getParameters();
Chris@350 699
Chris@350 700 Vamp::PluginBase::ParameterList parameters =
Chris@350 701 plugin->getParameterDescriptors();
Chris@350 702
Chris@350 703 for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
Chris@350 704 i != parameters.end(); ++i) {
Chris@350 705 QString key = i->identifier.c_str();
Chris@350 706 Transform::ParameterMap::const_iterator pmi = pmap.find(key);
Chris@350 707 if (pmi != pmap.end()) {
Chris@350 708 plugin->setParameter(i->identifier, pmi->second);
Chris@350 709 }
Chris@350 710 }
Chris@350 711 }
Chris@350 712
Chris@350 713 void
Chris@332 714 TransformFactory::makeContextConsistentWithPlugin(Transform &transform,
Chris@332 715 Vamp::PluginBase *plugin)
Chris@332 716 {
Chris@350 717 const Vamp::Plugin *vp = downcastVampPlugin(plugin);
Chris@332 718
Chris@332 719 if (!vp) {
Chris@332 720 // time domain input for real-time effects plugin
Chris@332 721 if (!transform.getBlockSize()) {
Chris@332 722 if (!transform.getStepSize()) transform.setStepSize(1024);
Chris@332 723 transform.setBlockSize(transform.getStepSize());
Chris@332 724 } else {
Chris@332 725 transform.setStepSize(transform.getBlockSize());
Chris@332 726 }
Chris@332 727 } else {
Chris@332 728 Vamp::Plugin::InputDomain domain = vp->getInputDomain();
Chris@332 729 if (!transform.getStepSize()) {
Chris@332 730 transform.setStepSize(vp->getPreferredStepSize());
Chris@332 731 }
Chris@332 732 if (!transform.getBlockSize()) {
Chris@332 733 transform.setBlockSize(vp->getPreferredBlockSize());
Chris@332 734 }
Chris@332 735 if (!transform.getBlockSize()) {
Chris@332 736 transform.setBlockSize(1024);
Chris@332 737 }
Chris@332 738 if (!transform.getStepSize()) {
Chris@332 739 if (domain == Vamp::Plugin::FrequencyDomain) {
Chris@443 740 // cerr << "frequency domain, step = " << blockSize/2 << endl;
Chris@332 741 transform.setStepSize(transform.getBlockSize()/2);
Chris@332 742 } else {
Chris@443 743 // cerr << "time domain, step = " << blockSize/2 << endl;
Chris@332 744 transform.setStepSize(transform.getBlockSize());
Chris@332 745 }
Chris@332 746 }
Chris@332 747 }
Chris@332 748 }
Chris@332 749
Chris@350 750 QString
Chris@350 751 TransformFactory::getPluginConfigurationXml(const Transform &t)
Chris@332 752 {
Chris@350 753 QString xml;
Chris@350 754
Chris@351 755 Vamp::PluginBase *plugin = instantiateDefaultPluginFor
Chris@351 756 (t.getIdentifier(), 0);
Chris@350 757 if (!plugin) {
Chris@443 758 cerr << "TransformFactory::getPluginConfigurationXml: "
Chris@350 759 << "Unable to instantiate plugin for transform \""
Chris@443 760 << t.getIdentifier().toStdString() << "\"" << endl;
Chris@350 761 return xml;
Chris@332 762 }
Chris@332 763
Chris@351 764 setPluginParameters(t, plugin);
Chris@351 765
Chris@350 766 QTextStream out(&xml);
Chris@350 767 PluginXml(plugin).toXml(out);
Chris@350 768 delete plugin;
Chris@332 769
Chris@350 770 return xml;
Chris@350 771 }
Chris@332 772
Chris@350 773 void
Chris@350 774 TransformFactory::setParametersFromPluginConfigurationXml(Transform &t,
Chris@350 775 QString xml)
Chris@350 776 {
Chris@351 777 Vamp::PluginBase *plugin = instantiateDefaultPluginFor
Chris@351 778 (t.getIdentifier(), 0);
Chris@350 779 if (!plugin) {
Chris@443 780 cerr << "TransformFactory::setParametersFromPluginConfigurationXml: "
Chris@350 781 << "Unable to instantiate plugin for transform \""
Chris@443 782 << t.getIdentifier().toStdString() << "\"" << endl;
Chris@350 783 return;
Chris@332 784 }
Chris@332 785
Chris@350 786 PluginXml(plugin).setParametersFromXml(xml);
Chris@350 787 setParametersFromPlugin(t, plugin);
Chris@350 788 delete plugin;
Chris@332 789 }
Chris@443 790 /*
Chris@443 791 TransformFactory::SearchResults
Chris@443 792 TransformFactory::search(QStringList keywords)
Chris@443 793 {
Chris@443 794 SearchResults results;
Chris@443 795 SearchResults partial;
Chris@443 796 for (int i = 0; i < keywords.size(); ++i) {
Chris@443 797 partial = search(keywords[i]);
Chris@443 798 for (SearchResults::const_iterator j = partial.begin();
Chris@443 799 j != partial.end(); ++j) {
Chris@443 800 if (results.find(j->first) == results.end()) {
Chris@443 801 results[j->first] = j->second;
Chris@443 802 } else {
Chris@443 803 results[j->first].score += j->second.score;
Chris@443 804 results[j->first].fragments << j->second.fragments;
Chris@443 805 }
Chris@443 806 }
Chris@443 807 }
Chris@443 808 return results;
Chris@443 809 }
Chris@443 810 */
Chris@332 811
Chris@443 812 TransformFactory::SearchResults
Chris@443 813 TransformFactory::search(QString keyword)
Chris@443 814 {
Chris@443 815 QStringList keywords;
Chris@443 816 keywords << keyword;
Chris@443 817 return search(keywords);
Chris@443 818 }
Chris@443 819
Chris@443 820 TransformFactory::SearchResults
Chris@443 821 TransformFactory::search(QStringList keywords)
Chris@443 822 {
Chris@443 823 if (m_transforms.empty()) populateTransforms();
Chris@443 824
Chris@447 825 if (keywords.size() > 1) {
Chris@447 826 // Additional score for all keywords in a row
Chris@447 827 keywords.push_back(keywords.join(" "));
Chris@447 828 }
Chris@447 829
Chris@443 830 SearchResults results;
Chris@443 831
Chris@443 832 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@443 833 i != m_transforms.end(); ++i) {
Chris@443 834
Chris@443 835 Match match;
Chris@443 836
Chris@443 837 match.transform = i->first;
Chris@443 838
Chris@443 839 searchTest(match, keywords, i->second.type, tr("Plugin type"), 10);
Chris@443 840 searchTest(match, keywords, i->second.category, tr("Category"), 20);
Chris@443 841 searchTest(match, keywords, i->second.identifier, tr("System Identifier"), 5);
Chris@443 842 searchTest(match, keywords, i->second.name, tr("Name"), 30);
Chris@443 843 searchTest(match, keywords, i->second.description, tr("Description"), 20);
Chris@443 844 searchTest(match, keywords, i->second.maker, tr("Maker"), 10);
Chris@443 845 searchTest(match, keywords, i->second.units, tr("Units"), 10);
Chris@443 846
Chris@443 847 if (match.score > 0) results[i->first] = match;
Chris@443 848 }
Chris@443 849
Chris@443 850 return results;
Chris@443 851 }
Chris@443 852
Chris@443 853 void
Chris@443 854 TransformFactory::searchTest(Match &match, QStringList keywords, QString text,
Chris@443 855 QString textType, int score)
Chris@443 856 {
Chris@443 857 /*
Chris@443 858 if (text.toLower() == keyword.toLower()) {
Chris@443 859 match.score += score * 1.5;
Chris@443 860 match.fragments << tr("%1: <b>%2</b>").arg(textType).arg(text);
Chris@443 861 return;
Chris@443 862 }
Chris@443 863 */
Chris@443 864 int len = text.length();
Chris@443 865 int prevEnd = 0;
Chris@443 866 QString fragment;
Chris@443 867
Chris@443 868 while (1) {
Chris@443 869
Chris@443 870 bool first = (prevEnd == 0);
Chris@443 871
Chris@443 872 int idx = -1;
Chris@443 873 QString keyword;
Chris@443 874
Chris@443 875 for (int ki = 0; ki < keywords.size(); ++ki) {
Chris@443 876 int midx = text.indexOf(keywords[ki], prevEnd, Qt::CaseInsensitive);
Chris@443 877 if (midx >= 0 && midx < len) {
Chris@443 878 if (midx < idx || idx == -1) {
Chris@443 879 idx = midx;
Chris@443 880 keyword = keywords[ki];
Chris@443 881 }
Chris@443 882 }
Chris@443 883 }
Chris@443 884
Chris@443 885 if (idx < 0 || idx >= len) break;
Chris@443 886
Chris@443 887 int klen = keyword.length();
Chris@443 888
Chris@443 889 if (first) {
Chris@443 890 match.score += score;
Chris@443 891 } else {
Chris@443 892 match.score += score / 4;
Chris@443 893 }
Chris@443 894
Chris@443 895 int start = idx;
Chris@443 896 int end = start + klen;
Chris@443 897
Chris@443 898 if (start == 0) match.score += 1;
Chris@443 899 if (end == len) match.score += 1;
Chris@443 900
Chris@443 901 if (start > prevEnd + 14) {
Chris@443 902 QString s = text.right((len - start) + 10);
Chris@446 903 s = XmlExportable::encodeEntities(s.left(10)) + "<b>" +
Chris@446 904 XmlExportable::encodeEntities(s.left(klen + 10).right(klen))
Chris@446 905 + "</b>";
Chris@443 906 fragment += tr("...%1").arg(s);
Chris@443 907 } else {
Chris@443 908 QString s = text.right(len - prevEnd);
Chris@446 909 s = XmlExportable::encodeEntities(s.left(start - prevEnd)) + "<b>" +
Chris@446 910 XmlExportable::encodeEntities(s.left(end - prevEnd).right(klen))
Chris@446 911 + "</b>";
Chris@443 912 fragment += s;
Chris@443 913 }
Chris@443 914
Chris@443 915 prevEnd = end;
Chris@443 916 }
Chris@443 917
Chris@443 918 if (prevEnd > 0 && prevEnd < len) {
Chris@443 919 int n = len - prevEnd;
Chris@446 920 fragment +=
Chris@446 921 XmlExportable::encodeEntities(text.right(n).left(n < 8 ? n : 8));
Chris@443 922 }
Chris@443 923
Chris@443 924 if (fragment != "") {
Chris@444 925 match.fragments[textType] = fragment;
Chris@443 926 }
Chris@443 927 }
Chris@443 928
Chris@444 929 bool
Chris@444 930 TransformFactory::Match::operator<(const Match &m) const
Chris@444 931 {
Chris@444 932 if (score != m.score) {
Chris@444 933 return score < m.score;
Chris@444 934 }
Chris@444 935 if (transform != m.transform) {
Chris@444 936 return transform < m.transform;
Chris@444 937 }
Chris@444 938 if (fragments.size() != m.fragments.size()) {
Chris@444 939 return fragments.size() < m.fragments.size();
Chris@444 940 }
Chris@444 941
Chris@444 942 for (FragmentMap::const_iterator
Chris@444 943 i = fragments.begin(),
Chris@444 944 j = m.fragments.begin();
Chris@444 945 i != fragments.end(); ++i, ++j) {
Chris@444 946 if (i->first != j->first) return i->first < j->first;
Chris@444 947 if (i->second != j->second) return i->second < j->second;
Chris@444 948 }
Chris@444 949
Chris@444 950 return false;
Chris@444 951 }
Chris@444 952