annotate transform/TransformFactory.cpp @ 25:e74f508db18c

* Add setRatio method to the time stretcher, and make it possible to change the ratio without having to construct and replace the time stretcher. This means we can do it seamlessly. Add a lot more ratios to the time stretch control in the main window
author Chris Cannam
date Fri, 15 Sep 2006 15:35:06 +0000
parents 582f4d6e82c7
children d88d117e0c34
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@0 353 int &channel,
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@0 385 if (configurationXml != "") {
Chris@0 386 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@0 387 }
Chris@0 388
Chris@0 389 int sourceChannels = 1;
Chris@0 390 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@0 391 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@0 392 ->getChannelCount();
Chris@0 393 }
Chris@0 394
Chris@0 395 int minChannels = 1, maxChannels = sourceChannels;
Chris@0 396 getChannelRange(name, plugin, minChannels, maxChannels);
Chris@0 397
Chris@0 398 int targetChannels = sourceChannels;
Chris@0 399 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@0 400 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@0 401
Chris@0 402 int defaultChannel = channel;
Chris@0 403
Chris@0 404 PluginParameterDialog *dialog = new PluginParameterDialog(plugin,
Chris@0 405 sourceChannels,
Chris@0 406 targetChannels,
Chris@0 407 defaultChannel,
Chris@10 408 output,
Chris@10 409 true,
Chris@10 410 frequency);
Chris@0 411 if (dialog->exec() == QDialog::Accepted) {
Chris@0 412 ok = true;
Chris@0 413 }
Chris@0 414 configurationXml = PluginXml(plugin).toXmlString();
Chris@0 415 channel = dialog->getChannel();
Chris@0 416 delete dialog;
Chris@0 417 delete plugin;
Chris@0 418 }
Chris@0 419
Chris@0 420 if (ok) m_lastConfigurations[name] = configurationXml;
Chris@0 421
Chris@0 422 return ok;
Chris@0 423 }
Chris@0 424
Chris@0 425 Transform *
Chris@0 426 TransformFactory::createTransform(TransformName name, Model *inputModel,
Chris@0 427 int channel, QString configurationXml, bool start)
Chris@0 428 {
Chris@0 429 Transform *transform = 0;
Chris@0 430
Chris@0 431 //!!! use channel
Chris@0 432
Chris@0 433 QString id = name.section(':', 0, 2);
Chris@0 434 QString output = name.section(':', 3);
Chris@0 435
Chris@0 436 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@0 437 transform = new FeatureExtractionPluginTransform(inputModel,
Chris@0 438 id,
Chris@0 439 channel,
Chris@0 440 configurationXml,
Chris@0 441 output);
Chris@0 442 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@0 443 transform = new RealTimePluginTransform(inputModel,
Chris@0 444 id,
Chris@0 445 channel,
Chris@0 446 configurationXml,
Chris@0 447 getTransformUnits(name),
Chris@0 448 output.toInt());
Chris@0 449 } else {
Chris@0 450 std::cerr << "TransformFactory::createTransform: Unknown transform \""
Chris@0 451 << name.toStdString() << "\"" << std::endl;
Chris@0 452 return transform;
Chris@0 453 }
Chris@0 454
Chris@0 455 if (start && transform) transform->start();
Chris@0 456 transform->setObjectName(name);
Chris@0 457 return transform;
Chris@0 458 }
Chris@0 459
Chris@0 460 Model *
Chris@0 461 TransformFactory::transform(TransformName name, Model *inputModel,
Chris@0 462 int channel, QString configurationXml)
Chris@0 463 {
Chris@0 464 Transform *t = createTransform(name, inputModel, channel,
Chris@0 465 configurationXml, false);
Chris@0 466
Chris@0 467 if (!t) return 0;
Chris@0 468
Chris@0 469 connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
Chris@0 470
Chris@0 471 t->start();
Chris@0 472 return t->detachOutputModel();
Chris@0 473 }
Chris@0 474
Chris@0 475 void
Chris@0 476 TransformFactory::transformFinished()
Chris@0 477 {
Chris@0 478 QObject *s = sender();
Chris@0 479 Transform *transform = dynamic_cast<Transform *>(s);
Chris@0 480
Chris@0 481 if (!transform) {
Chris@0 482 std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
Chris@0 483 return;
Chris@0 484 }
Chris@0 485
Chris@0 486 transform->wait(); // unnecessary but reassuring
Chris@0 487 delete transform;
Chris@0 488 }
Chris@0 489