comparison transform/ModelTransformerFactory.cpp @ 388:370aa9714ef5

* Move plugin/transform to plain transform. This way transform can depend on model and GUI classes, but plugin doesn't have to.
author Chris Cannam
date Wed, 12 Mar 2008 18:02:17 +0000
parents plugin/transform/ModelTransformerFactory.cpp@399ea254afd6
children a1b6d2e33cab
comparison
equal deleted inserted replaced
387:7aa1de571880 388:370aa9714ef5
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
16 #include "ModelTransformerFactory.h"
17
18 #include "FeatureExtractionModelTransformer.h"
19 #include "RealTimeEffectModelTransformer.h"
20
21 #include "TransformFactory.h"
22
23 #include "plugin/FeatureExtractionPluginFactory.h"
24 #include "plugin/RealTimePluginFactory.h"
25 #include "plugin/PluginXml.h"
26
27 #include "widgets/PluginParameterDialog.h"
28
29 #include "data/model/DenseTimeValueModel.h"
30
31 #include "vamp-sdk/PluginHostAdapter.h"
32
33 #include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here
34
35 #include <iostream>
36 #include <set>
37
38 #include <QRegExp>
39
40 ModelTransformerFactory *
41 ModelTransformerFactory::m_instance = new ModelTransformerFactory;
42
43 ModelTransformerFactory *
44 ModelTransformerFactory::getInstance()
45 {
46 return m_instance;
47 }
48
49 ModelTransformerFactory::~ModelTransformerFactory()
50 {
51 }
52
53 bool
54 ModelTransformerFactory::getChannelRange(TransformId identifier,
55 Vamp::PluginBase *plugin,
56 int &minChannels, int &maxChannels)
57 {
58 Vamp::Plugin *vp = 0;
59 if ((vp = dynamic_cast<Vamp::Plugin *>(plugin)) ||
60 (vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin))) {
61 minChannels = vp->getMinChannelCount();
62 maxChannels = vp->getMaxChannelCount();
63 return true;
64 } else {
65 return TransformFactory::getInstance()->
66 getTransformChannelRange(identifier, minChannels, maxChannels);
67 }
68 }
69
70 ModelTransformer::Input
71 ModelTransformerFactory::getConfigurationForTransform(Transform &transform,
72 const std::vector<Model *> &candidateInputModels,
73 Model *defaultInputModel,
74 AudioCallbackPlaySource *source,
75 size_t startFrame,
76 size_t duration)
77 {
78 ModelTransformer::Input input(0);
79
80 if (candidateInputModels.empty()) return input;
81
82 //!!! This will need revision -- we'll have to have a callback
83 //from the dialog for when the candidate input model is changed,
84 //as we'll need to reinitialise the channel settings in the dialog
85 Model *inputModel = candidateInputModels[0];
86 QStringList candidateModelNames;
87 QString defaultModelName;
88 std::map<QString, Model *> modelMap;
89 for (size_t i = 0; i < candidateInputModels.size(); ++i) {
90 QString modelName = candidateInputModels[i]->objectName();
91 QString origModelName = modelName;
92 int dupcount = 1;
93 while (modelMap.find(modelName) != modelMap.end()) {
94 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
95 }
96 modelMap[modelName] = candidateInputModels[i];
97 candidateModelNames.push_back(modelName);
98 if (candidateInputModels[i] == defaultInputModel) {
99 defaultModelName = modelName;
100 }
101 }
102
103 QString id = transform.getPluginIdentifier();
104 QString output = transform.getOutput();
105 QString outputLabel = "";
106 QString outputDescription = "";
107
108 bool ok = false;
109 QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
110
111 std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
112
113 Vamp::PluginBase *plugin = 0;
114
115 bool frequency = false;
116 bool effect = false;
117 bool generator = false;
118
119 if (FeatureExtractionPluginFactory::instanceFor(id)) {
120
121 std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl;
122
123 Vamp::Plugin *vp =
124 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
125 (id, inputModel->getSampleRate());
126
127 if (vp) {
128
129 plugin = vp;
130 frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
131
132 std::vector<Vamp::Plugin::OutputDescriptor> od =
133 vp->getOutputDescriptors();
134 if (od.size() > 1) {
135 for (size_t i = 0; i < od.size(); ++i) {
136 if (od[i].identifier == output.toStdString()) {
137 outputLabel = od[i].name.c_str();
138 outputDescription = od[i].description.c_str();
139 break;
140 }
141 }
142 }
143 }
144
145 } else if (RealTimePluginFactory::instanceFor(id)) {
146
147 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
148 const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id);
149
150 if (desc->audioInputPortCount > 0 &&
151 desc->audioOutputPortCount > 0 &&
152 !desc->isSynth) {
153 effect = true;
154 }
155
156 if (desc->audioInputPortCount == 0) {
157 generator = true;
158 }
159
160 if (output != "A") {
161 int outputNo = output.toInt();
162 if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) {
163 outputLabel = desc->controlOutputPortNames[outputNo].c_str();
164 }
165 }
166
167 size_t sampleRate = inputModel->getSampleRate();
168 size_t blockSize = 1024;
169 size_t channels = 1;
170 if (effect && source) {
171 sampleRate = source->getTargetSampleRate();
172 blockSize = source->getTargetBlockSize();
173 channels = source->getTargetChannelCount();
174 }
175
176 RealTimePluginInstance *rtp = factory->instantiatePlugin
177 (id, 0, 0, sampleRate, blockSize, channels);
178
179 plugin = rtp;
180
181 if (effect && source && rtp) {
182 source->setAuditioningPlugin(rtp);
183 }
184 }
185
186 if (plugin) {
187
188 // Ensure block size etc are valid
189 TransformFactory::getInstance()->
190 makeContextConsistentWithPlugin(transform, plugin);
191
192 // Prepare the plugin with any existing parameters already
193 // found in the transform
194 TransformFactory::getInstance()->
195 setPluginParameters(transform, plugin);
196
197 // For this interactive usage, we want to override those with
198 // whatever the user chose last time around
199 PluginXml(plugin).setParametersFromXml(configurationXml);
200
201 int sourceChannels = 1;
202 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
203 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
204 ->getChannelCount();
205 }
206
207 int minChannels = 1, maxChannels = sourceChannels;
208 getChannelRange(transform.getIdentifier(), plugin,
209 minChannels, maxChannels);
210
211 int targetChannels = sourceChannels;
212 if (!effect) {
213 if (sourceChannels < minChannels) targetChannels = minChannels;
214 if (sourceChannels > maxChannels) targetChannels = maxChannels;
215 }
216
217 int defaultChannel = -1; //!!! no longer saved! [was context.channel]
218
219 PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
220
221 if (candidateModelNames.size() > 1 && !generator) {
222 dialog->setCandidateInputModels(candidateModelNames,
223 defaultModelName);
224 }
225
226 if (startFrame != 0 || duration != 0) {
227 dialog->setShowSelectionOnlyOption(true);
228 }
229
230 if (targetChannels > 0) {
231 dialog->setChannelArrangement(sourceChannels, targetChannels,
232 defaultChannel);
233 }
234
235 dialog->setOutputLabel(outputLabel, outputDescription);
236
237 dialog->setShowProcessingOptions(true, frequency);
238
239 if (dialog->exec() == QDialog::Accepted) {
240 ok = true;
241 }
242
243 QString selectedInput = dialog->getInputModel();
244 if (selectedInput != "") {
245 if (modelMap.find(selectedInput) != modelMap.end()) {
246 inputModel = modelMap[selectedInput];
247 std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl;
248 } else {
249 std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl;
250 }
251 } else {
252 std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
253 }
254
255 // Write parameters back to transform object
256 TransformFactory::getInstance()->
257 setParametersFromPlugin(transform, plugin);
258
259 input.setChannel(dialog->getChannel());
260
261 //!!! The dialog ought to be taking & returning transform
262 //objects and input objects and stuff rather than passing
263 //around all this misc stuff, but that's for tomorrow
264 //(whenever that may be)
265
266 if (startFrame != 0 || duration != 0) {
267 if (dialog->getSelectionOnly()) {
268 transform.setStartTime(RealTime::frame2RealTime
269 (startFrame, inputModel->getSampleRate()));
270 transform.setDuration(RealTime::frame2RealTime
271 (duration, inputModel->getSampleRate()));
272 }
273 }
274
275 size_t stepSize = 0, blockSize = 0;
276 WindowType windowType = HanningWindow;
277
278 dialog->getProcessingParameters(stepSize,
279 blockSize,
280 windowType);
281
282 transform.setStepSize(stepSize);
283 transform.setBlockSize(blockSize);
284 transform.setWindowType(windowType);
285
286 TransformFactory::getInstance()->
287 makeContextConsistentWithPlugin(transform, plugin);
288
289 configurationXml = PluginXml(plugin).toXmlString();
290
291 delete dialog;
292
293 if (effect && source) {
294 source->setAuditioningPlugin(0); // will delete our plugin
295 } else {
296 delete plugin;
297 }
298 }
299
300 if (ok) {
301 m_lastConfigurations[transform.getIdentifier()] = configurationXml;
302 input.setModel(inputModel);
303 }
304
305 return input;
306 }
307 /*!!!
308 PluginTransformer::ExecutionContext
309 ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier,
310 Model *inputModel)
311 {
312 PluginTransformer::ExecutionContext context(-1);
313
314 QString id = identifier.section(':', 0, 2);
315
316 if (FeatureExtractionPluginFactory::instanceFor(id)) {
317
318 Vamp::Plugin *vp =
319 FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
320 (id, inputModel ? inputModel->getSampleRate() : 48000);
321
322 if (vp) {
323 context = PluginTransformer::ExecutionContext(-1, vp);
324 delete vp;
325 }
326 }
327
328 return context;
329 }
330 */
331 ModelTransformer *
332 ModelTransformerFactory::createTransformer(const Transform &transform,
333 const ModelTransformer::Input &input)
334 {
335 ModelTransformer *transformer = 0;
336
337 QString id = transform.getPluginIdentifier();
338
339 if (FeatureExtractionPluginFactory::instanceFor(id)) {
340
341 transformer =
342 new FeatureExtractionModelTransformer(input, transform);
343
344 } else if (RealTimePluginFactory::instanceFor(id)) {
345
346 transformer =
347 new RealTimeEffectModelTransformer(input, transform);
348
349 } else {
350 std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \""
351 << transform.getIdentifier().toStdString() << "\"" << std::endl;
352 return transformer;
353 }
354
355 if (transformer) transformer->setObjectName(transform.getIdentifier());
356 return transformer;
357 }
358
359 Model *
360 ModelTransformerFactory::transform(const Transform &transform,
361 const ModelTransformer::Input &input,
362 QString &message)
363 {
364 ModelTransformer *t = createTransformer(transform, input);
365 if (!t) return 0;
366
367 connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
368
369 m_runningTransformers.insert(t);
370
371 t->start();
372 Model *model = t->detachOutputModel();
373
374 if (model) {
375 QString imn = input.getModel()->objectName();
376 QString trn =
377 TransformFactory::getInstance()->getTransformFriendlyName
378 (transform.getIdentifier());
379 if (imn != "") {
380 if (trn != "") {
381 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
382 } else {
383 model->setObjectName(imn);
384 }
385 } else if (trn != "") {
386 model->setObjectName(trn);
387 }
388 } else {
389 t->wait();
390 }
391
392 message = t->getMessage();
393
394 return model;
395 }
396
397 void
398 ModelTransformerFactory::transformerFinished()
399 {
400 QObject *s = sender();
401 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
402
403 std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::endl;
404
405 if (!transformer) {
406 std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl;
407 return;
408 }
409
410 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
411 std::cerr << "WARNING: ModelTransformerFactory::transformerFinished("
412 << transformer
413 << "): I have no record of this transformer running!"
414 << std::endl;
415 }
416
417 m_runningTransformers.erase(transformer);
418
419 transformer->wait(); // unnecessary but reassuring
420 delete transformer;
421 }
422
423 void
424 ModelTransformerFactory::modelAboutToBeDeleted(Model *m)
425 {
426 TransformerSet affected;
427
428 for (TransformerSet::iterator i = m_runningTransformers.begin();
429 i != m_runningTransformers.end(); ++i) {
430
431 ModelTransformer *t = *i;
432
433 if (t->getInputModel() == m || t->getOutputModel() == m) {
434 affected.insert(t);
435 }
436 }
437
438 for (TransformerSet::iterator i = affected.begin();
439 i != affected.end(); ++i) {
440
441 ModelTransformer *t = *i;
442
443 t->abandon();
444
445 t->wait(); // this should eventually call back on
446 // transformerFinished, which will remove from
447 // m_runningTransformers and delete.
448 }
449 }
450