annotate transform/TransformFactory.cpp @ 27:61259228d029

* More to do with passing around step/blocksize etc from plugin dialog to plugins. Still some puzzling unresolved details.
author Chris Cannam
date Tue, 19 Sep 2006 14:37:06 +0000
parents d88d117e0c34
children b5f55ea61bb8
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@0 7 This file copyright 2006 Chris Cannam.
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@0 29 #include <iostream>
Chris@0 30 #include <set>
Chris@0 31
Chris@0 32 #include <QRegExp>
Chris@0 33
Chris@0 34 TransformFactory *
Chris@0 35 TransformFactory::m_instance = new TransformFactory;
Chris@0 36
Chris@0 37 TransformFactory *
Chris@0 38 TransformFactory::getInstance()
Chris@0 39 {
Chris@0 40 return m_instance;
Chris@0 41 }
Chris@0 42
Chris@0 43 TransformFactory::~TransformFactory()
Chris@0 44 {
Chris@0 45 }
Chris@0 46
Chris@0 47 TransformFactory::TransformList
Chris@0 48 TransformFactory::getAllTransforms()
Chris@0 49 {
Chris@0 50 if (m_transforms.empty()) populateTransforms();
Chris@0 51
Chris@0 52 TransformList list;
Chris@0 53 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@0 54 i != m_transforms.end(); ++i) {
Chris@0 55 list.push_back(i->second);
Chris@0 56 }
Chris@0 57
Chris@0 58 return list;
Chris@0 59 }
Chris@0 60
Chris@0 61 std::vector<QString>
Chris@0 62 TransformFactory::getAllTransformTypes()
Chris@0 63 {
Chris@0 64 if (m_transforms.empty()) populateTransforms();
Chris@0 65
Chris@0 66 std::set<QString> types;
Chris@0 67 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@0 68 i != m_transforms.end(); ++i) {
Chris@0 69 types.insert(i->second.type);
Chris@0 70 }
Chris@0 71
Chris@0 72 std::vector<QString> rv;
Chris@0 73 for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@0 74 rv.push_back(*i);
Chris@0 75 }
Chris@0 76
Chris@0 77 return rv;
Chris@0 78 }
Chris@0 79
Chris@0 80 void
Chris@0 81 TransformFactory::populateTransforms()
Chris@0 82 {
Chris@0 83 TransformDescriptionMap transforms;
Chris@0 84
Chris@0 85 populateFeatureExtractionPlugins(transforms);
Chris@0 86 populateRealTimePlugins(transforms);
Chris@0 87
Chris@0 88 // disambiguate plugins with similar descriptions
Chris@0 89
Chris@0 90 std::map<QString, int> descriptions;
Chris@0 91
Chris@0 92 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@0 93 i != transforms.end(); ++i) {
Chris@0 94
Chris@0 95 TransformDesc desc = i->second;
Chris@0 96
Chris@0 97 ++descriptions[desc.description];
Chris@0 98 ++descriptions[QString("%1 [%2]").arg(desc.description).arg(desc.maker)];
Chris@0 99 }
Chris@0 100
Chris@0 101 std::map<QString, int> counts;
Chris@0 102 m_transforms.clear();
Chris@0 103
Chris@0 104 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@0 105 i != transforms.end(); ++i) {
Chris@0 106
Chris@0 107 TransformDesc desc = i->second;
Chris@0 108 QString name = desc.name;
Chris@0 109 QString description = desc.description;
Chris@0 110 QString maker = desc.maker;
Chris@0 111
Chris@0 112 if (descriptions[description] > 1) {
Chris@0 113 description = QString("%1 [%2]").arg(description).arg(maker);
Chris@0 114 if (descriptions[description] > 1) {
Chris@0 115 description = QString("%1 <%2>")
Chris@0 116 .arg(description).arg(++counts[description]);
Chris@0 117 }
Chris@0 118 }
Chris@0 119
Chris@0 120 desc.description = description;
Chris@0 121 m_transforms[name] = desc;
Chris@0 122 }
Chris@0 123 }
Chris@0 124
Chris@0 125 void
Chris@0 126 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
Chris@0 127 {
Chris@0 128 std::vector<QString> plugs =
Chris@0 129 FeatureExtractionPluginFactory::getAllPluginIdentifiers();
Chris@0 130
Chris@0 131 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@0 132
Chris@0 133 QString pluginId = plugs[i];
Chris@0 134
Chris@0 135 FeatureExtractionPluginFactory *factory =
Chris@0 136 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@0 137
Chris@0 138 if (!factory) {
Chris@0 139 std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 140 continue;
Chris@0 141 }
Chris@0 142
Chris@0 143 Vamp::Plugin *plugin =
Chris@0 144 factory->instantiatePlugin(pluginId, 48000);
Chris@0 145
Chris@0 146 if (!plugin) {
Chris@0 147 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 148 continue;
Chris@0 149 }
Chris@0 150
Chris@0 151 QString pluginDescription = plugin->getDescription().c_str();
Chris@0 152 Vamp::Plugin::OutputList outputs =
Chris@0 153 plugin->getOutputDescriptors();
Chris@0 154
Chris@0 155 for (size_t j = 0; j < outputs.size(); ++j) {
Chris@0 156
Chris@0 157 QString transformName = QString("%1:%2")
Chris@0 158 .arg(pluginId).arg(outputs[j].name.c_str());
Chris@0 159
Chris@0 160 QString userDescription;
Chris@0 161 QString friendlyName;
Chris@0 162 QString units = outputs[j].unit.c_str();
Chris@0 163
Chris@0 164 if (outputs.size() == 1) {
Chris@0 165 userDescription = pluginDescription;
Chris@0 166 friendlyName = pluginDescription;
Chris@0 167 } else {
Chris@0 168 userDescription = QString("%1: %2")
Chris@0 169 .arg(pluginDescription)
Chris@0 170 .arg(outputs[j].description.c_str());
Chris@0 171 friendlyName = outputs[j].description.c_str();
Chris@0 172 }
Chris@0 173
Chris@0 174 bool configurable = (!plugin->getPrograms().empty() ||
Chris@0 175 !plugin->getParameterDescriptors().empty());
Chris@0 176
Chris@0 177 transforms[transformName] =
Chris@0 178 TransformDesc(tr("Analysis Plugins"),
Chris@0 179 transformName,
Chris@0 180 userDescription,
Chris@0 181 friendlyName,
Chris@0 182 plugin->getMaker().c_str(),
Chris@0 183 units,
Chris@0 184 configurable);
Chris@0 185 }
Chris@0 186 }
Chris@0 187 }
Chris@0 188
Chris@0 189 void
Chris@0 190 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
Chris@0 191 {
Chris@0 192 std::vector<QString> plugs =
Chris@0 193 RealTimePluginFactory::getAllPluginIdentifiers();
Chris@0 194
Chris@0 195 QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
Chris@0 196
Chris@0 197 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@0 198
Chris@0 199 QString pluginId = plugs[i];
Chris@0 200
Chris@0 201 RealTimePluginFactory *factory =
Chris@0 202 RealTimePluginFactory::instanceFor(pluginId);
Chris@0 203
Chris@0 204 if (!factory) {
Chris@0 205 std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 206 continue;
Chris@0 207 }
Chris@0 208
Chris@0 209 const RealTimePluginDescriptor *descriptor =
Chris@0 210 factory->getPluginDescriptor(pluginId);
Chris@0 211
Chris@0 212 if (!descriptor) {
Chris@0 213 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@0 214 continue;
Chris@0 215 }
Chris@0 216
Chris@0 217 if (descriptor->controlOutputPortCount == 0 ||
Chris@0 218 descriptor->audioInputPortCount == 0) continue;
Chris@0 219
Chris@0 220 // std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " output ports" << std::endl;
Chris@0 221
Chris@0 222 QString pluginDescription = descriptor->name.c_str();
Chris@0 223
Chris@0 224 for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
Chris@0 225
Chris@0 226 QString transformName = QString("%1:%2").arg(pluginId).arg(j);
Chris@0 227 QString userDescription;
Chris@0 228 QString units;
Chris@0 229
Chris@0 230 if (j < descriptor->controlOutputPortNames.size() &&
Chris@0 231 descriptor->controlOutputPortNames[j] != "") {
Chris@0 232
Chris@0 233 QString portName = descriptor->controlOutputPortNames[j].c_str();
Chris@0 234
Chris@0 235 userDescription = tr("%1: %2")
Chris@0 236 .arg(pluginDescription)
Chris@0 237 .arg(portName);
Chris@0 238
Chris@0 239 if (unitRE.indexIn(portName) >= 0) {
Chris@0 240 units = unitRE.cap(1);
Chris@0 241 }
Chris@0 242
Chris@0 243 } else if (descriptor->controlOutputPortCount > 1) {
Chris@0 244
Chris@0 245 userDescription = tr("%1: Output %2")
Chris@0 246 .arg(pluginDescription)
Chris@0 247 .arg(j + 1);
Chris@0 248
Chris@0 249 } else {
Chris@0 250
Chris@0 251 userDescription = pluginDescription;
Chris@0 252 }
Chris@0 253
Chris@0 254
Chris@0 255 bool configurable = (descriptor->parameterCount > 0);
Chris@0 256
Chris@0 257 transforms[transformName] =
Chris@0 258 TransformDesc(tr("Other Plugins"),
Chris@0 259 transformName,
Chris@0 260 userDescription,
Chris@0 261 userDescription,
Chris@0 262 descriptor->maker.c_str(),
Chris@0 263 units,
Chris@0 264 configurable);
Chris@0 265 }
Chris@0 266 }
Chris@0 267 }
Chris@0 268
Chris@0 269 QString
Chris@0 270 TransformFactory::getTransformDescription(TransformName name)
Chris@0 271 {
Chris@0 272 if (m_transforms.find(name) != m_transforms.end()) {
Chris@0 273 return m_transforms[name].description;
Chris@0 274 } else return "";
Chris@0 275 }
Chris@0 276
Chris@0 277 QString
Chris@0 278 TransformFactory::getTransformFriendlyName(TransformName name)
Chris@0 279 {
Chris@0 280 if (m_transforms.find(name) != m_transforms.end()) {
Chris@0 281 return m_transforms[name].friendlyName;
Chris@0 282 } else return "";
Chris@0 283 }
Chris@0 284
Chris@0 285 QString
Chris@0 286 TransformFactory::getTransformUnits(TransformName name)
Chris@0 287 {
Chris@0 288 if (m_transforms.find(name) != m_transforms.end()) {
Chris@0 289 return m_transforms[name].units;
Chris@0 290 } else return "";
Chris@0 291 }
Chris@0 292
Chris@0 293 bool
Chris@0 294 TransformFactory::isTransformConfigurable(TransformName name)
Chris@0 295 {
Chris@0 296 if (m_transforms.find(name) != m_transforms.end()) {
Chris@0 297 return m_transforms[name].configurable;
Chris@0 298 } else return false;
Chris@0 299 }
Chris@0 300
Chris@0 301 bool
Chris@0 302 TransformFactory::getTransformChannelRange(TransformName name,
Chris@0 303 int &min, int &max)
Chris@0 304 {
Chris@0 305 QString id = name.section(':', 0, 2);
Chris@0 306
Chris@0 307 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 308
Chris@0 309 Vamp::Plugin *plugin =
Chris@0 310 FeatureExtractionPluginFactory::instanceFor(id)->
Chris@0 311 instantiatePlugin(id, 48000);
Chris@0 312 if (!plugin) return false;
Chris@0 313
Chris@0 314 min = plugin->getMinChannelCount();
Chris@0 315 max = plugin->getMaxChannelCount();
Chris@0 316 delete plugin;
Chris@0 317
Chris@0 318 return true;
Chris@0 319
Chris@0 320 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 321
Chris@0 322 const RealTimePluginDescriptor *descriptor =
Chris@0 323 RealTimePluginFactory::instanceFor(id)->
Chris@0 324 getPluginDescriptor(id);
Chris@0 325 if (!descriptor) return false;
Chris@0 326
Chris@0 327 min = descriptor->audioInputPortCount;
Chris@0 328 max = descriptor->audioInputPortCount;
Chris@0 329
Chris@0 330 return true;
Chris@0 331 }
Chris@0 332
Chris@0 333 return false;
Chris@0 334 }
Chris@0 335
Chris@0 336 bool
Chris@0 337 TransformFactory::getChannelRange(TransformName name, Vamp::PluginBase *plugin,
Chris@0 338 int &minChannels, int &maxChannels)
Chris@0 339 {
Chris@0 340 Vamp::Plugin *vp = 0;
Chris@0 341 if ((vp = dynamic_cast<Vamp::Plugin *>(plugin))) {
Chris@0 342 minChannels = vp->getMinChannelCount();
Chris@0 343 maxChannels = vp->getMaxChannelCount();
Chris@0 344 return true;
Chris@0 345 } else {
Chris@0 346 return getTransformChannelRange(name, minChannels, maxChannels);
Chris@0 347 }
Chris@0 348 }
Chris@0 349
Chris@0 350 bool
Chris@0 351 TransformFactory::getConfigurationForTransform(TransformName name,
Chris@0 352 Model *inputModel,
Chris@27 353 PluginTransform::ExecutionContext &context,
Chris@0 354 QString &configurationXml)
Chris@0 355 {
Chris@0 356 QString id = name.section(':', 0, 2);
Chris@0 357 QString output = name.section(':', 3);
Chris@0 358
Chris@0 359 bool ok = false;
Chris@0 360 configurationXml = m_lastConfigurations[name];
Chris@0 361
Chris@0 362 // std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
Chris@0 363
Chris@0 364 Vamp::PluginBase *plugin = 0;
Chris@0 365
Chris@10 366 bool frequency = false;
Chris@10 367
Chris@0 368 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 369
Chris@10 370 Vamp::Plugin *vp =
Chris@10 371 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@0 372 (id, inputModel->getSampleRate());
Chris@10 373 if (vp) {
Chris@10 374 plugin = vp;
Chris@10 375 frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
Chris@10 376 }
Chris@0 377
Chris@0 378 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 379
Chris@0 380 plugin = RealTimePluginFactory::instanceFor(id)->instantiatePlugin
Chris@0 381 (id, 0, 0, inputModel->getSampleRate(), 1024, 1);
Chris@0 382 }
Chris@0 383
Chris@0 384 if (plugin) {
Chris@27 385
Chris@27 386 context = PluginTransform::ExecutionContext(context.channel, plugin);
Chris@27 387
Chris@0 388 if (configurationXml != "") {
Chris@0 389 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@0 390 }
Chris@0 391
Chris@0 392 int sourceChannels = 1;
Chris@0 393 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@0 394 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@0 395 ->getChannelCount();
Chris@0 396 }
Chris@0 397
Chris@0 398 int minChannels = 1, maxChannels = sourceChannels;
Chris@0 399 getChannelRange(name, plugin, minChannels, maxChannels);
Chris@0 400
Chris@0 401 int targetChannels = sourceChannels;
Chris@0 402 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@0 403 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@0 404
Chris@27 405 int defaultChannel = context.channel;
Chris@0 406
Chris@0 407 PluginParameterDialog *dialog = new PluginParameterDialog(plugin,
Chris@0 408 sourceChannels,
Chris@0 409 targetChannels,
Chris@0 410 defaultChannel,
Chris@10 411 output,
Chris@10 412 true,
Chris@10 413 frequency);
Chris@0 414 if (dialog->exec() == QDialog::Accepted) {
Chris@0 415 ok = true;
Chris@0 416 }
Chris@0 417 configurationXml = PluginXml(plugin).toXmlString();
Chris@27 418 context.channel = dialog->getChannel();
Chris@26 419
Chris@27 420 dialog->getProcessingParameters(context.stepSize,
Chris@27 421 context.blockSize,
Chris@27 422 context.windowType);
Chris@27 423
Chris@27 424 context.makeConsistentWithPlugin(plugin);
Chris@26 425
Chris@0 426 delete dialog;
Chris@0 427 delete plugin;
Chris@0 428 }
Chris@0 429
Chris@0 430 if (ok) m_lastConfigurations[name] = configurationXml;
Chris@0 431
Chris@0 432 return ok;
Chris@0 433 }
Chris@0 434
Chris@0 435 Transform *
Chris@0 436 TransformFactory::createTransform(TransformName name, Model *inputModel,
Chris@27 437 const PluginTransform::ExecutionContext &context,
Chris@27 438 QString configurationXml, bool start)
Chris@0 439 {
Chris@0 440 Transform *transform = 0;
Chris@0 441
Chris@0 442 QString id = name.section(':', 0, 2);
Chris@0 443 QString output = name.section(':', 3);
Chris@0 444
Chris@0 445 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 446 transform = new FeatureExtractionPluginTransform(inputModel,
Chris@0 447 id,
Chris@27 448 context,
Chris@0 449 configurationXml,
Chris@0 450 output);
Chris@0 451 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 452 transform = new RealTimePluginTransform(inputModel,
Chris@0 453 id,
Chris@27 454 context,
Chris@0 455 configurationXml,
Chris@0 456 getTransformUnits(name),
Chris@0 457 output.toInt());
Chris@0 458 } else {
Chris@0 459 std::cerr << "TransformFactory::createTransform: Unknown transform \""
Chris@0 460 << name.toStdString() << "\"" << std::endl;
Chris@0 461 return transform;
Chris@0 462 }
Chris@0 463
Chris@0 464 if (start && transform) transform->start();
Chris@0 465 transform->setObjectName(name);
Chris@0 466 return transform;
Chris@0 467 }
Chris@0 468
Chris@0 469 Model *
Chris@0 470 TransformFactory::transform(TransformName name, Model *inputModel,
Chris@27 471 const PluginTransform::ExecutionContext &context,
Chris@27 472 QString configurationXml)
Chris@0 473 {
Chris@27 474 Transform *t = createTransform(name, inputModel, context,
Chris@0 475 configurationXml, false);
Chris@0 476
Chris@0 477 if (!t) return 0;
Chris@0 478
Chris@0 479 connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
Chris@0 480
Chris@0 481 t->start();
Chris@0 482 return t->detachOutputModel();
Chris@0 483 }
Chris@0 484
Chris@0 485 void
Chris@0 486 TransformFactory::transformFinished()
Chris@0 487 {
Chris@0 488 QObject *s = sender();
Chris@0 489 Transform *transform = dynamic_cast<Transform *>(s);
Chris@0 490
Chris@0 491 if (!transform) {
Chris@0 492 std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
Chris@0 493 return;
Chris@0 494 }
Chris@0 495
Chris@0 496 transform->wait(); // unnecessary but reassuring
Chris@0 497 delete transform;
Chris@0 498 }
Chris@0 499