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@1727
|
59 const vector<Model *> &candidateInputModels,
|
Chris@350
|
60 Model *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@1582
|
68 ModelTransformer::Input input(nullptr);
|
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@345
|
75 Model *inputModel = candidateInputModels[0];
|
Chris@320
|
76 QStringList candidateModelNames;
|
Chris@345
|
77 QString defaultModelName;
|
Chris@653
|
78 QMap<QString, Model *> modelMap;
|
Chris@930
|
79 for (int i = 0; i < (int)candidateInputModels.size(); ++i) {
|
Chris@320
|
80 QString modelName = candidateInputModels[i]->objectName();
|
Chris@320
|
81 QString origModelName = modelName;
|
Chris@320
|
82 int dupcount = 1;
|
Chris@653
|
83 while (modelMap.contains(modelName)) {
|
Chris@320
|
84 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
|
Chris@320
|
85 }
|
Chris@320
|
86 modelMap[modelName] = candidateInputModels[i];
|
Chris@320
|
87 candidateModelNames.push_back(modelName);
|
Chris@345
|
88 if (candidateInputModels[i] == defaultInputModel) {
|
Chris@345
|
89 defaultModelName = modelName;
|
Chris@345
|
90 }
|
Chris@320
|
91 }
|
Chris@320
|
92
|
Chris@350
|
93 QString id = transform.getPluginIdentifier();
|
Chris@320
|
94
|
Chris@653
|
95 bool ok = true;
|
Chris@350
|
96 QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
|
Chris@320
|
97
|
Chris@1264
|
98 SVDEBUG << "ModelTransformer: last configuration for identifier " << transform.getIdentifier() << ": " << configurationXml << endl;
|
Chris@320
|
99
|
Chris@1582
|
100 Vamp::PluginBase *plugin = nullptr;
|
Chris@320
|
101
|
Chris@1225
|
102 if (RealTimePluginFactory::instanceFor(id)) {
|
Chris@320
|
103
|
Chris@1264
|
104 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating real-time plugin" << endl;
|
Chris@1264
|
105
|
Chris@320
|
106 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
|
Chris@320
|
107
|
Chris@1040
|
108 sv_samplerate_t sampleRate = inputModel->getSampleRate();
|
Chris@930
|
109 int blockSize = 1024;
|
Chris@930
|
110 int channels = 1;
|
Chris@653
|
111 if (source) {
|
Chris@1321
|
112 sampleRate = source->getSourceSampleRate();
|
Chris@320
|
113 blockSize = source->getTargetBlockSize();
|
Chris@320
|
114 channels = source->getTargetChannelCount();
|
Chris@320
|
115 }
|
Chris@320
|
116
|
Chris@320
|
117 RealTimePluginInstance *rtp = factory->instantiatePlugin
|
Chris@320
|
118 (id, 0, 0, sampleRate, blockSize, channels);
|
Chris@320
|
119
|
Chris@320
|
120 plugin = rtp;
|
Chris@1225
|
121
|
Chris@1225
|
122 } else {
|
Chris@1225
|
123
|
Chris@1264
|
124 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating Vamp plugin" << endl;
|
Chris@1225
|
125
|
Chris@1225
|
126 Vamp::Plugin *vp =
|
Chris@1225
|
127 FeatureExtractionPluginFactory::instance()->instantiatePlugin
|
Chris@1225
|
128 (id, float(inputModel->getSampleRate()));
|
Chris@1225
|
129
|
Chris@1225
|
130 plugin = vp;
|
Chris@320
|
131 }
|
Chris@320
|
132
|
Chris@320
|
133 if (plugin) {
|
Chris@320
|
134
|
Chris@350
|
135 // Ensure block size etc are valid
|
Chris@350
|
136 TransformFactory::getInstance()->
|
Chris@350
|
137 makeContextConsistentWithPlugin(transform, plugin);
|
Chris@320
|
138
|
Chris@350
|
139 // Prepare the plugin with any existing parameters already
|
Chris@350
|
140 // found in the transform
|
Chris@350
|
141 TransformFactory::getInstance()->
|
Chris@350
|
142 setPluginParameters(transform, plugin);
|
Chris@350
|
143
|
Chris@350
|
144 // For this interactive usage, we want to override those with
|
Chris@350
|
145 // whatever the user chose last time around
|
Chris@350
|
146 PluginXml(plugin).setParametersFromXml(configurationXml);
|
Chris@320
|
147
|
Chris@653
|
148 if (configurator) {
|
Chris@653
|
149 ok = configurator->configure(input, transform, plugin,
|
Chris@653
|
150 inputModel, source,
|
Chris@653
|
151 startFrame, duration,
|
Chris@653
|
152 modelMap,
|
Chris@653
|
153 candidateModelNames,
|
Chris@653
|
154 defaultModelName);
|
Chris@320
|
155 }
|
Chris@320
|
156
|
Chris@516
|
157
|
Chris@350
|
158 TransformFactory::getInstance()->
|
Chris@350
|
159 makeContextConsistentWithPlugin(transform, plugin);
|
Chris@350
|
160
|
Chris@350
|
161 configurationXml = PluginXml(plugin).toXmlString();
|
Chris@320
|
162
|
Chris@1264
|
163 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: got configuration, deleting plugin" << endl;
|
Chris@1264
|
164
|
Chris@653
|
165 delete plugin;
|
Chris@320
|
166 }
|
Chris@320
|
167
|
Chris@350
|
168 if (ok) {
|
Chris@350
|
169 m_lastConfigurations[transform.getIdentifier()] = configurationXml;
|
Chris@350
|
170 input.setModel(inputModel);
|
Chris@350
|
171 }
|
Chris@320
|
172
|
Chris@350
|
173 return input;
|
Chris@320
|
174 }
|
Chris@320
|
175
|
Chris@331
|
176 ModelTransformer *
|
Chris@850
|
177 ModelTransformerFactory::createTransformer(const Transforms &transforms,
|
Chris@350
|
178 const ModelTransformer::Input &input)
|
Chris@320
|
179 {
|
Chris@1582
|
180 ModelTransformer *transformer = nullptr;
|
Chris@320
|
181
|
Chris@850
|
182 QString id = transforms[0].getPluginIdentifier();
|
Chris@320
|
183
|
Chris@1225
|
184 if (RealTimePluginFactory::instanceFor(id)) {
|
Chris@350
|
185
|
Chris@350
|
186 transformer =
|
Chris@850
|
187 new RealTimeEffectModelTransformer(input, transforms[0]);
|
Chris@350
|
188
|
Chris@320
|
189 } else {
|
Chris@1225
|
190
|
Chris@1225
|
191 transformer =
|
Chris@1225
|
192 new FeatureExtractionModelTransformer(input, transforms);
|
Chris@320
|
193 }
|
Chris@320
|
194
|
Chris@850
|
195 if (transformer) transformer->setObjectName(transforms[0].getIdentifier());
|
Chris@331
|
196 return transformer;
|
Chris@320
|
197 }
|
Chris@320
|
198
|
Chris@320
|
199 Model *
|
Chris@350
|
200 ModelTransformerFactory::transform(const Transform &transform,
|
Chris@361
|
201 const ModelTransformer::Input &input,
|
Chris@877
|
202 QString &message,
|
Chris@877
|
203 AdditionalModelHandler *handler)
|
Chris@320
|
204 {
|
Chris@690
|
205 SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl;
|
Chris@408
|
206
|
Chris@850
|
207 Transforms transforms;
|
Chris@850
|
208 transforms.push_back(transform);
|
Chris@877
|
209 vector<Model *> mm = transformMultiple(transforms, input, message, handler);
|
Chris@1582
|
210 if (mm.empty()) return nullptr;
|
Chris@850
|
211 else return mm[0];
|
Chris@320
|
212 }
|
Chris@320
|
213
|
Chris@849
|
214 vector<Model *>
|
Chris@849
|
215 ModelTransformerFactory::transformMultiple(const Transforms &transforms,
|
Chris@849
|
216 const ModelTransformer::Input &input,
|
Chris@877
|
217 QString &message,
|
Chris@877
|
218 AdditionalModelHandler *handler)
|
Chris@849
|
219 {
|
Chris@849
|
220 SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl;
|
Chris@849
|
221
|
Chris@1703
|
222 QMutexLocker locker(&m_mutex);
|
Chris@1703
|
223
|
Chris@849
|
224 ModelTransformer *t = createTransformer(transforms, input);
|
Chris@850
|
225 if (!t) return vector<Model *>();
|
Chris@849
|
226
|
Chris@877
|
227 if (handler) {
|
Chris@877
|
228 m_handlers[t] = handler;
|
Chris@877
|
229 }
|
Chris@849
|
230
|
Chris@849
|
231 m_runningTransformers.insert(t);
|
Chris@849
|
232
|
Chris@877
|
233 connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
|
Chris@877
|
234
|
Chris@849
|
235 t->start();
|
Chris@850
|
236 vector<Model *> models = t->detachOutputModels();
|
Chris@849
|
237
|
Chris@850
|
238 if (!models.empty()) {
|
Chris@849
|
239 QString imn = input.getModel()->objectName();
|
Chris@849
|
240 QString trn =
|
Chris@849
|
241 TransformFactory::getInstance()->getTransformFriendlyName
|
Chris@850
|
242 (transforms[0].getIdentifier());
|
Chris@924
|
243 for (int i = 0; i < (int)models.size(); ++i) {
|
Chris@850
|
244 if (imn != "") {
|
Chris@850
|
245 if (trn != "") {
|
Chris@850
|
246 models[i]->setObjectName(tr("%1: %2").arg(imn).arg(trn));
|
Chris@850
|
247 } else {
|
Chris@850
|
248 models[i]->setObjectName(imn);
|
Chris@850
|
249 }
|
Chris@850
|
250 } else if (trn != "") {
|
Chris@850
|
251 models[i]->setObjectName(trn);
|
Chris@849
|
252 }
|
Chris@849
|
253 }
|
Chris@849
|
254 } else {
|
Chris@849
|
255 t->wait();
|
Chris@849
|
256 }
|
Chris@849
|
257
|
Chris@849
|
258 message = t->getMessage();
|
Chris@849
|
259
|
Chris@850
|
260 return models;
|
Chris@849
|
261 }
|
Chris@849
|
262
|
Chris@320
|
263 void
|
Chris@331
|
264 ModelTransformerFactory::transformerFinished()
|
Chris@320
|
265 {
|
Chris@320
|
266 QObject *s = sender();
|
Chris@331
|
267 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
|
Chris@320
|
268
|
Chris@690
|
269 // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl;
|
Chris@320
|
270
|
Chris@331
|
271 if (!transformer) {
|
Chris@1429
|
272 cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl;
|
Chris@1429
|
273 return;
|
Chris@320
|
274 }
|
Chris@320
|
275
|
Chris@1727
|
276 m_mutex.lock();
|
Chris@1727
|
277
|
Chris@331
|
278 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
|
Chris@843
|
279 cerr << "WARNING: ModelTransformerFactory::transformerFinished("
|
Chris@331
|
280 << transformer
|
Chris@331
|
281 << "): I have no record of this transformer running!"
|
Chris@843
|
282 << endl;
|
Chris@320
|
283 }
|
Chris@320
|
284
|
Chris@331
|
285 m_runningTransformers.erase(transformer);
|
Chris@320
|
286
|
Chris@1727
|
287 map<AdditionalModelHandler *, vector<Model *>> toNotifyOfMore;
|
Chris@1727
|
288 vector<AdditionalModelHandler *> toNotifyOfNoMore;
|
Chris@1727
|
289
|
Chris@877
|
290 if (m_handlers.find(transformer) != m_handlers.end()) {
|
Chris@877
|
291 if (transformer->willHaveAdditionalOutputModels()) {
|
Chris@877
|
292 vector<Model *> mm = transformer->detachAdditionalOutputModels();
|
Chris@1727
|
293 toNotifyOfMore[m_handlers[transformer]] = mm;
|
Chris@878
|
294 } else {
|
Chris@1727
|
295 toNotifyOfNoMore.push_back(m_handlers[transformer]);
|
Chris@877
|
296 }
|
Chris@877
|
297 m_handlers.erase(transformer);
|
Chris@877
|
298 }
|
Chris@877
|
299
|
Chris@1727
|
300 m_mutex.unlock();
|
Chris@1727
|
301
|
Chris@1727
|
302 // These calls have to be made without the mutex held, as they may
|
Chris@1727
|
303 // ultimately call back on us (e.g. we have one baroque situation
|
Chris@1727
|
304 // where this could trigger a command to create a layer, which
|
Chris@1727
|
305 // triggers the command history to clip the stack, which deletes a
|
Chris@1727
|
306 // spare old model, which calls back on our modelAboutToBeDeleted)
|
Chris@1727
|
307
|
Chris@1727
|
308 for (const auto &i: toNotifyOfMore) {
|
Chris@1727
|
309 i.first->moreModelsAvailable(i.second);
|
Chris@1727
|
310 }
|
Chris@1727
|
311 for (AdditionalModelHandler *handler: toNotifyOfNoMore) {
|
Chris@1727
|
312 handler->noMoreModelsAvailable();
|
Chris@1727
|
313 }
|
Chris@1727
|
314
|
Chris@1079
|
315 if (transformer->isAbandoned()) {
|
Chris@1079
|
316 if (transformer->getMessage() != "") {
|
Chris@1079
|
317 emit transformFailed("", transformer->getMessage());
|
Chris@1079
|
318 }
|
Chris@1079
|
319 }
|
Chris@1079
|
320
|
Chris@331
|
321 transformer->wait(); // unnecessary but reassuring
|
Chris@331
|
322 delete transformer;
|
Chris@320
|
323 }
|
Chris@320
|
324
|
Chris@320
|
325 void
|
Chris@331
|
326 ModelTransformerFactory::modelAboutToBeDeleted(Model *m)
|
Chris@320
|
327 {
|
Chris@328
|
328 TransformerSet affected;
|
Chris@320
|
329
|
Chris@1703
|
330 {
|
Chris@1703
|
331 QMutexLocker locker(&m_mutex);
|
Chris@1703
|
332
|
Chris@1703
|
333 for (TransformerSet::iterator i = m_runningTransformers.begin();
|
Chris@1703
|
334 i != m_runningTransformers.end(); ++i) {
|
Chris@320
|
335
|
Chris@1703
|
336 ModelTransformer *t = *i;
|
Chris@320
|
337
|
Chris@1703
|
338 if (t->getInputModel() == m) {
|
Chris@1703
|
339 affected.insert(t);
|
Chris@1703
|
340 } else {
|
Chris@1703
|
341 vector<Model *> mm = t->getOutputModels();
|
Chris@1703
|
342 for (int i = 0; i < (int)mm.size(); ++i) {
|
Chris@1703
|
343 if (mm[i] == m) affected.insert(t);
|
Chris@1703
|
344 }
|
Chris@850
|
345 }
|
Chris@320
|
346 }
|
Chris@320
|
347 }
|
Chris@320
|
348
|
Chris@328
|
349 for (TransformerSet::iterator i = affected.begin();
|
Chris@320
|
350 i != affected.end(); ++i) {
|
Chris@320
|
351
|
Chris@331
|
352 ModelTransformer *t = *i;
|
Chris@320
|
353
|
Chris@320
|
354 t->abandon();
|
Chris@320
|
355
|
Chris@320
|
356 t->wait(); // this should eventually call back on
|
Chris@331
|
357 // transformerFinished, which will remove from
|
Chris@328
|
358 // m_runningTransformers and delete.
|
Chris@320
|
359 }
|
Chris@320
|
360 }
|
Chris@320
|
361
|
Chris@1703
|
362 bool
|
Chris@1703
|
363 ModelTransformerFactory::haveRunningTransformers() const
|
Chris@1703
|
364 {
|
Chris@1703
|
365 QMutexLocker locker(&m_mutex);
|
Chris@1703
|
366
|
Chris@1703
|
367 return (!m_runningTransformers.empty());
|
Chris@1703
|
368 }
|