Chris@49
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@52
|
4 Sonic Visualiser
|
Chris@52
|
5 An audio file viewer and annotation editor.
|
Chris@52
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@52
|
7 This file copyright 2006 Chris Cannam.
|
Chris@0
|
8
|
Chris@52
|
9 This program is free software; you can redistribute it and/or
|
Chris@52
|
10 modify it under the terms of the GNU General Public License as
|
Chris@52
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@52
|
12 License, or (at your option) any later version. See the file
|
Chris@52
|
13 COPYING included with this distribution for more information.
|
Chris@0
|
14 */
|
Chris@0
|
15
|
Chris@0
|
16 #include "TransformFactory.h"
|
Chris@0
|
17
|
Chris@0
|
18 #include "FeatureExtractionPluginTransform.h"
|
Chris@60
|
19 #include "RealTimePluginTransform.h"
|
Chris@0
|
20
|
Chris@0
|
21 #include "plugin/FeatureExtractionPluginFactory.h"
|
Chris@60
|
22 #include "plugin/RealTimePluginFactory.h"
|
Chris@66
|
23 #include "plugin/PluginXml.h"
|
Chris@0
|
24
|
Chris@56
|
25 #include "widgets/PluginParameterDialog.h"
|
Chris@56
|
26
|
Chris@64
|
27 #include "model/DenseTimeValueModel.h"
|
Chris@64
|
28
|
Chris@0
|
29 #include <iostream>
|
Chris@63
|
30 #include <set>
|
Chris@63
|
31
|
Chris@63
|
32 #include <QRegExp>
|
Chris@0
|
33
|
Chris@0
|
34 TransformFactory *
|
Chris@0
|
35 TransformFactory::m_instance = new TransformFactory;
|
Chris@0
|
36
|
Chris@0
|
37 TransformFactory *
|
Chris@0
|
38 TransformFactory::instance()
|
Chris@0
|
39 {
|
Chris@0
|
40 return m_instance;
|
Chris@0
|
41 }
|
Chris@0
|
42
|
Chris@0
|
43 TransformFactory::~TransformFactory()
|
Chris@0
|
44 {
|
Chris@0
|
45 }
|
Chris@0
|
46
|
Chris@0
|
47 TransformFactory::TransformList
|
Chris@0
|
48 TransformFactory::getAllTransforms()
|
Chris@0
|
49 {
|
Chris@16
|
50 if (m_transforms.empty()) populateTransforms();
|
Chris@16
|
51
|
Chris@0
|
52 TransformList list;
|
Chris@56
|
53 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
|
Chris@16
|
54 i != m_transforms.end(); ++i) {
|
Chris@56
|
55 list.push_back(i->second);
|
Chris@16
|
56 }
|
Chris@0
|
57
|
Chris@16
|
58 return list;
|
Chris@16
|
59 }
|
Chris@16
|
60
|
Chris@63
|
61 std::vector<QString>
|
Chris@63
|
62 TransformFactory::getAllTransformTypes()
|
Chris@63
|
63 {
|
Chris@63
|
64 if (m_transforms.empty()) populateTransforms();
|
Chris@63
|
65
|
Chris@63
|
66 std::set<QString> types;
|
Chris@63
|
67 for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
|
Chris@63
|
68 i != m_transforms.end(); ++i) {
|
Chris@63
|
69 types.insert(i->second.type);
|
Chris@63
|
70 }
|
Chris@63
|
71
|
Chris@63
|
72 std::vector<QString> rv;
|
Chris@63
|
73 for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
|
Chris@63
|
74 rv.push_back(*i);
|
Chris@63
|
75 }
|
Chris@63
|
76
|
Chris@63
|
77 return rv;
|
Chris@63
|
78 }
|
Chris@63
|
79
|
Chris@16
|
80 void
|
Chris@16
|
81 TransformFactory::populateTransforms()
|
Chris@16
|
82 {
|
Chris@60
|
83 TransformDescriptionMap transforms;
|
Chris@60
|
84
|
Chris@60
|
85 populateFeatureExtractionPlugins(transforms);
|
Chris@60
|
86 populateRealTimePlugins(transforms);
|
Chris@60
|
87
|
Chris@60
|
88 // disambiguate plugins with similar descriptions
|
Chris@60
|
89
|
Chris@60
|
90 std::map<QString, int> descriptions;
|
Chris@60
|
91
|
Chris@60
|
92 for (TransformDescriptionMap::iterator i = transforms.begin();
|
Chris@60
|
93 i != transforms.end(); ++i) {
|
Chris@60
|
94
|
Chris@60
|
95 TransformDesc desc = i->second;
|
Chris@60
|
96
|
Chris@60
|
97 ++descriptions[desc.description];
|
Chris@60
|
98 ++descriptions[QString("%1 [%2]").arg(desc.description).arg(desc.maker)];
|
Chris@60
|
99 }
|
Chris@60
|
100
|
Chris@60
|
101 std::map<QString, int> counts;
|
Chris@60
|
102 m_transforms.clear();
|
Chris@60
|
103
|
Chris@60
|
104 for (TransformDescriptionMap::iterator i = transforms.begin();
|
Chris@60
|
105 i != transforms.end(); ++i) {
|
Chris@60
|
106
|
Chris@60
|
107 TransformDesc desc = i->second;
|
Chris@60
|
108 QString name = desc.name;
|
Chris@60
|
109 QString description = desc.description;
|
Chris@60
|
110 QString maker = desc.maker;
|
Chris@60
|
111
|
Chris@60
|
112 if (descriptions[description] > 1) {
|
Chris@60
|
113 description = QString("%1 [%2]").arg(description).arg(maker);
|
Chris@60
|
114 if (descriptions[description] > 1) {
|
Chris@60
|
115 description = QString("%1 <%2>")
|
Chris@60
|
116 .arg(description).arg(++counts[description]);
|
Chris@60
|
117 }
|
Chris@60
|
118 }
|
Chris@60
|
119
|
Chris@60
|
120 desc.description = description;
|
Chris@60
|
121 m_transforms[name] = desc;
|
Chris@60
|
122 }
|
Chris@60
|
123 }
|
Chris@60
|
124
|
Chris@60
|
125 void
|
Chris@60
|
126 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
|
Chris@60
|
127 {
|
Chris@60
|
128 std::vector<QString> plugs =
|
Chris@0
|
129 FeatureExtractionPluginFactory::getAllPluginIdentifiers();
|
Chris@0
|
130
|
Chris@60
|
131 for (size_t i = 0; i < plugs.size(); ++i) {
|
Chris@47
|
132
|
Chris@60
|
133 QString pluginId = plugs[i];
|
Chris@0
|
134
|
Chris@0
|
135 FeatureExtractionPluginFactory *factory =
|
Chris@0
|
136 FeatureExtractionPluginFactory::instanceFor(pluginId);
|
Chris@0
|
137
|
Chris@20
|
138 if (!factory) {
|
Chris@20
|
139 std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
|
Chris@20
|
140 continue;
|
Chris@20
|
141 }
|
Chris@0
|
142
|
Chris@66
|
143 Vamp::Plugin *plugin =
|
Chris@20
|
144 factory->instantiatePlugin(pluginId, 48000);
|
Chris@0
|
145
|
Chris@20
|
146 if (!plugin) {
|
Chris@20
|
147 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
|
Chris@20
|
148 continue;
|
Chris@20
|
149 }
|
Chris@20
|
150
|
Chris@20
|
151 QString pluginDescription = plugin->getDescription().c_str();
|
Chris@66
|
152 Vamp::Plugin::OutputList outputs =
|
Chris@20
|
153 plugin->getOutputDescriptors();
|
Chris@0
|
154
|
Chris@20
|
155 for (size_t j = 0; j < outputs.size(); ++j) {
|
Chris@0
|
156
|
Chris@20
|
157 QString transformName = QString("%1:%2")
|
Chris@20
|
158 .arg(pluginId).arg(outputs[j].name.c_str());
|
Chris@20
|
159
|
Chris@20
|
160 QString userDescription;
|
Chris@60
|
161 QString friendlyName;
|
Chris@63
|
162 QString units = outputs[j].unit.c_str();
|
Chris@20
|
163
|
Chris@20
|
164 if (outputs.size() == 1) {
|
Chris@20
|
165 userDescription = pluginDescription;
|
Chris@60
|
166 friendlyName = pluginDescription;
|
Chris@20
|
167 } else {
|
Chris@20
|
168 userDescription = QString("%1: %2")
|
Chris@20
|
169 .arg(pluginDescription)
|
Chris@20
|
170 .arg(outputs[j].description.c_str());
|
Chris@60
|
171 friendlyName = outputs[j].description.c_str();
|
Chris@0
|
172 }
|
Chris@20
|
173
|
Chris@56
|
174 bool configurable = (!plugin->getPrograms().empty() ||
|
Chris@56
|
175 !plugin->getParameterDescriptors().empty());
|
Chris@56
|
176
|
Chris@60
|
177 transforms[transformName] =
|
Chris@63
|
178 TransformDesc(tr("Analysis Plugin"),
|
Chris@63
|
179 transformName,
|
Chris@56
|
180 userDescription,
|
Chris@60
|
181 friendlyName,
|
Chris@60
|
182 plugin->getMaker().c_str(),
|
Chris@63
|
183 units,
|
Chris@56
|
184 configurable);
|
Chris@0
|
185 }
|
Chris@0
|
186 }
|
Chris@60
|
187 }
|
Chris@47
|
188
|
Chris@60
|
189 void
|
Chris@60
|
190 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
|
Chris@60
|
191 {
|
Chris@60
|
192 std::vector<QString> plugs =
|
Chris@60
|
193 RealTimePluginFactory::getAllPluginIdentifiers();
|
Chris@47
|
194
|
Chris@63
|
195 QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
|
Chris@63
|
196
|
Chris@60
|
197 for (size_t i = 0; i < plugs.size(); ++i) {
|
Chris@60
|
198
|
Chris@60
|
199 QString pluginId = plugs[i];
|
Chris@47
|
200
|
Chris@60
|
201 RealTimePluginFactory *factory =
|
Chris@60
|
202 RealTimePluginFactory::instanceFor(pluginId);
|
Chris@47
|
203
|
Chris@60
|
204 if (!factory) {
|
Chris@60
|
205 std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
|
Chris@60
|
206 continue;
|
Chris@47
|
207 }
|
Chris@47
|
208
|
Chris@60
|
209 const RealTimePluginDescriptor *descriptor =
|
Chris@60
|
210 factory->getPluginDescriptor(pluginId);
|
Chris@60
|
211
|
Chris@60
|
212 if (!descriptor) {
|
Chris@60
|
213 std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
|
Chris@60
|
214 continue;
|
Chris@60
|
215 }
|
Chris@60
|
216
|
Chris@60
|
217 if (descriptor->controlOutputPortCount == 0 ||
|
Chris@60
|
218 descriptor->audioInputPortCount == 0) continue;
|
Chris@60
|
219
|
Chris@60
|
220 std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " output ports" << std::endl;
|
Chris@60
|
221
|
Chris@60
|
222 QString pluginDescription = descriptor->name.c_str();
|
Chris@60
|
223
|
Chris@60
|
224 for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
|
Chris@60
|
225
|
Chris@60
|
226 QString transformName = QString("%1:%2").arg(pluginId).arg(j);
|
Chris@60
|
227 QString userDescription;
|
Chris@63
|
228 QString units;
|
Chris@60
|
229
|
Chris@60
|
230 if (j < descriptor->controlOutputPortNames.size() &&
|
Chris@60
|
231 descriptor->controlOutputPortNames[j] != "") {
|
Chris@63
|
232
|
Chris@63
|
233 QString portName = descriptor->controlOutputPortNames[j].c_str();
|
Chris@63
|
234
|
Chris@60
|
235 userDescription = tr("%1: %2")
|
Chris@60
|
236 .arg(pluginDescription)
|
Chris@63
|
237 .arg(portName);
|
Chris@63
|
238
|
Chris@63
|
239 if (unitRE.indexIn(portName) >= 0) {
|
Chris@63
|
240 units = unitRE.cap(1);
|
Chris@63
|
241 }
|
Chris@63
|
242
|
Chris@60
|
243 } else if (descriptor->controlOutputPortCount > 1) {
|
Chris@63
|
244
|
Chris@60
|
245 userDescription = tr("%1: Output %2")
|
Chris@60
|
246 .arg(pluginDescription)
|
Chris@60
|
247 .arg(j + 1);
|
Chris@63
|
248
|
Chris@60
|
249 } else {
|
Chris@63
|
250
|
Chris@60
|
251 userDescription = pluginDescription;
|
Chris@60
|
252 }
|
Chris@60
|
253
|
Chris@63
|
254
|
Chris@60
|
255 bool configurable = (descriptor->parameterCount > 0);
|
Chris@60
|
256
|
Chris@60
|
257 transforms[transformName] =
|
Chris@63
|
258 TransformDesc(tr("Real-Time Plugin"),
|
Chris@63
|
259 transformName,
|
Chris@60
|
260 userDescription,
|
Chris@60
|
261 userDescription,
|
Chris@60
|
262 descriptor->maker.c_str(),
|
Chris@63
|
263 units,
|
Chris@60
|
264 configurable);
|
Chris@60
|
265 }
|
Chris@60
|
266 }
|
Chris@16
|
267 }
|
Chris@16
|
268
|
Chris@16
|
269 QString
|
Chris@16
|
270 TransformFactory::getTransformDescription(TransformName name)
|
Chris@16
|
271 {
|
Chris@16
|
272 if (m_transforms.find(name) != m_transforms.end()) {
|
Chris@56
|
273 return m_transforms[name].description;
|
Chris@16
|
274 } else return "";
|
Chris@0
|
275 }
|
Chris@0
|
276
|
Chris@18
|
277 QString
|
Chris@18
|
278 TransformFactory::getTransformFriendlyName(TransformName name)
|
Chris@18
|
279 {
|
Chris@60
|
280 if (m_transforms.find(name) != m_transforms.end()) {
|
Chris@60
|
281 return m_transforms[name].friendlyName;
|
Chris@60
|
282 } else return "";
|
Chris@18
|
283 }
|
Chris@18
|
284
|
Chris@63
|
285 QString
|
Chris@63
|
286 TransformFactory::getTransformUnits(TransformName name)
|
Chris@63
|
287 {
|
Chris@63
|
288 if (m_transforms.find(name) != m_transforms.end()) {
|
Chris@63
|
289 return m_transforms[name].units;
|
Chris@63
|
290 } else return "";
|
Chris@63
|
291 }
|
Chris@63
|
292
|
Chris@56
|
293 bool
|
Chris@57
|
294 TransformFactory::isTransformConfigurable(TransformName name)
|
Chris@57
|
295 {
|
Chris@57
|
296 if (m_transforms.find(name) != m_transforms.end()) {
|
Chris@57
|
297 return m_transforms[name].configurable;
|
Chris@57
|
298 } else return false;
|
Chris@57
|
299 }
|
Chris@57
|
300
|
Chris@57
|
301 bool
|
Chris@64
|
302 TransformFactory::getTransformChannelRange(TransformName name,
|
Chris@64
|
303 int &min, int &max)
|
Chris@64
|
304 {
|
Chris@64
|
305 QString id = name.section(':', 0, 2);
|
Chris@64
|
306
|
Chris@64
|
307 if (FeatureExtractionPluginFactory::instanceFor(id)) {
|
Chris@64
|
308
|
Chris@66
|
309 Vamp::Plugin *plugin =
|
Chris@64
|
310 FeatureExtractionPluginFactory::instanceFor(id)->
|
Chris@64
|
311 instantiatePlugin(id, 48000);
|
Chris@64
|
312 if (!plugin) return false;
|
Chris@64
|
313
|
Chris@64
|
314 min = plugin->getMinChannelCount();
|
Chris@64
|
315 max = plugin->getMaxChannelCount();
|
Chris@64
|
316 delete plugin;
|
Chris@64
|
317
|
Chris@64
|
318 return true;
|
Chris@64
|
319
|
Chris@64
|
320 } else if (RealTimePluginFactory::instanceFor(id)) {
|
Chris@64
|
321
|
Chris@64
|
322 const RealTimePluginDescriptor *descriptor =
|
Chris@64
|
323 RealTimePluginFactory::instanceFor(id)->
|
Chris@64
|
324 getPluginDescriptor(id);
|
Chris@64
|
325 if (!descriptor) return false;
|
Chris@64
|
326
|
Chris@64
|
327 min = descriptor->audioInputPortCount;
|
Chris@64
|
328 max = descriptor->audioInputPortCount;
|
Chris@64
|
329
|
Chris@64
|
330 return true;
|
Chris@64
|
331 }
|
Chris@64
|
332
|
Chris@64
|
333 return false;
|
Chris@64
|
334 }
|
Chris@64
|
335
|
Chris@64
|
336 bool
|
Chris@56
|
337 TransformFactory::getConfigurationForTransform(TransformName name,
|
Chris@56
|
338 Model *inputModel,
|
Chris@64
|
339 int &channel,
|
Chris@56
|
340 QString &configurationXml)
|
Chris@0
|
341 {
|
Chris@56
|
342 QString id = name.section(':', 0, 2);
|
Chris@56
|
343 QString output = name.section(':', 3);
|
Chris@56
|
344
|
Chris@56
|
345 bool ok = false;
|
Chris@56
|
346 configurationXml = m_lastConfigurations[name];
|
Chris@56
|
347
|
Chris@56
|
348 std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
|
Chris@56
|
349
|
Chris@66
|
350 Vamp::PluginBase *plugin = 0;
|
Chris@60
|
351
|
Chris@56
|
352 if (FeatureExtractionPluginFactory::instanceFor(id)) {
|
Chris@60
|
353
|
Chris@60
|
354 plugin = FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
|
Chris@56
|
355 (id, inputModel->getSampleRate());
|
Chris@60
|
356
|
Chris@60
|
357 } else if (RealTimePluginFactory::instanceFor(id)) {
|
Chris@60
|
358
|
Chris@60
|
359 plugin = RealTimePluginFactory::instanceFor(id)->instantiatePlugin
|
Chris@60
|
360 (id, 0, 0, inputModel->getSampleRate(), 1024, 1);
|
Chris@60
|
361 }
|
Chris@60
|
362
|
Chris@60
|
363 if (plugin) {
|
Chris@60
|
364 if (configurationXml != "") {
|
Chris@66
|
365 PluginXml(plugin).setParametersFromXml(configurationXml);
|
Chris@56
|
366 }
|
Chris@64
|
367
|
Chris@64
|
368 int sourceChannels = 1;
|
Chris@64
|
369 if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
|
Chris@64
|
370 sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
|
Chris@64
|
371 ->getChannelCount();
|
Chris@64
|
372 }
|
Chris@64
|
373
|
Chris@64
|
374 int minChannels = 1, maxChannels = sourceChannels;
|
Chris@64
|
375 getTransformChannelRange(name, minChannels, maxChannels);
|
Chris@64
|
376
|
Chris@64
|
377 int targetChannels = sourceChannels;
|
Chris@64
|
378 if (sourceChannels < minChannels) targetChannels = minChannels;
|
Chris@64
|
379 if (sourceChannels > maxChannels) targetChannels = maxChannels;
|
Chris@64
|
380
|
Chris@64
|
381 int defaultChannel = channel;
|
Chris@64
|
382
|
Chris@64
|
383 PluginParameterDialog *dialog = new PluginParameterDialog(plugin,
|
Chris@64
|
384 sourceChannels,
|
Chris@64
|
385 targetChannels,
|
Chris@64
|
386 defaultChannel);
|
Chris@60
|
387 if (dialog->exec() == QDialog::Accepted) {
|
Chris@60
|
388 ok = true;
|
Chris@60
|
389 }
|
Chris@66
|
390 configurationXml = PluginXml(plugin).toXmlString();
|
Chris@64
|
391 channel = dialog->getChannel();
|
Chris@60
|
392 delete dialog;
|
Chris@60
|
393 delete plugin;
|
Chris@56
|
394 }
|
Chris@56
|
395
|
Chris@56
|
396 if (ok) m_lastConfigurations[name] = configurationXml;
|
Chris@56
|
397
|
Chris@56
|
398 return ok;
|
Chris@0
|
399 }
|
Chris@0
|
400
|
Chris@0
|
401 Transform *
|
Chris@0
|
402 TransformFactory::createTransform(TransformName name, Model *inputModel,
|
Chris@64
|
403 int channel, QString configurationXml, bool start)
|
Chris@0
|
404 {
|
Chris@0
|
405 Transform *transform = 0;
|
Chris@0
|
406
|
Chris@64
|
407 //!!! use channel
|
Chris@64
|
408
|
Chris@56
|
409 QString id = name.section(':', 0, 2);
|
Chris@56
|
410 QString output = name.section(':', 3);
|
Chris@56
|
411
|
Chris@56
|
412 if (FeatureExtractionPluginFactory::instanceFor(id)) {
|
Chris@56
|
413 transform = new FeatureExtractionPluginTransform(inputModel,
|
Chris@56
|
414 id,
|
Chris@64
|
415 channel,
|
Chris@56
|
416 configurationXml,
|
Chris@56
|
417 output);
|
Chris@60
|
418 } else if (RealTimePluginFactory::instanceFor(id)) {
|
Chris@60
|
419 transform = new RealTimePluginTransform(inputModel,
|
Chris@60
|
420 id,
|
Chris@64
|
421 channel,
|
Chris@60
|
422 configurationXml,
|
Chris@63
|
423 getTransformUnits(name),
|
Chris@60
|
424 output.toInt());
|
Chris@0
|
425 } else {
|
Chris@56
|
426 std::cerr << "TransformFactory::createTransform: Unknown transform "
|
Chris@56
|
427 << name.toStdString() << std::endl;
|
Chris@0
|
428 }
|
Chris@0
|
429
|
Chris@0
|
430 if (start && transform) transform->start();
|
Chris@16
|
431 transform->setObjectName(name);
|
Chris@0
|
432 return transform;
|
Chris@0
|
433 }
|
Chris@0
|
434
|
Chris@0
|
435 Model *
|
Chris@56
|
436 TransformFactory::transform(TransformName name, Model *inputModel,
|
Chris@64
|
437 int channel, QString configurationXml)
|
Chris@0
|
438 {
|
Chris@64
|
439 Transform *t = createTransform(name, inputModel, channel,
|
Chris@64
|
440 configurationXml, false);
|
Chris@0
|
441
|
Chris@0
|
442 if (!t) return 0;
|
Chris@0
|
443
|
Chris@0
|
444 connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
|
Chris@0
|
445
|
Chris@0
|
446 t->start();
|
Chris@0
|
447 return t->detachOutputModel();
|
Chris@0
|
448 }
|
Chris@0
|
449
|
Chris@0
|
450 void
|
Chris@0
|
451 TransformFactory::transformFinished()
|
Chris@0
|
452 {
|
Chris@0
|
453 QObject *s = sender();
|
Chris@0
|
454 Transform *transform = dynamic_cast<Transform *>(s);
|
Chris@0
|
455
|
Chris@0
|
456 if (!transform) {
|
Chris@0
|
457 std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
|
Chris@0
|
458 return;
|
Chris@0
|
459 }
|
Chris@0
|
460
|
Chris@0
|
461 transform->wait(); // unnecessary but reassuring
|
Chris@0
|
462 delete transform;
|
Chris@0
|
463 }
|
Chris@0
|
464
|