annotate transform/TransformFactory.cpp @ 31:37af203dbd15

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