annotate transform/ModelTransformerFactory.cpp @ 1879:652c5360e682

Ensure transforms are populated before instantiateDefaultPluginFor runs - otherwise if we have prior knowledge of a transform id, we can find ourselves trying to instantiate it before the plugin factory has heard of it and e.g. knows which server to use
author Chris Cannam
date Thu, 25 Jun 2020 12:20:06 +0100
parents 5f8fbbde08ff
children
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 "data/model/DenseTimeValueModel.h"
Chris@320 30
Chris@475 31 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@320 32
Chris@320 33 #include <iostream>
Chris@320 34 #include <set>
Chris@1727 35 #include <map>
Chris@320 36
Chris@320 37 #include <QRegExp>
Chris@1703 38 #include <QMutexLocker>
Chris@320 39
Chris@850 40 using std::vector;
Chris@1727 41 using std::set;
Chris@1727 42 using std::map;
Chris@1830 43 using std::shared_ptr;
Chris@1830 44 using std::make_shared;
Chris@850 45
Chris@331 46 ModelTransformerFactory *
Chris@331 47 ModelTransformerFactory::m_instance = new ModelTransformerFactory;
Chris@320 48
Chris@331 49 ModelTransformerFactory *
Chris@331 50 ModelTransformerFactory::getInstance()
Chris@320 51 {
Chris@320 52 return m_instance;
Chris@320 53 }
Chris@320 54
Chris@331 55 ModelTransformerFactory::~ModelTransformerFactory()
Chris@320 56 {
Chris@320 57 }
Chris@320 58
Chris@350 59 ModelTransformer::Input
Chris@350 60 ModelTransformerFactory::getConfigurationForTransform(Transform &transform,
Chris@1740 61 vector<ModelId> candidateInputModels,
Chris@1740 62 ModelId defaultInputModel,
Chris@389 63 AudioPlaySource *source,
Chris@1048 64 sv_frame_t startFrame,
Chris@1048 65 sv_frame_t duration,
Chris@653 66 UserConfigurator *configurator)
Chris@320 67 {
Chris@1703 68 QMutexLocker locker(&m_mutex);
Chris@1703 69
Chris@1740 70 ModelTransformer::Input input({});
Chris@350 71
Chris@350 72 if (candidateInputModels.empty()) return input;
Chris@320 73
Chris@320 74 //!!! This will need revision -- we'll have to have a callback
Chris@320 75 //from the dialog for when the candidate input model is changed,
Chris@320 76 //as we'll need to reinitialise the channel settings in the dialog
Chris@1740 77 ModelId inputModel = candidateInputModels[0];
Chris@320 78 QStringList candidateModelNames;
Chris@345 79 QString defaultModelName;
Chris@1740 80 QMap<QString, ModelId> modelMap;
Chris@1740 81
Chris@1740 82 sv_samplerate_t defaultSampleRate;
Chris@1740 83 { auto im = ModelById::get(inputModel);
Chris@1740 84 if (!im) return input;
Chris@1740 85 defaultSampleRate = im->getSampleRate();
Chris@1740 86 }
Chris@1740 87
Chris@1740 88 for (int i = 0; in_range_for(candidateInputModels, i); ++i) {
Chris@1740 89
Chris@1740 90 auto model = ModelById::get(candidateInputModels[i]);
Chris@1740 91 if (!model) return input;
Chris@1740 92
Chris@1740 93 QString modelName = model->objectName();
Chris@320 94 QString origModelName = modelName;
Chris@320 95 int dupcount = 1;
Chris@653 96 while (modelMap.contains(modelName)) {
Chris@320 97 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
Chris@320 98 }
Chris@1740 99
Chris@320 100 modelMap[modelName] = candidateInputModels[i];
Chris@320 101 candidateModelNames.push_back(modelName);
Chris@1740 102
Chris@345 103 if (candidateInputModels[i] == defaultInputModel) {
Chris@345 104 defaultModelName = modelName;
Chris@345 105 }
Chris@320 106 }
Chris@320 107
Chris@350 108 QString id = transform.getPluginIdentifier();
Chris@320 109
Chris@653 110 bool ok = true;
Chris@350 111 QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
Chris@320 112
Chris@1264 113 SVDEBUG << "ModelTransformer: last configuration for identifier " << transform.getIdentifier() << ": " << configurationXml << endl;
Chris@320 114
Chris@1830 115 shared_ptr<Vamp::PluginBase> plugin;
Chris@320 116
Chris@1225 117 if (RealTimePluginFactory::instanceFor(id)) {
Chris@320 118
Chris@1264 119 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating real-time plugin" << endl;
Chris@1264 120
Chris@320 121 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
Chris@320 122
Chris@1740 123 sv_samplerate_t sampleRate = defaultSampleRate;
Chris@930 124 int blockSize = 1024;
Chris@930 125 int channels = 1;
Chris@653 126 if (source) {
Chris@1321 127 sampleRate = source->getSourceSampleRate();
Chris@320 128 blockSize = source->getTargetBlockSize();
Chris@320 129 channels = source->getTargetChannelCount();
Chris@320 130 }
Chris@320 131
Chris@1830 132 plugin = factory->instantiatePlugin
Chris@320 133 (id, 0, 0, sampleRate, blockSize, channels);
Chris@320 134
Chris@1225 135 } else {
Chris@1225 136
Chris@1264 137 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating Vamp plugin" << endl;
Chris@1225 138
Chris@1830 139 plugin =
Chris@1225 140 FeatureExtractionPluginFactory::instance()->instantiatePlugin
Chris@1740 141 (id, float(defaultSampleRate));
Chris@320 142 }
Chris@320 143
Chris@320 144 if (plugin) {
Chris@320 145
Chris@350 146 // Ensure block size etc are valid
Chris@350 147 TransformFactory::getInstance()->
Chris@350 148 makeContextConsistentWithPlugin(transform, plugin);
Chris@320 149
Chris@350 150 // Prepare the plugin with any existing parameters already
Chris@350 151 // found in the transform
Chris@350 152 TransformFactory::getInstance()->
Chris@350 153 setPluginParameters(transform, plugin);
Chris@350 154
Chris@350 155 // For this interactive usage, we want to override those with
Chris@350 156 // whatever the user chose last time around
Chris@350 157 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@320 158
Chris@653 159 if (configurator) {
Chris@653 160 ok = configurator->configure(input, transform, plugin,
Chris@653 161 inputModel, source,
Chris@653 162 startFrame, duration,
Chris@653 163 modelMap,
Chris@653 164 candidateModelNames,
Chris@653 165 defaultModelName);
Chris@320 166 }
Chris@320 167
Chris@516 168
Chris@350 169 TransformFactory::getInstance()->
Chris@350 170 makeContextConsistentWithPlugin(transform, plugin);
Chris@350 171
Chris@350 172 configurationXml = PluginXml(plugin).toXmlString();
Chris@320 173 }
Chris@320 174
Chris@350 175 if (ok) {
Chris@350 176 m_lastConfigurations[transform.getIdentifier()] = configurationXml;
Chris@350 177 input.setModel(inputModel);
Chris@350 178 }
Chris@320 179
Chris@350 180 return input;
Chris@320 181 }
Chris@320 182
Chris@331 183 ModelTransformer *
Chris@850 184 ModelTransformerFactory::createTransformer(const Transforms &transforms,
Chris@350 185 const ModelTransformer::Input &input)
Chris@320 186 {
Chris@1582 187 ModelTransformer *transformer = nullptr;
Chris@320 188
Chris@850 189 QString id = transforms[0].getPluginIdentifier();
Chris@320 190
Chris@1225 191 if (RealTimePluginFactory::instanceFor(id)) {
Chris@350 192
Chris@350 193 transformer =
Chris@850 194 new RealTimeEffectModelTransformer(input, transforms[0]);
Chris@350 195
Chris@320 196 } else {
Chris@1225 197
Chris@1225 198 transformer =
Chris@1225 199 new FeatureExtractionModelTransformer(input, transforms);
Chris@320 200 }
Chris@320 201
Chris@850 202 if (transformer) transformer->setObjectName(transforms[0].getIdentifier());
Chris@331 203 return transformer;
Chris@320 204 }
Chris@320 205
Chris@1740 206 ModelId
Chris@350 207 ModelTransformerFactory::transform(const Transform &transform,
Chris@361 208 const ModelTransformer::Input &input,
Chris@877 209 QString &message,
Chris@877 210 AdditionalModelHandler *handler)
Chris@320 211 {
Chris@690 212 SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl;
Chris@408 213
Chris@850 214 Transforms transforms;
Chris@850 215 transforms.push_back(transform);
Chris@1740 216 vector<ModelId> mm = transformMultiple(transforms, input, message, handler);
Chris@1740 217 if (mm.empty()) return {};
Chris@850 218 else return mm[0];
Chris@320 219 }
Chris@320 220
Chris@1740 221 vector<ModelId>
Chris@849 222 ModelTransformerFactory::transformMultiple(const Transforms &transforms,
Chris@849 223 const ModelTransformer::Input &input,
Chris@877 224 QString &message,
Chris@877 225 AdditionalModelHandler *handler)
Chris@849 226 {
Chris@849 227 SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl;
Chris@849 228
Chris@1703 229 QMutexLocker locker(&m_mutex);
Chris@1740 230
Chris@1740 231 auto inputModel = ModelById::get(input.getModel());
Chris@1740 232 if (!inputModel) return {};
Chris@1703 233
Chris@849 234 ModelTransformer *t = createTransformer(transforms, input);
Chris@1740 235 if (!t) return {};
Chris@849 236
Chris@877 237 if (handler) {
Chris@877 238 m_handlers[t] = handler;
Chris@877 239 }
Chris@849 240
Chris@849 241 m_runningTransformers.insert(t);
Chris@849 242
Chris@877 243 connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
Chris@877 244
Chris@849 245 t->start();
Chris@1740 246 vector<ModelId> models = t->getOutputModels();
Chris@1740 247
Chris@850 248 if (!models.empty()) {
Chris@1740 249 QString imn = inputModel->objectName();
Chris@849 250 QString trn =
Chris@849 251 TransformFactory::getInstance()->getTransformFriendlyName
Chris@850 252 (transforms[0].getIdentifier());
Chris@1740 253 for (int i = 0; in_range_for(models, i); ++i) {
Chris@1740 254 auto model = ModelById::get(models[i]);
Chris@1740 255 if (!model) continue;
Chris@850 256 if (imn != "") {
Chris@850 257 if (trn != "") {
Chris@1740 258 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
Chris@850 259 } else {
Chris@1740 260 model->setObjectName(imn);
Chris@850 261 }
Chris@850 262 } else if (trn != "") {
Chris@1740 263 model->setObjectName(trn);
Chris@849 264 }
Chris@849 265 }
Chris@849 266 } else {
Chris@849 267 t->wait();
Chris@849 268 }
Chris@849 269
Chris@849 270 message = t->getMessage();
Chris@849 271
Chris@850 272 return models;
Chris@849 273 }
Chris@849 274
Chris@320 275 void
Chris@331 276 ModelTransformerFactory::transformerFinished()
Chris@320 277 {
Chris@320 278 QObject *s = sender();
Chris@331 279 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
Chris@320 280
Chris@690 281 // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl;
Chris@320 282
Chris@331 283 if (!transformer) {
Chris@1429 284 cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl;
Chris@1429 285 return;
Chris@320 286 }
Chris@320 287
Chris@1727 288 m_mutex.lock();
Chris@1727 289
Chris@331 290 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
Chris@843 291 cerr << "WARNING: ModelTransformerFactory::transformerFinished("
Chris@331 292 << transformer
Chris@331 293 << "): I have no record of this transformer running!"
Chris@843 294 << endl;
Chris@320 295 }
Chris@320 296
Chris@331 297 m_runningTransformers.erase(transformer);
Chris@320 298
Chris@1740 299 map<AdditionalModelHandler *, vector<ModelId>> toNotifyOfMore;
Chris@1727 300 vector<AdditionalModelHandler *> toNotifyOfNoMore;
Chris@1727 301
Chris@877 302 if (m_handlers.find(transformer) != m_handlers.end()) {
Chris@877 303 if (transformer->willHaveAdditionalOutputModels()) {
Chris@1740 304 vector<ModelId> mm = transformer->getAdditionalOutputModels();
Chris@1727 305 toNotifyOfMore[m_handlers[transformer]] = mm;
Chris@878 306 } else {
Chris@1727 307 toNotifyOfNoMore.push_back(m_handlers[transformer]);
Chris@877 308 }
Chris@877 309 m_handlers.erase(transformer);
Chris@877 310 }
Chris@877 311
Chris@1727 312 m_mutex.unlock();
Chris@1727 313
Chris@1740 314 // We make these calls without the mutex held, in case they
Chris@1740 315 // ultimately call back on us - not such a concern as in the old
Chris@1740 316 // model lifecycle but just in case
Chris@1727 317
Chris@1727 318 for (const auto &i: toNotifyOfMore) {
Chris@1727 319 i.first->moreModelsAvailable(i.second);
Chris@1727 320 }
Chris@1727 321 for (AdditionalModelHandler *handler: toNotifyOfNoMore) {
Chris@1727 322 handler->noMoreModelsAvailable();
Chris@1727 323 }
Chris@1727 324
Chris@1079 325 if (transformer->isAbandoned()) {
Chris@1079 326 if (transformer->getMessage() != "") {
Chris@1079 327 emit transformFailed("", transformer->getMessage());
Chris@1079 328 }
Chris@1079 329 }
Chris@1079 330
Chris@331 331 transformer->wait(); // unnecessary but reassuring
Chris@331 332 delete transformer;
Chris@320 333 }
Chris@320 334
Chris@1703 335 bool
Chris@1703 336 ModelTransformerFactory::haveRunningTransformers() const
Chris@1703 337 {
Chris@1703 338 QMutexLocker locker(&m_mutex);
Chris@1703 339
Chris@1703 340 return (!m_runningTransformers.empty());
Chris@1703 341 }