Mercurial > hg > svapp
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) |