annotate transform/TransformFactory.cpp @ 184:ebd906049fb6

* Change WaveFileModel API from getValues(start,end) to getData(start,count). It's much less error-prone to pass in frame counts instead of start/end locations. Should have done this ages ago. This closes #1794563. * Add option to apply a transform to only the selection region, instead of the whole audio. * (to make the above work properly) Add start frame offset to wave models
author Chris Cannam
date Mon, 01 Oct 2007 13:48:38 +0000
parents 21a76c9ed5c3
children
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@184 519 AudioCallbackPlaySource *source,
Chris@184 520 size_t startFrame,
Chris@184 521 size_t duration)
Chris@0 522 {
Chris@53 523 if (candidateInputModels.empty()) return 0;
Chris@53 524
Chris@54 525 //!!! This will need revision -- we'll have to have a callback
Chris@54 526 //from the dialog for when the candidate input model is changed,
Chris@54 527 //as we'll need to reinitialise the channel settings in the dialog
Chris@53 528 Model *inputModel = candidateInputModels[0]; //!!! for now
Chris@54 529 QStringList candidateModelNames;
Chris@54 530 std::map<QString, Model *> modelMap;
Chris@54 531 for (size_t i = 0; i < candidateInputModels.size(); ++i) {
Chris@54 532 QString modelName = candidateInputModels[i]->objectName();
Chris@54 533 QString origModelName = modelName;
Chris@54 534 int dupcount = 1;
Chris@54 535 while (modelMap.find(modelName) != modelMap.end()) {
Chris@54 536 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
Chris@54 537 }
Chris@54 538 modelMap[modelName] = candidateInputModels[i];
Chris@54 539 candidateModelNames.push_back(modelName);
Chris@54 540 }
Chris@53 541
Chris@107 542 QString id = identifier.section(':', 0, 2);
Chris@107 543 QString output = identifier.section(':', 3);
Chris@53 544 QString outputLabel = "";
Chris@108 545 QString outputDescription = "";
Chris@0 546
Chris@0 547 bool ok = false;
Chris@107 548 configurationXml = m_lastConfigurations[identifier];
Chris@0 549
Chris@0 550 // std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
Chris@0 551
Chris@0 552 Vamp::PluginBase *plugin = 0;
Chris@0 553
Chris@10 554 bool frequency = false;
Chris@41 555 bool effect = false;
Chris@58 556 bool generator = false;
Chris@10 557
Chris@0 558 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 559
Chris@182 560 std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl;
Chris@182 561
Chris@10 562 Vamp::Plugin *vp =
Chris@10 563 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@0 564 (id, inputModel->getSampleRate());
Chris@53 565
Chris@10 566 if (vp) {
Chris@53 567
Chris@10 568 plugin = vp;
Chris@10 569 frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
Chris@53 570
Chris@53 571 std::vector<Vamp::Plugin::OutputDescriptor> od =
Chris@53 572 vp->getOutputDescriptors();
Chris@53 573 if (od.size() > 1) {
Chris@53 574 for (size_t i = 0; i < od.size(); ++i) {
Chris@107 575 if (od[i].identifier == output.toStdString()) {
Chris@107 576 outputLabel = od[i].name.c_str();
Chris@108 577 outputDescription = od[i].description.c_str();
Chris@53 578 break;
Chris@53 579 }
Chris@53 580 }
Chris@53 581 }
Chris@10 582 }
Chris@0 583
Chris@0 584 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 585
Chris@41 586 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
Chris@41 587 const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id);
Chris@41 588
Chris@41 589 if (desc->audioInputPortCount > 0 &&
Chris@41 590 desc->audioOutputPortCount > 0 &&
Chris@41 591 !desc->isSynth) {
Chris@41 592 effect = true;
Chris@41 593 }
Chris@41 594
Chris@58 595 if (desc->audioInputPortCount == 0) {
Chris@58 596 generator = true;
Chris@58 597 }
Chris@58 598
Chris@53 599 if (output != "A") {
Chris@53 600 int outputNo = output.toInt();
Chris@137 601 if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) {
Chris@53 602 outputLabel = desc->controlOutputPortNames[outputNo].c_str();
Chris@53 603 }
Chris@53 604 }
Chris@53 605
Chris@41 606 size_t sampleRate = inputModel->getSampleRate();
Chris@41 607 size_t blockSize = 1024;
Chris@41 608 size_t channels = 1;
Chris@41 609 if (effect && source) {
Chris@41 610 sampleRate = source->getTargetSampleRate();
Chris@41 611 blockSize = source->getTargetBlockSize();
Chris@41 612 channels = source->getTargetChannelCount();
Chris@41 613 }
Chris@41 614
Chris@41 615 RealTimePluginInstance *rtp = factory->instantiatePlugin
Chris@41 616 (id, 0, 0, sampleRate, blockSize, channels);
Chris@41 617
Chris@41 618 plugin = rtp;
Chris@41 619
Chris@41 620 if (effect && source && rtp) {
Chris@41 621 source->setAuditioningPlugin(rtp);
Chris@41 622 }
Chris@0 623 }
Chris@0 624
Chris@0 625 if (plugin) {
Chris@27 626
Chris@27 627 context = PluginTransform::ExecutionContext(context.channel, plugin);
Chris@27 628
Chris@0 629 if (configurationXml != "") {
Chris@0 630 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@0 631 }
Chris@0 632
Chris@0 633 int sourceChannels = 1;
Chris@0 634 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@0 635 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@0 636 ->getChannelCount();
Chris@0 637 }
Chris@0 638
Chris@0 639 int minChannels = 1, maxChannels = sourceChannels;
Chris@107 640 getChannelRange(identifier, plugin, minChannels, maxChannels);
Chris@0 641
Chris@0 642 int targetChannels = sourceChannels;
Chris@44 643 if (!effect) {
Chris@44 644 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@44 645 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@44 646 }
Chris@0 647
Chris@27 648 int defaultChannel = context.channel;
Chris@0 649
Chris@53 650 PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
Chris@53 651
Chris@58 652 if (candidateModelNames.size() > 1 && !generator) {
Chris@54 653 dialog->setCandidateInputModels(candidateModelNames);
Chris@54 654 }
Chris@54 655
Chris@184 656 if (startFrame != 0 || duration != 0) {
Chris@184 657 dialog->setShowSelectionOnlyOption(true);
Chris@184 658 }
Chris@184 659
Chris@58 660 if (targetChannels > 0) {
Chris@58 661 dialog->setChannelArrangement(sourceChannels, targetChannels,
Chris@58 662 defaultChannel);
Chris@58 663 }
Chris@53 664
Chris@108 665 dialog->setOutputLabel(outputLabel, outputDescription);
Chris@53 666
Chris@53 667 dialog->setShowProcessingOptions(true, frequency);
Chris@53 668
Chris@0 669 if (dialog->exec() == QDialog::Accepted) {
Chris@0 670 ok = true;
Chris@0 671 }
Chris@42 672
Chris@54 673 QString selectedInput = dialog->getInputModel();
Chris@54 674 if (selectedInput != "") {
Chris@54 675 if (modelMap.find(selectedInput) != modelMap.end()) {
Chris@54 676 inputModel = modelMap[selectedInput];
Chris@54 677 std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl;
Chris@54 678 } else {
Chris@54 679 std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl;
Chris@54 680 }
Chris@174 681 } else {
Chris@174 682 std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
Chris@54 683 }
Chris@54 684
Chris@0 685 configurationXml = PluginXml(plugin).toXmlString();
Chris@27 686 context.channel = dialog->getChannel();
Chris@184 687
Chris@184 688 if (startFrame != 0 || duration != 0) {
Chris@184 689 if (dialog->getSelectionOnly()) {
Chris@184 690 context.startFrame = startFrame;
Chris@184 691 context.duration = duration;
Chris@184 692 }
Chris@184 693 }
Chris@26 694
Chris@27 695 dialog->getProcessingParameters(context.stepSize,
Chris@27 696 context.blockSize,
Chris@27 697 context.windowType);
Chris@27 698
Chris@27 699 context.makeConsistentWithPlugin(plugin);
Chris@26 700
Chris@0 701 delete dialog;
Chris@41 702
Chris@41 703 if (effect && source) {
Chris@41 704 source->setAuditioningPlugin(0); // will delete our plugin
Chris@41 705 } else {
Chris@41 706 delete plugin;
Chris@41 707 }
Chris@0 708 }
Chris@0 709
Chris@107 710 if (ok) m_lastConfigurations[identifier] = configurationXml;
Chris@0 711
Chris@53 712 return ok ? inputModel : 0;
Chris@0 713 }
Chris@0 714
Chris@73 715 PluginTransform::ExecutionContext
Chris@107 716 TransformFactory::getDefaultContextForTransform(TransformId identifier,
Chris@73 717 Model *inputModel)
Chris@73 718 {
Chris@73 719 PluginTransform::ExecutionContext context(-1);
Chris@73 720
Chris@107 721 QString id = identifier.section(':', 0, 2);
Chris@73 722
Chris@73 723 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@73 724
Chris@73 725 Vamp::Plugin *vp =
Chris@73 726 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@73 727 (id, inputModel ? inputModel->getSampleRate() : 48000);
Chris@73 728
Chris@182 729 if (vp) {
Chris@182 730 context = PluginTransform::ExecutionContext(-1, vp);
Chris@182 731 delete vp;
Chris@182 732 }
Chris@73 733 }
Chris@73 734
Chris@73 735 return context;
Chris@73 736 }
Chris@73 737
Chris@0 738 Transform *
Chris@107 739 TransformFactory::createTransform(TransformId identifier, Model *inputModel,
Chris@27 740 const PluginTransform::ExecutionContext &context,
Chris@118 741 QString configurationXml)
Chris@0 742 {
Chris@0 743 Transform *transform = 0;
Chris@0 744
Chris@107 745 QString id = identifier.section(':', 0, 2);
Chris@107 746 QString output = identifier.section(':', 3);
Chris@0 747
Chris@0 748 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 749 transform = new FeatureExtractionPluginTransform(inputModel,
Chris@0 750 id,
Chris@27 751 context,
Chris@0 752 configurationXml,
Chris@0 753 output);
Chris@0 754 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 755 transform = new RealTimePluginTransform(inputModel,
Chris@0 756 id,
Chris@27 757 context,
Chris@0 758 configurationXml,
Chris@107 759 getTransformUnits(identifier),
Chris@34 760 output == "A" ? -1 :
Chris@0 761 output.toInt());
Chris@0 762 } else {
Chris@0 763 std::cerr << "TransformFactory::createTransform: Unknown transform \""
Chris@107 764 << identifier.toStdString() << "\"" << std::endl;
Chris@0 765 return transform;
Chris@0 766 }
Chris@0 767
Chris@118 768 if (transform) transform->setObjectName(identifier);
Chris@0 769 return transform;
Chris@0 770 }
Chris@0 771
Chris@0 772 Model *
Chris@107 773 TransformFactory::transform(TransformId identifier, Model *inputModel,
Chris@27 774 const PluginTransform::ExecutionContext &context,
Chris@27 775 QString configurationXml)
Chris@0 776 {
Chris@107 777 Transform *t = createTransform(identifier, inputModel, context,
Chris@118 778 configurationXml);
Chris@0 779
Chris@0 780 if (!t) return 0;
Chris@0 781
Chris@0 782 connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
Chris@0 783
Chris@118 784 m_runningTransforms.insert(t);
Chris@118 785
Chris@0 786 t->start();
Chris@55 787 Model *model = t->detachOutputModel();
Chris@55 788
Chris@55 789 if (model) {
Chris@55 790 QString imn = inputModel->objectName();
Chris@107 791 QString trn = getTransformFriendlyName(identifier);
Chris@55 792 if (imn != "") {
Chris@55 793 if (trn != "") {
Chris@55 794 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
Chris@55 795 } else {
Chris@55 796 model->setObjectName(imn);
Chris@55 797 }
Chris@55 798 } else if (trn != "") {
Chris@55 799 model->setObjectName(trn);
Chris@55 800 }
Chris@182 801 } else {
Chris@182 802 t->wait();
Chris@55 803 }
Chris@55 804
Chris@55 805 return model;
Chris@0 806 }
Chris@0 807
Chris@0 808 void
Chris@0 809 TransformFactory::transformFinished()
Chris@0 810 {
Chris@0 811 QObject *s = sender();
Chris@0 812 Transform *transform = dynamic_cast<Transform *>(s);
Chris@0 813
Chris@118 814 std::cerr << "TransformFactory::transformFinished(" << transform << ")" << std::endl;
Chris@118 815
Chris@0 816 if (!transform) {
Chris@0 817 std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
Chris@0 818 return;
Chris@0 819 }
Chris@0 820
Chris@118 821 if (m_runningTransforms.find(transform) == m_runningTransforms.end()) {
Chris@118 822 std::cerr << "WARNING: TransformFactory::transformFinished("
Chris@118 823 << transform
Chris@118 824 << "): I have no record of this transform running!"
Chris@118 825 << std::endl;
Chris@118 826 }
Chris@118 827
Chris@118 828 m_runningTransforms.erase(transform);
Chris@118 829
Chris@0 830 transform->wait(); // unnecessary but reassuring
Chris@0 831 delete transform;
Chris@0 832 }
Chris@0 833
Chris@118 834 void
Chris@118 835 TransformFactory::modelAboutToBeDeleted(Model *m)
Chris@118 836 {
Chris@118 837 TransformSet affected;
Chris@118 838
Chris@118 839 for (TransformSet::iterator i = m_runningTransforms.begin();
Chris@118 840 i != m_runningTransforms.end(); ++i) {
Chris@118 841
Chris@118 842 Transform *t = *i;
Chris@118 843
Chris@118 844 if (t->getInputModel() == m || t->getOutputModel() == m) {
Chris@118 845 affected.insert(t);
Chris@118 846 }
Chris@118 847 }
Chris@118 848
Chris@118 849 for (TransformSet::iterator i = affected.begin();
Chris@118 850 i != affected.end(); ++i) {
Chris@118 851
Chris@118 852 Transform *t = *i;
Chris@118 853
Chris@118 854 t->abandon();
Chris@118 855
Chris@118 856 t->wait(); // this should eventually call back on
Chris@118 857 // transformFinished, which will remove from
Chris@118 858 // m_runningTransforms and delete.
Chris@118 859 }
Chris@118 860 }
Chris@118 861