annotate data/model/AlignmentModel.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents cd42620e3f40
children 13f53ecc8bb5
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@297 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@338 30 m_path(0),
Chris@297 31 m_reversePath(0),
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@947 40 connect(m_rawPath, SIGNAL(modelChangedWithin(int, int)),
Chris@947 41 this, SLOT(pathChangedWithin(int, int)));
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@407 57 if (m_inputModel) m_inputModel->aboutToDelete();
Chris@297 58 delete m_inputModel;
Chris@407 59
Chris@407 60 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@338 61 delete m_rawPath;
Chris@407 62
Chris@407 63 if (m_path) m_path->aboutToDelete();
Chris@297 64 delete m_path;
Chris@407 65
Chris@407 66 if (m_reversePath) m_reversePath->aboutToDelete();
Chris@297 67 delete m_reversePath;
Chris@297 68 }
Chris@297 69
Chris@297 70 bool
Chris@297 71 AlignmentModel::isOK() const
Chris@297 72 {
Chris@338 73 if (m_rawPath) return m_rawPath->isOK();
Chris@338 74 else return true;
Chris@297 75 }
Chris@297 76
Chris@929 77 int
Chris@297 78 AlignmentModel::getStartFrame() const
Chris@297 79 {
Chris@929 80 int a = m_reference->getStartFrame();
Chris@929 81 int b = m_aligned->getStartFrame();
Chris@297 82 return std::min(a, b);
Chris@297 83 }
Chris@297 84
Chris@929 85 int
Chris@297 86 AlignmentModel::getEndFrame() const
Chris@297 87 {
Chris@929 88 int a = m_reference->getEndFrame();
Chris@929 89 int b = m_aligned->getEndFrame();
Chris@297 90 return std::max(a, b);
Chris@297 91 }
Chris@297 92
Chris@929 93 int
Chris@297 94 AlignmentModel::getSampleRate() const
Chris@297 95 {
Chris@297 96 return m_reference->getSampleRate();
Chris@297 97 }
Chris@297 98
Chris@297 99 Model *
Chris@297 100 AlignmentModel::clone() const
Chris@297 101 {
Chris@297 102 return new AlignmentModel
Chris@297 103 (m_reference, m_aligned,
Chris@297 104 m_inputModel ? m_inputModel->clone() : 0,
Chris@338 105 m_rawPath ? static_cast<SparseTimeValueModel *>(m_rawPath->clone()) : 0);
Chris@297 106 }
Chris@297 107
Chris@297 108 bool
Chris@297 109 AlignmentModel::isReady(int *completion) const
Chris@297 110 {
Chris@411 111 if (!m_pathBegun && m_rawPath) {
Chris@338 112 if (completion) *completion = 0;
Chris@323 113 return false;
Chris@323 114 }
Chris@338 115 if (m_pathComplete || !m_rawPath) {
Chris@338 116 if (completion) *completion = 100;
Chris@338 117 return true;
Chris@338 118 }
Chris@338 119 return m_rawPath->isReady(completion);
Chris@297 120 }
Chris@297 121
Chris@297 122 const ZoomConstraint *
Chris@297 123 AlignmentModel::getZoomConstraint() const
Chris@297 124 {
Chris@338 125 return 0;
Chris@297 126 }
Chris@297 127
Chris@297 128 const Model *
Chris@297 129 AlignmentModel::getReferenceModel() const
Chris@297 130 {
Chris@297 131 return m_reference;
Chris@297 132 }
Chris@297 133
Chris@297 134 const Model *
Chris@297 135 AlignmentModel::getAlignedModel() const
Chris@297 136 {
Chris@297 137 return m_aligned;
Chris@297 138 }
Chris@297 139
Chris@929 140 int
Chris@929 141 AlignmentModel::toReference(int frame) const
Chris@297 142 {
Chris@376 143 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 144 SVDEBUG << "AlignmentModel::toReference(" << frame << ")" << endl;
Chris@376 145 #endif
Chris@371 146 if (!m_path) {
Chris@371 147 if (!m_rawPath) return frame;
Chris@371 148 constructPath();
Chris@371 149 }
Chris@339 150 return align(m_path, frame);
Chris@297 151 }
Chris@297 152
Chris@929 153 int
Chris@929 154 AlignmentModel::fromReference(int frame) const
Chris@297 155 {
Chris@376 156 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 157 SVDEBUG << "AlignmentModel::fromReference(" << frame << ")" << endl;
Chris@376 158 #endif
Chris@371 159 if (!m_reversePath) {
Chris@371 160 if (!m_rawPath) return frame;
Chris@371 161 constructReversePath();
Chris@371 162 }
Chris@339 163 return align(m_reversePath, frame);
Chris@297 164 }
Chris@297 165
Chris@297 166 void
Chris@297 167 AlignmentModel::pathChanged()
Chris@297 168 {
Chris@339 169 if (m_pathComplete) {
Chris@843 170 cerr << "AlignmentModel: deleting raw path model" << endl;
Chris@407 171 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@339 172 delete m_rawPath;
Chris@339 173 m_rawPath = 0;
Chris@339 174 }
Chris@297 175 }
Chris@297 176
Chris@297 177 void
Chris@947 178 AlignmentModel::pathChangedWithin(int, int)
Chris@297 179 {
Chris@297 180 if (!m_pathComplete) return;
Chris@338 181 constructPath();
Chris@297 182 constructReversePath();
Chris@297 183 }
Chris@297 184
Chris@297 185 void
Chris@297 186 AlignmentModel::pathCompletionChanged()
Chris@297 187 {
Chris@339 188 if (!m_rawPath) return;
Chris@323 189 m_pathBegun = true;
Chris@323 190
Chris@297 191 if (!m_pathComplete) {
Chris@338 192
Chris@297 193 int completion = 0;
Chris@338 194 m_rawPath->isReady(&completion);
Chris@338 195
Chris@376 196 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 197 SVDEBUG << "AlignmentModel::pathCompletionChanged: completion = "
Chris@687 198 << completion << endl;
Chris@376 199 #endif
Chris@338 200
Chris@323 201 m_pathComplete = (completion == 100);
Chris@338 202
Chris@297 203 if (m_pathComplete) {
Chris@338 204
Chris@338 205 constructPath();
Chris@297 206 constructReversePath();
Chris@407 207
Chris@407 208 if (m_inputModel) m_inputModel->aboutToDelete();
Chris@297 209 delete m_inputModel;
Chris@297 210 m_inputModel = 0;
Chris@297 211 }
Chris@297 212 }
Chris@323 213
Chris@297 214 emit completionChanged();
Chris@297 215 }
Chris@297 216
Chris@297 217 void
Chris@338 218 AlignmentModel::constructPath() const
Chris@297 219 {
Chris@338 220 if (!m_path) {
Chris@338 221 if (!m_rawPath) {
Chris@843 222 cerr << "ERROR: AlignmentModel::constructPath: "
Chris@843 223 << "No raw path available" << endl;
Chris@338 224 return;
Chris@338 225 }
Chris@338 226 m_path = new PathModel
Chris@338 227 (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false);
Chris@338 228 } else {
Chris@338 229 if (!m_rawPath) return;
Chris@297 230 }
Chris@297 231
Chris@338 232 m_path->clear();
Chris@297 233
Chris@338 234 SparseTimeValueModel::PointList points = m_rawPath->getPoints();
Chris@297 235
Chris@297 236 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@297 237 i != points.end(); ++i) {
Chris@297 238 long frame = i->frame;
Chris@297 239 float value = i->value;
Chris@297 240 long rframe = lrintf(value * m_aligned->getSampleRate());
Chris@338 241 m_path->addPoint(PathPoint(frame, rframe));
Chris@297 242 }
Chris@297 243
Chris@376 244 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 245 SVDEBUG << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 246 #endif
Chris@338 247 }
Chris@338 248
Chris@338 249 void
Chris@338 250 AlignmentModel::constructReversePath() const
Chris@338 251 {
Chris@338 252 if (!m_reversePath) {
Chris@407 253 if (!m_path) {
Chris@843 254 cerr << "ERROR: AlignmentModel::constructReversePath: "
Chris@843 255 << "No forward path available" << endl;
Chris@407 256 return;
Chris@407 257 }
Chris@407 258 m_reversePath = new PathModel
Chris@407 259 (m_path->getSampleRate(), m_path->getResolution(), false);
Chris@338 260 } else {
Chris@407 261 if (!m_path) return;
Chris@338 262 }
Chris@338 263
Chris@338 264 m_reversePath->clear();
Chris@407 265
Chris@407 266 PathModel::PointList points = m_path->getPoints();
Chris@407 267
Chris@407 268 for (PathModel::PointList::const_iterator i = points.begin();
Chris@407 269 i != points.end(); ++i) {
Chris@407 270 long frame = i->frame;
Chris@407 271 long rframe = i->mapframe;
Chris@407 272 m_reversePath->addPoint(PathPoint(rframe, frame));
Chris@407 273 }
Chris@338 274
Chris@376 275 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 276 SVDEBUG << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 277 #endif
Chris@297 278 }
Chris@297 279
Chris@929 280 int
Chris@929 281 AlignmentModel::align(PathModel *path, int frame) const
Chris@297 282 {
Chris@339 283 if (!path) return frame;
Chris@339 284
Chris@338 285 // The path consists of a series of points, each with frame equal
Chris@338 286 // to the frame on the source model and mapframe equal to the
Chris@338 287 // frame on the target model. Both should be monotonically
Chris@338 288 // increasing.
Chris@297 289
Chris@338 290 const PathModel::PointList &points = path->getPoints();
Chris@297 291
Chris@297 292 if (points.empty()) {
Chris@379 293 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 294 SVDEBUG << "AlignmentModel::align: No points" << endl;
Chris@379 295 #endif
Chris@297 296 return frame;
Chris@297 297 }
Chris@297 298
Chris@376 299 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 300 SVDEBUG << "AlignmentModel::align: frame " << frame << " requested" << endl;
Chris@376 301 #endif
Chris@376 302
Chris@338 303 PathModel::Point point(frame);
Chris@338 304 PathModel::PointList::const_iterator i = points.lower_bound(point);
Chris@376 305 if (i == points.end()) {
Chris@376 306 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 307 cerr << "Note: i == points.end()" << endl;
Chris@376 308 #endif
Chris@376 309 --i;
Chris@376 310 }
Chris@338 311 while (i != points.begin() && i->frame > long(frame)) --i;
Chris@297 312
Chris@312 313 long foundFrame = i->frame;
Chris@338 314 long foundMapFrame = i->mapframe;
Chris@297 315
Chris@312 316 long followingFrame = foundFrame;
Chris@338 317 long followingMapFrame = foundMapFrame;
Chris@297 318
Chris@312 319 if (++i != points.end()) {
Chris@376 320 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 321 cerr << "another point available" << endl;
Chris@376 322 #endif
Chris@312 323 followingFrame = i->frame;
Chris@338 324 followingMapFrame = i->mapframe;
Chris@376 325 } else {
Chris@376 326 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 327 cerr << "no other point available" << endl;
Chris@376 328 #endif
Chris@376 329 }
Chris@312 330
Chris@338 331 if (foundMapFrame < 0) return 0;
Chris@312 332
Chris@929 333 int resultFrame = foundMapFrame;
Chris@312 334
Chris@338 335 if (followingFrame != foundFrame && long(frame) > foundFrame) {
Chris@338 336 float interp =
Chris@338 337 float(frame - foundFrame) /
Chris@338 338 float(followingFrame - foundFrame);
Chris@338 339 resultFrame += lrintf((followingMapFrame - foundMapFrame) * interp);
Chris@312 340 }
Chris@312 341
Chris@376 342 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 343 SVDEBUG << "AlignmentModel::align: resultFrame = " << resultFrame << endl;
Chris@376 344 #endif
Chris@312 345
Chris@312 346 return resultFrame;
Chris@297 347 }
Chris@407 348
Chris@407 349 void
Chris@407 350 AlignmentModel::setPath(PathModel *path)
Chris@407 351 {
Chris@407 352 if (m_path) m_path->aboutToDelete();
Chris@407 353 delete m_path;
Chris@407 354 m_path = path;
Chris@662 355 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 356 SVDEBUG << "AlignmentModel::setPath: path = " << m_path << endl;
Chris@662 357 #endif
Chris@407 358 constructReversePath();
Chris@662 359 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@690 360 SVDEBUG << "AlignmentModel::setPath: after construction path = "
Chris@687 361 << m_path << ", rpath = " << m_reversePath << endl;
Chris@662 362 #endif
Chris@407 363 }
Chris@297 364
Chris@407 365 void
Chris@407 366 AlignmentModel::toXml(QTextStream &stream,
Chris@407 367 QString indent,
Chris@407 368 QString extraAttributes) const
Chris@407 369 {
Chris@407 370 if (!m_path) {
Chris@690 371 SVDEBUG << "AlignmentModel::toXml: no path" << endl;
Chris@407 372 return;
Chris@407 373 }
Chris@407 374
Chris@407 375 m_path->toXml(stream, indent, "");
Chris@407 376
Chris@407 377 Model::toXml(stream, indent,
Chris@407 378 QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4")
Chris@407 379 .arg(getObjectExportId(m_reference))
Chris@407 380 .arg(getObjectExportId(m_aligned))
Chris@407 381 .arg(getObjectExportId(m_path))
Chris@407 382 .arg(extraAttributes));
Chris@407 383 }
Chris@407 384
Chris@407 385