comparison framework/Align.cpp @ 683:0736beb8b852 by-id

Toward updating Document for ModelById
author Chris Cannam
date Wed, 03 Jul 2019 13:01:26 +0100
parents c7406ebcd51c
children e0b0f3e163ca
comparison
equal deleted inserted replaced
682:161063152ddd 683:0736beb8b852
31 #include <QProcess> 31 #include <QProcess>
32 #include <QSettings> 32 #include <QSettings>
33 #include <QApplication> 33 #include <QApplication>
34 34
35 bool 35 bool
36 Align::alignModel(Document *doc, Model *ref, Model *other, QString &error) 36 Align::alignModel(Document *doc, ModelId ref, ModelId other, QString &error)
37 { 37 {
38 QSettings settings; 38 QSettings settings;
39 settings.beginGroup("Preferences"); 39 settings.beginGroup("Preferences");
40 bool useProgram = settings.value("use-external-alignment", false).toBool(); 40 bool useProgram = settings.value("use-external-alignment", false).toBool();
41 QString program = settings.value("external-alignment-program", "").toString(); 41 QString program = settings.value("external-alignment-program", "").toString();
87 return factory->haveTransform(id) && 87 return factory->haveTransform(id) &&
88 (tdId == "" || factory->haveTransform(tdId)); 88 (tdId == "" || factory->haveTransform(tdId));
89 } 89 }
90 90
91 bool 91 bool
92 Align::alignModelViaTransform(Document *doc, Model *ref, Model *other, 92 Align::alignModelViaTransform(Document *doc, ModelId ref, ModelId other,
93 QString &error) 93 QString &error)
94 { 94 {
95 QMutexLocker locker (&m_mutex); 95 QMutexLocker locker (&m_mutex);
96 96
97 RangeSummarisableTimeValueModel *reference = qobject_cast 97 auto reference = ModelById::getAs<RangeSummarisableTimeValueModel>(ref);
98 <RangeSummarisableTimeValueModel *>(ref); 98 auto rm = ModelById::getAs<RangeSummarisableTimeValueModel>(other);
99 99 if (!reference || !rm) return false;
100 RangeSummarisableTimeValueModel *rm = qobject_cast
101 <RangeSummarisableTimeValueModel *>(other);
102
103 if (!reference || !rm) return false; // but this should have been tested already
104 100
105 // This involves creating either three or four new models: 101 // This involves creating either three or four new models:
106 // 102 //
107 // 1. an AggregateWaveModel to provide the mixdowns of the main 103 // 1. an AggregateWaveModel to provide the mixdowns of the main
108 // model and the new model in its two channels, as input to the 104 // model and the new model in its two channels, as input to the
128 // deleted by us. The SparseTimeValueModel [2b] is passed to the 124 // deleted by us. The SparseTimeValueModel [2b] is passed to the
129 // AlignmentModel, which takes ownership of it. The AlignmentModel 125 // AlignmentModel, which takes ownership of it. The AlignmentModel
130 // is attached to the new model we are aligning, which also takes 126 // 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 127 // ownership of it. The only one of these models that we need to
132 // delete here is the SparseTimeValueModel [2a]. 128 // delete here is the SparseTimeValueModel [2a].
129 //!!! todo: review the above, especially management of AlignmentModel
133 // 130 //
134 // (We also create a sneaky additional SparseTimeValueModel 131 // (We also create a sneaky additional SparseTimeValueModel
135 // temporarily so we can attach completion information to it - 132 // temporarily so we can attach completion information to it -
136 // this is quite unnecessary from the perspective of simply 133 // this is quite unnecessary from the perspective of simply
137 // producing the results.) 134 // producing the results.)
142 (reference->getId(), -1)); 139 (reference->getId(), -1));
143 140
144 components.push_back(AggregateWaveModel::ModelChannelSpec 141 components.push_back(AggregateWaveModel::ModelChannelSpec
145 (rm->getId(), -1)); 142 (rm->getId(), -1));
146 143
147 AggregateWaveModel *aggregateModel = new AggregateWaveModel(components); 144 auto aggregateModel = std::make_shared<AggregateWaveModel>(components);
148 doc->addAggregateModel(aggregateModel); 145 ModelById::add(aggregateModel);
149 146 doc->addAggregateModel(aggregateModel->getId());
150 AlignmentModel *alignmentModel = 147
151 new AlignmentModel(reference, other, nullptr); 148 auto alignmentModel = std::make_shared<AlignmentModel>(ref, other,
149 ModelId());
150 ModelById::add(alignmentModel);
152 151
153 TransformId tdId = getTuningDifferenceTransformName(); 152 TransformId tdId = getTuningDifferenceTransformName();
154 153
155 if (tdId == "") { 154 if (tdId == "") {
156 155
157 if (beginTransformDrivenAlignment(aggregateModel, alignmentModel)) { 156 if (beginTransformDrivenAlignment(aggregateModel->getId(),
158 rm->setAlignment(alignmentModel); 157 alignmentModel->getId())) {
158 rm->setAlignment(alignmentModel->getId());
159 } else { 159 } else {
160 error = alignmentModel->getError(); 160 error = alignmentModel->getError();
161 delete alignmentModel; 161 ModelById::release(alignmentModel);
162 return false; 162 return false;
163 } 163 }
164 164
165 } else { 165 } else {
166 166
179 SVDEBUG << "Align::alignModel: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; 179 SVDEBUG << "Align::alignModel: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
180 180
181 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); 181 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
182 182
183 QString message; 183 QString message;
184 Model *transformOutput = mtf->transform(transform, aggregateModel, message); 184 ModelId transformOutput = mtf->transform(transform,
185 185 aggregateModel->getId(),
186 SparseTimeValueModel *tdout = dynamic_cast<SparseTimeValueModel *> 186 message);
187 (transformOutput); 187
188 188 auto tdout = ModelById::getAs<SparseTimeValueModel>(transformOutput);
189 if (!tdout) { 189 if (!tdout) {
190 SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; 190 SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl;
191 delete tdout;
192 error = message; 191 error = message;
193 return false; 192 return false;
194 } 193 }
195 194
196 rm->setAlignment(alignmentModel); 195 rm->setAlignment(alignmentModel->getId());
197 196
198 connect(tdout, SIGNAL(completionChanged()), 197 connect(tdout.get(), SIGNAL(completionChanged()),
199 this, SLOT(tuningDifferenceCompletionChanged())); 198 this, SLOT(tuningDifferenceCompletionChanged()));
200 199
201 TuningDiffRec rec; 200 TuningDiffRec rec;
202 rec.input = aggregateModel; 201 rec.input = aggregateModel->getId();
203 rec.alignment = alignmentModel; 202 rec.alignment = alignmentModel->getId();
204
205 connect(aggregateModel, SIGNAL(aboutToBeDeleted()),
206 this, SLOT(aggregateModelAboutToBeDeleted()));
207 203
208 // This model exists only so that the AlignmentModel can get a 204 // This model exists only so that the AlignmentModel can get a
209 // completion value from somewhere while the tuning difference 205 // completion value from somewhere while the tuning difference
210 // calculation is going on 206 // calculation is going on
211 rec.preparatory = new SparseTimeValueModel 207 auto preparatoryModel = std::make_shared<SparseTimeValueModel>
212 (aggregateModel->getSampleRate(), 1);; 208 (aggregateModel->getSampleRate(), 1);
213 rec.preparatory->setCompletion(0); 209 ModelById::add(preparatoryModel);
210 preparatoryModel->setCompletion(0);
211 rec.preparatory = preparatoryModel->getId();
214 alignmentModel->setPathFrom(rec.preparatory); 212 alignmentModel->setPathFrom(rec.preparatory);
215 213
216 m_pendingTuningDiffs[tdout] = rec; 214 m_pendingTuningDiffs[transformOutput] = rec;
217 } 215 }
218 216
219 return true; 217 return true;
220 }
221
222 void
223 Align::aggregateModelAboutToBeDeleted()
224 {
225 SVCERR << "Align::aggregateModelAboutToBeDeleted" << endl;
226
227 QObject *s = sender();
228 AggregateWaveModel *awm = qobject_cast<AggregateWaveModel *>(s);
229 if (!awm) return;
230 QMutexLocker locker(&m_mutex);
231
232 SVCERR << "Align::aggregateModelAboutToBeDeleted: awm = " << awm
233 << endl;
234
235 for (const auto &p : m_pendingTuningDiffs) {
236 if (p.second.input == awm) {
237 SVCERR << "we have a record of this, getting rid of it" << endl;
238 m_pendingTuningDiffs.erase(p.first);
239 return;
240 }
241 }
242 } 218 }
243 219
244 void 220 void
245 Align::tuningDifferenceCompletionChanged() 221 Align::tuningDifferenceCompletionChanged()
246 { 222 {
247 QMutexLocker locker (&m_mutex); 223 QMutexLocker locker (&m_mutex);
248 224
249 SparseTimeValueModel *td = qobject_cast<SparseTimeValueModel *>(sender()); 225 ModelId tdId;
250 if (!td) return; 226 if (Model *modelPtr = qobject_cast<Model *>(sender())) {
251 227 tdId = modelPtr->getId();
252 if (m_pendingTuningDiffs.find(td) == m_pendingTuningDiffs.end()) { 228 } else {
229 return;
230 }
231
232 if (m_pendingTuningDiffs.find(tdId) == m_pendingTuningDiffs.end()) {
253 SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model " 233 SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model "
254 << td << " not found in pending tuning diff map!" << endl; 234 << tdId << " not found in pending tuning diff map!" << endl;
255 return; 235 return;
256 } 236 }
257 237
258 TuningDiffRec rec = m_pendingTuningDiffs[td]; 238 auto td = ModelById::getAs<SparseTimeValueModel>(tdId);
259 239 if (!td) {
240 SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged: Model "
241 << tdId << " not known as SparseTimeValueModel" << endl;
242 return;
243 }
244
245 TuningDiffRec rec = m_pendingTuningDiffs[tdId];
246
247 auto alignment = ModelById::getAs<AlignmentModel>(rec.alignment);
248 if (!alignment) {
249 SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged:"
250 << "alignment model has disappeared" << endl;
251 return;
252 }
253
260 int completion = 0; 254 int completion = 0;
261 bool done = td->isReady(&completion); 255 bool done = td->isReady(&completion);
262 256
263 // SVCERR << "Align::tuningDifferenceCompletionChanged: done = " << done << ", completion = " << completion << endl; 257 // SVCERR << "Align::tuningDifferenceCompletionChanged: done = " << done << ", completion = " << completion << endl;
264 258
267 // before the alignment actually begins. It goes up from 0 to 261 // before the alignment actually begins. It goes up from 0 to
268 // 99 (not 100!) and then back to 0 again when we start 262 // 99 (not 100!) and then back to 0 again when we start
269 // calculating the actual path in the following phase 263 // calculating the actual path in the following phase
270 int clamped = (completion == 100 ? 99 : completion); 264 int clamped = (completion == 100 ? 99 : completion);
271 // SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl; 265 // SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl;
272 rec.preparatory->setCompletion(clamped); 266 auto preparatory = ModelById::getAs<SparseTimeValueModel>
267 (rec.preparatory);
268 if (preparatory) {
269 preparatory->setCompletion(clamped);
270 }
273 return; 271 return;
274 } 272 }
275 273
276 float tuningFrequency = 440.f; 274 float tuningFrequency = 440.f;
277 275
280 SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl; 278 SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl;
281 } else { 279 } else {
282 SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; 280 SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl;
283 } 281 }
284 282
285 m_pendingTuningDiffs.erase(td); 283 m_pendingTuningDiffs.erase(tdId);
286 td->aboutToDelete(); 284 ModelById::release(tdId);
287 delete td; 285
288 286 alignment->setPathFrom({});
289 rec.alignment->setPathFrom(nullptr);
290 287
291 beginTransformDrivenAlignment 288 beginTransformDrivenAlignment
292 (rec.input, rec.alignment, tuningFrequency); 289 (rec.input, rec.alignment, tuningFrequency);
293 } 290 }
294 291
295 bool 292 bool
296 Align::beginTransformDrivenAlignment(AggregateWaveModel *aggregateModel, 293 Align::beginTransformDrivenAlignment(ModelId aggregateModelId,
297 AlignmentModel *alignmentModel, 294 ModelId alignmentModelId,
298 float tuningFrequency) 295 float tuningFrequency)
299 { 296 {
300 TransformId id = getAlignmentTransformName(); 297 TransformId id = getAlignmentTransformName();
301 298
302 TransformFactory *tf = TransformFactory::getInstance(); 299 TransformFactory *tf = TransformFactory::getInstance();
303 300
301 auto aggregateModel = ModelById::getAs<AggregateWaveModel>(aggregateModelId);
302 auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId);
303
304 if (!aggregateModel || !alignmentModel) {
305 SVCERR << "Align::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl;
306 return false;
307 }
308
304 Transform transform = tf->getDefaultTransformFor 309 Transform transform = tf->getDefaultTransformFor
305 (id, aggregateModel->getSampleRate()); 310 (id, aggregateModel->getSampleRate());
306 311
307 transform.setStepSize(transform.getBlockSize()/2); 312 transform.setStepSize(transform.getBlockSize()/2);
308 transform.setParameter("serialise", 1); 313 transform.setParameter("serialise", 1);
315 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; 320 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
316 321
317 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); 322 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
318 323
319 QString message; 324 QString message;
320 Model *transformOutput = mtf->transform 325 ModelId transformOutput = mtf->transform
321 (transform, aggregateModel, message); 326 (transform, aggregateModelId, message);
322 327
323 if (!transformOutput) { 328 if (transformOutput.isNone()) {
324 transform.setStepSize(0); 329 transform.setStepSize(0);
325 transformOutput = mtf->transform 330 transformOutput = mtf->transform
326 (transform, aggregateModel, message); 331 (transform, aggregateModelId, message);
327 } 332 }
328 333
329 SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> 334 auto path = ModelById::getAs<SparseTimeValueModel>(transformOutput);
330 (transformOutput);
331 335
332 //!!! callers will need to be updated to get error from 336 //!!! callers will need to be updated to get error from
333 //!!! alignment model after initial call 337 //!!! alignment model after initial call
334 338
335 if (!path) { 339 if (!path) {
336 SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; 340 SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl;
337 delete transformOutput; 341 ModelById::release(transformOutput);
338 alignmentModel->setError(message); 342 alignmentModel->setError(message);
339 return false; 343 return false;
340 } 344 }
341 345
342 path->setCompletion(0); 346 path->setCompletion(0);
343 alignmentModel->setPathFrom(path); 347 alignmentModel->setPathFrom(transformOutput); //!!! who releases transformOutput?
344 348
345 connect(alignmentModel, SIGNAL(completionChanged()), 349 connect(alignmentModel.get(), SIGNAL(completionChanged()),
346 this, SLOT(alignmentCompletionChanged())); 350 this, SLOT(alignmentCompletionChanged()));
347 351
348 return true; 352 return true;
349 } 353 }
350 354
351 void 355 void
352 Align::alignmentCompletionChanged() 356 Align::alignmentCompletionChanged()
353 { 357 {
354 QMutexLocker locker (&m_mutex); 358 QMutexLocker locker (&m_mutex);
355 359
356 AlignmentModel *am = qobject_cast<AlignmentModel *>(sender()); 360 if (AlignmentModel *amPtr = qobject_cast<AlignmentModel *>(sender())) {
357 if (!am) return; 361
358 if (am->isReady()) { 362 auto am = ModelById::getAs<AlignmentModel>(amPtr->getId());
359 disconnect(am, SIGNAL(completionChanged()), 363 if (am && am->isReady()) {
360 this, SLOT(alignmentCompletionChanged())); 364 disconnect(am.get(), SIGNAL(completionChanged()),
361 emit alignmentComplete(am); 365 this, SLOT(alignmentCompletionChanged()));
366 emit alignmentComplete(am->getId());
367 }
362 } 368 }
363 } 369 }
364 370
365 bool 371 bool
366 Align::alignModelViaProgram(Document *, Model *ref, Model *other, 372 Align::alignModelViaProgram(Document *, ModelId ref, ModelId other,
367 QString program, QString &error) 373 QString program, QString &error)
368 { 374 {
369 QMutexLocker locker (&m_mutex); 375 QMutexLocker locker (&m_mutex);
370 376
371 WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref); 377 auto reference = ModelById::getAs<RangeSummarisableTimeValueModel>(ref);
372 WaveFileModel *rm = qobject_cast<WaveFileModel *>(other); 378 auto rm = ModelById::getAs<RangeSummarisableTimeValueModel>(other);
373 379 if (!reference || !rm) return false;
374 if (!reference || !rm) {
375 return false; // but this should have been tested already
376 }
377 380
378 while (!reference->isReady(nullptr) || !rm->isReady(nullptr)) { 381 while (!reference->isReady(nullptr) || !rm->isReady(nullptr)) {
379 qApp->processEvents(); 382 qApp->processEvents();
380 } 383 }
381 384
382 // Run an external program, passing to it paths to the main 385 // Run an external program, passing to it paths to the main
383 // model's audio file and the new model's audio file. It returns 386 // model's audio file and the new model's audio file. It returns
384 // the path in CSV form through stdout. 387 // the path in CSV form through stdout.
385 388
386 ReadOnlyWaveFileModel *roref = qobject_cast<ReadOnlyWaveFileModel *>(reference); 389 auto roref = ModelById::getAs<ReadOnlyWaveFileModel>(ref);
387 ReadOnlyWaveFileModel *rorm = qobject_cast<ReadOnlyWaveFileModel *>(rm); 390 auto rorm = ModelById::getAs<ReadOnlyWaveFileModel>(other);
388 if (!roref || !rorm) { 391 if (!roref || !rorm) {
389 SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; 392 SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl;
390 return false; 393 return false;
391 } 394 }
392 395
396 if (refPath == "" || otherPath == "") { 399 if (refPath == "" || otherPath == "") {
397 error = "Failed to find local filepath for wave-file model"; 400 error = "Failed to find local filepath for wave-file model";
398 return false; 401 return false;
399 } 402 }
400 403
401 AlignmentModel *alignmentModel = 404 auto alignmentModel = std::make_shared<AlignmentModel>(ref, other,
402 new AlignmentModel(reference, other, nullptr); 405 ModelId());
403 rm->setAlignment(alignmentModel); 406 ModelById::add(alignmentModel);
407 rm->setAlignment(alignmentModel->getId());
404 408
405 QProcess *process = new QProcess; 409 QProcess *process = new QProcess;
406 QStringList args; 410 QStringList args;
407 args << refPath << otherPath; 411 args << refPath << otherPath;
408 412
409 connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), 413 connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
410 this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus))); 414 this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus)));
411 415
412 m_pendingProcesses[process] = alignmentModel; 416 m_pendingProcesses[process] = alignmentModel->getId();
413 process->start(program, args); 417 process->start(program, args);
414 418
415 bool success = process->waitForStarted(); 419 bool success = process->waitForStarted();
416 420
417 if (!success) { 421 if (!success) {
418 SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start" 422 SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start"
419 << endl; 423 << endl;
420 error = "Alignment program could not be started"; 424 error = "Alignment program could not be started";
421 m_pendingProcesses.erase(process); 425 m_pendingProcesses.erase(process);
422 rm->setAlignment(nullptr); // deletes alignmentModel as well 426 //!!! who releases alignmentModel? does this? review
427 rm->setAlignment({});
423 delete process; 428 delete process;
424 } 429 }
425 430
426 return success; 431 return success;
427 } 432 }
439 SVCERR << "ERROR: Align::alignmentProgramFinished: Process " << process 444 SVCERR << "ERROR: Align::alignmentProgramFinished: Process " << process
440 << " not found in process model map!" << endl; 445 << " not found in process model map!" << endl;
441 return; 446 return;
442 } 447 }
443 448
444 AlignmentModel *alignmentModel = m_pendingProcesses[process]; 449 ModelId alignmentModelId = m_pendingProcesses[process];
450 auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId);
451 if (!alignmentModel) return;
445 452
446 if (exitCode == 0 && status == 0) { 453 if (exitCode == 0 && status == 0) {
447 454
448 CSVFormat format; 455 CSVFormat format;
449 format.setModelType(CSVFormat::TwoDimensionalModel); 456 format.setModelType(CSVFormat::TwoDimensionalModel);
469 (QString("Failed to parse output of program: %1") 476 (QString("Failed to parse output of program: %1")
470 .arg(reader.getError())); 477 .arg(reader.getError()));
471 goto done; 478 goto done;
472 } 479 }
473 480
481 //!!! to use ById?
482
474 Model *csvOutput = reader.load(); 483 Model *csvOutput = reader.load();
475 484
476 SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput); 485 SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput);
477 if (!path) { 486 if (!path) {
478 SVCERR << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" 487 SVCERR << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model"
479 << endl; 488 << endl;
480 alignmentModel->setError 489 alignmentModel->setError
481 ("Output of program did not produce sparse time-value model"); 490 ("Output of program did not produce sparse time-value model");
491 delete csvOutput;
482 goto done; 492 goto done;
483 } 493 }
484 494
485 if (path->isEmpty()) { 495 if (path->isEmpty()) {
486 SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" 496 SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings"
487 << endl; 497 << endl;
488 alignmentModel->setError 498 alignmentModel->setError
489 ("Output of alignment program contained no mappings"); 499 ("Output of alignment program contained no mappings");
500 delete path;
490 goto done; 501 goto done;
491 } 502 }
492 503
493 SVCERR << "Align::alignmentProgramFinished: Setting alignment path (" 504 SVCERR << "Align::alignmentProgramFinished: Setting alignment path ("
494 << path->getEventCount() << " point(s))" << endl; 505 << path->getEventCount() << " point(s))" << endl;
495 506
496 alignmentModel->setPathFrom(path); 507 ModelById::add(std::shared_ptr<SparseTimeValueModel>(path));
497 508 alignmentModel->setPathFrom(path->getId());
498 emit alignmentComplete(alignmentModel); 509
510 emit alignmentComplete(alignmentModelId);
499 511
500 } else { 512 } else {
501 SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program " 513 SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program "
502 << "failed: exit code " << exitCode << ", status " << status 514 << "failed: exit code " << exitCode << ", status " << status
503 << endl; 515 << endl;