annotate transform/TransformFactory.cpp @ 115:90ade4fa63be

* Fix serious failure to reload "imported" (i.e. all non-derived non-main) models from .sv file * Give a short playback duration to notes with formal duration of 0 or 1 * Show crosshairs on spectrogram even when there is another layer on top (if it isn't opaque) * Always paste to the same time in the layer as the cut/copy was from, rather than to the playback pointer -- less flexible, but more predictable and less annoying. We probably need a way to get the old behaviour if pasting from somewhere else in the future (e.g. from a text file), but we can't do that yet anyway * Use a compound operation for dragging and resizing selections, so as to ensure a single undo operation works * Use a note model as the target for feature extraction plugins that output variable samplerate data with more than one value per feature * Avoid possible crashes in cut/paste if a layer proves to have no model
author Chris Cannam
date Thu, 11 May 2006 11:35:46 +0000
parents 0a846f83a4b7
children c30728d5625c
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@52 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 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@60 19 #include "RealTimePluginTransform.h"
Chris@0 20
Chris@0 21 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@60 22 #include "plugin/RealTimePluginFactory.h"
Chris@66 23 #include "plugin/PluginXml.h"
Chris@0 24
Chris@56 25 #include "widgets/PluginParameterDialog.h"
Chris@56 26
Chris@64 27 #include "model/DenseTimeValueModel.h"
Chris@64 28
Chris@0 29 #include <iostream>
Chris@63 30 #include <set>
Chris@63 31
Chris@63 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::instance()
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@16 50 if (m_transforms.empty()) populateTransforms();
Chris@16 51
Chris@0 52 TransformList list;
Chris@56 53 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@16 54 i != m_transforms.end(); ++i) {
Chris@56 55 list.push_back(i->second);
Chris@16 56 }
Chris@0 57
Chris@16 58 return list;
Chris@16 59 }
Chris@16 60
Chris@63 61 std::vector<QString>
Chris@63 62 TransformFactory::getAllTransformTypes()
Chris@63 63 {
Chris@63 64 if (m_transforms.empty()) populateTransforms();
Chris@63 65
Chris@63 66 std::set<QString> types;
Chris@63 67 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@63 68 i != m_transforms.end(); ++i) {
Chris@63 69 types.insert(i->second.type);
Chris@63 70 }
Chris@63 71
Chris@63 72 std::vector<QString> rv;
Chris@63 73 for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@63 74 rv.push_back(*i);
Chris@63 75 }
Chris@63 76
Chris@63 77 return rv;
Chris@63 78 }
Chris@63 79
Chris@16 80 void
Chris@16 81 TransformFactory::populateTransforms()
Chris@16 82 {
Chris@60 83 TransformDescriptionMap transforms;
Chris@60 84
Chris@60 85 populateFeatureExtractionPlugins(transforms);
Chris@60 86 populateRealTimePlugins(transforms);
Chris@60 87
Chris@60 88 // disambiguate plugins with similar descriptions
Chris@60 89
Chris@60 90 std::map<QString, int> descriptions;
Chris@60 91
Chris@60 92 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@60 93 i != transforms.end(); ++i) {
Chris@60 94
Chris@60 95 TransformDesc desc = i->second;
Chris@60 96
Chris@60 97 ++descriptions[desc.description];
Chris@60 98 ++descriptions[QString("%1 [%2]").arg(desc.description).arg(desc.maker)];
Chris@60 99 }
Chris@60 100
Chris@60 101 std::map<QString, int> counts;
Chris@60 102 m_transforms.clear();
Chris@60 103
Chris@60 104 for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@60 105 i != transforms.end(); ++i) {
Chris@60 106
Chris@60 107 TransformDesc desc = i->second;
Chris@60 108 QString name = desc.name;
Chris@60 109 QString description = desc.description;
Chris@60 110 QString maker = desc.maker;
Chris@60 111
Chris@60 112 if (descriptions[description] > 1) {
Chris@60 113 description = QString("%1 [%2]").arg(description).arg(maker);
Chris@60 114 if (descriptions[description] > 1) {
Chris@60 115 description = QString("%1 <%2>")
Chris@60 116 .arg(description).arg(++counts[description]);
Chris@60 117 }
Chris@60 118 }
Chris@60 119
Chris@60 120 desc.description = description;
Chris@60 121 m_transforms[name] = desc;
Chris@60 122 }
Chris@60 123 }
Chris@60 124
Chris@60 125 void
Chris@60 126 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
Chris@60 127 {
Chris@60 128 std::vector<QString> plugs =
Chris@0 129 FeatureExtractionPluginFactory::getAllPluginIdentifiers();
Chris@0 130
Chris@60 131 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@47 132
Chris@60 133 QString pluginId = plugs[i];
Chris@0 134
Chris@0 135 FeatureExtractionPluginFactory *factory =
Chris@0 136 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@0 137
Chris@20 138 if (!factory) {
Chris@20 139 std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@20 140 continue;
Chris@20 141 }
Chris@0 142
Chris@66 143 Vamp::Plugin *plugin =
Chris@20 144 factory->instantiatePlugin(pluginId, 48000);
Chris@0 145
Chris@20 146 if (!plugin) {
Chris@20 147 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@20 148 continue;
Chris@20 149 }
Chris@20 150
Chris@20 151 QString pluginDescription = plugin->getDescription().c_str();
Chris@66 152 Vamp::Plugin::OutputList outputs =
Chris@20 153 plugin->getOutputDescriptors();
Chris@0 154
Chris@20 155 for (size_t j = 0; j < outputs.size(); ++j) {
Chris@0 156
Chris@20 157 QString transformName = QString("%1:%2")
Chris@20 158 .arg(pluginId).arg(outputs[j].name.c_str());
Chris@20 159
Chris@20 160 QString userDescription;
Chris@60 161 QString friendlyName;
Chris@63 162 QString units = outputs[j].unit.c_str();
Chris@20 163
Chris@20 164 if (outputs.size() == 1) {
Chris@20 165 userDescription = pluginDescription;
Chris@60 166 friendlyName = pluginDescription;
Chris@20 167 } else {
Chris@20 168 userDescription = QString("%1: %2")
Chris@20 169 .arg(pluginDescription)
Chris@20 170 .arg(outputs[j].description.c_str());
Chris@60 171 friendlyName = outputs[j].description.c_str();
Chris@0 172 }
Chris@20 173
Chris@56 174 bool configurable = (!plugin->getPrograms().empty() ||
Chris@56 175 !plugin->getParameterDescriptors().empty());
Chris@56 176
Chris@60 177 transforms[transformName] =
Chris@102 178 TransformDesc(tr("Analysis Plugins"),
Chris@63 179 transformName,
Chris@56 180 userDescription,
Chris@60 181 friendlyName,
Chris@60 182 plugin->getMaker().c_str(),
Chris@63 183 units,
Chris@56 184 configurable);
Chris@0 185 }
Chris@0 186 }
Chris@60 187 }
Chris@47 188
Chris@60 189 void
Chris@60 190 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
Chris@60 191 {
Chris@60 192 std::vector<QString> plugs =
Chris@60 193 RealTimePluginFactory::getAllPluginIdentifiers();
Chris@47 194
Chris@63 195 QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
Chris@63 196
Chris@60 197 for (size_t i = 0; i < plugs.size(); ++i) {
Chris@60 198
Chris@60 199 QString pluginId = plugs[i];
Chris@47 200
Chris@60 201 RealTimePluginFactory *factory =
Chris@60 202 RealTimePluginFactory::instanceFor(pluginId);
Chris@47 203
Chris@60 204 if (!factory) {
Chris@60 205 std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
Chris@60 206 continue;
Chris@47 207 }
Chris@47 208
Chris@60 209 const RealTimePluginDescriptor *descriptor =
Chris@60 210 factory->getPluginDescriptor(pluginId);
Chris@60 211
Chris@60 212 if (!descriptor) {
Chris@60 213 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
Chris@60 214 continue;
Chris@60 215 }
Chris@60 216
Chris@60 217 if (descriptor->controlOutputPortCount == 0 ||
Chris@60 218 descriptor->audioInputPortCount == 0) continue;
Chris@60 219
Chris@60 220 std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " output ports" << std::endl;
Chris@60 221
Chris@60 222 QString pluginDescription = descriptor->name.c_str();
Chris@60 223
Chris@60 224 for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
Chris@60 225
Chris@60 226 QString transformName = QString("%1:%2").arg(pluginId).arg(j);
Chris@60 227 QString userDescription;
Chris@63 228 QString units;
Chris@60 229
Chris@60 230 if (j < descriptor->controlOutputPortNames.size() &&
Chris@60 231 descriptor->controlOutputPortNames[j] != "") {
Chris@63 232
Chris@63 233 QString portName = descriptor->controlOutputPortNames[j].c_str();
Chris@63 234
Chris@60 235 userDescription = tr("%1: %2")
Chris@60 236 .arg(pluginDescription)
Chris@63 237 .arg(portName);
Chris@63 238
Chris@63 239 if (unitRE.indexIn(portName) >= 0) {
Chris@63 240 units = unitRE.cap(1);
Chris@63 241 }
Chris@63 242
Chris@60 243 } else if (descriptor->controlOutputPortCount > 1) {
Chris@63 244
Chris@60 245 userDescription = tr("%1: Output %2")
Chris@60 246 .arg(pluginDescription)
Chris@60 247 .arg(j + 1);
Chris@63 248
Chris@60 249 } else {
Chris@63 250
Chris@60 251 userDescription = pluginDescription;
Chris@60 252 }
Chris@60 253
Chris@63 254
Chris@60 255 bool configurable = (descriptor->parameterCount > 0);
Chris@60 256
Chris@60 257 transforms[transformName] =
Chris@102 258 TransformDesc(tr("Other Plugins"),
Chris@63 259 transformName,
Chris@60 260 userDescription,
Chris@60 261 userDescription,
Chris@60 262 descriptor->maker.c_str(),
Chris@63 263 units,
Chris@60 264 configurable);
Chris@60 265 }
Chris@60 266 }
Chris@16 267 }
Chris@16 268
Chris@16 269 QString
Chris@16 270 TransformFactory::getTransformDescription(TransformName name)
Chris@16 271 {
Chris@16 272 if (m_transforms.find(name) != m_transforms.end()) {
Chris@56 273 return m_transforms[name].description;
Chris@16 274 } else return "";
Chris@0 275 }
Chris@0 276
Chris@18 277 QString
Chris@18 278 TransformFactory::getTransformFriendlyName(TransformName name)
Chris@18 279 {
Chris@60 280 if (m_transforms.find(name) != m_transforms.end()) {
Chris@60 281 return m_transforms[name].friendlyName;
Chris@60 282 } else return "";
Chris@18 283 }
Chris@18 284
Chris@63 285 QString
Chris@63 286 TransformFactory::getTransformUnits(TransformName name)
Chris@63 287 {
Chris@63 288 if (m_transforms.find(name) != m_transforms.end()) {
Chris@63 289 return m_transforms[name].units;
Chris@63 290 } else return "";
Chris@63 291 }
Chris@63 292
Chris@56 293 bool
Chris@57 294 TransformFactory::isTransformConfigurable(TransformName name)
Chris@57 295 {
Chris@57 296 if (m_transforms.find(name) != m_transforms.end()) {
Chris@57 297 return m_transforms[name].configurable;
Chris@57 298 } else return false;
Chris@57 299 }
Chris@57 300
Chris@57 301 bool
Chris@64 302 TransformFactory::getTransformChannelRange(TransformName name,
Chris@64 303 int &min, int &max)
Chris@64 304 {
Chris@64 305 QString id = name.section(':', 0, 2);
Chris@64 306
Chris@64 307 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@64 308
Chris@66 309 Vamp::Plugin *plugin =
Chris@64 310 FeatureExtractionPluginFactory::instanceFor(id)->
Chris@64 311 instantiatePlugin(id, 48000);
Chris@64 312 if (!plugin) return false;
Chris@64 313
Chris@64 314 min = plugin->getMinChannelCount();
Chris@64 315 max = plugin->getMaxChannelCount();
Chris@64 316 delete plugin;
Chris@64 317
Chris@64 318 return true;
Chris@64 319
Chris@64 320 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@64 321
Chris@64 322 const RealTimePluginDescriptor *descriptor =
Chris@64 323 RealTimePluginFactory::instanceFor(id)->
Chris@64 324 getPluginDescriptor(id);
Chris@64 325 if (!descriptor) return false;
Chris@64 326
Chris@64 327 min = descriptor->audioInputPortCount;
Chris@64 328 max = descriptor->audioInputPortCount;
Chris@64 329
Chris@64 330 return true;
Chris@64 331 }
Chris@64 332
Chris@64 333 return false;
Chris@64 334 }
Chris@64 335
Chris@64 336 bool
Chris@79 337 TransformFactory::getChannelRange(TransformName name, Vamp::PluginBase *plugin,
Chris@79 338 int &minChannels, int &maxChannels)
Chris@79 339 {
Chris@79 340 Vamp::Plugin *vp = 0;
Chris@79 341 if ((vp = dynamic_cast<Vamp::Plugin *>(plugin))) {
Chris@79 342 minChannels = vp->getMinChannelCount();
Chris@79 343 maxChannels = vp->getMaxChannelCount();
Chris@79 344 return true;
Chris@79 345 } else {
Chris@79 346 return getTransformChannelRange(name, minChannels, maxChannels);
Chris@79 347 }
Chris@79 348 }
Chris@79 349
Chris@79 350 bool
Chris@56 351 TransformFactory::getConfigurationForTransform(TransformName name,
Chris@56 352 Model *inputModel,
Chris@64 353 int &channel,
Chris@56 354 QString &configurationXml)
Chris@0 355 {
Chris@56 356 QString id = name.section(':', 0, 2);
Chris@56 357 QString output = name.section(':', 3);
Chris@56 358
Chris@56 359 bool ok = false;
Chris@56 360 configurationXml = m_lastConfigurations[name];
Chris@56 361
Chris@56 362 std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
Chris@56 363
Chris@66 364 Vamp::PluginBase *plugin = 0;
Chris@60 365
Chris@56 366 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@60 367
Chris@60 368 plugin = FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@56 369 (id, inputModel->getSampleRate());
Chris@60 370
Chris@60 371 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@60 372
Chris@60 373 plugin = RealTimePluginFactory::instanceFor(id)->instantiatePlugin
Chris@60 374 (id, 0, 0, inputModel->getSampleRate(), 1024, 1);
Chris@60 375 }
Chris@60 376
Chris@60 377 if (plugin) {
Chris@60 378 if (configurationXml != "") {
Chris@66 379 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@56 380 }
Chris@64 381
Chris@64 382 int sourceChannels = 1;
Chris@64 383 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@64 384 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@64 385 ->getChannelCount();
Chris@64 386 }
Chris@64 387
Chris@64 388 int minChannels = 1, maxChannels = sourceChannels;
Chris@79 389 getChannelRange(name, plugin, minChannels, maxChannels);
Chris@64 390
Chris@64 391 int targetChannels = sourceChannels;
Chris@64 392 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@64 393 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@64 394
Chris@64 395 int defaultChannel = channel;
Chris@64 396
Chris@64 397 PluginParameterDialog *dialog = new PluginParameterDialog(plugin,
Chris@64 398 sourceChannels,
Chris@64 399 targetChannels,
Chris@64 400 defaultChannel);
Chris@60 401 if (dialog->exec() == QDialog::Accepted) {
Chris@60 402 ok = true;
Chris@60 403 }
Chris@66 404 configurationXml = PluginXml(plugin).toXmlString();
Chris@64 405 channel = dialog->getChannel();
Chris@60 406 delete dialog;
Chris@60 407 delete plugin;
Chris@56 408 }
Chris@56 409
Chris@56 410 if (ok) m_lastConfigurations[name] = configurationXml;
Chris@56 411
Chris@56 412 return ok;
Chris@0 413 }
Chris@0 414
Chris@0 415 Transform *
Chris@0 416 TransformFactory::createTransform(TransformName name, Model *inputModel,
Chris@64 417 int channel, QString configurationXml, bool start)
Chris@0 418 {
Chris@0 419 Transform *transform = 0;
Chris@0 420
Chris@64 421 //!!! use channel
Chris@64 422
Chris@56 423 QString id = name.section(':', 0, 2);
Chris@56 424 QString output = name.section(':', 3);
Chris@56 425
Chris@56 426 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@56 427 transform = new FeatureExtractionPluginTransform(inputModel,
Chris@56 428 id,
Chris@64 429 channel,
Chris@56 430 configurationXml,
Chris@56 431 output);
Chris@60 432 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@60 433 transform = new RealTimePluginTransform(inputModel,
Chris@60 434 id,
Chris@64 435 channel,
Chris@60 436 configurationXml,
Chris@63 437 getTransformUnits(name),
Chris@60 438 output.toInt());
Chris@0 439 } else {
Chris@77 440 std::cerr << "TransformFactory::createTransform: Unknown transform \""
Chris@77 441 << name.toStdString() << "\"" << std::endl;
Chris@77 442 return transform;
Chris@0 443 }
Chris@0 444
Chris@0 445 if (start && transform) transform->start();
Chris@16 446 transform->setObjectName(name);
Chris@0 447 return transform;
Chris@0 448 }
Chris@0 449
Chris@0 450 Model *
Chris@56 451 TransformFactory::transform(TransformName name, Model *inputModel,
Chris@64 452 int channel, QString configurationXml)
Chris@0 453 {
Chris@64 454 Transform *t = createTransform(name, inputModel, channel,
Chris@64 455 configurationXml, false);
Chris@0 456
Chris@0 457 if (!t) return 0;
Chris@0 458
Chris@0 459 connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
Chris@0 460
Chris@0 461 t->start();
Chris@0 462 return t->detachOutputModel();
Chris@0 463 }
Chris@0 464
Chris@0 465 void
Chris@0 466 TransformFactory::transformFinished()
Chris@0 467 {
Chris@0 468 QObject *s = sender();
Chris@0 469 Transform *transform = dynamic_cast<Transform *>(s);
Chris@0 470
Chris@0 471 if (!transform) {
Chris@0 472 std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
Chris@0 473 return;
Chris@0 474 }
Chris@0 475
Chris@0 476 transform->wait(); // unnecessary but reassuring
Chris@0 477 delete transform;
Chris@0 478 }
Chris@0 479