annotate plugin/transform/TransformFactory.cpp @ 331:f620ce48c950

* Further naming change: Transformer -> ModelTransformer. The Transform class now describes a thing that can be done, and the ModelTransformer does it to a Model.
author Chris Cannam
date Wed, 07 Nov 2007 12:59:01 +0000
parents 6e9dcf09b7fe
children 13e5870040e6
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@330 20 #include "plugin/PluginXml.h"
Chris@330 21
Chris@330 22 #include "vamp-sdk/PluginHostAdapter.h"
Chris@330 23
Chris@330 24 #include <iostream>
Chris@330 25 #include <set>
Chris@330 26
Chris@330 27 #include <QRegExp>
Chris@330 28
Chris@330 29 TransformFactory *
Chris@330 30 TransformFactory::m_instance = new TransformFactory;
Chris@330 31
Chris@330 32 TransformFactory *
Chris@330 33 TransformFactory::getInstance()
Chris@330 34 {
Chris@330 35 return m_instance;
Chris@330 36 }
Chris@330 37
Chris@330 38 TransformFactory::~TransformFactory()
Chris@330 39 {
Chris@330 40 }
Chris@330 41
Chris@330 42 TransformList
Chris@330 43 TransformFactory::getAllTransforms()
Chris@330 44 {
Chris@330 45 if (m_transforms.empty()) populateTransforms();
Chris@330 46
Chris@330 47 std::set<TransformDescription> dset;
Chris@330 48 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 49 i != m_transforms.end(); ++i) {
Chris@330 50 dset.insert(i->second);
Chris@330 51 }
Chris@330 52
Chris@330 53 TransformList list;
Chris@330 54 for (std::set<TransformDescription>::const_iterator i = dset.begin();
Chris@330 55 i != dset.end(); ++i) {
Chris@330 56 list.push_back(*i);
Chris@330 57 }
Chris@330 58
Chris@330 59 return list;
Chris@330 60 }
Chris@330 61
Chris@330 62 std::vector<QString>
Chris@330 63 TransformFactory::getAllTransformTypes()
Chris@330 64 {
Chris@330 65 if (m_transforms.empty()) populateTransforms();
Chris@330 66
Chris@330 67 std::set<QString> types;
Chris@330 68 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 69 i != m_transforms.end(); ++i) {
Chris@330 70 types.insert(i->second.type);
Chris@330 71 }
Chris@330 72
Chris@330 73 std::vector<QString> rv;
Chris@330 74 for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@330 75 rv.push_back(*i);
Chris@330 76 }
Chris@330 77
Chris@330 78 return rv;
Chris@330 79 }
Chris@330 80
Chris@330 81 std::vector<QString>
Chris@330 82 TransformFactory::getTransformCategories(QString transformType)
Chris@330 83 {
Chris@330 84 if (m_transforms.empty()) populateTransforms();
Chris@330 85
Chris@330 86 std::set<QString> categories;
Chris@330 87 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 88 i != m_transforms.end(); ++i) {
Chris@330 89 if (i->second.type == transformType) {
Chris@330 90 categories.insert(i->second.category);
Chris@330 91 }
Chris@330 92 }
Chris@330 93
Chris@330 94 bool haveEmpty = false;
Chris@330 95
Chris@330 96 std::vector<QString> rv;
Chris@330 97 for (std::set<QString>::iterator i = categories.begin();
Chris@330 98 i != categories.end(); ++i) {
Chris@330 99 if (*i != "") rv.push_back(*i);
Chris@330 100 else haveEmpty = true;
Chris@330 101 }
Chris@330 102
Chris@330 103 if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@330 104
Chris@330 105 return rv;
Chris@330 106 }
Chris@330 107
Chris@330 108 std::vector<QString>
Chris@330 109 TransformFactory::getTransformMakers(QString transformType)
Chris@330 110 {
Chris@330 111 if (m_transforms.empty()) populateTransforms();
Chris@330 112
Chris@330 113 std::set<QString> makers;
Chris@330 114 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330 115 i != m_transforms.end(); ++i) {
Chris@330 116 if (i->second.type == transformType) {
Chris@330 117 makers.insert(i->second.maker);
Chris@330 118 }
Chris@330 119 }
Chris@330 120
Chris@330 121 bool haveEmpty = false;
Chris@330 122
Chris@330 123 std::vector<QString> rv;
Chris@330 124 for (std::set<QString>::iterator i = makers.begin();
Chris@330 125 i != makers.end(); ++i) {
Chris@330 126 if (*i != "") rv.push_back(*i);
Chris@330 127 else haveEmpty = true;
Chris@330 128 }
Chris@330 129
Chris@330 130 if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@330 131
Chris@330 132 return rv;
Chris@330 133 }
Chris@330 134
Chris@330 135 void
Chris@330 136 TransformFactory::populateTransforms()
Chris@330 137 {
Chris@330 138 TransformDescriptionMap transforms;
Chris@330 139
Chris@330 140 populateFeatureExtractionPlugins(transforms);
Chris@330 141 populateRealTimePlugins(transforms);
Chris@330 142
Chris@330 143 // disambiguate plugins with similar names
Chris@330 144
Chris@330 145 std::map<QString, int> names;
Chris@330 146 std::map<QString, QString> pluginSources;
Chris@330 147 std::map<QString, QString> pluginMakers;
Chris@330 148
Chris@330 149 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@330 150 i != transforms.end(); ++i) {
Chris@330 151
Chris@330 152 TransformDescription desc = i->second;
Chris@330 153
Chris@330 154 QString td = desc.name;
Chris@330 155 QString tn = td.section(": ", 0, 0);
Chris@330 156 QString pn = desc.identifier.section(":", 1, 1);
Chris@330 157
Chris@330 158 if (pluginSources.find(tn) != pluginSources.end()) {
Chris@330 159 if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) {
Chris@330 160 ++names[tn];
Chris@330 161 }
Chris@330 162 } else {
Chris@330 163 ++names[tn];
Chris@330 164 pluginSources[tn] = pn;
Chris@330 165 pluginMakers[tn] = desc.maker;
Chris@330 166 }
Chris@330 167 }
Chris@330 168
Chris@330 169 std::map<QString, int> counts;
Chris@330 170 m_transforms.clear();
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 QString identifier = desc.identifier;
Chris@330 177 QString maker = desc.maker;
Chris@330 178
Chris@330 179 QString td = desc.name;
Chris@330 180 QString tn = td.section(": ", 0, 0);
Chris@330 181 QString to = td.section(": ", 1);
Chris@330 182
Chris@330 183 if (names[tn] > 1) {
Chris@330 184 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@330 185 tn = QString("%1 [%2]").arg(tn).arg(maker);
Chris@330 186 }
Chris@330 187
Chris@330 188 if (to != "") {
Chris@330 189 desc.name = QString("%1: %2").arg(tn).arg(to);
Chris@330 190 } else {
Chris@330 191 desc.name = tn;
Chris@330 192 }
Chris@330 193
Chris@330 194 m_transforms[identifier] = desc;
Chris@330 195 }
Chris@330 196 }
Chris@330 197
Chris@330 198 void
Chris@330 199 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
Chris@330 200 {
Chris@330 201 std::vector<QString> plugs =
Chris@330 202 FeatureExtractionPluginFactory::getAllPluginIdentifiers();
Chris@330 203
Chris@330 204 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@330 205
Chris@330 206 QString pluginId = plugs[i];
Chris@330 207
Chris@330 208 FeatureExtractionPluginFactory *factory =
Chris@330 209 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@330 210
Chris@330 211 if (!factory) {
Chris@330 212 std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@330 213 continue;
Chris@330 214 }
Chris@330 215
Chris@330 216 Vamp::Plugin *plugin =
Chris@330 217 factory->instantiatePlugin(pluginId, 48000);
Chris@330 218
Chris@330 219 if (!plugin) {
Chris@330 220 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@330 221 continue;
Chris@330 222 }
Chris@330 223
Chris@330 224 QString pluginName = plugin->getName().c_str();
Chris@330 225 QString category = factory->getPluginCategory(pluginId);
Chris@330 226
Chris@330 227 Vamp::Plugin::OutputList outputs =
Chris@330 228 plugin->getOutputDescriptors();
Chris@330 229
Chris@330 230 for (size_t j = 0; j < outputs.size(); ++j) {
Chris@330 231
Chris@330 232 QString transformId = QString("%1:%2")
Chris@330 233 .arg(pluginId).arg(outputs[j].identifier.c_str());
Chris@330 234
Chris@330 235 QString userName;
Chris@330 236 QString friendlyName;
Chris@330 237 QString units = outputs[j].unit.c_str();
Chris@330 238 QString description = plugin->getDescription().c_str();
Chris@330 239 QString maker = plugin->getMaker().c_str();
Chris@330 240 if (maker == "") maker = tr("<unknown maker>");
Chris@330 241
Chris@330 242 if (description == "") {
Chris@330 243 if (outputs.size() == 1) {
Chris@330 244 description = tr("Extract features using \"%1\" plugin (from %2)")
Chris@330 245 .arg(pluginName).arg(maker);
Chris@330 246 } else {
Chris@330 247 description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
Chris@330 248 .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@330 249 }
Chris@330 250 } else {
Chris@330 251 if (outputs.size() == 1) {
Chris@330 252 description = tr("%1 using \"%2\" plugin (from %3)")
Chris@330 253 .arg(description).arg(pluginName).arg(maker);
Chris@330 254 } else {
Chris@330 255 description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
Chris@330 256 .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@330 257 }
Chris@330 258 }
Chris@330 259
Chris@330 260 if (outputs.size() == 1) {
Chris@330 261 userName = pluginName;
Chris@330 262 friendlyName = pluginName;
Chris@330 263 } else {
Chris@330 264 userName = QString("%1: %2")
Chris@330 265 .arg(pluginName)
Chris@330 266 .arg(outputs[j].name.c_str());
Chris@330 267 friendlyName = outputs[j].name.c_str();
Chris@330 268 }
Chris@330 269
Chris@330 270 bool configurable = (!plugin->getPrograms().empty() ||
Chris@330 271 !plugin->getParameterDescriptors().empty());
Chris@330 272
Chris@330 273 // std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl;
Chris@330 274
Chris@330 275 transforms[transformId] =
Chris@330 276 TransformDescription(tr("Analysis"),
Chris@330 277 category,
Chris@330 278 transformId,
Chris@330 279 userName,
Chris@330 280 friendlyName,
Chris@330 281 description,
Chris@330 282 maker,
Chris@330 283 units,
Chris@330 284 configurable);
Chris@330 285 }
Chris@330 286
Chris@330 287 delete plugin;
Chris@330 288 }
Chris@330 289 }
Chris@330 290
Chris@330 291 void
Chris@330 292 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
Chris@330 293 {
Chris@330 294 std::vector<QString> plugs =
Chris@330 295 RealTimePluginFactory::getAllPluginIdentifiers();
Chris@330 296
Chris@330 297 static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
Chris@330 298
Chris@330 299 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@330 300
Chris@330 301 QString pluginId = plugs[i];
Chris@330 302
Chris@330 303 RealTimePluginFactory *factory =
Chris@330 304 RealTimePluginFactory::instanceFor(pluginId);
Chris@330 305
Chris@330 306 if (!factory) {
Chris@330 307 std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@330 308 continue;
Chris@330 309 }
Chris@330 310
Chris@330 311 const RealTimePluginDescriptor *descriptor =
Chris@330 312 factory->getPluginDescriptor(pluginId);
Chris@330 313
Chris@330 314 if (!descriptor) {
Chris@330 315 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@330 316 continue;
Chris@330 317 }
Chris@330 318
Chris@330 319 //!!! if (descriptor->controlOutputPortCount == 0 ||
Chris@330 320 // descriptor->audioInputPortCount == 0) continue;
Chris@330 321
Chris@330 322 // std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl;
Chris@330 323
Chris@330 324 QString pluginName = descriptor->name.c_str();
Chris@330 325 QString category = factory->getPluginCategory(pluginId);
Chris@330 326 bool configurable = (descriptor->parameterCount > 0);
Chris@330 327 QString maker = descriptor->maker.c_str();
Chris@330 328 if (maker == "") maker = tr("<unknown maker>");
Chris@330 329
Chris@330 330 if (descriptor->audioInputPortCount > 0) {
Chris@330 331
Chris@330 332 for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
Chris@330 333
Chris@330 334 QString transformId = QString("%1:%2").arg(pluginId).arg(j);
Chris@330 335 QString userName;
Chris@330 336 QString units;
Chris@330 337 QString portName;
Chris@330 338
Chris@330 339 if (j < descriptor->controlOutputPortNames.size() &&
Chris@330 340 descriptor->controlOutputPortNames[j] != "") {
Chris@330 341
Chris@330 342 portName = descriptor->controlOutputPortNames[j].c_str();
Chris@330 343
Chris@330 344 userName = tr("%1: %2")
Chris@330 345 .arg(pluginName)
Chris@330 346 .arg(portName);
Chris@330 347
Chris@330 348 if (unitRE.indexIn(portName) >= 0) {
Chris@330 349 units = unitRE.cap(1);
Chris@330 350 }
Chris@330 351
Chris@330 352 } else if (descriptor->controlOutputPortCount > 1) {
Chris@330 353
Chris@330 354 userName = tr("%1: Output %2")
Chris@330 355 .arg(pluginName)
Chris@330 356 .arg(j + 1);
Chris@330 357
Chris@330 358 } else {
Chris@330 359
Chris@330 360 userName = pluginName;
Chris@330 361 }
Chris@330 362
Chris@330 363 QString description;
Chris@330 364
Chris@330 365 if (portName != "") {
Chris@330 366 description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)")
Chris@330 367 .arg(portName)
Chris@330 368 .arg(pluginName)
Chris@330 369 .arg(maker);
Chris@330 370 } else {
Chris@330 371 description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)")
Chris@330 372 .arg(j + 1)
Chris@330 373 .arg(pluginName)
Chris@330 374 .arg(maker);
Chris@330 375 }
Chris@330 376
Chris@330 377 transforms[transformId] =
Chris@330 378 TransformDescription(tr("Effects Data"),
Chris@330 379 category,
Chris@330 380 transformId,
Chris@330 381 userName,
Chris@330 382 userName,
Chris@330 383 description,
Chris@330 384 maker,
Chris@330 385 units,
Chris@330 386 configurable);
Chris@330 387 }
Chris@330 388 }
Chris@330 389
Chris@330 390 if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) {
Chris@330 391
Chris@330 392 if (descriptor->audioOutputPortCount > 0) {
Chris@330 393
Chris@330 394 QString transformId = QString("%1:A").arg(pluginId);
Chris@330 395 QString type = tr("Effects");
Chris@330 396
Chris@330 397 QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)")
Chris@330 398 .arg(pluginName)
Chris@330 399 .arg(maker);
Chris@330 400
Chris@330 401 if (descriptor->audioInputPortCount == 0) {
Chris@330 402 type = tr("Generators");
Chris@330 403 QString description = tr("Generate audio signal using \"%1\" plugin (from %2)")
Chris@330 404 .arg(pluginName)
Chris@330 405 .arg(maker);
Chris@330 406 }
Chris@330 407
Chris@330 408 transforms[transformId] =
Chris@330 409 TransformDescription(type,
Chris@330 410 category,
Chris@330 411 transformId,
Chris@330 412 pluginName,
Chris@330 413 pluginName,
Chris@330 414 description,
Chris@330 415 maker,
Chris@330 416 "",
Chris@330 417 configurable);
Chris@330 418 }
Chris@330 419 }
Chris@330 420 }
Chris@330 421 }
Chris@330 422
Chris@330 423 bool
Chris@330 424 TransformFactory::haveTransform(TransformId identifier)
Chris@330 425 {
Chris@330 426 return (m_transforms.find(identifier) != m_transforms.end());
Chris@330 427 }
Chris@330 428
Chris@330 429 QString
Chris@330 430 TransformFactory::getTransformName(TransformId identifier)
Chris@330 431 {
Chris@330 432 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 433 return m_transforms[identifier].name;
Chris@330 434 } else return "";
Chris@330 435 }
Chris@330 436
Chris@330 437 QString
Chris@330 438 TransformFactory::getTransformFriendlyName(TransformId identifier)
Chris@330 439 {
Chris@330 440 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 441 return m_transforms[identifier].friendlyName;
Chris@330 442 } else return "";
Chris@330 443 }
Chris@330 444
Chris@330 445 QString
Chris@330 446 TransformFactory::getTransformUnits(TransformId identifier)
Chris@330 447 {
Chris@330 448 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 449 return m_transforms[identifier].units;
Chris@330 450 } else return "";
Chris@330 451 }
Chris@330 452
Chris@330 453 bool
Chris@330 454 TransformFactory::isTransformConfigurable(TransformId identifier)
Chris@330 455 {
Chris@330 456 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330 457 return m_transforms[identifier].configurable;
Chris@330 458 } else return false;
Chris@330 459 }
Chris@330 460
Chris@330 461 bool
Chris@330 462 TransformFactory::getTransformChannelRange(TransformId identifier,
Chris@330 463 int &min, int &max)
Chris@330 464 {
Chris@330 465 QString id = identifier.section(':', 0, 2);
Chris@330 466
Chris@330 467 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@330 468
Chris@330 469 Vamp::Plugin *plugin =
Chris@330 470 FeatureExtractionPluginFactory::instanceFor(id)->
Chris@330 471 instantiatePlugin(id, 48000);
Chris@330 472 if (!plugin) return false;
Chris@330 473
Chris@330 474 min = plugin->getMinChannelCount();
Chris@330 475 max = plugin->getMaxChannelCount();
Chris@330 476 delete plugin;
Chris@330 477
Chris@330 478 return true;
Chris@330 479
Chris@330 480 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@330 481
Chris@330 482 const RealTimePluginDescriptor *descriptor =
Chris@330 483 RealTimePluginFactory::instanceFor(id)->
Chris@330 484 getPluginDescriptor(id);
Chris@330 485 if (!descriptor) return false;
Chris@330 486
Chris@330 487 min = descriptor->audioInputPortCount;
Chris@330 488 max = descriptor->audioInputPortCount;
Chris@330 489
Chris@330 490 return true;
Chris@330 491 }
Chris@330 492
Chris@330 493 return false;
Chris@330 494 }