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 }
|