annotate transform/ModelTransformerFactory.cpp @ 1784:4eac4bf35b45

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