annotate data/model/AlignmentModel.cpp @ 1677:f97d64b8674f single-point

Make XmlExportables store their export IDs and always obtain a new one, avoiding reuse when an object is allocated at the same heap location as a previous one. This makes the ID system stable enough to be used in the export tests.
author Chris Cannam
date Thu, 28 Mar 2019 11:55:02 +0000
parents c7bf655955ae
children e73baeead27f
rev   line source
Chris@297 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@297 2
Chris@297 3 /*
Chris@297 4 Sonic Visualiser
Chris@297 5 An audio file viewer and annotation editor.
Chris@297 6 Centre for Digital Music, Queen Mary, University of London.
Chris@297 7 This file copyright 2007 QMUL.
Chris@297 8
Chris@297 9 This program is free software; you can redistribute it and/or
Chris@297 10 modify it under the terms of the GNU General Public License as
Chris@297 11 published by the Free Software Foundation; either version 2 of the
Chris@297 12 License, or (at your option) any later version. See the file
Chris@297 13 COPYING included with this distribution for more information.
Chris@297 14 */
Chris@297 15
Chris@297 16 #include "AlignmentModel.h"
Chris@297 17
Chris@297 18 #include "SparseTimeValueModel.h"
Chris@297 19
Chris@409 20 //#define DEBUG_ALIGNMENT_MODEL 1
Chris@376 21
Chris@297 22 AlignmentModel::AlignmentModel(Model *reference,
Chris@297 23 Model *aligned,
Chris@297 24 Model *inputModel,
Chris@1429 25 SparseTimeValueModel *path) :
Chris@297 26 m_reference(reference),
Chris@297 27 m_aligned(aligned),
Chris@297 28 m_inputModel(inputModel),
Chris@338 29 m_rawPath(path),
Chris@1582 30 m_path(nullptr),
Chris@1582 31 m_reversePath(nullptr),
Chris@323 32 m_pathBegun(false),
Chris@297 33 m_pathComplete(false)
Chris@297 34 {
Chris@371 35 if (m_rawPath) {
Chris@297 36
Chris@371 37 connect(m_rawPath, SIGNAL(modelChanged()),
Chris@371 38 this, SLOT(pathChanged()));
Chris@297 39
Chris@1038 40 connect(m_rawPath, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
Chris@1038 41 this, SLOT(pathChangedWithin(sv_frame_t, sv_frame_t)));
Chris@371 42
Chris@371 43 connect(m_rawPath, SIGNAL(completionChanged()),
Chris@371 44 this, SLOT(pathCompletionChanged()));
Chris@407 45
Chris@407 46 constructPath();
Chris@407 47 constructReversePath();
Chris@371 48 }
Chris@297 49
Chris@407 50 if (m_rawPath && m_rawPath->isReady()) {
Chris@407 51 pathCompletionChanged();
Chris@407 52 }
Chris@297 53 }
Chris@297 54
Chris@297 55 AlignmentModel::~AlignmentModel()
Chris@297 56 {
Chris@1666 57 SVDEBUG << "AlignmentModel(" << this << ")::~AlignmentModel()" << endl;
Chris@1666 58
Chris@407 59 if (m_inputModel) m_inputModel->aboutToDelete();
Chris@297 60 delete m_inputModel;
Chris@407 61
Chris@407 62 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@338 63 delete m_rawPath;
Chris@407 64
Chris@407 65 if (m_path) m_path->aboutToDelete();
Chris@297 66 delete m_path;
Chris@407 67
Chris@407 68 if (m_reversePath) m_reversePath->aboutToDelete();
Chris@297 69 delete m_reversePath;
Chris@297 70 }
Chris@297 71
Chris@297 72 bool
Chris@297 73 AlignmentModel::isOK() const
Chris@297 74 {
Chris@338 75 if (m_rawPath) return m_rawPath->isOK();
Chris@338 76 else return true;
Chris@297 77 }
Chris@297 78
Chris@1038 79 sv_frame_t
Chris@297 80 AlignmentModel::getStartFrame() const
Chris@297 81 {
Chris@1038 82 sv_frame_t a = m_reference->getStartFrame();
Chris@1038 83 sv_frame_t b = m_aligned->getStartFrame();
Chris@297 84 return std::min(a, b);
Chris@297 85 }
Chris@297 86
Chris@1038 87 sv_frame_t
Chris@297 88 AlignmentModel::getEndFrame() const
Chris@297 89 {
Chris@1038 90 sv_frame_t a = m_reference->getEndFrame();
Chris@1038 91 sv_frame_t b = m_aligned->getEndFrame();
Chris@297 92 return std::max(a, b);
Chris@297 93 }
Chris@297 94
Chris@1040 95 sv_samplerate_t
Chris@297 96 AlignmentModel::getSampleRate() const
Chris@297 97 {
Chris@297 98 return m_reference->getSampleRate();
Chris@297 99 }
Chris@297 100
Chris@297 101 bool
Chris@297 102 AlignmentModel::isReady(int *completion) const
Chris@297 103 {
Chris@411 104 if (!m_pathBegun && m_rawPath) {
Chris@338 105 if (completion) *completion = 0;
Chris@1561 106 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1561 107 SVDEBUG << "AlignmentModel::isReady: path not begun" << endl;
Chris@1561 108 #endif
Chris@323 109 return false;
Chris@323 110 }
Chris@1016 111 if (m_pathComplete) {
Chris@338 112 if (completion) *completion = 100;
Chris@1561 113 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1561 114 SVDEBUG << "AlignmentModel::isReady: path complete" << endl;
Chris@1561 115 #endif
Chris@338 116 return true;
Chris@338 117 }
Chris@1016 118 if (!m_rawPath) {
Chris@1016 119 // lack of raw path could mean path is complete (in which case
Chris@1016 120 // m_pathComplete true above) or else no alignment has been
Chris@1016 121 // set at all yet (this case)
Chris@1016 122 if (completion) *completion = 0;
Chris@1561 123 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1561 124 SVDEBUG << "AlignmentModel::isReady: no raw path" << endl;
Chris@1561 125 #endif
Chris@1016 126 return false;
Chris@1016 127 }
Chris@338 128 return m_rawPath->isReady(completion);
Chris@297 129 }
Chris@297 130
Chris@297 131 const ZoomConstraint *
Chris@297 132 AlignmentModel::getZoomConstraint() const
Chris@297 133 {
Chris@1582 134 return nullptr;
Chris@297 135 }
Chris@297 136
Chris@297 137 const Model *
Chris@297 138 AlignmentModel::getReferenceModel() const
Chris@297 139 {
Chris@297 140 return m_reference;
Chris@297 141 }
Chris@297 142
Chris@297 143 const Model *
Chris@297 144 AlignmentModel::getAlignedModel() const
Chris@297 145 {
Chris@297 146 return m_aligned;
Chris@297 147 }
Chris@297 148
Chris@1038 149 sv_frame_t
Chris@1038 150 AlignmentModel::toReference(sv_frame_t frame) const
Chris@297 151 {
Chris@376 152 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 153 cerr << "AlignmentModel::toReference(" << frame << ")" << endl;
Chris@376 154 #endif
Chris@371 155 if (!m_path) {
Chris@371 156 if (!m_rawPath) return frame;
Chris@371 157 constructPath();
Chris@371 158 }
Chris@339 159 return align(m_path, frame);
Chris@297 160 }
Chris@297 161
Chris@1038 162 sv_frame_t
Chris@1038 163 AlignmentModel::fromReference(sv_frame_t frame) const
Chris@297 164 {
Chris@376 165 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 166 cerr << "AlignmentModel::fromReference(" << frame << ")" << endl;
Chris@376 167 #endif
Chris@371 168 if (!m_reversePath) {
Chris@371 169 if (!m_rawPath) return frame;
Chris@371 170 constructReversePath();
Chris@371 171 }
Chris@339 172 return align(m_reversePath, frame);
Chris@297 173 }
Chris@297 174
Chris@297 175 void
Chris@297 176 AlignmentModel::pathChanged()
Chris@297 177 {
Chris@339 178 if (m_pathComplete) {
Chris@843 179 cerr << "AlignmentModel: deleting raw path model" << endl;
Chris@407 180 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@339 181 delete m_rawPath;
Chris@1582 182 m_rawPath = nullptr;
Chris@339 183 }
Chris@297 184 }
Chris@297 185
Chris@297 186 void
Chris@1038 187 AlignmentModel::pathChangedWithin(sv_frame_t, sv_frame_t)
Chris@297 188 {
Chris@297 189 if (!m_pathComplete) return;
Chris@338 190 constructPath();
Chris@297 191 constructReversePath();
Chris@297 192 }
Chris@297 193
Chris@297 194 void
Chris@297 195 AlignmentModel::pathCompletionChanged()
Chris@297 196 {
Chris@339 197 if (!m_rawPath) return;
Chris@323 198 m_pathBegun = true;
Chris@323 199
Chris@297 200 if (!m_pathComplete) {
Chris@338 201
Chris@297 202 int completion = 0;
Chris@338 203 m_rawPath->isReady(&completion);
Chris@338 204
Chris@376 205 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1666 206 SVCERR << "AlignmentModel::pathCompletionChanged: completion = "
Chris@1666 207 << completion << endl;
Chris@376 208 #endif
Chris@338 209
Chris@323 210 m_pathComplete = (completion == 100);
Chris@338 211
Chris@297 212 if (m_pathComplete) {
Chris@338 213
Chris@338 214 constructPath();
Chris@297 215 constructReversePath();
Chris@1666 216
Chris@1666 217 SVDEBUG << "AlignmentModel: path complete, deleting input" << endl;
Chris@407 218 if (m_inputModel) m_inputModel->aboutToDelete();
Chris@297 219 delete m_inputModel;
Chris@1582 220 m_inputModel = nullptr;
Chris@297 221 }
Chris@297 222 }
Chris@323 223
Chris@297 224 emit completionChanged();
Chris@297 225 }
Chris@297 226
Chris@297 227 void
Chris@338 228 AlignmentModel::constructPath() const
Chris@297 229 {
Chris@338 230 if (!m_path) {
Chris@338 231 if (!m_rawPath) {
Chris@843 232 cerr << "ERROR: AlignmentModel::constructPath: "
Chris@843 233 << "No raw path available" << endl;
Chris@338 234 return;
Chris@338 235 }
Chris@338 236 m_path = new PathModel
Chris@338 237 (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false);
Chris@338 238 } else {
Chris@338 239 if (!m_rawPath) return;
Chris@297 240 }
Chris@297 241
Chris@338 242 m_path->clear();
Chris@297 243
Chris@1651 244 EventVector points = m_rawPath->getAllEvents();
Chris@1651 245
Chris@1651 246 for (const auto &p: points) {
Chris@1651 247 sv_frame_t frame = p.getFrame();
Chris@1651 248 double value = p.getValue();
Chris@1038 249 sv_frame_t rframe = lrint(value * m_aligned->getSampleRate());
Chris@1662 250 m_path->add(PathPoint(frame, rframe));
Chris@297 251 }
Chris@297 252
Chris@376 253 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 254 cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 255 #endif
Chris@338 256 }
Chris@338 257
Chris@338 258 void
Chris@338 259 AlignmentModel::constructReversePath() const
Chris@338 260 {
Chris@338 261 if (!m_reversePath) {
Chris@407 262 if (!m_path) {
Chris@843 263 cerr << "ERROR: AlignmentModel::constructReversePath: "
Chris@843 264 << "No forward path available" << endl;
Chris@407 265 return;
Chris@407 266 }
Chris@407 267 m_reversePath = new PathModel
Chris@407 268 (m_path->getSampleRate(), m_path->getResolution(), false);
Chris@338 269 } else {
Chris@407 270 if (!m_path) return;
Chris@338 271 }
Chris@338 272
Chris@338 273 m_reversePath->clear();
Chris@407 274
Chris@407 275 PathModel::PointList points = m_path->getPoints();
Chris@407 276
Chris@407 277 for (PathModel::PointList::const_iterator i = points.begin();
Chris@407 278 i != points.end(); ++i) {
Chris@1038 279 sv_frame_t frame = i->frame;
Chris@1038 280 sv_frame_t rframe = i->mapframe;
Chris@1662 281 m_reversePath->add(PathPoint(rframe, frame));
Chris@407 282 }
Chris@338 283
Chris@376 284 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 285 cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 286 #endif
Chris@297 287 }
Chris@297 288
Chris@1038 289 sv_frame_t
Chris@1038 290 AlignmentModel::align(PathModel *path, sv_frame_t frame) const
Chris@297 291 {
Chris@339 292 if (!path) return frame;
Chris@339 293
Chris@338 294 // The path consists of a series of points, each with frame equal
Chris@338 295 // to the frame on the source model and mapframe equal to the
Chris@338 296 // frame on the target model. Both should be monotonically
Chris@338 297 // increasing.
Chris@297 298
Chris@338 299 const PathModel::PointList &points = path->getPoints();
Chris@297 300
Chris@297 301 if (points.empty()) {
Chris@379 302 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 303 cerr << "AlignmentModel::align: No points" << endl;
Chris@379 304 #endif
Chris@297 305 return frame;
Chris@297 306 }
Chris@297 307
Chris@376 308 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 309 cerr << "AlignmentModel::align: frame " << frame << " requested" << endl;
Chris@376 310 #endif
Chris@376 311
Chris@1662 312 PathPoint point(frame);
Chris@338 313 PathModel::PointList::const_iterator i = points.lower_bound(point);
Chris@376 314 if (i == points.end()) {
Chris@376 315 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 316 cerr << "Note: i == points.end()" << endl;
Chris@376 317 #endif
Chris@376 318 --i;
Chris@376 319 }
Chris@1038 320 while (i != points.begin() && i->frame > frame) --i;
Chris@297 321
Chris@1038 322 sv_frame_t foundFrame = i->frame;
Chris@1038 323 sv_frame_t foundMapFrame = i->mapframe;
Chris@297 324
Chris@1038 325 sv_frame_t followingFrame = foundFrame;
Chris@1038 326 sv_frame_t followingMapFrame = foundMapFrame;
Chris@297 327
Chris@312 328 if (++i != points.end()) {
Chris@376 329 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 330 cerr << "another point available" << endl;
Chris@376 331 #endif
Chris@312 332 followingFrame = i->frame;
Chris@338 333 followingMapFrame = i->mapframe;
Chris@376 334 } else {
Chris@376 335 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 336 cerr << "no other point available" << endl;
Chris@376 337 #endif
Chris@376 338 }
Chris@312 339
Chris@1075 340 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 341 cerr << "foundFrame = " << foundFrame << ", foundMapFrame = " << foundMapFrame
Chris@1075 342 << ", followingFrame = " << followingFrame << ", followingMapFrame = "
Chris@1075 343 << followingMapFrame << endl;
Chris@1075 344 #endif
Chris@1075 345
Chris@338 346 if (foundMapFrame < 0) return 0;
Chris@312 347
Chris@1038 348 sv_frame_t resultFrame = foundMapFrame;
Chris@312 349
Chris@1038 350 if (followingFrame != foundFrame && frame > foundFrame) {
Chris@1038 351 double interp =
Chris@1038 352 double(frame - foundFrame) /
Chris@1038 353 double(followingFrame - foundFrame);
Chris@1038 354 resultFrame += lrint(double(followingMapFrame - foundMapFrame) * interp);
Chris@312 355 }
Chris@312 356
Chris@376 357 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 358 cerr << "AlignmentModel::align: resultFrame = " << resultFrame << endl;
Chris@376 359 #endif
Chris@312 360
Chris@312 361 return resultFrame;
Chris@297 362 }
Chris@407 363
Chris@407 364 void
Chris@1016 365 AlignmentModel::setPathFrom(SparseTimeValueModel *rawpath)
Chris@1016 366 {
Chris@1016 367 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@1016 368 delete m_rawPath;
Chris@1016 369
Chris@1016 370 m_rawPath = rawpath;
Chris@1016 371
Chris@1016 372 connect(m_rawPath, SIGNAL(modelChanged()),
Chris@1016 373 this, SLOT(pathChanged()));
Chris@1016 374
Chris@1046 375 connect(m_rawPath, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
Chris@1046 376 this, SLOT(pathChangedWithin(sv_frame_t, sv_frame_t)));
Chris@1016 377
Chris@1016 378 connect(m_rawPath, SIGNAL(completionChanged()),
Chris@1016 379 this, SLOT(pathCompletionChanged()));
Chris@1016 380
Chris@1016 381 constructPath();
Chris@1016 382 constructReversePath();
Chris@1016 383
Chris@1016 384 if (m_rawPath->isReady()) {
Chris@1016 385 pathCompletionChanged();
Chris@1016 386 }
Chris@1016 387 }
Chris@1016 388
Chris@1016 389 void
Chris@407 390 AlignmentModel::setPath(PathModel *path)
Chris@407 391 {
Chris@407 392 if (m_path) m_path->aboutToDelete();
Chris@407 393 delete m_path;
Chris@407 394 m_path = path;
Chris@1560 395 m_pathComplete = true;
Chris@662 396 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 397 cerr << "AlignmentModel::setPath: path = " << m_path << endl;
Chris@662 398 #endif
Chris@407 399 constructReversePath();
Chris@662 400 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 401 cerr << "AlignmentModel::setPath: after construction path = "
Chris@687 402 << m_path << ", rpath = " << m_reversePath << endl;
Chris@662 403 #endif
Chris@407 404 }
Chris@297 405
Chris@407 406 void
Chris@407 407 AlignmentModel::toXml(QTextStream &stream,
Chris@407 408 QString indent,
Chris@407 409 QString extraAttributes) const
Chris@407 410 {
Chris@407 411 if (!m_path) {
Chris@690 412 SVDEBUG << "AlignmentModel::toXml: no path" << endl;
Chris@407 413 return;
Chris@407 414 }
Chris@407 415
Chris@407 416 m_path->toXml(stream, indent, "");
Chris@407 417
Chris@407 418 Model::toXml(stream, indent,
Chris@407 419 QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4")
Chris@1677 420 .arg(m_reference->getExportId())
Chris@1677 421 .arg(m_aligned->getExportId())
Chris@1677 422 .arg(m_path->getExportId())
Chris@407 423 .arg(extraAttributes));
Chris@407 424 }
Chris@407 425
Chris@407 426