Mercurial > hg > svcore
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 |