AlignmentModel.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2007 QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "AlignmentModel.h"
17 
18 #include "SparseTimeValueModel.h"
19 
20 //#define DEBUG_ALIGNMENT_MODEL 1
21 
23  ModelId aligned,
24  ModelId pathSource) :
25  m_reference(reference),
26  m_aligned(aligned),
27  m_pathSource(pathSource),
28  m_path(nullptr),
29  m_reversePath(nullptr),
30  m_pathBegun(false),
31  m_pathComplete(false),
32  m_relativePitch(0),
33  m_explicitlySetCompletion(-1)
34 {
35  setPathFrom(pathSource);
36 
37  if (m_reference == m_aligned) {
38  // Trivial alignment, e.g. of main model to itself, which we
39  // record so that we can distinguish the reference model for
40  // alignments from an unaligned model. No path required
41  m_pathComplete = true;
42  }
43 }
44 
46 {
47 #ifdef DEBUG_ALIGNMENT_MODEL
48  SVCERR << "AlignmentModel(" << this << ")::~AlignmentModel()" << endl;
49 #endif
50 }
51 
52 bool
54 {
55  if (m_error != "") return false;
56  if (m_pathSource.isNone()) return true;
57  auto pathSourceModel =
58  ModelById::getAs<SparseTimeValueModel>(m_pathSource);
59  if (pathSourceModel) {
60  return pathSourceModel->isOK();
61  }
62  return true;
63 }
64 
67 {
68  auto reference = ModelById::get(m_reference);
69  auto aligned = ModelById::get(m_aligned);
70 
71  if (reference && aligned) {
72  sv_frame_t a = reference->getStartFrame();
73  sv_frame_t b = aligned->getStartFrame();
74  return std::min(a, b);
75  } else {
76  return 0;
77  }
78 }
79 
82 {
83  auto reference = ModelById::get(m_reference);
84  auto aligned = ModelById::get(m_aligned);
85 
86  if (reference && aligned) {
87  sv_frame_t a = reference->getEndFrame();
88  sv_frame_t b = aligned->getEndFrame();
89  return std::max(a, b);
90  } else {
91  return 0;
92  }
93 }
94 
97 {
98  auto reference = ModelById::get(m_reference);
99  if (reference) {
100  return reference->getSampleRate();
101  } else {
102  return 0;
103  }
104 }
105 
106 bool
107 AlignmentModel::isReady(int *completion) const
108 {
109  if (m_explicitlySetCompletion != -1) {
110  if (completion) *completion = m_explicitlySetCompletion;
111  return (m_explicitlySetCompletion == 100);
112  }
113  if (!m_pathBegun && !m_pathSource.isNone()) {
114  if (completion) *completion = 0;
115 #ifdef DEBUG_ALIGNMENT_MODEL
116  SVCERR << "AlignmentModel::isReady: path not begun" << endl;
117 #endif
118  return false;
119  }
120  if (m_pathComplete) {
121  if (completion) *completion = 100;
122 #ifdef DEBUG_ALIGNMENT_MODEL
123  SVCERR << "AlignmentModel::isReady: path complete" << endl;
124 #endif
125  return true;
126  }
127  if (m_pathSource.isNone()) {
128  // lack of raw path could mean path is complete (in which case
129  // m_pathComplete true above) or else no path source has been
130  // set at all yet (this case)
131  if (completion) *completion = 0;
132 #ifdef DEBUG_ALIGNMENT_MODEL
133  SVCERR << "AlignmentModel::isReady: no raw path" << endl;
134 #endif
135  return false;
136  }
137  auto pathSourceModel =
138  ModelById::getAs<SparseTimeValueModel>(m_pathSource);
139  if (pathSourceModel) {
140  return pathSourceModel->isReady(completion);
141  } else {
142  return true; // there is no meaningful answer here
143  }
144 }
145 
146 const ZoomConstraint *
148 {
149  return nullptr;
150 }
151 
152 ModelId
154 {
155  return m_reference;
156 }
157 
158 ModelId
160 {
161  return m_aligned;
162 }
163 
164 void
166 {
167  m_explicitlySetCompletion = completion;
168  emit completionChanged(getId());
169 }
170 
173 {
174 #ifdef DEBUG_ALIGNMENT_MODEL
175  cerr << "AlignmentModel::toReference(" << frame << ")" << endl;
176 #endif
177  if (!m_path) {
178  if (m_pathSource.isNone()) {
179  return frame;
180  }
181  constructPath();
182  }
183  if (!m_path) {
184  return frame;
185  }
186 
187  return performAlignment(*m_path, frame);
188 }
189 
192 {
193 #ifdef DEBUG_ALIGNMENT_MODEL
194  cerr << "AlignmentModel::fromReference(" << frame << ")" << endl;
195 #endif
196  if (!m_reversePath) {
197  if (m_pathSource.isNone()) {
198  return frame;
199  }
201  }
202  if (!m_reversePath) {
203  return frame;
204  }
205 
206  return performAlignment(*m_reversePath, frame);
207 }
208 
209 void
211 {
212  if (!m_pathComplete) return;
213  constructPath();
215 }
216 
217 void
219 {
220  auto pathSourceModel =
221  ModelById::getAs<SparseTimeValueModel>(m_pathSource);
222  if (!pathSourceModel) return;
223 
224  m_pathBegun = true;
225 
226  if (!m_pathComplete) {
227 
228  int completion = 0;
229  pathSourceModel->isReady(&completion);
230 
231 #ifdef DEBUG_ALIGNMENT_MODEL
232  SVCERR << "AlignmentModel::pathCompletionChanged: completion = "
233  << completion << endl;
234 #endif
235 
236  m_pathComplete = (completion == 100);
237 
238  if (m_pathComplete) {
239 
240  constructPath();
242 
243 #ifdef DEBUG_ALIGNMENT_MODEL
244  SVCERR << "AlignmentModel: path complete" << endl;
245 #endif
246  }
247  }
248 
249  emit completionChanged(getId());
250 }
251 
252 void
254 {
255  auto alignedModel = ModelById::get(m_aligned);
256  if (!alignedModel) return;
257 
258  auto pathSourceModel =
259  ModelById::getAs<SparseTimeValueModel>(m_pathSource);
260  if (!m_path) {
261  if (!pathSourceModel) {
262  cerr << "ERROR: AlignmentModel::constructPath: "
263  << "No raw path available (id is " << m_pathSource
264  << ")" << endl;
265  return;
266  }
267  m_path.reset(new Path
268  (pathSourceModel->getSampleRate(),
269  pathSourceModel->getResolution()));
270  } else {
271  if (!pathSourceModel) return;
272  }
273 
274  m_path->clear();
275 
276  EventVector points = pathSourceModel->getAllEvents();
277 
278  for (const auto &p: points) {
279  sv_frame_t frame = p.getFrame();
280  double value = p.getValue();
281  sv_frame_t rframe = lrint(value * alignedModel->getSampleRate());
282  m_path->add(PathPoint(frame, rframe));
283  }
284 
285 #ifdef DEBUG_ALIGNMENT_MODEL
286  cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
287 #endif
288 }
289 
290 void
292 {
293  if (!m_reversePath) {
294  if (!m_path) {
295  cerr << "ERROR: AlignmentModel::constructReversePath: "
296  << "No forward path available" << endl;
297  return;
298  }
299  m_reversePath.reset(new Path
300  (m_path->getSampleRate(),
301  m_path->getResolution()));
302  } else {
303  if (!m_path) return;
304  }
305 
306  m_reversePath->clear();
307 
308  Path::Points points = m_path->getPoints();
309 
310  for (auto p: points) {
311  sv_frame_t frame = p.frame;
312  sv_frame_t rframe = p.mapframe;
313  m_reversePath->add(PathPoint(rframe, frame));
314  }
315 
316 #ifdef DEBUG_ALIGNMENT_MODEL
317  cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
318 #endif
319 }
320 
323 {
324  // The path consists of a series of points, each with frame equal
325  // to the frame on the source model (aligned model) and mapframe
326  // equal to the frame on the target model (reference model). Both
327  // should be monotonically increasing.
328 
329  const Path::Points &points = path.getPoints();
330 
331  if (points.empty()) {
332 #ifdef DEBUG_ALIGNMENT_MODEL
333  cerr << "AlignmentModel::align: No points" << endl;
334 #endif
335  return frame;
336  }
337 
338 #ifdef DEBUG_ALIGNMENT_MODEL
339  cerr << "AlignmentModel::align: frame " << frame << " requested" << endl;
340 #endif
341 
342  PathPoint point(frame);
343  Path::Points::const_iterator i = points.lower_bound(point);
344  if (i == points.end()) {
345 #ifdef DEBUG_ALIGNMENT_MODEL
346  cerr << "Note: i == points.end()" << endl;
347 #endif
348  --i;
349  }
350  while (i != points.begin() && i->frame > frame) {
351  --i;
352  }
353 
354  sv_frame_t foundFrame = i->frame;
355  sv_frame_t foundMapFrame = i->mapframe;
356 
357  sv_frame_t followingFrame = foundFrame;
358  sv_frame_t followingMapFrame = foundMapFrame;
359 
360  if (++i != points.end()) {
361 #ifdef DEBUG_ALIGNMENT_MODEL
362  cerr << "another point available" << endl;
363 #endif
364  followingFrame = i->frame;
365  followingMapFrame = i->mapframe;
366  } else {
367 #ifdef DEBUG_ALIGNMENT_MODEL
368  cerr << "no other point available" << endl;
369 #endif
370  }
371 
372 #ifdef DEBUG_ALIGNMENT_MODEL
373  cerr << "foundFrame = " << foundFrame << ", foundMapFrame = " << foundMapFrame
374  << ", followingFrame = " << followingFrame << ", followingMapFrame = "
375  << followingMapFrame << endl;
376 #endif
377 
378  if (foundMapFrame < 0) {
379  return 0;
380  }
381 
382  sv_frame_t resultFrame = foundMapFrame;
383 
384  if (followingFrame != foundFrame && frame > foundFrame) {
385  double interp =
386  double(frame - foundFrame) /
387  double(followingFrame - foundFrame);
388  resultFrame += lrint(double(followingMapFrame - foundMapFrame) * interp);
389  }
390 
391 #ifdef DEBUG_ALIGNMENT_MODEL
392  cerr << "AlignmentModel::align: resultFrame = " << resultFrame << endl;
393 #endif
394 
395  return resultFrame;
396 }
397 
398 void
400 {
401  m_pathSource = pathSource;
402 
403  auto pathSourceModel =
404  ModelById::getAs<SparseTimeValueModel>(m_pathSource);
405 
406  if (pathSourceModel) {
407 
408  connect(pathSourceModel.get(),
411 
412  connect(pathSourceModel.get(), SIGNAL(completionChanged(ModelId)),
413  this, SLOT(pathSourceCompletionChanged(ModelId)));
414 
415  constructPath();
417 
418  if (pathSourceModel->isReady()) {
420  }
421  }
422 }
423 
424 void
426 {
427  m_path.reset(new Path(path));
428  m_pathComplete = true;
430 }
431 
432 void
433 AlignmentModel::toXml(QTextStream &stream,
434  QString indent,
435  QString extraAttributes) const
436 {
437  if (!m_path) {
438  SVDEBUG << "AlignmentModel::toXml: no path" << endl;
439  return;
440  }
441 
442  m_path->toXml(stream, indent, "");
443 
444  Model::toXml(stream, indent,
445  QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4")
448  .arg(m_path->getExportId())
449  .arg(extraAttributes));
450 }
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
bool isOK() const override
Return true if the model was constructed successfully.
std::set< PathPoint > Points
Definition: Path.h:70
bool isNone() const
Definition: ById.h:133
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
Definition: Model.cpp:204
void constructPath() const
bool isReady(int *completion=0) const override
Return true if the model has finished loading or calculating all its data, for a model that is capabl...
void setPathFrom(ModelId pathSource)
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
void setPath(const Path &path)
sv_frame_t toReference(sv_frame_t frame) const
void pathSourceCompletionChanged(ModelId)
ModelId m_pathSource
void setCompletion(int completion)
sv_frame_t fromReference(sv_frame_t frame) const
void pathSourceChangedWithin(ModelId, sv_frame_t startFrame, sv_frame_t endFrame)
AlignmentModel(ModelId reference, ModelId aligned, ModelId path)
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
const Points & getPoints() const
Definition: Path.h:79
Id getId() const
Return an id for this object.
Definition: ById.h:193
sv_frame_t getTrueEndFrame() const override
Return the audio frame at the end of the model.
sv_samplerate_t getSampleRate() const override
Return the frame rate in frames per second.
Definition: Path.h:25
std::unique_ptr< Path > m_reversePath
Definition: Path.h:60
sv_frame_t performAlignment(const Path &path, sv_frame_t frame) const
const ZoomConstraint * getZoomConstraint() const override
If this model imposes a zoom constraint, i.e.
std::unique_ptr< Path > m_path
sv_frame_t getStartFrame() const override
Return the first audio frame spanned by the model.
void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
#define SVDEBUG
Definition: Debug.h:106
ZoomConstraint is a simple interface that describes a limitation on the available zoom sizes for a vi...
ModelId getReferenceModel() const
#define SVCERR
Definition: Debug.h:109
static int getExportId(Id id)
If the Item type is an XmlExportable, return the export ID of the given item ID.
Definition: ById.h:263
int m_explicitlySetCompletion
ModelId getAlignedModel() const
std::vector< Event > EventVector
Definition: Event.h:494
void constructReversePath() const
void completionChanged(ModelId)
Definition: ById.h:115
static std::shared_ptr< Item > get(Id id)
Definition: ById.h:251