ModelTransformerFactory.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
17 
20 
21 #include "TransformFactory.h"
22 
23 #include "base/AudioPlaySource.h"
24 
27 #include "plugin/PluginXml.h"
28 
30 
31 #include <vamp-hostsdk/PluginHostAdapter.h>
32 
33 #include <iostream>
34 #include <set>
35 #include <map>
36 
37 #include <QRegExp>
38 #include <QMutexLocker>
39 
40 using std::vector;
41 using std::set;
42 using std::map;
43 using std::shared_ptr;
44 using std::make_shared;
45 
48 
51 {
52  return m_instance;
53 }
54 
56 {
57 }
58 
61  vector<ModelId> candidateInputModels,
62  ModelId defaultInputModel,
63  AudioPlaySource *source,
64  sv_frame_t startFrame,
65  sv_frame_t duration,
66  UserConfigurator *configurator)
67 {
68  QMutexLocker locker(&m_mutex);
69 
70  ModelTransformer::Input input({});
71 
72  if (candidateInputModels.empty()) return input;
73 
75  //from the dialog for when the candidate input model is changed,
76  //as we'll need to reinitialise the channel settings in the dialog
77  ModelId inputModel = candidateInputModels[0];
78  QStringList candidateModelNames;
79  QString defaultModelName;
80  QMap<QString, ModelId> modelMap;
81 
82  sv_samplerate_t defaultSampleRate;
83  { auto im = ModelById::get(inputModel);
84  if (!im) return input;
85  defaultSampleRate = im->getSampleRate();
86  }
87 
88  for (int i = 0; in_range_for(candidateInputModels, i); ++i) {
89 
90  auto model = ModelById::get(candidateInputModels[i]);
91  if (!model) return input;
92 
93  QString modelName = model->objectName();
94  QString origModelName = modelName;
95  int dupcount = 1;
96  while (modelMap.contains(modelName)) {
97  modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
98  }
99 
100  modelMap[modelName] = candidateInputModels[i];
101  candidateModelNames.push_back(modelName);
102 
103  if (candidateInputModels[i] == defaultInputModel) {
104  defaultModelName = modelName;
105  }
106  }
107 
108  QString id = transform.getPluginIdentifier();
109 
110  bool ok = true;
111  QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
112 
113  SVDEBUG << "ModelTransformer: last configuration for identifier " << transform.getIdentifier() << ": " << configurationXml << endl;
114 
115  shared_ptr<Vamp::PluginBase> plugin;
116 
118 
119  SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating real-time plugin" << endl;
120 
122 
123  sv_samplerate_t sampleRate = defaultSampleRate;
124  int blockSize = 1024;
125  int channels = 1;
126  if (source) {
127  sampleRate = source->getSourceSampleRate();
128  blockSize = source->getTargetBlockSize();
129  channels = source->getTargetChannelCount();
130  }
131 
132  plugin = factory->instantiatePlugin
133  (id, 0, 0, sampleRate, blockSize, channels);
134 
135  } else {
136 
137  SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating Vamp plugin" << endl;
138 
139  plugin =
141  (id, float(defaultSampleRate));
142  }
143 
144  if (plugin) {
145 
146  // Ensure block size etc are valid
148  makeContextConsistentWithPlugin(transform, plugin);
149 
150  // Prepare the plugin with any existing parameters already
151  // found in the transform
153  setPluginParameters(transform, plugin);
154 
155  // For this interactive usage, we want to override those with
156  // whatever the user chose last time around
157  PluginXml(plugin).setParametersFromXml(configurationXml);
158 
159  if (configurator) {
160  ok = configurator->configure(input, transform, plugin,
161  inputModel, source,
162  startFrame, duration,
163  modelMap,
164  candidateModelNames,
165  defaultModelName);
166  }
167 
168 
170  makeContextConsistentWithPlugin(transform, plugin);
171 
172  configurationXml = PluginXml(plugin).toXmlString();
173  }
174 
175  if (ok) {
176  m_lastConfigurations[transform.getIdentifier()] = configurationXml;
177  input.setModel(inputModel);
178  }
179 
180  return input;
181 }
182 
185  const ModelTransformer::Input &input)
186 {
187  ModelTransformer *transformer = nullptr;
188 
189  QString id = transforms[0].getPluginIdentifier();
190 
192 
193  transformer =
194  new RealTimeEffectModelTransformer(input, transforms[0]);
195 
196  } else {
197 
198  transformer =
199  new FeatureExtractionModelTransformer(input, transforms);
200  }
201 
202  if (transformer) transformer->setObjectName(transforms[0].getIdentifier());
203  return transformer;
204 }
205 
206 ModelId
208  const ModelTransformer::Input &input,
209  QString &message,
210  AdditionalModelHandler *handler)
211 {
212  SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl;
213 
214  Transforms transforms;
215  transforms.push_back(transform);
216  vector<ModelId> mm = transformMultiple(transforms, input, message, handler);
217  if (mm.empty()) return {};
218  else return mm[0];
219 }
220 
221 vector<ModelId>
223  const ModelTransformer::Input &input,
224  QString &message,
225  AdditionalModelHandler *handler)
226 {
227  SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl;
228 
229  QMutexLocker locker(&m_mutex);
230 
231  auto inputModel = ModelById::get(input.getModel());
232  if (!inputModel) return {};
233 
234  ModelTransformer *t = createTransformer(transforms, input);
235  if (!t) return {};
236 
237  if (handler) {
238  m_handlers[t] = handler;
239  }
240 
241  m_runningTransformers.insert(t);
242 
243  connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
244 
245  t->start();
246  vector<ModelId> models = t->getOutputModels();
247 
248  if (!models.empty()) {
249  QString imn = inputModel->objectName();
250  QString trn =
252  (transforms[0].getIdentifier());
253  for (int i = 0; in_range_for(models, i); ++i) {
254  auto model = ModelById::get(models[i]);
255  if (!model) continue;
256  if (imn != "") {
257  if (trn != "") {
258  model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
259  } else {
260  model->setObjectName(imn);
261  }
262  } else if (trn != "") {
263  model->setObjectName(trn);
264  }
265  }
266  } else {
267  t->wait();
268  }
269 
270  message = t->getMessage();
271 
272  return models;
273 }
274 
275 void
277 {
278  QObject *s = sender();
279  ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
280 
281 // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl;
282 
283  if (!transformer) {
284  cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl;
285  return;
286  }
287 
288  m_mutex.lock();
289 
290  if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
291  cerr << "WARNING: ModelTransformerFactory::transformerFinished("
292  << transformer
293  << "): I have no record of this transformer running!"
294  << endl;
295  }
296 
297  m_runningTransformers.erase(transformer);
298 
299  map<AdditionalModelHandler *, vector<ModelId>> toNotifyOfMore;
300  vector<AdditionalModelHandler *> toNotifyOfNoMore;
301 
302  if (m_handlers.find(transformer) != m_handlers.end()) {
303  if (transformer->willHaveAdditionalOutputModels()) {
304  vector<ModelId> mm = transformer->getAdditionalOutputModels();
305  toNotifyOfMore[m_handlers[transformer]] = mm;
306  } else {
307  toNotifyOfNoMore.push_back(m_handlers[transformer]);
308  }
309  m_handlers.erase(transformer);
310  }
311 
312  m_mutex.unlock();
313 
314  // We make these calls without the mutex held, in case they
315  // ultimately call back on us - not such a concern as in the old
316  // model lifecycle but just in case
317 
318  for (const auto &i: toNotifyOfMore) {
319  i.first->moreModelsAvailable(i.second);
320  }
321  for (AdditionalModelHandler *handler: toNotifyOfNoMore) {
322  handler->noMoreModelsAvailable();
323  }
324 
325  if (transformer->isAbandoned()) {
326  if (transformer->getMessage() != "") {
327  emit transformFailed("", transformer->getMessage());
328  }
329  }
330 
331  transformer->wait(); // unnecessary but reassuring
332  delete transformer;
333 }
334 
335 bool
337 {
338  QMutexLocker locker(&m_mutex);
339 
340  return (!m_runningTransformers.empty());
341 }
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
Simple interface for audio playback.
virtual void setParametersFromXml(QString xml)
Set the parameters and program of a plugin from an XML plugin element as returned by toXml...
Definition: PluginXml.cpp:195
ModelId transform(const Transform &transform, const ModelTransformer::Input &input, QString &message, AdditionalModelHandler *handler=0)
Return the output model resulting from applying the named transform to the given input model...
ModelTransformer * createTransformer(const Transforms &transforms, const ModelTransformer::Input &input)
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
virtual int getTargetChannelCount() const =0
Get the number of channels of audio that will be provided to the play target.
std::vector< Transform > Transforms
Definition: Transform.h:204
static ModelTransformerFactory * getInstance()
virtual bool configure(ModelTransformer::Input &input, Transform &transform, std::shared_ptr< Vamp::PluginBase > plugin, ModelId &inputModel, AudioPlaySource *source, sv_frame_t startFrame, sv_frame_t duration, const QMap< QString, ModelId > &modelMap, QStringList candidateModelNames, QString defaultModelName)=0
static RealTimePluginFactory * instanceFor(QString identifier)
static ModelTransformerFactory * m_instance
void start()
Definition: Thread.cpp:34
QString getMessage() const
Return a warning or error message.
ModelTransformer::Input getConfigurationForTransform(Transform &transform, std::vector< ModelId > candidateInputModels, ModelId defaultInputModel, AudioPlaySource *source=0, sv_frame_t startFrame=0, sv_frame_t duration=0, UserConfigurator *configurator=0)
Fill out the configuration for the given transform (may include asking the user by calling back on th...
std::vector< ModelId > transformMultiple(const Transforms &transform, const ModelTransformer::Input &input, QString &message, AdditionalModelHandler *handler=0)
Return the multiple output models resulting from applying the named transforms to the given input mod...
virtual QString toXmlString(QString indent="", QString extraAttributes="") const
Convert this exportable object to XML in a string.
QString getTransformFriendlyName(TransformId identifier)
Brief but friendly name of a transform, suitable for use as the name of the output layer...
static TransformFactory * getInstance()
virtual std::shared_ptr< RealTimePluginInstance > instantiatePlugin(QString identifier, int clientId, int position, sv_samplerate_t sampleRate, int blockSize, int channels)=0
Instantiate a plugin.
virtual sv_samplerate_t getSourceSampleRate() const =0
Return the sample rate of the source material – any material that wants to play at a different rate ...
void transformFailed(QString transformName, QString message)
TransformerConfigurationMap m_lastConfigurations
virtual Models getAdditionalOutputModels()
Return any additional models that were created during processing.
ModelId getModel() const
virtual std::shared_ptr< Vamp::Plugin > instantiatePlugin(QString identifier, sv_samplerate_t inputSampleRate)=0
Instantiate (load) and return pointer to the plugin with the given identifier, at the given sample ra...
virtual bool willHaveAdditionalOutputModels()
Return true if the current transform is one that may produce additional models (to be retrieved throu...
static FeatureExtractionPluginFactory * instance()
bool in_range_for(const C &container, T i)
Check whether an integer index is in range for a container, avoiding overflows and signed/unsigned co...
Definition: BaseTypes.h:37
#define SVDEBUG
Definition: Debug.h:106
Models getOutputModels()
Return the set of output model IDs created by the transform or transforms.
TransformId getIdentifier() const
Definition: Transform.cpp:179
A ModelTransformer turns one data model into another.
QString getPluginIdentifier() const
Definition: Transform.cpp:213
virtual int getTargetBlockSize() const =0
Get the block size of the target audio device.
bool isAbandoned() const
Return true if the processing thread is being or has been abandoned, i.e.
Definition: ById.h:115
static std::shared_ptr< Item > get(Id id)
Definition: ById.h:251