annotate plugin/transform/ModelTransformerFactory.cpp @ 339:ba30f4a3e3be

* Some work on correct alignment when moving panes during playback * Overhaul alignment for playback frame values (view manager now always refers to reference-timeline values, only the play source deals in playback model timeline values) * When making a selection, ensure the selection regions shown in other panes (and used for playback constraints if appropriate) are aligned correctly. This may be the coolest feature ever implemented in any program ever.
author Chris Cannam
date Thu, 22 Nov 2007 14:17:19 +0000
parents 13e5870040e6
children 700cd3350391 6f6ab834449d
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@320 23 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@320 24 #include "plugin/RealTimePluginFactory.h"
Chris@320 25 #include "plugin/PluginXml.h"
Chris@320 26
Chris@320 27 #include "widgets/PluginParameterDialog.h"
Chris@320 28
Chris@320 29 #include "data/model/DenseTimeValueModel.h"
Chris@320 30
Chris@320 31 #include "vamp-sdk/PluginHostAdapter.h"
Chris@320 32
Chris@320 33 #include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here
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@331 54 ModelTransformerFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin,
Chris@332 55 int &minChannels, int &maxChannels)
Chris@320 56 {
Chris@320 57 Vamp::Plugin *vp = 0;
Chris@320 58 if ((vp = dynamic_cast<Vamp::Plugin *>(plugin)) ||
Chris@320 59 (vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin))) {
Chris@320 60 minChannels = vp->getMinChannelCount();
Chris@320 61 maxChannels = vp->getMaxChannelCount();
Chris@320 62 return true;
Chris@320 63 } else {
Chris@332 64 return TransformFactory::getInstance()->
Chris@332 65 getTransformChannelRange(identifier, minChannels, maxChannels);
Chris@320 66 }
Chris@320 67 }
Chris@320 68
Chris@320 69 Model *
Chris@331 70 ModelTransformerFactory::getConfigurationForTransformer(TransformId identifier,
Chris@320 71 const std::vector<Model *> &candidateInputModels,
Chris@328 72 PluginTransformer::ExecutionContext &context,
Chris@320 73 QString &configurationXml,
Chris@320 74 AudioCallbackPlaySource *source,
Chris@320 75 size_t startFrame,
Chris@320 76 size_t duration)
Chris@320 77 {
Chris@320 78 if (candidateInputModels.empty()) return 0;
Chris@320 79
Chris@320 80 //!!! This will need revision -- we'll have to have a callback
Chris@320 81 //from the dialog for when the candidate input model is changed,
Chris@320 82 //as we'll need to reinitialise the channel settings in the dialog
Chris@320 83 Model *inputModel = candidateInputModels[0]; //!!! for now
Chris@320 84 QStringList candidateModelNames;
Chris@320 85 std::map<QString, Model *> modelMap;
Chris@320 86 for (size_t i = 0; i < candidateInputModels.size(); ++i) {
Chris@320 87 QString modelName = candidateInputModels[i]->objectName();
Chris@320 88 QString origModelName = modelName;
Chris@320 89 int dupcount = 1;
Chris@320 90 while (modelMap.find(modelName) != modelMap.end()) {
Chris@320 91 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
Chris@320 92 }
Chris@320 93 modelMap[modelName] = candidateInputModels[i];
Chris@320 94 candidateModelNames.push_back(modelName);
Chris@320 95 }
Chris@320 96
Chris@320 97 QString id = identifier.section(':', 0, 2);
Chris@320 98 QString output = identifier.section(':', 3);
Chris@320 99 QString outputLabel = "";
Chris@320 100 QString outputDescription = "";
Chris@320 101
Chris@320 102 bool ok = false;
Chris@320 103 configurationXml = m_lastConfigurations[identifier];
Chris@320 104
Chris@320 105 // std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
Chris@320 106
Chris@320 107 Vamp::PluginBase *plugin = 0;
Chris@320 108
Chris@320 109 bool frequency = false;
Chris@320 110 bool effect = false;
Chris@320 111 bool generator = false;
Chris@320 112
Chris@320 113 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@320 114
Chris@328 115 std::cerr << "getConfigurationForTransformer: instantiating Vamp plugin" << std::endl;
Chris@320 116
Chris@320 117 Vamp::Plugin *vp =
Chris@320 118 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@320 119 (id, inputModel->getSampleRate());
Chris@320 120
Chris@320 121 if (vp) {
Chris@320 122
Chris@320 123 plugin = vp;
Chris@320 124 frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
Chris@320 125
Chris@320 126 std::vector<Vamp::Plugin::OutputDescriptor> od =
Chris@320 127 vp->getOutputDescriptors();
Chris@320 128 if (od.size() > 1) {
Chris@320 129 for (size_t i = 0; i < od.size(); ++i) {
Chris@320 130 if (od[i].identifier == output.toStdString()) {
Chris@320 131 outputLabel = od[i].name.c_str();
Chris@320 132 outputDescription = od[i].description.c_str();
Chris@320 133 break;
Chris@320 134 }
Chris@320 135 }
Chris@320 136 }
Chris@320 137 }
Chris@320 138
Chris@320 139 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@320 140
Chris@320 141 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
Chris@320 142 const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id);
Chris@320 143
Chris@320 144 if (desc->audioInputPortCount > 0 &&
Chris@320 145 desc->audioOutputPortCount > 0 &&
Chris@320 146 !desc->isSynth) {
Chris@320 147 effect = true;
Chris@320 148 }
Chris@320 149
Chris@320 150 if (desc->audioInputPortCount == 0) {
Chris@320 151 generator = true;
Chris@320 152 }
Chris@320 153
Chris@320 154 if (output != "A") {
Chris@320 155 int outputNo = output.toInt();
Chris@320 156 if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) {
Chris@320 157 outputLabel = desc->controlOutputPortNames[outputNo].c_str();
Chris@320 158 }
Chris@320 159 }
Chris@320 160
Chris@320 161 size_t sampleRate = inputModel->getSampleRate();
Chris@320 162 size_t blockSize = 1024;
Chris@320 163 size_t channels = 1;
Chris@320 164 if (effect && source) {
Chris@320 165 sampleRate = source->getTargetSampleRate();
Chris@320 166 blockSize = source->getTargetBlockSize();
Chris@320 167 channels = source->getTargetChannelCount();
Chris@320 168 }
Chris@320 169
Chris@320 170 RealTimePluginInstance *rtp = factory->instantiatePlugin
Chris@320 171 (id, 0, 0, sampleRate, blockSize, channels);
Chris@320 172
Chris@320 173 plugin = rtp;
Chris@320 174
Chris@320 175 if (effect && source && rtp) {
Chris@320 176 source->setAuditioningPlugin(rtp);
Chris@320 177 }
Chris@320 178 }
Chris@320 179
Chris@320 180 if (plugin) {
Chris@320 181
Chris@328 182 context = PluginTransformer::ExecutionContext(context.channel, plugin);
Chris@320 183
Chris@320 184 if (configurationXml != "") {
Chris@320 185 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@320 186 }
Chris@320 187
Chris@320 188 int sourceChannels = 1;
Chris@320 189 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
Chris@320 190 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
Chris@320 191 ->getChannelCount();
Chris@320 192 }
Chris@320 193
Chris@320 194 int minChannels = 1, maxChannels = sourceChannels;
Chris@320 195 getChannelRange(identifier, plugin, minChannels, maxChannels);
Chris@320 196
Chris@320 197 int targetChannels = sourceChannels;
Chris@320 198 if (!effect) {
Chris@320 199 if (sourceChannels < minChannels) targetChannels = minChannels;
Chris@320 200 if (sourceChannels > maxChannels) targetChannels = maxChannels;
Chris@320 201 }
Chris@320 202
Chris@320 203 int defaultChannel = context.channel;
Chris@320 204
Chris@320 205 PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
Chris@320 206
Chris@320 207 if (candidateModelNames.size() > 1 && !generator) {
Chris@320 208 dialog->setCandidateInputModels(candidateModelNames);
Chris@320 209 }
Chris@320 210
Chris@320 211 if (startFrame != 0 || duration != 0) {
Chris@320 212 dialog->setShowSelectionOnlyOption(true);
Chris@320 213 }
Chris@320 214
Chris@320 215 if (targetChannels > 0) {
Chris@320 216 dialog->setChannelArrangement(sourceChannels, targetChannels,
Chris@320 217 defaultChannel);
Chris@320 218 }
Chris@320 219
Chris@320 220 dialog->setOutputLabel(outputLabel, outputDescription);
Chris@320 221
Chris@320 222 dialog->setShowProcessingOptions(true, frequency);
Chris@320 223
Chris@320 224 if (dialog->exec() == QDialog::Accepted) {
Chris@320 225 ok = true;
Chris@320 226 }
Chris@320 227
Chris@320 228 QString selectedInput = dialog->getInputModel();
Chris@320 229 if (selectedInput != "") {
Chris@320 230 if (modelMap.find(selectedInput) != modelMap.end()) {
Chris@320 231 inputModel = modelMap[selectedInput];
Chris@320 232 std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl;
Chris@320 233 } else {
Chris@320 234 std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl;
Chris@320 235 }
Chris@320 236 } else {
Chris@320 237 std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
Chris@320 238 }
Chris@320 239
Chris@320 240 configurationXml = PluginXml(plugin).toXmlString();
Chris@320 241 context.channel = dialog->getChannel();
Chris@320 242
Chris@320 243 if (startFrame != 0 || duration != 0) {
Chris@320 244 if (dialog->getSelectionOnly()) {
Chris@320 245 context.startFrame = startFrame;
Chris@320 246 context.duration = duration;
Chris@320 247 }
Chris@320 248 }
Chris@320 249
Chris@320 250 dialog->getProcessingParameters(context.stepSize,
Chris@320 251 context.blockSize,
Chris@320 252 context.windowType);
Chris@320 253
Chris@320 254 context.makeConsistentWithPlugin(plugin);
Chris@320 255
Chris@320 256 delete dialog;
Chris@320 257
Chris@320 258 if (effect && source) {
Chris@320 259 source->setAuditioningPlugin(0); // will delete our plugin
Chris@320 260 } else {
Chris@320 261 delete plugin;
Chris@320 262 }
Chris@320 263 }
Chris@320 264
Chris@320 265 if (ok) m_lastConfigurations[identifier] = configurationXml;
Chris@320 266
Chris@320 267 return ok ? inputModel : 0;
Chris@320 268 }
Chris@320 269
Chris@328 270 PluginTransformer::ExecutionContext
Chris@331 271 ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier,
Chris@320 272 Model *inputModel)
Chris@320 273 {
Chris@328 274 PluginTransformer::ExecutionContext context(-1);
Chris@320 275
Chris@320 276 QString id = identifier.section(':', 0, 2);
Chris@320 277
Chris@320 278 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@320 279
Chris@320 280 Vamp::Plugin *vp =
Chris@320 281 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
Chris@320 282 (id, inputModel ? inputModel->getSampleRate() : 48000);
Chris@320 283
Chris@320 284 if (vp) {
Chris@328 285 context = PluginTransformer::ExecutionContext(-1, vp);
Chris@320 286 delete vp;
Chris@320 287 }
Chris@320 288 }
Chris@320 289
Chris@320 290 return context;
Chris@320 291 }
Chris@320 292
Chris@331 293 ModelTransformer *
Chris@331 294 ModelTransformerFactory::createTransformer(TransformId identifier, Model *inputModel,
Chris@328 295 const PluginTransformer::ExecutionContext &context,
Chris@320 296 QString configurationXml)
Chris@320 297 {
Chris@331 298 ModelTransformer *transformer = 0;
Chris@320 299
Chris@320 300 QString id = identifier.section(':', 0, 2);
Chris@320 301 QString output = identifier.section(':', 3);
Chris@320 302
Chris@320 303 if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@332 304 transformer = new FeatureExtractionModelTransformer
Chris@332 305 (inputModel, id, context, configurationXml, output);
Chris@331 306 } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@332 307 transformer = new RealTimeEffectModelTransformer
Chris@332 308 (inputModel, id, context, configurationXml,
Chris@332 309 TransformFactory::getInstance()->getTransformUnits(identifier),
Chris@332 310 output == "A" ? -1 : output.toInt());
Chris@320 311 } else {
Chris@331 312 std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \""
Chris@320 313 << identifier.toStdString() << "\"" << std::endl;
Chris@331 314 return transformer;
Chris@320 315 }
Chris@320 316
Chris@331 317 if (transformer) transformer->setObjectName(identifier);
Chris@331 318 return transformer;
Chris@320 319 }
Chris@320 320
Chris@320 321 Model *
Chris@331 322 ModelTransformerFactory::transform(TransformId identifier, Model *inputModel,
Chris@328 323 const PluginTransformer::ExecutionContext &context,
Chris@320 324 QString configurationXml)
Chris@320 325 {
Chris@331 326 ModelTransformer *t = createTransformer(identifier, inputModel, context,
Chris@331 327 configurationXml);
Chris@320 328
Chris@320 329 if (!t) return 0;
Chris@320 330
Chris@331 331 connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
Chris@320 332
Chris@328 333 m_runningTransformers.insert(t);
Chris@320 334
Chris@320 335 t->start();
Chris@320 336 Model *model = t->detachOutputModel();
Chris@320 337
Chris@320 338 if (model) {
Chris@320 339 QString imn = inputModel->objectName();
Chris@332 340 QString trn =
Chris@332 341 TransformFactory::getInstance()->getTransformFriendlyName
Chris@332 342 (identifier);
Chris@320 343 if (imn != "") {
Chris@320 344 if (trn != "") {
Chris@320 345 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
Chris@320 346 } else {
Chris@320 347 model->setObjectName(imn);
Chris@320 348 }
Chris@320 349 } else if (trn != "") {
Chris@320 350 model->setObjectName(trn);
Chris@320 351 }
Chris@320 352 } else {
Chris@320 353 t->wait();
Chris@320 354 }
Chris@320 355
Chris@320 356 return model;
Chris@320 357 }
Chris@320 358
Chris@320 359 void
Chris@331 360 ModelTransformerFactory::transformerFinished()
Chris@320 361 {
Chris@320 362 QObject *s = sender();
Chris@331 363 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
Chris@320 364
Chris@331 365 std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::endl;
Chris@320 366
Chris@331 367 if (!transformer) {
Chris@331 368 std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl;
Chris@320 369 return;
Chris@320 370 }
Chris@320 371
Chris@331 372 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
Chris@331 373 std::cerr << "WARNING: ModelTransformerFactory::transformerFinished("
Chris@331 374 << transformer
Chris@331 375 << "): I have no record of this transformer running!"
Chris@320 376 << std::endl;
Chris@320 377 }
Chris@320 378
Chris@331 379 m_runningTransformers.erase(transformer);
Chris@320 380
Chris@331 381 transformer->wait(); // unnecessary but reassuring
Chris@331 382 delete transformer;
Chris@320 383 }
Chris@320 384
Chris@320 385 void
Chris@331 386 ModelTransformerFactory::modelAboutToBeDeleted(Model *m)
Chris@320 387 {
Chris@328 388 TransformerSet affected;
Chris@320 389
Chris@328 390 for (TransformerSet::iterator i = m_runningTransformers.begin();
Chris@328 391 i != m_runningTransformers.end(); ++i) {
Chris@320 392
Chris@331 393 ModelTransformer *t = *i;
Chris@320 394
Chris@320 395 if (t->getInputModel() == m || t->getOutputModel() == m) {
Chris@320 396 affected.insert(t);
Chris@320 397 }
Chris@320 398 }
Chris@320 399
Chris@328 400 for (TransformerSet::iterator i = affected.begin();
Chris@320 401 i != affected.end(); ++i) {
Chris@320 402
Chris@331 403 ModelTransformer *t = *i;
Chris@320 404
Chris@320 405 t->abandon();
Chris@320 406
Chris@320 407 t->wait(); // this should eventually call back on
Chris@331 408 // transformerFinished, which will remove from
Chris@328 409 // m_runningTransformers and delete.
Chris@320 410 }
Chris@320 411 }
Chris@320 412