annotate transform/ModelTransformerFactory.cpp @ 493:3931711b5671

* RDF importer: add model titles where possible * RDF transform factory: report whether something appears to be RDF or not (so we can avoid trying to load it as something else if the RDF query fails)
author Chris Cannam
date Tue, 25 Nov 2008 13:43:56 +0000
parents a70dcfed59c1
children 5ab561f664f2
rev   line source
Chris@320 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@320 2
Chris@320 3 /*
Chris@320 4 Sonic Visualiser
Chris@320 5 An audio file viewer and annotation editor.
Chris@320 6 Centre for Digital Music, Queen Mary, University of London.
Chris@320 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@320 8
Chris@320 9 This program is free software; you can redistribute it and/or
Chris@320 10 modify it under the terms of the GNU General Public License as
Chris@320 11 published by the Free Software Foundation; either version 2 of the
Chris@320 12 License, or (at your option) any later version. See the file
Chris@320 13 COPYING included with this distribution for more information.
Chris@320 14 */
Chris@320 15
Chris@331 16 #include "ModelTransformerFactory.h"
Chris@320 17
Chris@331 18 #include "FeatureExtractionModelTransformer.h"
Chris@331 19 #include "RealTimeEffectModelTransformer.h"
Chris@320 20
Chris@332 21 #include "TransformFactory.h"
Chris@332 22
Chris@389 23 #include "base/AudioPlaySource.h"
Chris@389 24
Chris@320 25 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@320 26 #include "plugin/RealTimePluginFactory.h"
Chris@320 27 #include "plugin/PluginXml.h"
Chris@320 28
Chris@320 29 #include "widgets/PluginParameterDialog.h"
Chris@320 30
Chris@320 31 #include "data/model/DenseTimeValueModel.h"
Chris@320 32
Chris@475 33 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@320 34
Chris@320 35 #include <iostream>
Chris@320 36 #include <set>
Chris@320 37
Chris@320 38 #include <QRegExp>
Chris@320 39
Chris@331 40 ModelTransformerFactory *
Chris@331 41 ModelTransformerFactory::m_instance = new ModelTransformerFactory;
Chris@320 42
Chris@331 43 ModelTransformerFactory *
Chris@331 44 ModelTransformerFactory::getInstance()
Chris@320 45 {
Chris@320 46 return m_instance;
Chris@320 47 }
Chris@320 48
Chris@331 49 ModelTransformerFactory::~ModelTransformerFactory()
Chris@320 50 {
Chris@320 51 }
Chris@320 52
Chris@320 53 bool
Chris@350 54 ModelTransformerFactory::getChannelRange(TransformId identifier,
Chris@350 55 Vamp::PluginBase *plugin,
Chris@332 56 int &minChannels, int &maxChannels)
Chris@320 57 {
Chris@320 58 Vamp::Plugin *vp = 0;
Chris@320 59 if ((vp = dynamic_cast<Vamp::Plugin *>(plugin)) ||
Chris@320 60 (vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin))) {
Chris@320 61 minChannels = vp->getMinChannelCount();
Chris@320 62 maxChannels = vp->getMaxChannelCount();
Chris@320 63 return true;
Chris@320 64 } else {
Chris@332 65 return TransformFactory::getInstance()->
Chris@332 66 getTransformChannelRange(identifier, minChannels, maxChannels);
Chris@320 67 }
Chris@320 68 }
Chris@320 69
Chris@350 70 ModelTransformer::Input
Chris@350 71 ModelTransformerFactory::getConfigurationForTransform(Transform &transform,
Chris@350 72 const std::vector<Model *> &candidateInputModels,
Chris@350 73 Model *defaultInputModel,
Chris@389 74 AudioPlaySource *source,
Chris@350 75 size_t startFrame,
Chris@350 76 size_t duration)
Chris@320 77 {
Chris@350 78 ModelTransformer::Input input(0);
Chris@350 79
Chris@350 80 if (candidateInputModels.empty()) return input;
Chris@320 81
Chris@320 82 //!!! This will need revision -- we'll have to have a callback
Chris@320 83 //from the dialog for when the candidate input model is changed,
Chris@320 84 //as we'll need to reinitialise the channel settings in the dialog
Chris@345 85 Model *inputModel = candidateInputModels[0];
Chris@320 86 QStringList candidateModelNames;
Chris@345 87 QString defaultModelName;
Chris@320 88 std::map<QString, Model *> modelMap;
Chris@320 89 for (size_t i = 0; i < candidateInputModels.size(); ++i) {
Chris@320 90 QString modelName = candidateInputModels[i]->objectName();
Chris@320 91 QString origModelName = modelName;
Chris@320 92 int dupcount = 1;
Chris@320 93 while (modelMap.find(modelName) != modelMap.end()) {
Chris@320 94 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
Chris@320 95 }
Chris@320 96 modelMap[modelName] = candidateInputModels[i];
Chris@320 97 candidateModelNames.push_back(modelName);
Chris@345 98 if (candidateInputModels[i] == defaultInputModel) {
Chris@345 99 defaultModelName = modelName;
Chris@345 100 }
Chris@320 101 }
Chris@320 102
Chris@350 103 QString id = transform.getPluginIdentifier();
Chris@350 104 QString output = transform.getOutput();
Chris@320 105 QString outputLabel = "";
Chris@320 106 QString outputDescription = "";
Chris@320 107
Chris@320 108 bool ok = false;
Chris@350 109 QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
Chris@320 110
Chris@350 111 std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
Chris@320 112
Chris@320 113 Vamp::PluginBase *plugin = 0;
Chris@320 114
Chris@320 115 bool frequency = false;
Chris@320 116 bool effect = false;
Chris@320 117 bool generator = false;
Chris@320 118
Chris@320 119 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@320 120
Chris@350 121 std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl;
Chris@320 122
Chris@320 123 Vamp::Plugin *vp =
Chris@320 124 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@320 125 (id, inputModel->getSampleRate());
Chris@320 126
Chris@320 127 if (vp) {
Chris@320 128
Chris@320 129 plugin = vp;
Chris@320 130 frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
Chris@320 131
Chris@320 132 std::vector<Vamp::Plugin::OutputDescriptor> od =
Chris@320 133 vp->getOutputDescriptors();
Chris@320 134 if (od.size() > 1) {
Chris@320 135 for (size_t i = 0; i < od.size(); ++i) {
Chris@320 136 if (od[i].identifier == output.toStdString()) {
Chris@320 137 outputLabel = od[i].name.c_str();
Chris@320 138 outputDescription = od[i].description.c_str();
Chris@320 139 break;
Chris@320 140 }
Chris@320 141 }
Chris@320 142 }
Chris@320 143 }
Chris@320 144
Chris@320 145 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@320 146
Chris@320 147 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
Chris@320 148 const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id);
Chris@320 149
Chris@320 150 if (desc->audioInputPortCount > 0 &&
Chris@320 151 desc->audioOutputPortCount > 0 &&
Chris@320 152 !desc->isSynth) {
Chris@320 153 effect = true;
Chris@320 154 }
Chris@320 155
Chris@320 156 if (desc->audioInputPortCount == 0) {
Chris@320 157 generator = true;
Chris@320 158 }
Chris@320 159
Chris@320 160 if (output != "A") {
Chris@320 161 int outputNo = output.toInt();
Chris@320 162 if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) {
Chris@320 163 outputLabel = desc->controlOutputPortNames[outputNo].c_str();
Chris@320 164 }
Chris@320 165 }
Chris@320 166
Chris@320 167 size_t sampleRate = inputModel->getSampleRate();
Chris@320 168 size_t blockSize = 1024;
Chris@320 169 size_t channels = 1;
Chris@320 170 if (effect && source) {
Chris@320 171 sampleRate = source->getTargetSampleRate();
Chris@320 172 blockSize = source->getTargetBlockSize();
Chris@320 173 channels = source->getTargetChannelCount();
Chris@320 174 }
Chris@320 175
Chris@320 176 RealTimePluginInstance *rtp = factory->instantiatePlugin
Chris@320 177 (id, 0, 0, sampleRate, blockSize, channels);
Chris@320 178
Chris@320 179 plugin = rtp;
Chris@320 180
Chris@320 181 if (effect && source && rtp) {
Chris@389 182 source->setAuditioningEffect(rtp);
Chris@320 183 }
Chris@320 184 }
Chris@320 185
Chris@320 186 if (plugin) {
Chris@320 187
Chris@350 188 // Ensure block size etc are valid
Chris@350 189 TransformFactory::getInstance()->
Chris@350 190 makeContextConsistentWithPlugin(transform, plugin);
Chris@320 191
Chris@350 192 // Prepare the plugin with any existing parameters already
Chris@350 193 // found in the transform
Chris@350 194 TransformFactory::getInstance()->
Chris@350 195 setPluginParameters(transform, plugin);
Chris@350 196
Chris@350 197 // For this interactive usage, we want to override those with
Chris@350 198 // whatever the user chose last time around
Chris@350 199 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@320 200
Chris@320 201 int sourceChannels = 1;
Chris@320 202 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@320 203 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@320 204 ->getChannelCount();
Chris@320 205 }
Chris@320 206
Chris@320 207 int minChannels = 1, maxChannels = sourceChannels;
Chris@350 208 getChannelRange(transform.getIdentifier(), plugin,
Chris@350 209 minChannels, maxChannels);
Chris@320 210
Chris@320 211 int targetChannels = sourceChannels;
Chris@320 212 if (!effect) {
Chris@320 213 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@320 214 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@320 215 }
Chris@320 216
Chris@350 217 int defaultChannel = -1; //!!! no longer saved! [was context.channel]
Chris@320 218
Chris@320 219 PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
Chris@320 220
Chris@472 221 dialog->setMoreInfoUrl(TransformFactory::getInstance()->
Chris@472 222 getTransformInfoUrl(transform.getIdentifier()));
Chris@472 223
Chris@320 224 if (candidateModelNames.size() > 1 && !generator) {
Chris@345 225 dialog->setCandidateInputModels(candidateModelNames,
Chris@345 226 defaultModelName);
Chris@320 227 }
Chris@320 228
Chris@320 229 if (startFrame != 0 || duration != 0) {
Chris@320 230 dialog->setShowSelectionOnlyOption(true);
Chris@320 231 }
Chris@320 232
Chris@320 233 if (targetChannels > 0) {
Chris@320 234 dialog->setChannelArrangement(sourceChannels, targetChannels,
Chris@320 235 defaultChannel);
Chris@320 236 }
Chris@320 237
Chris@320 238 dialog->setOutputLabel(outputLabel, outputDescription);
Chris@320 239
Chris@320 240 dialog->setShowProcessingOptions(true, frequency);
Chris@320 241
Chris@320 242 if (dialog->exec() == QDialog::Accepted) {
Chris@320 243 ok = true;
Chris@320 244 }
Chris@320 245
Chris@320 246 QString selectedInput = dialog->getInputModel();
Chris@320 247 if (selectedInput != "") {
Chris@320 248 if (modelMap.find(selectedInput) != modelMap.end()) {
Chris@320 249 inputModel = modelMap[selectedInput];
Chris@320 250 std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl;
Chris@320 251 } else {
Chris@320 252 std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl;
Chris@320 253 }
Chris@320 254 } else {
Chris@320 255 std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
Chris@320 256 }
Chris@350 257
Chris@350 258 // Write parameters back to transform object
Chris@350 259 TransformFactory::getInstance()->
Chris@350 260 setParametersFromPlugin(transform, plugin);
Chris@320 261
Chris@350 262 input.setChannel(dialog->getChannel());
Chris@320 263
Chris@350 264 //!!! The dialog ought to be taking & returning transform
Chris@350 265 //objects and input objects and stuff rather than passing
Chris@350 266 //around all this misc stuff, but that's for tomorrow
Chris@350 267 //(whenever that may be)
Chris@350 268
Chris@320 269 if (startFrame != 0 || duration != 0) {
Chris@320 270 if (dialog->getSelectionOnly()) {
Chris@350 271 transform.setStartTime(RealTime::frame2RealTime
Chris@350 272 (startFrame, inputModel->getSampleRate()));
Chris@350 273 transform.setDuration(RealTime::frame2RealTime
Chris@350 274 (duration, inputModel->getSampleRate()));
Chris@320 275 }
Chris@320 276 }
Chris@320 277
Chris@350 278 size_t stepSize = 0, blockSize = 0;
Chris@350 279 WindowType windowType = HanningWindow;
Chris@320 280
Chris@350 281 dialog->getProcessingParameters(stepSize,
Chris@350 282 blockSize,
Chris@350 283 windowType);
Chris@350 284
Chris@350 285 transform.setStepSize(stepSize);
Chris@350 286 transform.setBlockSize(blockSize);
Chris@350 287 transform.setWindowType(windowType);
Chris@350 288
Chris@350 289 TransformFactory::getInstance()->
Chris@350 290 makeContextConsistentWithPlugin(transform, plugin);
Chris@350 291
Chris@350 292 configurationXml = PluginXml(plugin).toXmlString();
Chris@320 293
Chris@320 294 delete dialog;
Chris@320 295
Chris@320 296 if (effect && source) {
Chris@389 297 source->setAuditioningEffect(0); // will delete our plugin
Chris@320 298 } else {
Chris@320 299 delete plugin;
Chris@320 300 }
Chris@320 301 }
Chris@320 302
Chris@350 303 if (ok) {
Chris@350 304 m_lastConfigurations[transform.getIdentifier()] = configurationXml;
Chris@350 305 input.setModel(inputModel);
Chris@350 306 }
Chris@320 307
Chris@350 308 return input;
Chris@320 309 }
Chris@350 310 /*!!!
Chris@328 311 PluginTransformer::ExecutionContext
Chris@331 312 ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier,
Chris@320 313 Model *inputModel)
Chris@320 314 {
Chris@328 315 PluginTransformer::ExecutionContext context(-1);
Chris@320 316
Chris@320 317 QString id = identifier.section(':', 0, 2);
Chris@320 318
Chris@320 319 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@320 320
Chris@320 321 Vamp::Plugin *vp =
Chris@320 322 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@320 323 (id, inputModel ? inputModel->getSampleRate() : 48000);
Chris@320 324
Chris@320 325 if (vp) {
Chris@328 326 context = PluginTransformer::ExecutionContext(-1, vp);
Chris@320 327 delete vp;
Chris@320 328 }
Chris@320 329 }
Chris@320 330
Chris@320 331 return context;
Chris@320 332 }
Chris@350 333 */
Chris@331 334 ModelTransformer *
Chris@350 335 ModelTransformerFactory::createTransformer(const Transform &transform,
Chris@350 336 const ModelTransformer::Input &input)
Chris@320 337 {
Chris@331 338 ModelTransformer *transformer = 0;
Chris@320 339
Chris@350 340 QString id = transform.getPluginIdentifier();
Chris@320 341
Chris@320 342 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@350 343
Chris@350 344 transformer =
Chris@350 345 new FeatureExtractionModelTransformer(input, transform);
Chris@350 346
Chris@331 347 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@350 348
Chris@350 349 transformer =
Chris@350 350 new RealTimeEffectModelTransformer(input, transform);
Chris@350 351
Chris@320 352 } else {
Chris@331 353 std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \""
Chris@350 354 << transform.getIdentifier().toStdString() << "\"" << std::endl;
Chris@331 355 return transformer;
Chris@320 356 }
Chris@320 357
Chris@350 358 if (transformer) transformer->setObjectName(transform.getIdentifier());
Chris@331 359 return transformer;
Chris@320 360 }
Chris@320 361
Chris@320 362 Model *
Chris@350 363 ModelTransformerFactory::transform(const Transform &transform,
Chris@361 364 const ModelTransformer::Input &input,
Chris@361 365 QString &message)
Chris@320 366 {
Chris@408 367 std::cerr << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << std::endl;
Chris@408 368
Chris@350 369 ModelTransformer *t = createTransformer(transform, input);
Chris@320 370 if (!t) return 0;
Chris@320 371
Chris@331 372 connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
Chris@320 373
Chris@328 374 m_runningTransformers.insert(t);
Chris@320 375
Chris@320 376 t->start();
Chris@320 377 Model *model = t->detachOutputModel();
Chris@320 378
Chris@320 379 if (model) {
Chris@350 380 QString imn = input.getModel()->objectName();
Chris@332 381 QString trn =
Chris@332 382 TransformFactory::getInstance()->getTransformFriendlyName
Chris@350 383 (transform.getIdentifier());
Chris@320 384 if (imn != "") {
Chris@320 385 if (trn != "") {
Chris@320 386 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
Chris@320 387 } else {
Chris@320 388 model->setObjectName(imn);
Chris@320 389 }
Chris@320 390 } else if (trn != "") {
Chris@320 391 model->setObjectName(trn);
Chris@320 392 }
Chris@320 393 } else {
Chris@320 394 t->wait();
Chris@320 395 }
Chris@320 396
Chris@361 397 message = t->getMessage();
Chris@361 398
Chris@320 399 return model;
Chris@320 400 }
Chris@320 401
Chris@320 402 void
Chris@331 403 ModelTransformerFactory::transformerFinished()
Chris@320 404 {
Chris@320 405 QObject *s = sender();
Chris@331 406 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
Chris@320 407
Chris@436 408 // std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::endl;
Chris@320 409
Chris@331 410 if (!transformer) {
Chris@331 411 std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl;
Chris@320 412 return;
Chris@320 413 }
Chris@320 414
Chris@331 415 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
Chris@331 416 std::cerr << "WARNING: ModelTransformerFactory::transformerFinished("
Chris@331 417 << transformer
Chris@331 418 << "): I have no record of this transformer running!"
Chris@320 419 << std::endl;
Chris@320 420 }
Chris@320 421
Chris@331 422 m_runningTransformers.erase(transformer);
Chris@320 423
Chris@331 424 transformer->wait(); // unnecessary but reassuring
Chris@331 425 delete transformer;
Chris@320 426 }
Chris@320 427
Chris@320 428 void
Chris@331 429 ModelTransformerFactory::modelAboutToBeDeleted(Model *m)
Chris@320 430 {
Chris@328 431 TransformerSet affected;
Chris@320 432
Chris@328 433 for (TransformerSet::iterator i = m_runningTransformers.begin();
Chris@328 434 i != m_runningTransformers.end(); ++i) {
Chris@320 435
Chris@331 436 ModelTransformer *t = *i;
Chris@320 437
Chris@320 438 if (t->getInputModel() == m || t->getOutputModel() == m) {
Chris@320 439 affected.insert(t);
Chris@320 440 }
Chris@320 441 }
Chris@320 442
Chris@328 443 for (TransformerSet::iterator i = affected.begin();
Chris@320 444 i != affected.end(); ++i) {
Chris@320 445
Chris@331 446 ModelTransformer *t = *i;
Chris@320 447
Chris@320 448 t->abandon();
Chris@320 449
Chris@320 450 t->wait(); // this should eventually call back on
Chris@331 451 // transformerFinished, which will remove from
Chris@328 452 // m_runningTransformers and delete.
Chris@320 453 }
Chris@320 454 }
Chris@320 455