annotate plugin/transform/ModelTransformerFactory.cpp @ 344:277006c62fea

* Fix idiotic bug in previous commit
author Chris Cannam
date Wed, 28 Nov 2007 13:20:40 +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