Mercurial > hg > svapp
comparison align/TransformDTWAligner.cpp @ 768:1b1960009be6 pitch-align
Provide callback for output preprocessing before DTW, use it for freq-pitch conversion; use direct setting of completion on alignment models instead of creating fake outputs for completion only
author | Chris Cannam |
---|---|
date | Fri, 22 May 2020 17:17:44 +0100 |
parents | dd742e566e60 |
children | a316cb6fed81 |
comparison
equal
deleted
inserted
replaced
767:dd742e566e60 | 768:1b1960009be6 |
---|---|
37 Transform transform, | 37 Transform transform, |
38 DTWType dtwType) : | 38 DTWType dtwType) : |
39 m_document(doc), | 39 m_document(doc), |
40 m_reference(reference), | 40 m_reference(reference), |
41 m_toAlign(toAlign), | 41 m_toAlign(toAlign), |
42 m_referenceTransformComplete(false), | |
43 m_toAlignTransformComplete(false), | |
44 m_transform(transform), | 42 m_transform(transform), |
45 m_dtwType(dtwType), | 43 m_dtwType(dtwType), |
46 m_incomplete(true) | 44 m_incomplete(true), |
45 m_outputPreprocessor([](double x) { return x; }) | |
46 { | |
47 } | |
48 | |
49 TransformDTWAligner::TransformDTWAligner(Document *doc, | |
50 ModelId reference, | |
51 ModelId toAlign, | |
52 Transform transform, | |
53 DTWType dtwType, | |
54 std::function<double(double)> | |
55 outputPreprocessor) : | |
56 m_document(doc), | |
57 m_reference(reference), | |
58 m_toAlign(toAlign), | |
59 m_transform(transform), | |
60 m_dtwType(dtwType), | |
61 m_incomplete(true), | |
62 m_outputPreprocessor(outputPreprocessor) | |
47 { | 63 { |
48 } | 64 } |
49 | 65 |
50 TransformDTWAligner::~TransformDTWAligner() | 66 TransformDTWAligner::~TransformDTWAligner() |
51 { | 67 { |
55 } | 71 } |
56 } | 72 } |
57 | 73 |
58 ModelById::release(m_referenceOutputModel); | 74 ModelById::release(m_referenceOutputModel); |
59 ModelById::release(m_toAlignOutputModel); | 75 ModelById::release(m_toAlignOutputModel); |
60 ModelById::release(m_alignmentProgressModel); | |
61 } | 76 } |
62 | 77 |
63 bool | 78 bool |
64 TransformDTWAligner::isAvailable() | 79 TransformDTWAligner::isAvailable() |
65 { | 80 { |
112 | 127 |
113 connect(referenceOutputModel.get(), SIGNAL(completionChanged(ModelId)), | 128 connect(referenceOutputModel.get(), SIGNAL(completionChanged(ModelId)), |
114 this, SLOT(completionChanged(ModelId))); | 129 this, SLOT(completionChanged(ModelId))); |
115 connect(toAlignOutputModel.get(), SIGNAL(completionChanged(ModelId)), | 130 connect(toAlignOutputModel.get(), SIGNAL(completionChanged(ModelId)), |
116 this, SLOT(completionChanged(ModelId))); | 131 this, SLOT(completionChanged(ModelId))); |
117 | |
118 auto alignmentProgressModel = std::make_shared<SparseTimeValueModel> | |
119 (reference->getSampleRate(), m_transform.getStepSize(), false); | |
120 alignmentProgressModel->setCompletion(0); | |
121 m_alignmentProgressModel = ModelById::add(alignmentProgressModel); | |
122 | 132 |
123 auto alignmentModel = std::make_shared<AlignmentModel> | 133 auto alignmentModel = std::make_shared<AlignmentModel> |
124 (m_reference, m_toAlign, m_alignmentProgressModel); | 134 (m_reference, m_toAlign, ModelId()); |
125 m_alignmentModel = ModelById::add(alignmentModel); | 135 m_alignmentModel = ModelById::add(alignmentModel); |
126 | 136 |
127 toAlign->setAlignment(m_alignmentModel); | 137 toAlign->setAlignment(m_alignmentModel); |
128 m_document->addNonDerivedModel(m_alignmentModel); | 138 m_document->addNonDerivedModel(m_alignmentModel); |
129 | 139 |
151 SVCERR << "TransformDTWAligner[" << this << "]: completionChanged: " | 161 SVCERR << "TransformDTWAligner[" << this << "]: completionChanged: " |
152 << "model " << id << endl; | 162 << "model " << id << endl; |
153 | 163 |
154 auto referenceOutputModel = ModelById::get(m_referenceOutputModel); | 164 auto referenceOutputModel = ModelById::get(m_referenceOutputModel); |
155 auto toAlignOutputModel = ModelById::get(m_toAlignOutputModel); | 165 auto toAlignOutputModel = ModelById::get(m_toAlignOutputModel); |
156 | 166 auto alignmentModel = ModelById::getAs<AlignmentModel>(m_alignmentModel); |
157 if (!referenceOutputModel || !toAlignOutputModel) { | 167 |
168 if (!referenceOutputModel || !toAlignOutputModel || !alignmentModel) { | |
158 return; | 169 return; |
159 } | 170 } |
160 | 171 |
161 int referenceCompletion = 0, toAlignCompletion = 0; | 172 int referenceCompletion = 0, toAlignCompletion = 0; |
162 bool referenceReady = referenceOutputModel->isReady(&referenceCompletion); | 173 bool referenceReady = referenceOutputModel->isReady(&referenceCompletion); |
163 bool toAlignReady = toAlignOutputModel->isReady(&toAlignCompletion); | 174 bool toAlignReady = toAlignOutputModel->isReady(&toAlignCompletion); |
164 | 175 |
165 auto alignmentProgressModel = | |
166 ModelById::getAs<SparseTimeValueModel>(m_alignmentProgressModel); | |
167 | |
168 if (referenceReady && toAlignReady) { | 176 if (referenceReady && toAlignReady) { |
169 | 177 |
170 SVCERR << "TransformDTWAligner[" << this << "]: completionChanged: " | 178 SVCERR << "TransformDTWAligner[" << this << "]: completionChanged: " |
171 << "ready, calling performAlignment" << endl; | 179 << "ready, calling performAlignment" << endl; |
172 | 180 |
173 if (alignmentProgressModel) { | 181 alignmentModel->setCompletion(95); |
174 alignmentProgressModel->setCompletion(95); | |
175 } | |
176 | 182 |
177 if (performAlignment()) { | 183 if (performAlignment()) { |
178 emit complete(m_alignmentModel); | 184 emit complete(m_alignmentModel); |
179 } else { | 185 } else { |
180 emit failed(m_toAlign, tr("Alignment of transform outputs failed")); | 186 emit failed(m_toAlign, tr("Alignment of transform outputs failed")); |
184 | 190 |
185 SVCERR << "TransformDTWAligner[" << this << "]: completionChanged: " | 191 SVCERR << "TransformDTWAligner[" << this << "]: completionChanged: " |
186 << "not ready yet: reference completion " << referenceCompletion | 192 << "not ready yet: reference completion " << referenceCompletion |
187 << ", toAlign completion " << toAlignCompletion << endl; | 193 << ", toAlign completion " << toAlignCompletion << endl; |
188 | 194 |
189 if (alignmentProgressModel) { | 195 int completion = std::min(referenceCompletion, |
190 int completion = std::min(referenceCompletion, | 196 toAlignCompletion); |
191 toAlignCompletion); | 197 completion = (completion * 94) / 100; |
192 completion = (completion * 94) / 100; | 198 alignmentModel->setCompletion(completion); |
193 alignmentProgressModel->setCompletion(completion); | |
194 } | |
195 } | 199 } |
196 } | 200 } |
197 | 201 |
198 bool | 202 bool |
199 TransformDTWAligner::performAlignment() | 203 TransformDTWAligner::performAlignment() |
227 vector<double> s1, s2; | 231 vector<double> s1, s2; |
228 | 232 |
229 { | 233 { |
230 auto events = referenceOutputSTVM->getAllEvents(); | 234 auto events = referenceOutputSTVM->getAllEvents(); |
231 for (auto e: events) { | 235 for (auto e: events) { |
232 s1.push_back(e.getValue()); | 236 s1.push_back(m_outputPreprocessor(e.getValue())); |
233 } | 237 } |
234 events = toAlignOutputSTVM->getAllEvents(); | 238 events = toAlignOutputSTVM->getAllEvents(); |
235 for (auto e: events) { | 239 for (auto e: events) { |
236 s2.push_back(e.getValue()); | 240 s2.push_back(m_outputPreprocessor(e.getValue())); |
237 } | 241 } |
238 } | 242 } |
239 | 243 |
240 SVCERR << "TransformDTWAligner[" << this << "]: performAlignment: " | 244 SVCERR << "TransformDTWAligner[" << this << "]: performAlignment: " |
241 << "Have " << s1.size() << " events from reference, " | 245 << "Have " << s1.size() << " events from reference, " |
258 for (int i = 0; i < alignment.size() && i < 100; ++i) { | 262 for (int i = 0; i < alignment.size() && i < 100; ++i) { |
259 SVCERR << alignment[i] << " "; | 263 SVCERR << alignment[i] << " "; |
260 } | 264 } |
261 SVCERR << endl; | 265 SVCERR << endl; |
262 | 266 |
263 auto alignmentProgressModel = | 267 alignmentModel->setCompletion(100); |
264 ModelById::getAs<SparseTimeValueModel>(m_alignmentProgressModel); | |
265 if (alignmentProgressModel) { | |
266 alignmentProgressModel->setCompletion(100); | |
267 } | |
268 | |
269 // clear the alignment progress model | |
270 alignmentModel->setPathFrom(ModelId()); | |
271 | 268 |
272 sv_frame_t resolution = referenceOutputSTVM->getResolution(); | 269 sv_frame_t resolution = referenceOutputSTVM->getResolution(); |
273 sv_frame_t sourceFrame = 0; | 270 sv_frame_t sourceFrame = 0; |
274 | 271 |
275 Path path(referenceOutputSTVM->getSampleRate(), resolution); | 272 Path path(referenceOutputSTVM->getSampleRate(), resolution); |
304 } | 301 } |
305 | 302 |
306 if (!alignmentModel) { | 303 if (!alignmentModel) { |
307 return false; | 304 return false; |
308 } | 305 } |
309 | 306 |
310 vector<RiseFallDTW::Value> s1, s2; | 307 auto convertEvents = |
311 double prev1 = 0.0, prev2 = 0.0; | 308 [this](const EventVector &ee) { |
312 | 309 vector<RiseFallDTW::Value> s; |
313 { | 310 double prev = 0.0; |
314 auto events = referenceOutputSTVM->getAllEvents(); | 311 for (auto e: ee) { |
315 for (auto e: events) { | 312 double v = m_outputPreprocessor(e.getValue()); |
316 double v = e.getValue(); | 313 if (v == prev || s.empty()) { |
317 //!!! the original does this using MIDI pitch for the | 314 s.push_back({ RiseFallDTW::Direction::None, 0.0 }); |
318 //!!! pYin transform... rework with a lambda passed in | 315 } else if (v > prev) { |
319 //!!! for modification maybe? + factor out s1/s2 of course | 316 s.push_back({ RiseFallDTW::Direction::Up, v - prev }); |
320 if (v > prev1) { | 317 } else { |
321 s1.push_back({ RiseFallDTW::Direction::Up, v - prev1 }); | 318 s.push_back({ RiseFallDTW::Direction::Down, prev - v }); |
322 } else { | 319 } |
323 s1.push_back({ RiseFallDTW::Direction::Down, prev1 - v }); | |
324 } | 320 } |
325 prev1 = v; | 321 return s; |
326 } | 322 }; |
327 events = toAlignOutputSTVM->getAllEvents(); | 323 |
328 for (auto e: events) { | 324 vector<RiseFallDTW::Value> s1 = |
329 double v = e.getValue(); | 325 convertEvents(referenceOutputSTVM->getAllEvents()); |
330 //!!! as above | 326 |
331 if (v > prev2) { | 327 vector<RiseFallDTW::Value> s2 = |
332 s2.push_back({ RiseFallDTW::Direction::Up, v - prev2 }); | 328 convertEvents(toAlignOutputSTVM->getAllEvents()); |
333 } else { | |
334 s2.push_back({ RiseFallDTW::Direction::Down, prev2 - v }); | |
335 } | |
336 prev2 = v; | |
337 } | |
338 } | |
339 | 329 |
340 SVCERR << "TransformDTWAligner[" << this << "]: performAlignment: " | 330 SVCERR << "TransformDTWAligner[" << this << "]: performAlignment: " |
341 << "Have " << s1.size() << " events from reference, " | 331 << "Have " << s1.size() << " events from reference, " |
342 << s2.size() << " from toAlign" << endl; | 332 << s2.size() << " from toAlign" << endl; |
343 | 333 |
359 for (int i = 0; i < alignment.size() && i < 100; ++i) { | 349 for (int i = 0; i < alignment.size() && i < 100; ++i) { |
360 SVCERR << alignment[i] << " "; | 350 SVCERR << alignment[i] << " "; |
361 } | 351 } |
362 SVCERR << endl; | 352 SVCERR << endl; |
363 | 353 |
364 auto alignmentProgressModel = | 354 alignmentModel->setCompletion(100); |
365 ModelById::getAs<SparseTimeValueModel>(m_alignmentProgressModel); | |
366 if (alignmentProgressModel) { | |
367 alignmentProgressModel->setCompletion(100); | |
368 } | |
369 | |
370 // clear the alignment progress model | |
371 alignmentModel->setPathFrom(ModelId()); | |
372 | 355 |
373 sv_frame_t resolution = referenceOutputSTVM->getResolution(); | 356 sv_frame_t resolution = referenceOutputSTVM->getResolution(); |
374 sv_frame_t sourceFrame = 0; | 357 sv_frame_t sourceFrame = 0; |
375 | 358 |
376 Path path(referenceOutputSTVM->getSampleRate(), resolution); | 359 Path path(referenceOutputSTVM->getSampleRate(), resolution); |