Mercurial > hg > svapp
comparison framework/Align.cpp @ 673:d62fd61082a1
Merge from branch tuning-difference
author | Chris Cannam |
---|---|
date | Fri, 17 May 2019 09:46:22 +0100 |
parents | ae7584dbd668 |
children | b375fdbb74bc |
comparison
equal
deleted
inserted
replaced
665:e19c609a7bec | 673:d62fd61082a1 |
---|---|
2 | 2 |
3 /* | 3 /* |
4 Sonic Visualiser | 4 Sonic Visualiser |
5 An audio file viewer and annotation editor. | 5 An audio file viewer and annotation editor. |
6 Centre for Digital Music, Queen Mary, University of London. | 6 Centre for Digital Music, Queen Mary, University of London. |
7 This file copyright 2006 Chris Cannam and QMUL. | |
8 | 7 |
9 This program is free software; you can redistribute it and/or | 8 This program is free software; you can redistribute it and/or |
10 modify it under the terms of the GNU General Public License as | 9 modify it under the terms of the GNU General Public License as |
11 published by the Free Software Foundation; either version 2 of the | 10 published by the Free Software Foundation; either version 2 of the |
12 License, or (at your option) any later version. See the file | 11 License, or (at your option) any later version. See the file |
32 #include <QProcess> | 31 #include <QProcess> |
33 #include <QSettings> | 32 #include <QSettings> |
34 #include <QApplication> | 33 #include <QApplication> |
35 | 34 |
36 bool | 35 bool |
37 Align::alignModel(Document *doc, Model *ref, Model *other) | 36 Align::alignModel(Document *doc, Model *ref, Model *other, QString &error) |
38 { | 37 { |
39 QSettings settings; | 38 QSettings settings; |
40 settings.beginGroup("Preferences"); | 39 settings.beginGroup("Preferences"); |
41 bool useProgram = settings.value("use-external-alignment", false).toBool(); | 40 bool useProgram = settings.value("use-external-alignment", false).toBool(); |
42 QString program = settings.value("external-alignment-program", "").toString(); | 41 QString program = settings.value("external-alignment-program", "").toString(); |
43 settings.endGroup(); | 42 settings.endGroup(); |
44 | 43 |
45 if (useProgram && (program != "")) { | 44 if (useProgram && (program != "")) { |
46 return alignModelViaProgram(doc, ref, other, program); | 45 return alignModelViaProgram(doc, ref, other, program, error); |
47 } else { | 46 } else { |
48 return alignModelViaTransform(doc, ref, other); | 47 return alignModelViaTransform(doc, ref, other, error); |
49 } | 48 } |
50 } | 49 } |
51 | 50 |
52 QString | 51 QString |
53 Align::getAlignmentTransformName() | 52 Align::getAlignmentTransformName() |
59 "vamp:match-vamp-plugin:match:path").toString(); | 58 "vamp:match-vamp-plugin:match:path").toString(); |
60 settings.endGroup(); | 59 settings.endGroup(); |
61 return id; | 60 return id; |
62 } | 61 } |
63 | 62 |
63 QString | |
64 Align::getTuningDifferenceTransformName() | |
65 { | |
66 QSettings settings; | |
67 settings.beginGroup("Alignment"); | |
68 bool performPitchCompensation = | |
69 settings.value("align-pitch-aware", false).toBool(); | |
70 QString id = ""; | |
71 if (performPitchCompensation) { | |
72 id = settings.value | |
73 ("tuning-difference-transform-id", | |
74 "vamp:tuning-difference:tuning-difference:tuningfreq") | |
75 .toString(); | |
76 } | |
77 settings.endGroup(); | |
78 return id; | |
79 } | |
80 | |
64 bool | 81 bool |
65 Align::canAlign() | 82 Align::canAlign() |
66 { | 83 { |
84 TransformFactory *factory = TransformFactory::getInstance(); | |
67 TransformId id = getAlignmentTransformName(); | 85 TransformId id = getAlignmentTransformName(); |
68 TransformFactory *factory = TransformFactory::getInstance(); | 86 TransformId tdId = getTuningDifferenceTransformName(); |
69 return factory->haveTransform(id); | 87 return factory->haveTransform(id) && |
88 (tdId == "" || factory->haveTransform(tdId)); | |
70 } | 89 } |
71 | 90 |
72 bool | 91 bool |
73 Align::alignModelViaTransform(Document *doc, Model *ref, Model *other) | 92 Align::alignModelViaTransform(Document *doc, Model *ref, Model *other, |
74 { | 93 QString &error) |
94 { | |
95 QMutexLocker locker (&m_mutex); | |
96 | |
75 RangeSummarisableTimeValueModel *reference = qobject_cast | 97 RangeSummarisableTimeValueModel *reference = qobject_cast |
76 <RangeSummarisableTimeValueModel *>(ref); | 98 <RangeSummarisableTimeValueModel *>(ref); |
77 | 99 |
78 RangeSummarisableTimeValueModel *rm = qobject_cast | 100 RangeSummarisableTimeValueModel *rm = qobject_cast |
79 <RangeSummarisableTimeValueModel *>(other); | 101 <RangeSummarisableTimeValueModel *>(other); |
80 | 102 |
81 if (!reference || !rm) return false; // but this should have been tested already | 103 if (!reference || !rm) return false; // but this should have been tested already |
82 | 104 |
83 // This involves creating three new models: | 105 // This involves creating either three or four new models: |
84 | 106 // |
85 // 1. an AggregateWaveModel to provide the mixdowns of the main | 107 // 1. an AggregateWaveModel to provide the mixdowns of the main |
86 // model and the new model in its two channels, as input to the | 108 // model and the new model in its two channels, as input to the |
87 // MATCH plugin | 109 // MATCH plugin |
88 | 110 // |
89 // 2. a SparseTimeValueModel, which is the model automatically | 111 // 2a. a SparseTimeValueModel which will be automatically created |
90 // created by FeatureExtractionPluginTransformer when running the | 112 // by FeatureExtractionModelTransformer when running the |
91 // MATCH plugin (thus containing the alignment path) | 113 // TuningDifference plugin to receive the relative tuning of the |
92 | 114 // second model (if pitch-aware alignment is enabled in the |
115 // preferences) | |
116 // | |
117 // 2b. a SparseTimeValueModel which will be automatically created | |
118 // by FeatureExtractionPluginTransformer when running the MATCH | |
119 // plugin to perform alignment (so containing the alignment path) | |
120 // | |
93 // 3. an AlignmentModel, which stores the path model and carries | 121 // 3. an AlignmentModel, which stores the path model and carries |
94 // out alignment lookups on it. | 122 // out alignment lookups on it. |
95 | 123 // |
96 // The first two of these are provided as arguments to the | 124 // The AggregateWaveModel [1] is registered with the document, |
97 // constructor for the third, which takes responsibility for | 125 // which deletes it when it is invalidated (when one of its |
98 // deleting them. The AlignmentModel, meanwhile, is passed to the | 126 // components is deleted). The SparseTimeValueModel [2a] is reused |
99 // new model we are aligning, which also takes responsibility for | 127 // by us when starting the alignment process proper, and is then |
100 // it. We should not have to delete any of these new models here. | 128 // deleted by us. The SparseTimeValueModel [2b] is passed to the |
129 // AlignmentModel, which takes ownership of it. The AlignmentModel | |
130 // is attached to the new model we are aligning, which also takes | |
131 // ownership of it. The only one of these models that we need to | |
132 // delete here is the SparseTimeValueModel [2a]. | |
133 // | |
134 // (We also create a sneaky additional SparseTimeValueModel | |
135 // temporarily so we can attach completion information to it - | |
136 // this is quite unnecessary from the perspective of simply | |
137 // producing the results.) | |
101 | 138 |
102 AggregateWaveModel::ChannelSpecList components; | 139 AggregateWaveModel::ChannelSpecList components; |
103 | 140 |
104 components.push_back(AggregateWaveModel::ModelChannelSpec | 141 components.push_back(AggregateWaveModel::ModelChannelSpec |
105 (reference, -1)); | 142 (reference, -1)); |
107 components.push_back(AggregateWaveModel::ModelChannelSpec | 144 components.push_back(AggregateWaveModel::ModelChannelSpec |
108 (rm, -1)); | 145 (rm, -1)); |
109 | 146 |
110 AggregateWaveModel *aggregateModel = new AggregateWaveModel(components); | 147 AggregateWaveModel *aggregateModel = new AggregateWaveModel(components); |
111 doc->addAggregateModel(aggregateModel); | 148 doc->addAggregateModel(aggregateModel); |
112 | 149 |
113 ModelTransformer::Input aggregate(aggregateModel); | 150 AlignmentModel *alignmentModel = |
114 | 151 new AlignmentModel(reference, other, nullptr); |
152 | |
153 TransformId tdId = getTuningDifferenceTransformName(); | |
154 | |
155 if (tdId == "") { | |
156 | |
157 if (beginTransformDrivenAlignment(aggregateModel, alignmentModel)) { | |
158 rm->setAlignment(alignmentModel); | |
159 } else { | |
160 error = alignmentModel->getError(); | |
161 delete alignmentModel; | |
162 return false; | |
163 } | |
164 | |
165 } else { | |
166 | |
167 // Have a tuning-difference transform id, so run it | |
168 // asynchronously first | |
169 | |
170 TransformFactory *tf = TransformFactory::getInstance(); | |
171 | |
172 Transform transform = tf->getDefaultTransformFor | |
173 (tdId, aggregateModel->getSampleRate()); | |
174 | |
175 transform.setParameter("maxduration", 50); | |
176 transform.setParameter("maxrange", 5); | |
177 | |
178 SVDEBUG << "Align::alignModel: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; | |
179 | |
180 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); | |
181 | |
182 QString message; | |
183 Model *transformOutput = mtf->transform(transform, aggregateModel, message); | |
184 | |
185 SparseTimeValueModel *tdout = dynamic_cast<SparseTimeValueModel *> | |
186 (transformOutput); | |
187 | |
188 if (!tdout) { | |
189 SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; | |
190 delete tdout; | |
191 error = message; | |
192 return false; | |
193 } | |
194 | |
195 rm->setAlignment(alignmentModel); | |
196 | |
197 connect(tdout, SIGNAL(completionChanged()), | |
198 this, SLOT(tuningDifferenceCompletionChanged())); | |
199 | |
200 TuningDiffRec rec; | |
201 rec.input = aggregateModel; | |
202 rec.alignment = alignmentModel; | |
203 | |
204 // This model exists only so that the AlignmentModel can get a | |
205 // completion value from somewhere while the tuning difference | |
206 // calculation is going on | |
207 rec.preparatory = new SparseTimeValueModel | |
208 (aggregateModel->getSampleRate(), 1);; | |
209 rec.preparatory->setCompletion(0); | |
210 alignmentModel->setPathFrom(rec.preparatory); | |
211 | |
212 m_pendingTuningDiffs[tdout] = rec; | |
213 } | |
214 | |
215 return true; | |
216 } | |
217 | |
218 void | |
219 Align::tuningDifferenceCompletionChanged() | |
220 { | |
221 QMutexLocker locker (&m_mutex); | |
222 | |
223 SparseTimeValueModel *td = qobject_cast<SparseTimeValueModel *>(sender()); | |
224 if (!td) return; | |
225 | |
226 if (m_pendingTuningDiffs.find(td) == m_pendingTuningDiffs.end()) { | |
227 SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model " | |
228 << td << " not found in pending tuning diff map!" << endl; | |
229 return; | |
230 } | |
231 | |
232 TuningDiffRec rec = m_pendingTuningDiffs[td]; | |
233 | |
234 int completion = 0; | |
235 bool done = td->isReady(&completion); | |
236 | |
237 SVCERR << "Align::tuningDifferenceCompletionChanged: done = " << done << ", completion = " << completion << endl; | |
238 | |
239 if (!done) { | |
240 // This will be the completion the alignment model reports, | |
241 // before the alignment actually begins. It goes up from 0 to | |
242 // 99 (not 100!) and then back to 0 again when we start | |
243 // calculating the actual path in the following phase | |
244 int clamped = (completion == 100 ? 99 : completion); | |
245 SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl; | |
246 rec.preparatory->setCompletion(clamped); | |
247 return; | |
248 } | |
249 | |
250 float tuningFrequency = 440.f; | |
251 | |
252 if (!td->isEmpty()) { | |
253 tuningFrequency = td->getAllEvents()[0].getValue(); | |
254 SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl; | |
255 } else { | |
256 SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; | |
257 } | |
258 | |
259 m_pendingTuningDiffs.erase(td); | |
260 td->aboutToDelete(); | |
261 delete td; | |
262 | |
263 rec.alignment->setPathFrom(nullptr); | |
264 | |
265 beginTransformDrivenAlignment | |
266 (rec.input, rec.alignment, tuningFrequency); | |
267 } | |
268 | |
269 bool | |
270 Align::beginTransformDrivenAlignment(AggregateWaveModel *aggregateModel, | |
271 AlignmentModel *alignmentModel, | |
272 float tuningFrequency) | |
273 { | |
115 TransformId id = getAlignmentTransformName(); | 274 TransformId id = getAlignmentTransformName(); |
116 | 275 |
117 TransformFactory *tf = TransformFactory::getInstance(); | 276 TransformFactory *tf = TransformFactory::getInstance(); |
118 | 277 |
119 Transform transform = tf->getDefaultTransformFor | 278 Transform transform = tf->getDefaultTransformFor |
121 | 280 |
122 transform.setStepSize(transform.getBlockSize()/2); | 281 transform.setStepSize(transform.getBlockSize()/2); |
123 transform.setParameter("serialise", 1); | 282 transform.setParameter("serialise", 1); |
124 transform.setParameter("smooth", 0); | 283 transform.setParameter("smooth", 0); |
125 | 284 |
285 if (tuningFrequency != 0.f) { | |
286 transform.setParameter("freq2", tuningFrequency); | |
287 } | |
288 | |
126 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; | 289 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; |
127 | 290 |
128 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); | 291 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); |
129 | 292 |
130 QString message; | 293 QString message; |
131 Model *transformOutput = mtf->transform(transform, aggregate, message); | 294 Model *transformOutput = mtf->transform |
295 (transform, aggregateModel, message); | |
132 | 296 |
133 if (!transformOutput) { | 297 if (!transformOutput) { |
134 transform.setStepSize(0); | 298 transform.setStepSize(0); |
135 transformOutput = mtf->transform(transform, aggregate, message); | 299 transformOutput = mtf->transform |
300 (transform, aggregateModel, message); | |
136 } | 301 } |
137 | 302 |
138 SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> | 303 SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> |
139 (transformOutput); | 304 (transformOutput); |
140 | 305 |
306 //!!! callers will need to be updated to get error from | |
307 //!!! alignment model after initial call | |
308 | |
141 if (!path) { | 309 if (!path) { |
142 cerr << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; | 310 SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; |
143 delete transformOutput; | 311 delete transformOutput; |
144 delete aggregateModel; | 312 alignmentModel->setError(message); |
145 m_error = message; | |
146 return false; | 313 return false; |
147 } | 314 } |
148 | 315 |
149 path->setCompletion(0); | 316 path->setCompletion(0); |
150 | 317 alignmentModel->setPathFrom(path); |
151 AlignmentModel *alignmentModel = new AlignmentModel | |
152 (reference, other, path); | |
153 | 318 |
154 connect(alignmentModel, SIGNAL(completionChanged()), | 319 connect(alignmentModel, SIGNAL(completionChanged()), |
155 this, SLOT(alignmentCompletionChanged())); | 320 this, SLOT(alignmentCompletionChanged())); |
156 | |
157 rm->setAlignment(alignmentModel); | |
158 | 321 |
159 return true; | 322 return true; |
160 } | 323 } |
161 | 324 |
162 void | 325 void |
163 Align::alignmentCompletionChanged() | 326 Align::alignmentCompletionChanged() |
164 { | 327 { |
328 QMutexLocker locker (&m_mutex); | |
329 | |
165 AlignmentModel *am = qobject_cast<AlignmentModel *>(sender()); | 330 AlignmentModel *am = qobject_cast<AlignmentModel *>(sender()); |
166 if (!am) return; | 331 if (!am) return; |
167 if (am->isReady()) { | 332 if (am->isReady()) { |
168 disconnect(am, SIGNAL(completionChanged()), | 333 disconnect(am, SIGNAL(completionChanged()), |
169 this, SLOT(alignmentCompletionChanged())); | 334 this, SLOT(alignmentCompletionChanged())); |
170 emit alignmentComplete(am); | 335 emit alignmentComplete(am); |
171 } | 336 } |
172 } | 337 } |
173 | 338 |
174 bool | 339 bool |
175 Align::alignModelViaProgram(Document *, Model *ref, Model *other, QString program) | 340 Align::alignModelViaProgram(Document *, Model *ref, Model *other, |
176 { | 341 QString program, QString &error) |
342 { | |
343 QMutexLocker locker (&m_mutex); | |
344 | |
177 WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref); | 345 WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref); |
178 WaveFileModel *rm = qobject_cast<WaveFileModel *>(other); | 346 WaveFileModel *rm = qobject_cast<WaveFileModel *>(other); |
179 | 347 |
180 if (!reference || !rm) { | 348 if (!reference || !rm) { |
181 return false; // but this should have been tested already | 349 return false; // but this should have been tested already |
190 // the path in CSV form through stdout. | 358 // the path in CSV form through stdout. |
191 | 359 |
192 ReadOnlyWaveFileModel *roref = qobject_cast<ReadOnlyWaveFileModel *>(reference); | 360 ReadOnlyWaveFileModel *roref = qobject_cast<ReadOnlyWaveFileModel *>(reference); |
193 ReadOnlyWaveFileModel *rorm = qobject_cast<ReadOnlyWaveFileModel *>(rm); | 361 ReadOnlyWaveFileModel *rorm = qobject_cast<ReadOnlyWaveFileModel *>(rm); |
194 if (!roref || !rorm) { | 362 if (!roref || !rorm) { |
195 cerr << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; | 363 SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; |
196 return false; | 364 return false; |
197 } | 365 } |
198 | 366 |
199 QString refPath = roref->getLocalFilename(); | 367 QString refPath = roref->getLocalFilename(); |
200 QString otherPath = rorm->getLocalFilename(); | 368 QString otherPath = rorm->getLocalFilename(); |
201 | 369 |
202 if (refPath == "" || otherPath == "") { | 370 if (refPath == "" || otherPath == "") { |
203 m_error = "Failed to find local filepath for wave-file model"; | 371 error = "Failed to find local filepath for wave-file model"; |
204 return false; | 372 return false; |
205 } | 373 } |
206 | 374 |
207 m_error = ""; | |
208 | |
209 AlignmentModel *alignmentModel = | 375 AlignmentModel *alignmentModel = |
210 new AlignmentModel(reference, other, nullptr); | 376 new AlignmentModel(reference, other, nullptr); |
211 rm->setAlignment(alignmentModel); | 377 rm->setAlignment(alignmentModel); |
212 | 378 |
213 QProcess *process = new QProcess; | 379 QProcess *process = new QProcess; |
215 args << refPath << otherPath; | 381 args << refPath << otherPath; |
216 | 382 |
217 connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), | 383 connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), |
218 this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus))); | 384 this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus))); |
219 | 385 |
220 m_processModels[process] = alignmentModel; | 386 m_pendingProcesses[process] = alignmentModel; |
221 process->start(program, args); | 387 process->start(program, args); |
222 | 388 |
223 bool success = process->waitForStarted(); | 389 bool success = process->waitForStarted(); |
224 | 390 |
225 if (!success) { | 391 if (!success) { |
226 cerr << "ERROR: Align::alignModelViaProgram: Program did not start" | 392 SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start" |
227 << endl; | 393 << endl; |
228 m_error = "Alignment program could not be started"; | 394 error = "Alignment program could not be started"; |
229 m_processModels.erase(process); | 395 m_pendingProcesses.erase(process); |
230 rm->setAlignment(nullptr); // deletes alignmentModel as well | 396 rm->setAlignment(nullptr); // deletes alignmentModel as well |
231 delete process; | 397 delete process; |
232 } | 398 } |
233 | 399 |
234 return success; | 400 return success; |
235 } | 401 } |
236 | 402 |
237 void | 403 void |
238 Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status) | 404 Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status) |
239 { | 405 { |
240 cerr << "Align::alignmentProgramFinished" << endl; | 406 QMutexLocker locker (&m_mutex); |
407 | |
408 SVCERR << "Align::alignmentProgramFinished" << endl; | |
241 | 409 |
242 QProcess *process = qobject_cast<QProcess *>(sender()); | 410 QProcess *process = qobject_cast<QProcess *>(sender()); |
243 | 411 |
244 if (m_processModels.find(process) == m_processModels.end()) { | 412 if (m_pendingProcesses.find(process) == m_pendingProcesses.end()) { |
245 cerr << "ERROR: Align::alignmentProgramFinished: Process " << process | 413 SVCERR << "ERROR: Align::alignmentProgramFinished: Process " << process |
246 << " not found in process model map!" << endl; | 414 << " not found in process model map!" << endl; |
247 return; | 415 return; |
248 } | 416 } |
249 | 417 |
250 AlignmentModel *alignmentModel = m_processModels[process]; | 418 AlignmentModel *alignmentModel = m_pendingProcesses[process]; |
251 | 419 |
252 if (exitCode == 0 && status == 0) { | 420 if (exitCode == 0 && status == 0) { |
253 | 421 |
254 CSVFormat format; | 422 CSVFormat format; |
255 format.setModelType(CSVFormat::TwoDimensionalModel); | 423 format.setModelType(CSVFormat::TwoDimensionalModel); |
267 format.setAllowQuoting(false); | 435 format.setAllowQuoting(false); |
268 format.setSeparator(','); | 436 format.setSeparator(','); |
269 | 437 |
270 CSVFileReader reader(process, format, alignmentModel->getSampleRate()); | 438 CSVFileReader reader(process, format, alignmentModel->getSampleRate()); |
271 if (!reader.isOK()) { | 439 if (!reader.isOK()) { |
272 cerr << "ERROR: Align::alignmentProgramFinished: Failed to parse output" | 440 SVCERR << "ERROR: Align::alignmentProgramFinished: Failed to parse output" |
273 << endl; | 441 << endl; |
274 m_error = QString("Failed to parse output of program: %1") | 442 alignmentModel->setError |
275 .arg(reader.getError()); | 443 (QString("Failed to parse output of program: %1") |
444 .arg(reader.getError())); | |
276 goto done; | 445 goto done; |
277 } | 446 } |
278 | 447 |
279 Model *csvOutput = reader.load(); | 448 Model *csvOutput = reader.load(); |
280 | 449 |
281 SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput); | 450 SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput); |
282 if (!path) { | 451 if (!path) { |
283 cerr << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" | 452 SVCERR << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" |
284 << endl; | 453 << endl; |
285 m_error = QString("Output of program did not produce sparse time-value model"); | 454 alignmentModel->setError |
455 ("Output of program did not produce sparse time-value model"); | |
286 goto done; | 456 goto done; |
287 } | 457 } |
288 | 458 |
289 if (path->getPoints().empty()) { | 459 if (path->isEmpty()) { |
290 cerr << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" | 460 SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" |
291 << endl; | 461 << endl; |
292 m_error = QString("Output of alignment program contained no mappings"); | 462 alignmentModel->setError |
463 ("Output of alignment program contained no mappings"); | |
293 goto done; | 464 goto done; |
294 } | 465 } |
295 | 466 |
296 cerr << "Align::alignmentProgramFinished: Setting alignment path (" | 467 SVCERR << "Align::alignmentProgramFinished: Setting alignment path (" |
297 << path->getPoints().size() << " point(s))" << endl; | 468 << path->getEventCount() << " point(s))" << endl; |
298 | 469 |
299 alignmentModel->setPathFrom(path); | 470 alignmentModel->setPathFrom(path); |
300 | 471 |
301 emit alignmentComplete(alignmentModel); | 472 emit alignmentComplete(alignmentModel); |
302 | 473 |
303 } else { | 474 } else { |
304 cerr << "ERROR: Align::alignmentProgramFinished: Aligner program " | 475 SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program " |
305 << "failed: exit code " << exitCode << ", status " << status | 476 << "failed: exit code " << exitCode << ", status " << status |
306 << endl; | 477 << endl; |
307 m_error = "Aligner process returned non-zero exit status"; | 478 alignmentModel->setError |
479 ("Aligner process returned non-zero exit status"); | |
308 } | 480 } |
309 | 481 |
310 done: | 482 done: |
311 m_processModels.erase(process); | 483 m_pendingProcesses.erase(process); |
312 delete process; | 484 delete process; |
313 } | 485 } |
314 | 486 |