annotate transform/TransformFactory.cpp @ 182:21a76c9ed5c3

* Merge transform directory from sv-match-alignment branch (the previous comment included notes for this stuff, but I missed it in the actual merge) * Fix crash when a transform fails to create an output model and the thread that created the transform then deletes its input model thinking it's no longer needed, even though the transform run thread is still using it -- fix is to wait() on the transform before returning the null output model
author Chris Cannam
date Fri, 28 Sep 2007 16:15:06 +0000
parents aaf806ce329a
children ebd906049fb6
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Visualiser
Chris@0 5 An audio file viewer and annotation editor.
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@77 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "TransformFactory.h"
Chris@0 17
Chris@0 18 #include "FeatureExtractionPluginTransform.h"
Chris@0 19 #include "RealTimePluginTransform.h"
Chris@0 20
Chris@0 21 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@0 22 #include "plugin/RealTimePluginFactory.h"
Chris@0 23 #include "plugin/PluginXml.h"
Chris@0 24
Chris@0 25 #include "widgets/PluginParameterDialog.h"
Chris@0 26
Chris@1 27 #include "data/model/DenseTimeValueModel.h"
Chris@0 28
Chris@28 29 #include "vamp-sdk/PluginHostAdapter.h"
Chris@28 30
Chris@41 31 #include "sv/audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here
Chris@41 32
Chris@0 33 #include <iostream>
Chris@0 34 #include <set>
Chris@0 35
Chris@0 36 #include <QRegExp>
Chris@0 37
Chris@0 38 TransformFactory *
Chris@0 39 TransformFactory::m_instance = new TransformFactory;
Chris@0 40
Chris@0 41 TransformFactory *
Chris@0 42 TransformFactory::getInstance()
Chris@0 43 {
Chris@0 44 return m_instance;
Chris@0 45 }
Chris@0 46
Chris@0 47 TransformFactory::~TransformFactory()
Chris@0 48 {
Chris@0 49 }
Chris@0 50
Chris@0 51 TransformFactory::TransformList
Chris@0 52 TransformFactory::getAllTransforms()
Chris@0 53 {
Chris@0 54 if (m_transforms.empty()) populateTransforms();
Chris@0 55
Chris@34 56 std::set<TransformDesc> dset;
Chris@0 57 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@0 58 i != m_transforms.end(); ++i) {
Chris@34 59 dset.insert(i->second);
Chris@34 60 }
Chris@34 61
Chris@34 62 TransformList list;
Chris@34 63 for (std::set<TransformDesc>::const_iterator i = dset.begin();
Chris@34 64 i != dset.end(); ++i) {
Chris@34 65 list.push_back(*i);
Chris@0 66 }
Chris@0 67
Chris@0 68 return list;
Chris@0 69 }
Chris@0 70
Chris@0 71 std::vector<QString>
Chris@0 72 TransformFactory::getAllTransformTypes()
Chris@0 73 {
Chris@0 74 if (m_transforms.empty()) populateTransforms();
Chris@0 75
Chris@0 76 std::set<QString> types;
Chris@0 77 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@0 78 i != m_transforms.end(); ++i) {
Chris@0 79 types.insert(i->second.type);
Chris@0 80 }
Chris@0 81
Chris@0 82 std::vector<QString> rv;
Chris@0 83 for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@0 84 rv.push_back(*i);
Chris@0 85 }
Chris@0 86
Chris@0 87 return rv;
Chris@0 88 }
Chris@0 89
Chris@33 90 std::vector<QString>
Chris@33 91 TransformFactory::getTransformCategories(QString transformType)
Chris@33 92 {
Chris@33 93 if (m_transforms.empty()) populateTransforms();
Chris@33 94
Chris@33 95 std::set<QString> categories;
Chris@33 96 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@33 97 i != m_transforms.end(); ++i) {
Chris@33 98 if (i->second.type == transformType) {
Chris@33 99 categories.insert(i->second.category);
Chris@33 100 }
Chris@33 101 }
Chris@33 102
Chris@33 103 bool haveEmpty = false;
Chris@33 104
Chris@33 105 std::vector<QString> rv;
Chris@33 106 for (std::set<QString>::iterator i = categories.begin();
Chris@33 107 i != categories.end(); ++i) {
Chris@33 108 if (*i != "") rv.push_back(*i);
Chris@33 109 else haveEmpty = true;
Chris@33 110 }
Chris@33 111
Chris@33 112 if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@33 113
Chris@33 114 return rv;
Chris@33 115 }
Chris@33 116
Chris@33 117 std::vector<QString>
Chris@33 118 TransformFactory::getTransformMakers(QString transformType)
Chris@33 119 {
Chris@33 120 if (m_transforms.empty()) populateTransforms();
Chris@33 121
Chris@33 122 std::set<QString> makers;
Chris@33 123 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@33 124 i != m_transforms.end(); ++i) {
Chris@33 125 if (i->second.type == transformType) {
Chris@33 126 makers.insert(i->second.maker);
Chris@33 127 }
Chris@33 128 }
Chris@33 129
Chris@33 130 bool haveEmpty = false;
Chris@33 131
Chris@33 132 std::vector<QString> rv;
Chris@33 133 for (std::set<QString>::iterator i = makers.begin();
Chris@33 134 i != makers.end(); ++i) {
Chris@33 135 if (*i != "") rv.push_back(*i);
Chris@33 136 else haveEmpty = true;
Chris@33 137 }
Chris@33 138
Chris@33 139 if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@33 140
Chris@33 141 return rv;
Chris@33 142 }
Chris@33 143
Chris@0 144 void
Chris@0 145 TransformFactory::populateTransforms()
Chris@0 146 {
Chris@0 147 TransformDescriptionMap transforms;
Chris@0 148
Chris@0 149 populateFeatureExtractionPlugins(transforms);
Chris@0 150 populateRealTimePlugins(transforms);
Chris@0 151
Chris@80 152 // disambiguate plugins with similar names
Chris@0 153
Chris@107 154 std::map<QString, int> names;
Chris@80 155 std::map<QString, QString> pluginSources;
Chris@80 156 std::map<QString, QString> pluginMakers;
Chris@0 157
Chris@0 158 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@0 159 i != transforms.end(); ++i) {
Chris@0 160
Chris@0 161 TransformDesc desc = i->second;
Chris@0 162
Chris@107 163 QString td = desc.name;
Chris@80 164 QString tn = td.section(": ", 0, 0);
Chris@107 165 QString pn = desc.identifier.section(":", 1, 1);
Chris@80 166
Chris@80 167 if (pluginSources.find(tn) != pluginSources.end()) {
Chris@80 168 if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) {
Chris@107 169 ++names[tn];
Chris@80 170 }
Chris@80 171 } else {
Chris@107 172 ++names[tn];
Chris@80 173 pluginSources[tn] = pn;
Chris@80 174 pluginMakers[tn] = desc.maker;
Chris@80 175 }
Chris@0 176 }
Chris@0 177
Chris@0 178 std::map<QString, int> counts;
Chris@0 179 m_transforms.clear();
Chris@0 180
Chris@0 181 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@0 182 i != transforms.end(); ++i) {
Chris@0 183
Chris@0 184 TransformDesc desc = i->second;
Chris@107 185 QString identifier = desc.identifier;
Chris@0 186 QString maker = desc.maker;
Chris@0 187
Chris@107 188 QString td = desc.name;
Chris@80 189 QString tn = td.section(": ", 0, 0);
Chris@80 190 QString to = td.section(": ", 1);
Chris@80 191
Chris@107 192 if (names[tn] > 1) {
Chris@80 193 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@80 194 tn = QString("%1 [%2]").arg(tn).arg(maker);
Chris@0 195 }
Chris@0 196
Chris@80 197 if (to != "") {
Chris@107 198 desc.name = QString("%1: %2").arg(tn).arg(to);
Chris@80 199 } else {
Chris@107 200 desc.name = tn;
Chris@80 201 }
Chris@80 202
Chris@107 203 m_transforms[identifier] = desc;
Chris@0 204 }
Chris@0 205 }
Chris@0 206
Chris@0 207 void
Chris@0 208 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
Chris@0 209 {
Chris@0 210 std::vector<QString> plugs =
Chris@0 211 FeatureExtractionPluginFactory::getAllPluginIdentifiers();
Chris@0 212
Chris@0 213 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@0 214
Chris@0 215 QString pluginId = plugs[i];
Chris@0 216
Chris@0 217 FeatureExtractionPluginFactory *factory =
Chris@0 218 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@0 219
Chris@0 220 if (!factory) {
Chris@0 221 std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 222 continue;
Chris@0 223 }
Chris@0 224
Chris@0 225 Vamp::Plugin *plugin =
Chris@0 226 factory->instantiatePlugin(pluginId, 48000);
Chris@0 227
Chris@0 228 if (!plugin) {
Chris@0 229 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 230 continue;
Chris@0 231 }
Chris@0 232
Chris@107 233 QString pluginName = plugin->getName().c_str();
Chris@33 234 QString category = factory->getPluginCategory(pluginId);
Chris@33 235
Chris@0 236 Vamp::Plugin::OutputList outputs =
Chris@0 237 plugin->getOutputDescriptors();
Chris@0 238
Chris@0 239 for (size_t j = 0; j < outputs.size(); ++j) {
Chris@0 240
Chris@107 241 QString transformId = QString("%1:%2")
Chris@107 242 .arg(pluginId).arg(outputs[j].identifier.c_str());
Chris@0 243
Chris@107 244 QString userName;
Chris@0 245 QString friendlyName;
Chris@0 246 QString units = outputs[j].unit.c_str();
Chris@108 247 QString description = plugin->getDescription().c_str();
Chris@108 248 QString maker = plugin->getMaker().c_str();
Chris@108 249 if (maker == "") maker = tr("<unknown maker>");
Chris@108 250
Chris@108 251 if (description == "") {
Chris@108 252 if (outputs.size() == 1) {
Chris@108 253 description = tr("Extract features using \"%1\" plugin (from %2)")
Chris@108 254 .arg(pluginName).arg(maker);
Chris@108 255 } else {
Chris@108 256 description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
Chris@108 257 .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@108 258 }
Chris@108 259 } else {
Chris@108 260 if (outputs.size() == 1) {
Chris@108 261 description = tr("%1 using \"%2\" plugin (from %3)")
Chris@108 262 .arg(description).arg(pluginName).arg(maker);
Chris@108 263 } else {
Chris@108 264 description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
Chris@108 265 .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@108 266 }
Chris@108 267 }
Chris@0 268
Chris@0 269 if (outputs.size() == 1) {
Chris@107 270 userName = pluginName;
Chris@107 271 friendlyName = pluginName;
Chris@0 272 } else {
Chris@107 273 userName = QString("%1: %2")
Chris@107 274 .arg(pluginName)
Chris@107 275 .arg(outputs[j].name.c_str());
Chris@107 276 friendlyName = outputs[j].name.c_str();
Chris@0 277 }
Chris@0 278
Chris@0 279 bool configurable = (!plugin->getPrograms().empty() ||
Chris@0 280 !plugin->getParameterDescriptors().empty());
Chris@0 281
Chris@137 282 // std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl;
Chris@107 283
Chris@107 284 transforms[transformId] =
Chris@34 285 TransformDesc(tr("Analysis"),
Chris@33 286 category,
Chris@107 287 transformId,
Chris@107 288 userName,
Chris@0 289 friendlyName,
Chris@108 290 description,
Chris@108 291 maker,
Chris@0 292 units,
Chris@0 293 configurable);
Chris@0 294 }
Chris@182 295
Chris@182 296 delete plugin;
Chris@0 297 }
Chris@0 298 }
Chris@0 299
Chris@0 300 void
Chris@0 301 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
Chris@0 302 {
Chris@0 303 std::vector<QString> plugs =
Chris@0 304 RealTimePluginFactory::getAllPluginIdentifiers();
Chris@0 305
Chris@34 306 static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
Chris@0 307
Chris@0 308 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@0 309
Chris@0 310 QString pluginId = plugs[i];
Chris@0 311
Chris@0 312 RealTimePluginFactory *factory =
Chris@0 313 RealTimePluginFactory::instanceFor(pluginId);
Chris@0 314
Chris@0 315 if (!factory) {
Chris@0 316 std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 317 continue;
Chris@0 318 }
Chris@0 319
Chris@0 320 const RealTimePluginDescriptor *descriptor =
Chris@0 321 factory->getPluginDescriptor(pluginId);
Chris@0 322
Chris@0 323 if (!descriptor) {
Chris@0 324 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 325 continue;
Chris@0 326 }
Chris@0 327
Chris@34 328 //!!! if (descriptor->controlOutputPortCount == 0 ||
Chris@34 329 // descriptor->audioInputPortCount == 0) continue;
Chris@0 330
Chris@35 331 // std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl;
Chris@0 332
Chris@107 333 QString pluginName = descriptor->name.c_str();
Chris@33 334 QString category = factory->getPluginCategory(pluginId);
Chris@34 335 bool configurable = (descriptor->parameterCount > 0);
Chris@108 336 QString maker = descriptor->maker.c_str();
Chris@108 337 if (maker == "") maker = tr("<unknown maker>");
Chris@0 338
Chris@34 339 if (descriptor->audioInputPortCount > 0) {
Chris@0 340
Chris@34 341 for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
Chris@0 342
Chris@107 343 QString transformId = QString("%1:%2").arg(pluginId).arg(j);
Chris@107 344 QString userName;
Chris@34 345 QString units;
Chris@108 346 QString portName;
Chris@0 347
Chris@34 348 if (j < descriptor->controlOutputPortNames.size() &&
Chris@34 349 descriptor->controlOutputPortNames[j] != "") {
Chris@0 350
Chris@108 351 portName = descriptor->controlOutputPortNames[j].c_str();
Chris@0 352
Chris@107 353 userName = tr("%1: %2")
Chris@107 354 .arg(pluginName)
Chris@34 355 .arg(portName);
Chris@34 356
Chris@34 357 if (unitRE.indexIn(portName) >= 0) {
Chris@34 358 units = unitRE.cap(1);
Chris@34 359 }
Chris@34 360
Chris@34 361 } else if (descriptor->controlOutputPortCount > 1) {
Chris@34 362
Chris@107 363 userName = tr("%1: Output %2")
Chris@107 364 .arg(pluginName)
Chris@34 365 .arg(j + 1);
Chris@34 366
Chris@34 367 } else {
Chris@34 368
Chris@107 369 userName = pluginName;
Chris@0 370 }
Chris@0 371
Chris@108 372 QString description;
Chris@108 373
Chris@108 374 if (portName != "") {
Chris@108 375 description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)")
Chris@108 376 .arg(portName)
Chris@108 377 .arg(pluginName)
Chris@108 378 .arg(maker);
Chris@108 379 } else {
Chris@108 380 description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)")
Chris@108 381 .arg(j + 1)
Chris@108 382 .arg(pluginName)
Chris@108 383 .arg(maker);
Chris@108 384 }
Chris@0 385
Chris@107 386 transforms[transformId] =
Chris@52 387 TransformDesc(tr("Effects Data"),
Chris@34 388 category,
Chris@107 389 transformId,
Chris@107 390 userName,
Chris@107 391 userName,
Chris@108 392 description,
Chris@108 393 maker,
Chris@34 394 units,
Chris@34 395 configurable);
Chris@34 396 }
Chris@34 397 }
Chris@0 398
Chris@34 399 if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) {
Chris@0 400
Chris@34 401 if (descriptor->audioOutputPortCount > 0) {
Chris@34 402
Chris@107 403 QString transformId = QString("%1:A").arg(pluginId);
Chris@34 404 QString type = tr("Effects");
Chris@108 405
Chris@108 406 QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)")
Chris@108 407 .arg(pluginName)
Chris@108 408 .arg(maker);
Chris@108 409
Chris@34 410 if (descriptor->audioInputPortCount == 0) {
Chris@34 411 type = tr("Generators");
Chris@108 412 QString description = tr("Generate audio signal using \"%1\" plugin (from %2)")
Chris@108 413 .arg(pluginName)
Chris@108 414 .arg(maker);
Chris@34 415 }
Chris@34 416
Chris@107 417 transforms[transformId] =
Chris@34 418 TransformDesc(type,
Chris@34 419 category,
Chris@107 420 transformId,
Chris@107 421 pluginName,
Chris@107 422 pluginName,
Chris@108 423 description,
Chris@108 424 maker,
Chris@34 425 "",
Chris@34 426 configurable);
Chris@0 427 }
Chris@34 428 }
Chris@0 429 }
Chris@0 430 }
Chris@0 431
Chris@0 432 QString
Chris@107 433 TransformFactory::getTransformName(TransformId identifier)
Chris@0 434 {
Chris@107 435 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@107 436 return m_transforms[identifier].name;
Chris@0 437 } else return "";
Chris@0 438 }
Chris@0 439
Chris@0 440 QString
Chris@107 441 TransformFactory::getTransformFriendlyName(TransformId identifier)
Chris@0 442 {
Chris@107 443 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@107 444 return m_transforms[identifier].friendlyName;
Chris@0 445 } else return "";
Chris@0 446 }
Chris@0 447
Chris@0 448 QString
Chris@107 449 TransformFactory::getTransformUnits(TransformId identifier)
Chris@0 450 {
Chris@107 451 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@107 452 return m_transforms[identifier].units;
Chris@0 453 } else return "";
Chris@0 454 }
Chris@0 455
Chris@0 456 bool
Chris@107 457 TransformFactory::isTransformConfigurable(TransformId identifier)
Chris@0 458 {
Chris@107 459 if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@107 460 return m_transforms[identifier].configurable;
Chris@0 461 } else return false;
Chris@0 462 }
Chris@0 463
Chris@0 464 bool
Chris@107 465 TransformFactory::getTransformChannelRange(TransformId identifier,
Chris@0 466 int &min, int &max)
Chris@0 467 {
Chris@107 468 QString id = identifier.section(':', 0, 2);
Chris@0 469
Chris@0 470 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 471
Chris@0 472 Vamp::Plugin *plugin =
Chris@0 473 FeatureExtractionPluginFactory::instanceFor(id)->
Chris@0 474 instantiatePlugin(id, 48000);
Chris@0 475 if (!plugin) return false;
Chris@0 476
Chris@0 477 min = plugin->getMinChannelCount();
Chris@0 478 max = plugin->getMaxChannelCount();
Chris@0 479 delete plugin;
Chris@0 480
Chris@0 481 return true;
Chris@0 482
Chris@0 483 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 484
Chris@0 485 const RealTimePluginDescriptor *descriptor =
Chris@0 486 RealTimePluginFactory::instanceFor(id)->
Chris@0 487 getPluginDescriptor(id);
Chris@0 488 if (!descriptor) return false;
Chris@0 489
Chris@0 490 min = descriptor->audioInputPortCount;
Chris@0 491 max = descriptor->audioInputPortCount;
Chris@0 492
Chris@0 493 return true;
Chris@0 494 }
Chris@0 495
Chris@0 496 return false;
Chris@0 497 }
Chris@0 498
Chris@0 499 bool
Chris@107 500 TransformFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin,
Chris@0 501 int &minChannels, int &maxChannels)
Chris@0 502 {
Chris@0 503 Vamp::Plugin *vp = 0;
Chris@28 504 if ((vp = dynamic_cast<Vamp::Plugin *>(plugin)) ||
Chris@28 505 (vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin))) {
Chris@0 506 minChannels = vp->getMinChannelCount();
Chris@0 507 maxChannels = vp->getMaxChannelCount();
Chris@0 508 return true;
Chris@0 509 } else {
Chris@107 510 return getTransformChannelRange(identifier, minChannels, maxChannels);
Chris@0 511 }
Chris@0 512 }
Chris@0 513
Chris@53 514 Model *
Chris@107 515 TransformFactory::getConfigurationForTransform(TransformId identifier,
Chris@53 516 const std::vector<Model *> &candidateInputModels,
Chris@27 517 PluginTransform::ExecutionContext &context,
Chris@41 518 QString &configurationXml,
Chris@41 519 AudioCallbackPlaySource *source)
Chris@0 520 {
Chris@53 521 if (candidateInputModels.empty()) return 0;
Chris@53 522
Chris@54 523 //!!! This will need revision -- we'll have to have a callback
Chris@54 524 //from the dialog for when the candidate input model is changed,
Chris@54 525 //as we'll need to reinitialise the channel settings in the dialog
Chris@53 526 Model *inputModel = candidateInputModels[0]; //!!! for now
Chris@54 527 QStringList candidateModelNames;
Chris@54 528 std::map<QString, Model *> modelMap;
Chris@54 529 for (size_t i = 0; i < candidateInputModels.size(); ++i) {
Chris@54 530 QString modelName = candidateInputModels[i]->objectName();
Chris@54 531 QString origModelName = modelName;
Chris@54 532 int dupcount = 1;
Chris@54 533 while (modelMap.find(modelName) != modelMap.end()) {
Chris@54 534 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
Chris@54 535 }
Chris@54 536 modelMap[modelName] = candidateInputModels[i];
Chris@54 537 candidateModelNames.push_back(modelName);
Chris@54 538 }
Chris@53 539
Chris@107 540 QString id = identifier.section(':', 0, 2);
Chris@107 541 QString output = identifier.section(':', 3);
Chris@53 542 QString outputLabel = "";
Chris@108 543 QString outputDescription = "";
Chris@0 544
Chris@0 545 bool ok = false;
Chris@107 546 configurationXml = m_lastConfigurations[identifier];
Chris@0 547
Chris@0 548 // std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
Chris@0 549
Chris@0 550 Vamp::PluginBase *plugin = 0;
Chris@0 551
Chris@10 552 bool frequency = false;
Chris@41 553 bool effect = false;
Chris@58 554 bool generator = false;
Chris@10 555
Chris@0 556 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 557
Chris@182 558 std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl;
Chris@182 559
Chris@10 560 Vamp::Plugin *vp =
Chris@10 561 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@0 562 (id, inputModel->getSampleRate());
Chris@53 563
Chris@10 564 if (vp) {
Chris@53 565
Chris@10 566 plugin = vp;
Chris@10 567 frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
Chris@53 568
Chris@53 569 std::vector<Vamp::Plugin::OutputDescriptor> od =
Chris@53 570 vp->getOutputDescriptors();
Chris@53 571 if (od.size() > 1) {
Chris@53 572 for (size_t i = 0; i < od.size(); ++i) {
Chris@107 573 if (od[i].identifier == output.toStdString()) {
Chris@107 574 outputLabel = od[i].name.c_str();
Chris@108 575 outputDescription = od[i].description.c_str();
Chris@53 576 break;
Chris@53 577 }
Chris@53 578 }
Chris@53 579 }
Chris@10 580 }
Chris@0 581
Chris@0 582 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 583
Chris@41 584 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
Chris@41 585 const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id);
Chris@41 586
Chris@41 587 if (desc->audioInputPortCount > 0 &&
Chris@41 588 desc->audioOutputPortCount > 0 &&
Chris@41 589 !desc->isSynth) {
Chris@41 590 effect = true;
Chris@41 591 }
Chris@41 592
Chris@58 593 if (desc->audioInputPortCount == 0) {
Chris@58 594 generator = true;
Chris@58 595 }
Chris@58 596
Chris@53 597 if (output != "A") {
Chris@53 598 int outputNo = output.toInt();
Chris@137 599 if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) {
Chris@53 600 outputLabel = desc->controlOutputPortNames[outputNo].c_str();
Chris@53 601 }
Chris@53 602 }
Chris@53 603
Chris@41 604 size_t sampleRate = inputModel->getSampleRate();
Chris@41 605 size_t blockSize = 1024;
Chris@41 606 size_t channels = 1;
Chris@41 607 if (effect && source) {
Chris@41 608 sampleRate = source->getTargetSampleRate();
Chris@41 609 blockSize = source->getTargetBlockSize();
Chris@41 610 channels = source->getTargetChannelCount();
Chris@41 611 }
Chris@41 612
Chris@41 613 RealTimePluginInstance *rtp = factory->instantiatePlugin
Chris@41 614 (id, 0, 0, sampleRate, blockSize, channels);
Chris@41 615
Chris@41 616 plugin = rtp;
Chris@41 617
Chris@41 618 if (effect && source && rtp) {
Chris@41 619 source->setAuditioningPlugin(rtp);
Chris@41 620 }
Chris@0 621 }
Chris@0 622
Chris@0 623 if (plugin) {
Chris@27 624
Chris@27 625 context = PluginTransform::ExecutionContext(context.channel, plugin);
Chris@27 626
Chris@0 627 if (configurationXml != "") {
Chris@0 628 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@0 629 }
Chris@0 630
Chris@0 631 int sourceChannels = 1;
Chris@0 632 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@0 633 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@0 634 ->getChannelCount();
Chris@0 635 }
Chris@0 636
Chris@0 637 int minChannels = 1, maxChannels = sourceChannels;
Chris@107 638 getChannelRange(identifier, plugin, minChannels, maxChannels);
Chris@0 639
Chris@0 640 int targetChannels = sourceChannels;
Chris@44 641 if (!effect) {
Chris@44 642 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@44 643 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@44 644 }
Chris@0 645
Chris@27 646 int defaultChannel = context.channel;
Chris@0 647
Chris@53 648 PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
Chris@53 649
Chris@58 650 if (candidateModelNames.size() > 1 && !generator) {
Chris@54 651 dialog->setCandidateInputModels(candidateModelNames);
Chris@54 652 }
Chris@54 653
Chris@58 654 if (targetChannels > 0) {
Chris@58 655 dialog->setChannelArrangement(sourceChannels, targetChannels,
Chris@58 656 defaultChannel);
Chris@58 657 }
Chris@53 658
Chris@108 659 dialog->setOutputLabel(outputLabel, outputDescription);
Chris@53 660
Chris@53 661 dialog->setShowProcessingOptions(true, frequency);
Chris@53 662
Chris@0 663 if (dialog->exec() == QDialog::Accepted) {
Chris@0 664 ok = true;
Chris@0 665 }
Chris@42 666
Chris@54 667 QString selectedInput = dialog->getInputModel();
Chris@54 668 if (selectedInput != "") {
Chris@54 669 if (modelMap.find(selectedInput) != modelMap.end()) {
Chris@54 670 inputModel = modelMap[selectedInput];
Chris@54 671 std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl;
Chris@54 672 } else {
Chris@54 673 std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl;
Chris@54 674 }
Chris@174 675 } else {
Chris@174 676 std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
Chris@54 677 }
Chris@54 678
Chris@0 679 configurationXml = PluginXml(plugin).toXmlString();
Chris@27 680 context.channel = dialog->getChannel();
Chris@26 681
Chris@27 682 dialog->getProcessingParameters(context.stepSize,
Chris@27 683 context.blockSize,
Chris@27 684 context.windowType);
Chris@27 685
Chris@27 686 context.makeConsistentWithPlugin(plugin);
Chris@26 687
Chris@0 688 delete dialog;
Chris@41 689
Chris@41 690 if (effect && source) {
Chris@41 691 source->setAuditioningPlugin(0); // will delete our plugin
Chris@41 692 } else {
Chris@41 693 delete plugin;
Chris@41 694 }
Chris@0 695 }
Chris@0 696
Chris@107 697 if (ok) m_lastConfigurations[identifier] = configurationXml;
Chris@0 698
Chris@53 699 return ok ? inputModel : 0;
Chris@0 700 }
Chris@0 701
Chris@73 702 PluginTransform::ExecutionContext
Chris@107 703 TransformFactory::getDefaultContextForTransform(TransformId identifier,
Chris@73 704 Model *inputModel)
Chris@73 705 {
Chris@73 706 PluginTransform::ExecutionContext context(-1);
Chris@73 707
Chris@107 708 QString id = identifier.section(':', 0, 2);
Chris@73 709
Chris@73 710 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@73 711
Chris@73 712 Vamp::Plugin *vp =
Chris@73 713 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@73 714 (id, inputModel ? inputModel->getSampleRate() : 48000);
Chris@73 715
Chris@182 716 if (vp) {
Chris@182 717 context = PluginTransform::ExecutionContext(-1, vp);
Chris@182 718 delete vp;
Chris@182 719 }
Chris@73 720 }
Chris@73 721
Chris@73 722 return context;
Chris@73 723 }
Chris@73 724
Chris@0 725 Transform *
Chris@107 726 TransformFactory::createTransform(TransformId identifier, Model *inputModel,
Chris@27 727 const PluginTransform::ExecutionContext &context,
Chris@118 728 QString configurationXml)
Chris@0 729 {
Chris@0 730 Transform *transform = 0;
Chris@0 731
Chris@107 732 QString id = identifier.section(':', 0, 2);
Chris@107 733 QString output = identifier.section(':', 3);
Chris@0 734
Chris@0 735 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 736 transform = new FeatureExtractionPluginTransform(inputModel,
Chris@0 737 id,
Chris@27 738 context,
Chris@0 739 configurationXml,
Chris@0 740 output);
Chris@0 741 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 742 transform = new RealTimePluginTransform(inputModel,
Chris@0 743 id,
Chris@27 744 context,
Chris@0 745 configurationXml,
Chris@107 746 getTransformUnits(identifier),
Chris@34 747 output == "A" ? -1 :
Chris@0 748 output.toInt());
Chris@0 749 } else {
Chris@0 750 std::cerr << "TransformFactory::createTransform: Unknown transform \""
Chris@107 751 << identifier.toStdString() << "\"" << std::endl;
Chris@0 752 return transform;
Chris@0 753 }
Chris@0 754
Chris@118 755 if (transform) transform->setObjectName(identifier);
Chris@0 756 return transform;
Chris@0 757 }
Chris@0 758
Chris@0 759 Model *
Chris@107 760 TransformFactory::transform(TransformId identifier, Model *inputModel,
Chris@27 761 const PluginTransform::ExecutionContext &context,
Chris@27 762 QString configurationXml)
Chris@0 763 {
Chris@107 764 Transform *t = createTransform(identifier, inputModel, context,
Chris@118 765 configurationXml);
Chris@0 766
Chris@0 767 if (!t) return 0;
Chris@0 768
Chris@0 769 connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
Chris@0 770
Chris@118 771 m_runningTransforms.insert(t);
Chris@118 772
Chris@0 773 t->start();
Chris@55 774 Model *model = t->detachOutputModel();
Chris@55 775
Chris@55 776 if (model) {
Chris@55 777 QString imn = inputModel->objectName();
Chris@107 778 QString trn = getTransformFriendlyName(identifier);
Chris@55 779 if (imn != "") {
Chris@55 780 if (trn != "") {
Chris@55 781 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
Chris@55 782 } else {
Chris@55 783 model->setObjectName(imn);
Chris@55 784 }
Chris@55 785 } else if (trn != "") {
Chris@55 786 model->setObjectName(trn);
Chris@55 787 }
Chris@182 788 } else {
Chris@182 789 t->wait();
Chris@55 790 }
Chris@55 791
Chris@55 792 return model;
Chris@0 793 }
Chris@0 794
Chris@0 795 void
Chris@0 796 TransformFactory::transformFinished()
Chris@0 797 {
Chris@0 798 QObject *s = sender();
Chris@0 799 Transform *transform = dynamic_cast<Transform *>(s);
Chris@0 800
Chris@118 801 std::cerr << "TransformFactory::transformFinished(" << transform << ")" << std::endl;
Chris@118 802
Chris@0 803 if (!transform) {
Chris@0 804 std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
Chris@0 805 return;
Chris@0 806 }
Chris@0 807
Chris@118 808 if (m_runningTransforms.find(transform) == m_runningTransforms.end()) {
Chris@118 809 std::cerr << "WARNING: TransformFactory::transformFinished("
Chris@118 810 << transform
Chris@118 811 << "): I have no record of this transform running!"
Chris@118 812 << std::endl;
Chris@118 813 }
Chris@118 814
Chris@118 815 m_runningTransforms.erase(transform);
Chris@118 816
Chris@0 817 transform->wait(); // unnecessary but reassuring
Chris@0 818 delete transform;
Chris@0 819 }
Chris@0 820
Chris@118 821 void
Chris@118 822 TransformFactory::modelAboutToBeDeleted(Model *m)
Chris@118 823 {
Chris@118 824 TransformSet affected;
Chris@118 825
Chris@118 826 for (TransformSet::iterator i = m_runningTransforms.begin();
Chris@118 827 i != m_runningTransforms.end(); ++i) {
Chris@118 828
Chris@118 829 Transform *t = *i;
Chris@118 830
Chris@118 831 if (t->getInputModel() == m || t->getOutputModel() == m) {
Chris@118 832 affected.insert(t);
Chris@118 833 }
Chris@118 834 }
Chris@118 835
Chris@118 836 for (TransformSet::iterator i = affected.begin();
Chris@118 837 i != affected.end(); ++i) {
Chris@118 838
Chris@118 839 Transform *t = *i;
Chris@118 840
Chris@118 841 t->abandon();
Chris@118 842
Chris@118 843 t->wait(); // this should eventually call back on
Chris@118 844 // transformFinished, which will remove from
Chris@118 845 // m_runningTransforms and delete.
Chris@118 846 }
Chris@118 847 }
Chris@118 848