Chris@420
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@420
|
2
|
Chris@420
|
3 /*
|
Chris@420
|
4 Sonic Visualiser
|
Chris@420
|
5 An audio file viewer and annotation editor.
|
Chris@420
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@420
|
7
|
Chris@420
|
8 This program is free software; you can redistribute it and/or
|
Chris@420
|
9 modify it under the terms of the GNU General Public License as
|
Chris@420
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@420
|
11 License, or (at your option) any later version. See the file
|
Chris@420
|
12 COPYING included with this distribution for more information.
|
Chris@420
|
13 */
|
Chris@420
|
14
|
Chris@420
|
15 #include "Align.h"
|
Chris@665
|
16 #include "Document.h"
|
Chris@420
|
17
|
Chris@420
|
18 #include "data/model/WaveFileModel.h"
|
Chris@515
|
19 #include "data/model/ReadOnlyWaveFileModel.h"
|
Chris@420
|
20 #include "data/model/AggregateWaveModel.h"
|
Chris@420
|
21 #include "data/model/RangeSummarisableTimeValueModel.h"
|
Chris@420
|
22 #include "data/model/SparseTimeValueModel.h"
|
Chris@420
|
23 #include "data/model/AlignmentModel.h"
|
Chris@420
|
24
|
Chris@420
|
25 #include "data/fileio/CSVFileReader.h"
|
Chris@420
|
26
|
Chris@420
|
27 #include "transform/TransformFactory.h"
|
Chris@420
|
28 #include "transform/ModelTransformerFactory.h"
|
Chris@420
|
29 #include "transform/FeatureExtractionModelTransformer.h"
|
Chris@420
|
30
|
Chris@420
|
31 #include <QProcess>
|
Chris@422
|
32 #include <QSettings>
|
Chris@430
|
33 #include <QApplication>
|
Chris@422
|
34
|
Chris@422
|
35 bool
|
Chris@670
|
36 Align::alignModel(Document *doc, Model *ref, Model *other, QString &error)
|
Chris@422
|
37 {
|
Chris@422
|
38 QSettings settings;
|
Chris@422
|
39 settings.beginGroup("Preferences");
|
Chris@422
|
40 bool useProgram = settings.value("use-external-alignment", false).toBool();
|
Chris@422
|
41 QString program = settings.value("external-alignment-program", "").toString();
|
Chris@422
|
42 settings.endGroup();
|
Chris@422
|
43
|
Chris@422
|
44 if (useProgram && (program != "")) {
|
Chris@670
|
45 return alignModelViaProgram(doc, ref, other, program, error);
|
Chris@422
|
46 } else {
|
Chris@670
|
47 return alignModelViaTransform(doc, ref, other, error);
|
Chris@422
|
48 }
|
Chris@422
|
49 }
|
Chris@420
|
50
|
Chris@428
|
51 QString
|
Chris@428
|
52 Align::getAlignmentTransformName()
|
Chris@428
|
53 {
|
Chris@428
|
54 QSettings settings;
|
Chris@428
|
55 settings.beginGroup("Alignment");
|
Chris@428
|
56 TransformId id =
|
Chris@428
|
57 settings.value("transform-id",
|
Chris@428
|
58 "vamp:match-vamp-plugin:match:path").toString();
|
Chris@428
|
59 settings.endGroup();
|
Chris@428
|
60 return id;
|
Chris@428
|
61 }
|
Chris@428
|
62
|
Chris@670
|
63 QString
|
Chris@670
|
64 Align::getTuningDifferenceTransformName()
|
Chris@670
|
65 {
|
Chris@670
|
66 QSettings settings;
|
Chris@670
|
67 settings.beginGroup("Alignment");
|
Chris@670
|
68 bool performPitchCompensation =
|
Chris@670
|
69 settings.value("align-pitch-aware", false).toBool();
|
Chris@670
|
70 QString id = "";
|
Chris@670
|
71 //!!! if (performPitchCompensation) {
|
Chris@670
|
72 id = settings.value
|
Chris@670
|
73 ("tuning-difference-transform-id",
|
Chris@670
|
74 "vamp:tuning-difference:tuning-difference:tuningfreq")
|
Chris@670
|
75 .toString();
|
Chris@670
|
76 // }
|
Chris@670
|
77 settings.endGroup();
|
Chris@670
|
78 return id;
|
Chris@670
|
79 }
|
Chris@670
|
80
|
Chris@428
|
81 bool
|
Chris@428
|
82 Align::canAlign()
|
Chris@428
|
83 {
|
Chris@670
|
84 TransformFactory *factory = TransformFactory::getInstance();
|
Chris@428
|
85 TransformId id = getAlignmentTransformName();
|
Chris@670
|
86 TransformId tdId = getTuningDifferenceTransformName();
|
Chris@670
|
87 return factory->haveTransform(id) &&
|
Chris@670
|
88 (tdId == "" || factory->haveTransform(tdId));
|
Chris@428
|
89 }
|
Chris@428
|
90
|
Chris@420
|
91 bool
|
Chris@670
|
92 Align::alignModelViaTransform(Document *doc, Model *ref, Model *other,
|
Chris@670
|
93 QString &error)
|
Chris@420
|
94 {
|
Chris@670
|
95 QMutexLocker locker (&m_mutex);
|
Chris@670
|
96
|
Chris@420
|
97 RangeSummarisableTimeValueModel *reference = qobject_cast
|
Chris@420
|
98 <RangeSummarisableTimeValueModel *>(ref);
|
Chris@420
|
99
|
Chris@420
|
100 RangeSummarisableTimeValueModel *rm = qobject_cast
|
Chris@420
|
101 <RangeSummarisableTimeValueModel *>(other);
|
Chris@420
|
102
|
Chris@420
|
103 if (!reference || !rm) return false; // but this should have been tested already
|
Chris@420
|
104
|
Chris@670
|
105 // This involves creating either three or four new models:
|
Chris@420
|
106
|
Chris@420
|
107 // 1. an AggregateWaveModel to provide the mixdowns of the main
|
Chris@420
|
108 // model and the new model in its two channels, as input to the
|
Chris@420
|
109 // MATCH plugin
|
Chris@420
|
110
|
Chris@670
|
111 // 2a. a SparseTimeValueModel which will be automatically created
|
Chris@670
|
112 // by FeatureExtractionModelTransformer when running the
|
Chris@670
|
113 // TuningDifference plugin to receive the relative tuning of the
|
Chris@670
|
114 // second model (if pitch-aware alignment is enabled in the
|
Chris@670
|
115 // preferences)
|
Chris@670
|
116
|
Chris@670
|
117 // 2b. a SparseTimeValueModel which will be automatically created
|
Chris@670
|
118 // by FeatureExtractionPluginTransformer when running the MATCH
|
Chris@670
|
119 // plugin to perform alignment (so containing the alignment path)
|
Chris@420
|
120
|
Chris@420
|
121 // 3. an AlignmentModel, which stores the path model and carries
|
Chris@420
|
122 // out alignment lookups on it.
|
Chris@420
|
123
|
Chris@670
|
124 // The AggregateWaveModel [1] is registered with the document,
|
Chris@670
|
125 // which deletes it when it is invalidated (when one of its
|
Chris@670
|
126 // components is deleted). The SparseTimeValueModel [2a] is reused
|
Chris@670
|
127 // by us when starting the alignment process proper, and is then
|
Chris@670
|
128 // deleted by us. The SparseTimeValueModel [2b] is passed to the
|
Chris@670
|
129 // AlignmentModel, which takes ownership of it. The AlignmentModel
|
Chris@670
|
130 // is attached to the new model we are aligning, which also takes
|
Chris@670
|
131 // ownership of it. The only one of these models that we need to
|
Chris@670
|
132 // delete here is the SparseTimeValueModel [2a].
|
Chris@420
|
133
|
Chris@420
|
134 AggregateWaveModel::ChannelSpecList components;
|
Chris@420
|
135
|
Chris@420
|
136 components.push_back(AggregateWaveModel::ModelChannelSpec
|
Chris@420
|
137 (reference, -1));
|
Chris@420
|
138
|
Chris@420
|
139 components.push_back(AggregateWaveModel::ModelChannelSpec
|
Chris@420
|
140 (rm, -1));
|
Chris@420
|
141
|
Chris@665
|
142 AggregateWaveModel *aggregateModel = new AggregateWaveModel(components);
|
Chris@665
|
143 doc->addAggregateModel(aggregateModel);
|
Chris@670
|
144
|
Chris@670
|
145 AlignmentModel *alignmentModel =
|
Chris@670
|
146 new AlignmentModel(reference, other, nullptr);
|
Chris@670
|
147
|
Chris@670
|
148 connect(alignmentModel, SIGNAL(completionChanged()),
|
Chris@670
|
149 this, SLOT(alignmentCompletionChanged()));
|
Chris@670
|
150
|
Chris@670
|
151 TransformId tdId = getTuningDifferenceTransformName();
|
Chris@670
|
152
|
Chris@670
|
153 if (tdId == "") {
|
Chris@670
|
154
|
Chris@670
|
155 if (beginTransformDrivenAlignment(aggregateModel, alignmentModel)) {
|
Chris@670
|
156 rm->setAlignment(alignmentModel);
|
Chris@670
|
157 } else {
|
Chris@670
|
158 error = alignmentModel->getError();
|
Chris@670
|
159 delete alignmentModel;
|
Chris@670
|
160 return false;
|
Chris@670
|
161 }
|
Chris@670
|
162
|
Chris@670
|
163 } else {
|
Chris@670
|
164
|
Chris@670
|
165 // Have a tuning-difference transform id, so run it
|
Chris@670
|
166 // asynchronously first
|
Chris@670
|
167
|
Chris@670
|
168 TransformFactory *tf = TransformFactory::getInstance();
|
Chris@670
|
169
|
Chris@670
|
170 Transform transform = tf->getDefaultTransformFor
|
Chris@670
|
171 (tdId, aggregateModel->getSampleRate());
|
Chris@670
|
172
|
Chris@670
|
173 SVDEBUG << "Align::alignModel: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
|
Chris@670
|
174
|
Chris@670
|
175 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
|
Chris@670
|
176
|
Chris@670
|
177 QString message;
|
Chris@670
|
178 Model *transformOutput = mtf->transform(transform, aggregateModel, message);
|
Chris@670
|
179
|
Chris@670
|
180 SparseTimeValueModel *tdout = dynamic_cast<SparseTimeValueModel *>
|
Chris@670
|
181 (transformOutput);
|
Chris@670
|
182
|
Chris@670
|
183 if (!tdout) {
|
Chris@670
|
184 SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl;
|
Chris@670
|
185 delete tdout;
|
Chris@670
|
186 error = message;
|
Chris@670
|
187 return false;
|
Chris@670
|
188 }
|
Chris@670
|
189
|
Chris@670
|
190 rm->setAlignment(alignmentModel);
|
Chris@665
|
191
|
Chris@670
|
192 connect(tdout, SIGNAL(completionChanged()),
|
Chris@670
|
193 this, SLOT(tuningDifferenceCompletionChanged()));
|
Chris@420
|
194
|
Chris@670
|
195 m_pendingTuningDiffs[tdout] =
|
Chris@670
|
196 std::pair<AggregateWaveModel *, AlignmentModel *>
|
Chris@670
|
197 (aggregateModel, alignmentModel);
|
Chris@670
|
198 }
|
Chris@670
|
199
|
Chris@670
|
200 return true;
|
Chris@670
|
201 }
|
Chris@670
|
202
|
Chris@670
|
203 bool
|
Chris@670
|
204 Align::beginTransformDrivenAlignment(AggregateWaveModel *aggregateModel,
|
Chris@670
|
205 AlignmentModel *alignmentModel,
|
Chris@670
|
206 float tuningFrequency)
|
Chris@670
|
207 {
|
Chris@428
|
208 TransformId id = getAlignmentTransformName();
|
Chris@420
|
209
|
Chris@420
|
210 TransformFactory *tf = TransformFactory::getInstance();
|
Chris@420
|
211
|
Chris@420
|
212 Transform transform = tf->getDefaultTransformFor
|
Chris@420
|
213 (id, aggregateModel->getSampleRate());
|
Chris@420
|
214
|
Chris@420
|
215 transform.setStepSize(transform.getBlockSize()/2);
|
Chris@420
|
216 transform.setParameter("serialise", 1);
|
Chris@420
|
217 transform.setParameter("smooth", 0);
|
Chris@420
|
218
|
Chris@670
|
219 if (tuningFrequency != 0.f) {
|
Chris@670
|
220 transform.setParameter("freq2", tuningFrequency);
|
Chris@670
|
221 }
|
Chris@670
|
222
|
Chris@420
|
223 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
|
Chris@420
|
224
|
Chris@420
|
225 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
|
Chris@420
|
226
|
Chris@420
|
227 QString message;
|
Chris@670
|
228 Model *transformOutput = mtf->transform
|
Chris@670
|
229 (transform, aggregateModel, message);
|
Chris@420
|
230
|
Chris@420
|
231 if (!transformOutput) {
|
Chris@420
|
232 transform.setStepSize(0);
|
Chris@670
|
233 transformOutput = mtf->transform
|
Chris@670
|
234 (transform, aggregateModel, message);
|
Chris@420
|
235 }
|
Chris@420
|
236
|
Chris@420
|
237 SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *>
|
Chris@420
|
238 (transformOutput);
|
Chris@420
|
239
|
Chris@670
|
240 //!!! callers will need to be updated to get error from
|
Chris@670
|
241 //!!! alignment model after initial call
|
Chris@670
|
242
|
Chris@420
|
243 if (!path) {
|
Chris@649
|
244 SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl;
|
Chris@420
|
245 delete transformOutput;
|
Chris@670
|
246 alignmentModel->setError(message);
|
Chris@420
|
247 return false;
|
Chris@420
|
248 }
|
Chris@420
|
249
|
Chris@420
|
250 path->setCompletion(0);
|
Chris@670
|
251 alignmentModel->setPathFrom(path);
|
Chris@420
|
252
|
Chris@428
|
253 connect(alignmentModel, SIGNAL(completionChanged()),
|
Chris@428
|
254 this, SLOT(alignmentCompletionChanged()));
|
Chris@420
|
255
|
Chris@420
|
256 return true;
|
Chris@420
|
257 }
|
Chris@420
|
258
|
Chris@428
|
259 void
|
Chris@670
|
260 Align::tuningDifferenceCompletionChanged()
|
Chris@670
|
261 {
|
Chris@670
|
262 QMutexLocker locker (&m_mutex);
|
Chris@670
|
263
|
Chris@670
|
264 SparseTimeValueModel *td = qobject_cast<SparseTimeValueModel *>(sender());
|
Chris@670
|
265 if (!td) return;
|
Chris@670
|
266 if (!td->isReady()) return;
|
Chris@670
|
267
|
Chris@670
|
268 disconnect(td, SIGNAL(completionChanged()),
|
Chris@670
|
269 this, SLOT(alignmentCompletionChanged()));
|
Chris@670
|
270
|
Chris@670
|
271 if (m_pendingTuningDiffs.find(td) == m_pendingTuningDiffs.end()) {
|
Chris@670
|
272 SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model "
|
Chris@670
|
273 << td << " not found in pending tuning diff map!" << endl;
|
Chris@670
|
274 return;
|
Chris@670
|
275 }
|
Chris@670
|
276
|
Chris@670
|
277 std::pair<AggregateWaveModel *, AlignmentModel *> models =
|
Chris@670
|
278 m_pendingTuningDiffs[td];
|
Chris@670
|
279
|
Chris@670
|
280 float tuningFrequency = 440.f;
|
Chris@670
|
281
|
Chris@670
|
282 if (!td->isEmpty()) {
|
Chris@670
|
283 tuningFrequency = td->getAllEvents()[0].getValue();
|
Chris@670
|
284 SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl;
|
Chris@670
|
285 } else {
|
Chris@670
|
286 SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl;
|
Chris@670
|
287 }
|
Chris@670
|
288
|
Chris@670
|
289 m_pendingTuningDiffs.erase(td);
|
Chris@670
|
290
|
Chris@670
|
291 beginTransformDrivenAlignment
|
Chris@670
|
292 (models.first, models.second, tuningFrequency);
|
Chris@670
|
293 }
|
Chris@670
|
294
|
Chris@670
|
295 void
|
Chris@428
|
296 Align::alignmentCompletionChanged()
|
Chris@428
|
297 {
|
Chris@670
|
298 QMutexLocker locker (&m_mutex);
|
Chris@670
|
299
|
Chris@428
|
300 AlignmentModel *am = qobject_cast<AlignmentModel *>(sender());
|
Chris@428
|
301 if (!am) return;
|
Chris@428
|
302 if (am->isReady()) {
|
Chris@428
|
303 disconnect(am, SIGNAL(completionChanged()),
|
Chris@428
|
304 this, SLOT(alignmentCompletionChanged()));
|
Chris@428
|
305 emit alignmentComplete(am);
|
Chris@428
|
306 }
|
Chris@428
|
307 }
|
Chris@428
|
308
|
Chris@420
|
309 bool
|
Chris@670
|
310 Align::alignModelViaProgram(Document *, Model *ref, Model *other,
|
Chris@670
|
311 QString program, QString &error)
|
Chris@420
|
312 {
|
Chris@670
|
313 QMutexLocker locker (&m_mutex);
|
Chris@670
|
314
|
Chris@420
|
315 WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref);
|
Chris@420
|
316 WaveFileModel *rm = qobject_cast<WaveFileModel *>(other);
|
Chris@420
|
317
|
Chris@515
|
318 if (!reference || !rm) {
|
Chris@515
|
319 return false; // but this should have been tested already
|
Chris@515
|
320 }
|
Chris@420
|
321
|
Chris@636
|
322 while (!reference->isReady(nullptr) || !rm->isReady(nullptr)) {
|
Chris@430
|
323 qApp->processEvents();
|
Chris@430
|
324 }
|
Chris@430
|
325
|
Chris@420
|
326 // Run an external program, passing to it paths to the main
|
Chris@420
|
327 // model's audio file and the new model's audio file. It returns
|
Chris@420
|
328 // the path in CSV form through stdout.
|
Chris@420
|
329
|
Chris@515
|
330 ReadOnlyWaveFileModel *roref = qobject_cast<ReadOnlyWaveFileModel *>(reference);
|
Chris@515
|
331 ReadOnlyWaveFileModel *rorm = qobject_cast<ReadOnlyWaveFileModel *>(rm);
|
Chris@515
|
332 if (!roref || !rorm) {
|
Chris@649
|
333 SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl;
|
Chris@515
|
334 return false;
|
Chris@515
|
335 }
|
Chris@515
|
336
|
Chris@515
|
337 QString refPath = roref->getLocalFilename();
|
Chris@515
|
338 QString otherPath = rorm->getLocalFilename();
|
Chris@420
|
339
|
Chris@420
|
340 if (refPath == "" || otherPath == "") {
|
Chris@670
|
341 error = "Failed to find local filepath for wave-file model";
|
Chris@595
|
342 return false;
|
Chris@420
|
343 }
|
Chris@420
|
344
|
Chris@665
|
345 AlignmentModel *alignmentModel =
|
Chris@665
|
346 new AlignmentModel(reference, other, nullptr);
|
Chris@423
|
347 rm->setAlignment(alignmentModel);
|
Chris@423
|
348
|
Chris@423
|
349 QProcess *process = new QProcess;
|
Chris@420
|
350 QStringList args;
|
Chris@420
|
351 args << refPath << otherPath;
|
Chris@423
|
352
|
Chris@423
|
353 connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
|
Chris@423
|
354 this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus)));
|
Chris@420
|
355
|
Chris@670
|
356 m_pendingProcesses[process] = alignmentModel;
|
Chris@423
|
357 process->start(program, args);
|
Chris@420
|
358
|
Chris@423
|
359 bool success = process->waitForStarted();
|
Chris@423
|
360
|
Chris@423
|
361 if (!success) {
|
Chris@649
|
362 SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start"
|
Chris@649
|
363 << endl;
|
Chris@670
|
364 error = "Alignment program could not be started";
|
Chris@670
|
365 m_pendingProcesses.erase(process);
|
Chris@636
|
366 rm->setAlignment(nullptr); // deletes alignmentModel as well
|
Chris@423
|
367 delete process;
|
Chris@423
|
368 }
|
Chris@423
|
369
|
Chris@423
|
370 return success;
|
Chris@423
|
371 }
|
Chris@423
|
372
|
Chris@423
|
373 void
|
Chris@423
|
374 Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status)
|
Chris@423
|
375 {
|
Chris@670
|
376 QMutexLocker locker (&m_mutex);
|
Chris@670
|
377
|
Chris@649
|
378 SVCERR << "Align::alignmentProgramFinished" << endl;
|
Chris@423
|
379
|
Chris@423
|
380 QProcess *process = qobject_cast<QProcess *>(sender());
|
Chris@423
|
381
|
Chris@670
|
382 if (m_pendingProcesses.find(process) == m_pendingProcesses.end()) {
|
Chris@649
|
383 SVCERR << "ERROR: Align::alignmentProgramFinished: Process " << process
|
Chris@649
|
384 << " not found in process model map!" << endl;
|
Chris@423
|
385 return;
|
Chris@423
|
386 }
|
Chris@423
|
387
|
Chris@670
|
388 AlignmentModel *alignmentModel = m_pendingProcesses[process];
|
Chris@423
|
389
|
Chris@423
|
390 if (exitCode == 0 && status == 0) {
|
Chris@420
|
391
|
Chris@595
|
392 CSVFormat format;
|
Chris@595
|
393 format.setModelType(CSVFormat::TwoDimensionalModel);
|
Chris@595
|
394 format.setTimingType(CSVFormat::ExplicitTiming);
|
Chris@595
|
395 format.setTimeUnits(CSVFormat::TimeSeconds);
|
Chris@595
|
396 format.setColumnCount(2);
|
Chris@425
|
397 // The output format has time in the reference file first, and
|
Chris@425
|
398 // time in the "other" file in the second column. This is a
|
Chris@425
|
399 // more natural approach for a command-line alignment tool,
|
Chris@425
|
400 // but it's the opposite of what we expect for native
|
Chris@425
|
401 // alignment paths, which map from "other" file to
|
Chris@425
|
402 // reference. These column purpose settings reflect that.
|
Chris@595
|
403 format.setColumnPurpose(1, CSVFormat::ColumnStartTime);
|
Chris@595
|
404 format.setColumnPurpose(0, CSVFormat::ColumnValue);
|
Chris@595
|
405 format.setAllowQuoting(false);
|
Chris@595
|
406 format.setSeparator(',');
|
Chris@420
|
407
|
Chris@595
|
408 CSVFileReader reader(process, format, alignmentModel->getSampleRate());
|
Chris@595
|
409 if (!reader.isOK()) {
|
Chris@649
|
410 SVCERR << "ERROR: Align::alignmentProgramFinished: Failed to parse output"
|
Chris@649
|
411 << endl;
|
Chris@670
|
412 alignmentModel->setError
|
Chris@670
|
413 (QString("Failed to parse output of program: %1")
|
Chris@670
|
414 .arg(reader.getError()));
|
Chris@423
|
415 goto done;
|
Chris@595
|
416 }
|
Chris@420
|
417
|
Chris@595
|
418 Model *csvOutput = reader.load();
|
Chris@420
|
419
|
Chris@595
|
420 SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput);
|
Chris@595
|
421 if (!path) {
|
Chris@649
|
422 SVCERR << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model"
|
Chris@649
|
423 << endl;
|
Chris@670
|
424 alignmentModel->setError
|
Chris@670
|
425 ("Output of program did not produce sparse time-value model");
|
Chris@423
|
426 goto done;
|
Chris@595
|
427 }
|
Chris@420
|
428
|
Chris@649
|
429 if (path->isEmpty()) {
|
Chris@649
|
430 SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings"
|
Chris@649
|
431 << endl;
|
Chris@670
|
432 alignmentModel->setError
|
Chris@670
|
433 ("Output of alignment program contained no mappings");
|
Chris@423
|
434 goto done;
|
Chris@595
|
435 }
|
Chris@420
|
436
|
Chris@649
|
437 SVCERR << "Align::alignmentProgramFinished: Setting alignment path ("
|
Chris@650
|
438 << path->getEventCount() << " point(s))" << endl;
|
Chris@650
|
439
|
Chris@423
|
440 alignmentModel->setPathFrom(path);
|
Chris@420
|
441
|
Chris@428
|
442 emit alignmentComplete(alignmentModel);
|
Chris@428
|
443
|
Chris@420
|
444 } else {
|
Chris@649
|
445 SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program "
|
Chris@649
|
446 << "failed: exit code " << exitCode << ", status " << status
|
Chris@649
|
447 << endl;
|
Chris@670
|
448 alignmentModel->setError
|
Chris@670
|
449 ("Aligner process returned non-zero exit status");
|
Chris@420
|
450 }
|
Chris@420
|
451
|
Chris@423
|
452 done:
|
Chris@670
|
453 m_pendingProcesses.erase(process);
|
Chris@423
|
454 delete process;
|
Chris@420
|
455 }
|
Chris@420
|
456
|