annotate transform/ModelTransformerFactory.cpp @ 537:3cc4b7cd2aa5

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