annotate plugin/transform/ModelTransformerFactory.cpp @ 345:700cd3350391

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