annotate transform/ModelTransformerFactory.cpp @ 1794:71e97de5053f

Merge from branch time-frequency-boxes
author Chris Cannam
date Wed, 25 Sep 2019 13:47:46 +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 }