comparison framework/Align.cpp @ 691:c8ba09756eff by-id

Work on management of alignment-related models
author Chris Cannam
date Fri, 12 Jul 2019 13:58:02 +0100
parents e0b0f3e163ca
children 3e34eeb92647
comparison
equal deleted inserted replaced
690:827a522a5da4 691:c8ba09756eff
101 auto other = 101 auto other =
102 ModelById::getAs<RangeSummarisableTimeValueModel>(otherId); 102 ModelById::getAs<RangeSummarisableTimeValueModel>(otherId);
103 103
104 if (!reference || !other) return false; 104 if (!reference || !other) return false;
105 105
106 // This involves creating either three or four new models: 106 // This involves creating a number of new models:
107 // 107 //
108 // 1. an AggregateWaveModel to provide the mixdowns of the main 108 // 1. an AggregateWaveModel to provide the mixdowns of the main
109 // model and the new model in its two channels, as input to the 109 // model and the new model in its two channels, as input to the
110 // MATCH plugin 110 // MATCH plugin. We just call this one aggregateModel
111 // 111 //
112 // 2a. a SparseTimeValueModel which will be automatically created 112 // 2a. a SparseTimeValueModel which will be automatically created
113 // by FeatureExtractionModelTransformer when running the 113 // by FeatureExtractionModelTransformer when running the
114 // TuningDifference plugin to receive the relative tuning of the 114 // TuningDifference plugin to receive the relative tuning of the
115 // second model (if pitch-aware alignment is enabled in the 115 // second model (if pitch-aware alignment is enabled in the
116 // preferences) 116 // preferences). We call this tuningDiffOutputModel.
117 // 117 //
118 // 2b. a SparseTimeValueModel which will be automatically created 118 // 2b. a SparseTimeValueModel which will be automatically created
119 // by FeatureExtractionPluginTransformer when running the MATCH 119 // by FeatureExtractionPluginTransformer when running the MATCH
120 // plugin to perform alignment (so containing the alignment path) 120 // plugin to perform alignment (so containing the alignment path).
121 // 121 // We call this one pathOutputModel.
122 // 3. an AlignmentModel, which stores the path model and carries 122 //
123 // out alignment lookups on it. 123 // 2c. a SparseTimeValueModel used solely to provide faked
124 // 124 // completion information to the AlignmentModel while a
125 // The AggregateWaveModel [1] is registered with the document, 125 // TuningDifference calculation is going on. We call this
126 // which deletes it when it is invalidated (when one of its 126 // preparatoryModel.
127 // components is deleted). The SparseTimeValueModel [2a] is reused 127 //
128 // by us when starting the alignment process proper, and is then 128 // 3. an AlignmentModel, which stores the path and carries out
129 // deleted by us. The SparseTimeValueModel [2b] is passed to the 129 // alignment lookups on it. We just call this one alignmentModel.
130 // AlignmentModel, which takes ownership of it. The AlignmentModel 130 //
131 // is attached to the new model we are aligning, which also takes 131 // Models 1 and 3 are registered with the document, which will
132 // ownership of it. The only one of these models that we need to 132 // eventually release them. We don't release them here except in
133 // delete here is the SparseTimeValueModel [2a]. 133 // the case where an activity fails before the point where we
134 //!!! todo: review the above, especially management of AlignmentModel 134 // would otherwise have registered them with the document.
135 // 135 //
136 // (We also create a sneaky additional SparseTimeValueModel 136 // Models 2a (tuningDiffOutputModel), 2b (pathOutputModel) and 2c
137 // temporarily so we can attach completion information to it - 137 // (preparatoryModel) are not registered with the document. Model
138 // this is quite unnecessary from the perspective of simply 138 // 2b (pathOutputModel) is not registered because we do not have a
139 // producing the results.) 139 // stable reference to the document at the point where it is
140 // created. Model 2c (preparatoryModel) is not registered because
141 // it is a bodge that we are embarrassed about, so we try to
142 // manage it ourselves without anyone else noticing. Model 2a is
143 // not registered for symmetry with the other two. These have to
144 // be released by us when finished with, but their lifespans do
145 // not extend beyond the end of the alignment procedure, so this
146 // should be ok.
140 147
141 AggregateWaveModel::ChannelSpecList components; 148 AggregateWaveModel::ChannelSpecList components;
142 149
143 components.push_back 150 components.push_back
144 (AggregateWaveModel::ModelChannelSpec(referenceId, -1)); 151 (AggregateWaveModel::ModelChannelSpec(referenceId, -1));
146 components.push_back 153 components.push_back
147 (AggregateWaveModel::ModelChannelSpec(otherId, -1)); 154 (AggregateWaveModel::ModelChannelSpec(otherId, -1));
148 155
149 auto aggregateModel = std::make_shared<AggregateWaveModel>(components); 156 auto aggregateModel = std::make_shared<AggregateWaveModel>(components);
150 auto aggregateModelId = ModelById::add(aggregateModel); 157 auto aggregateModelId = ModelById::add(aggregateModel);
151 doc->addAggregateModel(aggregateModelId); 158 doc->addNonDerivedModel(aggregateModelId);
152 159
153 auto alignmentModel = std::make_shared<AlignmentModel> 160 auto alignmentModel = std::make_shared<AlignmentModel>
154 (referenceId, otherId, ModelId()); 161 (referenceId, otherId, ModelId());
155 auto alignmentModelId = ModelById::add(alignmentModel); 162 auto alignmentModelId = ModelById::add(alignmentModel);
156 163
159 if (tdId == "") { 166 if (tdId == "") {
160 167
161 if (beginTransformDrivenAlignment(aggregateModelId, 168 if (beginTransformDrivenAlignment(aggregateModelId,
162 alignmentModelId)) { 169 alignmentModelId)) {
163 other->setAlignment(alignmentModelId); 170 other->setAlignment(alignmentModelId);
171 doc->addNonDerivedModel(alignmentModelId);
164 } else { 172 } else {
165 error = alignmentModel->getError(); 173 error = alignmentModel->getError();
166 ModelById::release(alignmentModel); 174 ModelById::release(alignmentModel);
167 return false; 175 return false;
168 } 176 }
184 SVDEBUG << "Align::alignModel: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; 192 SVDEBUG << "Align::alignModel: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
185 193
186 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); 194 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
187 195
188 QString message; 196 QString message;
189 ModelId transformOutput = mtf->transform(transform, 197 ModelId tuningDiffOutputModelId = mtf->transform(transform,
190 aggregateModelId, 198 aggregateModelId,
191 message); 199 message);
192 200
193 auto tdout = ModelById::getAs<SparseTimeValueModel>(transformOutput); 201 auto tuningDiffOutputModel =
194 if (!tdout) { 202 ModelById::getAs<SparseTimeValueModel>(tuningDiffOutputModelId);
203 if (!tuningDiffOutputModel) {
195 SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; 204 SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl;
196 error = message; 205 error = message;
206 ModelById::release(alignmentModel);
197 return false; 207 return false;
198 } 208 }
199 209
200 other->setAlignment(alignmentModelId); 210 other->setAlignment(alignmentModelId);
201 211 doc->addNonDerivedModel(alignmentModelId);
202 connect(tdout.get(), SIGNAL(completionChanged(ModelId)), 212
213 connect(tuningDiffOutputModel.get(),
214 SIGNAL(completionChanged(ModelId)),
203 this, SLOT(tuningDifferenceCompletionChanged(ModelId))); 215 this, SLOT(tuningDifferenceCompletionChanged(ModelId)));
204 216
205 TuningDiffRec rec; 217 TuningDiffRec rec;
206 rec.input = aggregateModelId; 218 rec.input = aggregateModelId;
207 rec.alignment = alignmentModelId; 219 rec.alignment = alignmentModelId;
214 auto preparatoryModelId = ModelById::add(preparatoryModel); 226 auto preparatoryModelId = ModelById::add(preparatoryModel);
215 preparatoryModel->setCompletion(0); 227 preparatoryModel->setCompletion(0);
216 rec.preparatory = preparatoryModelId; 228 rec.preparatory = preparatoryModelId;
217 alignmentModel->setPathFrom(rec.preparatory); 229 alignmentModel->setPathFrom(rec.preparatory);
218 230
219 m_pendingTuningDiffs[transformOutput] = rec; 231 m_pendingTuningDiffs[tuningDiffOutputModelId] = rec;
220 } 232 }
221 233
222 return true; 234 return true;
223 } 235 }
224 236
225 void 237 void
226 Align::tuningDifferenceCompletionChanged(ModelId tdId) 238 Align::tuningDifferenceCompletionChanged(ModelId tuningDiffOutputModelId)
227 { 239 {
228 QMutexLocker locker(&m_mutex); 240 QMutexLocker locker(&m_mutex);
229 241
230 if (m_pendingTuningDiffs.find(tdId) == m_pendingTuningDiffs.end()) { 242 if (m_pendingTuningDiffs.find(tuningDiffOutputModelId) ==
243 m_pendingTuningDiffs.end()) {
231 SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model " 244 SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model "
232 << tdId << " not found in pending tuning diff map!" << endl; 245 << tuningDiffOutputModelId
246 << " not found in pending tuning diff map!" << endl;
233 return; 247 return;
234 } 248 }
235 249
236 auto td = ModelById::getAs<SparseTimeValueModel>(tdId); 250 auto tuningDiffOutputModel =
237 if (!td) { 251 ModelById::getAs<SparseTimeValueModel>(tuningDiffOutputModelId);
252 if (!tuningDiffOutputModel) {
238 SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged: Model " 253 SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged: Model "
239 << tdId << " not known as SparseTimeValueModel" << endl; 254 << tuningDiffOutputModelId
255 << " not known as SparseTimeValueModel" << endl;
240 return; 256 return;
241 } 257 }
242 258
243 TuningDiffRec rec = m_pendingTuningDiffs[tdId]; 259 TuningDiffRec rec = m_pendingTuningDiffs[tuningDiffOutputModelId];
244 260
245 auto alignment = ModelById::getAs<AlignmentModel>(rec.alignment); 261 auto alignmentModel = ModelById::getAs<AlignmentModel>(rec.alignment);
246 if (!alignment) { 262 if (!alignmentModel) {
247 SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged:" 263 SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged:"
248 << "alignment model has disappeared" << endl; 264 << "alignment model has disappeared" << endl;
249 return; 265 return;
250 } 266 }
251 267
252 int completion = 0; 268 int completion = 0;
253 bool done = td->isReady(&completion); 269 bool done = tuningDiffOutputModel->isReady(&completion);
254
255 // SVCERR << "Align::tuningDifferenceCompletionChanged: done = " << done << ", completion = " << completion << endl;
256 270
257 if (!done) { 271 if (!done) {
258 // This will be the completion the alignment model reports, 272 // This will be the completion the alignment model reports,
259 // before the alignment actually begins. It goes up from 0 to 273 // before the alignment actually begins. It goes up from 0 to
260 // 99 (not 100!) and then back to 0 again when we start 274 // 99 (not 100!) and then back to 0 again when we start
261 // calculating the actual path in the following phase 275 // calculating the actual path in the following phase
262 int clamped = (completion == 100 ? 99 : completion); 276 int clamped = (completion == 100 ? 99 : completion);
263 // SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl; 277 auto preparatoryModel =
264 auto preparatory = ModelById::getAs<SparseTimeValueModel> 278 ModelById::getAs<SparseTimeValueModel>(rec.preparatory);
265 (rec.preparatory); 279 if (preparatoryModel) {
266 if (preparatory) { 280 preparatoryModel->setCompletion(clamped);
267 preparatory->setCompletion(clamped);
268 } 281 }
269 return; 282 return;
270 } 283 }
271 284
272 float tuningFrequency = 440.f; 285 float tuningFrequency = 440.f;
273 286
274 if (!td->isEmpty()) { 287 if (!tuningDiffOutputModel->isEmpty()) {
275 tuningFrequency = td->getAllEvents()[0].getValue(); 288 tuningFrequency = tuningDiffOutputModel->getAllEvents()[0].getValue();
276 SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl; 289 SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl;
277 } else { 290 } else {
278 SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; 291 SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl;
279 } 292 }
280 293
281 m_pendingTuningDiffs.erase(tdId); 294 ModelById::release(tuningDiffOutputModel);
282 ModelById::release(tdId); 295
283 296 alignmentModel->setPathFrom({}); // replace preparatoryModel
284 alignment->setPathFrom({}); 297 ModelById::release(rec.preparatory);
298 rec.preparatory = {};
299
300 m_pendingTuningDiffs.erase(tuningDiffOutputModelId);
285 301
286 beginTransformDrivenAlignment 302 beginTransformDrivenAlignment
287 (rec.input, rec.alignment, tuningFrequency); 303 (rec.input, rec.alignment, tuningFrequency);
288 } 304 }
289 305
294 { 310 {
295 TransformId id = getAlignmentTransformName(); 311 TransformId id = getAlignmentTransformName();
296 312
297 TransformFactory *tf = TransformFactory::getInstance(); 313 TransformFactory *tf = TransformFactory::getInstance();
298 314
299 auto aggregateModel = ModelById::getAs<AggregateWaveModel>(aggregateModelId); 315 auto aggregateModel =
300 auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId); 316 ModelById::getAs<AggregateWaveModel>(aggregateModelId);
317 auto alignmentModel =
318 ModelById::getAs<AlignmentModel>(alignmentModelId);
301 319
302 if (!aggregateModel || !alignmentModel) { 320 if (!aggregateModel || !alignmentModel) {
303 SVCERR << "Align::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl; 321 SVCERR << "Align::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl;
304 return false; 322 return false;
305 } 323 }
318 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; 336 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
319 337
320 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); 338 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
321 339
322 QString message; 340 QString message;
323 ModelId transformOutput = mtf->transform 341 ModelId pathOutputModelId = mtf->transform
324 (transform, aggregateModelId, message); 342 (transform, aggregateModelId, message);
325 343
326 if (transformOutput.isNone()) { 344 if (pathOutputModelId.isNone()) {
327 transform.setStepSize(0); 345 transform.setStepSize(0);
328 transformOutput = mtf->transform 346 pathOutputModelId = mtf->transform
329 (transform, aggregateModelId, message); 347 (transform, aggregateModelId, message);
330 } 348 }
331 349
332 auto path = ModelById::getAs<SparseTimeValueModel>(transformOutput); 350 auto pathOutputModel =
351 ModelById::getAs<SparseTimeValueModel>(pathOutputModelId);
333 352
334 //!!! callers will need to be updated to get error from 353 //!!! callers will need to be updated to get error from
335 //!!! alignment model after initial call 354 //!!! alignment model after initial call
336 355
337 if (!path) { 356 if (!pathOutputModel) {
338 SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; 357 SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl;
339 ModelById::release(transformOutput);
340 alignmentModel->setError(message); 358 alignmentModel->setError(message);
341 return false; 359 return false;
342 } 360 }
343 361
344 path->setCompletion(0); 362 pathOutputModel->setCompletion(0);
345 alignmentModel->setPathFrom(transformOutput); //!!! who releases transformOutput? 363 alignmentModel->setPathFrom(pathOutputModelId);
364
365 m_pendingAlignments[alignmentModelId] = pathOutputModelId;
346 366
347 connect(alignmentModel.get(), SIGNAL(completionChanged(ModelId)), 367 connect(alignmentModel.get(), SIGNAL(completionChanged(ModelId)),
348 this, SLOT(alignmentCompletionChanged(ModelId))); 368 this, SLOT(alignmentCompletionChanged(ModelId)));
349 369
350 return true; 370 return true;
351 } 371 }
352 372
353 void 373 void
354 Align::alignmentCompletionChanged(ModelId modelId) 374 Align::alignmentCompletionChanged(ModelId alignmentModelId)
355 { 375 {
356 QMutexLocker locker (&m_mutex); 376 QMutexLocker locker (&m_mutex);
357 377
358 auto am = ModelById::getAs<AlignmentModel>(modelId); 378 auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId);
359 if (am && am->isReady()) { 379
360 disconnect(am.get(), SIGNAL(completionChanged(ModelId)), 380 if (alignmentModel && alignmentModel->isReady()) {
381
382 if (m_pendingAlignments.find(alignmentModelId) !=
383 m_pendingAlignments.end()) {
384 ModelId pathOutputModelId = m_pendingAlignments[alignmentModelId];
385 ModelById::release(pathOutputModelId);
386 m_pendingAlignments.erase(alignmentModelId);
387 }
388
389 disconnect(alignmentModel.get(),
390 SIGNAL(completionChanged(ModelId)),
361 this, SLOT(alignmentCompletionChanged(ModelId))); 391 this, SLOT(alignmentCompletionChanged(ModelId)));
362 emit alignmentComplete(modelId); 392 emit alignmentComplete(alignmentModelId);
363 } 393 }
364 } 394 }
365 395
366 bool 396 bool
367 Align::alignModelViaProgram(Document *, 397 Align::alignModelViaProgram(Document *doc,
368 ModelId referenceId, 398 ModelId referenceId,
369 ModelId otherId, 399 ModelId otherId,
370 QString program, 400 QString program,
371 QString &error) 401 QString &error)
372 { 402 {
415 if (!success) { 445 if (!success) {
416 SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start" 446 SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start"
417 << endl; 447 << endl;
418 error = "Alignment program could not be started"; 448 error = "Alignment program could not be started";
419 m_pendingProcesses.erase(process); 449 m_pendingProcesses.erase(process);
420 //!!! who releases alignmentModel? does this? review
421 other->setAlignment({}); 450 other->setAlignment({});
451 ModelById::release(alignmentModelId);
422 delete process; 452 delete process;
423 } 453 }
424 454
455 doc->addNonDerivedModel(alignmentModelId);
425 return success; 456 return success;
426 } 457 }
427 458
428 void 459 void
429 Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status) 460 Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status)