annotate data/model/AlignmentModel.cpp @ 1305:9f9f55a8af92 mp3-gapless

Add gapless flag to MP3FileReader, and implement trimming the delay samples from the start (padding is not yet trimmed from end)
author Chris Cannam
date Tue, 29 Nov 2016 11:35:56 +0000
parents c1b522cfd1ff
children 48e9f538e6e9
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@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@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@1038 77 sv_frame_t
Chris@297 78 AlignmentModel::getStartFrame() const
Chris@297 79 {
Chris@1038 80 sv_frame_t a = m_reference->getStartFrame();
Chris@1038 81 sv_frame_t b = m_aligned->getStartFrame();
Chris@297 82 return std::min(a, b);
Chris@297 83 }
Chris@297 84
Chris@1038 85 sv_frame_t
Chris@297 86 AlignmentModel::getEndFrame() const
Chris@297 87 {
Chris@1038 88 sv_frame_t a = m_reference->getEndFrame();
Chris@1038 89 sv_frame_t b = m_aligned->getEndFrame();
Chris@297 90 return std::max(a, b);
Chris@297 91 }
Chris@297 92
Chris@1040 93 sv_samplerate_t
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 bool
Chris@297 100 AlignmentModel::isReady(int *completion) const
Chris@297 101 {
Chris@411 102 if (!m_pathBegun && m_rawPath) {
Chris@338 103 if (completion) *completion = 0;
Chris@323 104 return false;
Chris@323 105 }
Chris@1016 106 if (m_pathComplete) {
Chris@338 107 if (completion) *completion = 100;
Chris@338 108 return true;
Chris@338 109 }
Chris@1016 110 if (!m_rawPath) {
Chris@1016 111 // lack of raw path could mean path is complete (in which case
Chris@1016 112 // m_pathComplete true above) or else no alignment has been
Chris@1016 113 // set at all yet (this case)
Chris@1016 114 if (completion) *completion = 0;
Chris@1016 115 return false;
Chris@1016 116 }
Chris@338 117 return m_rawPath->isReady(completion);
Chris@297 118 }
Chris@297 119
Chris@297 120 const ZoomConstraint *
Chris@297 121 AlignmentModel::getZoomConstraint() const
Chris@297 122 {
Chris@338 123 return 0;
Chris@297 124 }
Chris@297 125
Chris@297 126 const Model *
Chris@297 127 AlignmentModel::getReferenceModel() const
Chris@297 128 {
Chris@297 129 return m_reference;
Chris@297 130 }
Chris@297 131
Chris@297 132 const Model *
Chris@297 133 AlignmentModel::getAlignedModel() const
Chris@297 134 {
Chris@297 135 return m_aligned;
Chris@297 136 }
Chris@297 137
Chris@1038 138 sv_frame_t
Chris@1038 139 AlignmentModel::toReference(sv_frame_t frame) const
Chris@297 140 {
Chris@376 141 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 142 cerr << "AlignmentModel::toReference(" << frame << ")" << endl;
Chris@376 143 #endif
Chris@371 144 if (!m_path) {
Chris@371 145 if (!m_rawPath) return frame;
Chris@371 146 constructPath();
Chris@371 147 }
Chris@339 148 return align(m_path, frame);
Chris@297 149 }
Chris@297 150
Chris@1038 151 sv_frame_t
Chris@1038 152 AlignmentModel::fromReference(sv_frame_t frame) const
Chris@297 153 {
Chris@376 154 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 155 cerr << "AlignmentModel::fromReference(" << frame << ")" << endl;
Chris@376 156 #endif
Chris@371 157 if (!m_reversePath) {
Chris@371 158 if (!m_rawPath) return frame;
Chris@371 159 constructReversePath();
Chris@371 160 }
Chris@339 161 return align(m_reversePath, frame);
Chris@297 162 }
Chris@297 163
Chris@297 164 void
Chris@297 165 AlignmentModel::pathChanged()
Chris@297 166 {
Chris@339 167 if (m_pathComplete) {
Chris@843 168 cerr << "AlignmentModel: deleting raw path model" << endl;
Chris@407 169 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@339 170 delete m_rawPath;
Chris@339 171 m_rawPath = 0;
Chris@339 172 }
Chris@297 173 }
Chris@297 174
Chris@297 175 void
Chris@1038 176 AlignmentModel::pathChangedWithin(sv_frame_t, sv_frame_t)
Chris@297 177 {
Chris@297 178 if (!m_pathComplete) return;
Chris@338 179 constructPath();
Chris@297 180 constructReversePath();
Chris@297 181 }
Chris@297 182
Chris@297 183 void
Chris@297 184 AlignmentModel::pathCompletionChanged()
Chris@297 185 {
Chris@339 186 if (!m_rawPath) return;
Chris@323 187 m_pathBegun = true;
Chris@323 188
Chris@297 189 if (!m_pathComplete) {
Chris@338 190
Chris@297 191 int completion = 0;
Chris@338 192 m_rawPath->isReady(&completion);
Chris@338 193
Chris@376 194 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 195 cerr << "AlignmentModel::pathCompletionChanged: completion = "
Chris@687 196 << completion << endl;
Chris@376 197 #endif
Chris@338 198
Chris@323 199 m_pathComplete = (completion == 100);
Chris@338 200
Chris@297 201 if (m_pathComplete) {
Chris@338 202
Chris@338 203 constructPath();
Chris@297 204 constructReversePath();
Chris@407 205
Chris@407 206 if (m_inputModel) m_inputModel->aboutToDelete();
Chris@297 207 delete m_inputModel;
Chris@297 208 m_inputModel = 0;
Chris@297 209 }
Chris@297 210 }
Chris@323 211
Chris@297 212 emit completionChanged();
Chris@297 213 }
Chris@297 214
Chris@297 215 void
Chris@338 216 AlignmentModel::constructPath() const
Chris@297 217 {
Chris@338 218 if (!m_path) {
Chris@338 219 if (!m_rawPath) {
Chris@843 220 cerr << "ERROR: AlignmentModel::constructPath: "
Chris@843 221 << "No raw path available" << endl;
Chris@338 222 return;
Chris@338 223 }
Chris@338 224 m_path = new PathModel
Chris@338 225 (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false);
Chris@338 226 } else {
Chris@338 227 if (!m_rawPath) return;
Chris@297 228 }
Chris@297 229
Chris@338 230 m_path->clear();
Chris@297 231
Chris@338 232 SparseTimeValueModel::PointList points = m_rawPath->getPoints();
Chris@297 233
Chris@297 234 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@297 235 i != points.end(); ++i) {
Chris@1038 236 sv_frame_t frame = i->frame;
Chris@1038 237 double value = i->value;
Chris@1038 238 sv_frame_t rframe = lrint(value * m_aligned->getSampleRate());
Chris@338 239 m_path->addPoint(PathPoint(frame, rframe));
Chris@297 240 }
Chris@297 241
Chris@376 242 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 243 cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 244 #endif
Chris@338 245 }
Chris@338 246
Chris@338 247 void
Chris@338 248 AlignmentModel::constructReversePath() const
Chris@338 249 {
Chris@338 250 if (!m_reversePath) {
Chris@407 251 if (!m_path) {
Chris@843 252 cerr << "ERROR: AlignmentModel::constructReversePath: "
Chris@843 253 << "No forward path available" << endl;
Chris@407 254 return;
Chris@407 255 }
Chris@407 256 m_reversePath = new PathModel
Chris@407 257 (m_path->getSampleRate(), m_path->getResolution(), false);
Chris@338 258 } else {
Chris@407 259 if (!m_path) return;
Chris@338 260 }
Chris@338 261
Chris@338 262 m_reversePath->clear();
Chris@407 263
Chris@407 264 PathModel::PointList points = m_path->getPoints();
Chris@407 265
Chris@407 266 for (PathModel::PointList::const_iterator i = points.begin();
Chris@407 267 i != points.end(); ++i) {
Chris@1038 268 sv_frame_t frame = i->frame;
Chris@1038 269 sv_frame_t rframe = i->mapframe;
Chris@407 270 m_reversePath->addPoint(PathPoint(rframe, frame));
Chris@407 271 }
Chris@338 272
Chris@376 273 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 274 cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 275 #endif
Chris@297 276 }
Chris@297 277
Chris@1038 278 sv_frame_t
Chris@1038 279 AlignmentModel::align(PathModel *path, sv_frame_t frame) const
Chris@297 280 {
Chris@339 281 if (!path) return frame;
Chris@339 282
Chris@338 283 // The path consists of a series of points, each with frame equal
Chris@338 284 // to the frame on the source model and mapframe equal to the
Chris@338 285 // frame on the target model. Both should be monotonically
Chris@338 286 // increasing.
Chris@297 287
Chris@338 288 const PathModel::PointList &points = path->getPoints();
Chris@297 289
Chris@297 290 if (points.empty()) {
Chris@379 291 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 292 cerr << "AlignmentModel::align: No points" << endl;
Chris@379 293 #endif
Chris@297 294 return frame;
Chris@297 295 }
Chris@297 296
Chris@376 297 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 298 cerr << "AlignmentModel::align: frame " << frame << " requested" << endl;
Chris@376 299 #endif
Chris@376 300
Chris@338 301 PathModel::Point point(frame);
Chris@338 302 PathModel::PointList::const_iterator i = points.lower_bound(point);
Chris@376 303 if (i == points.end()) {
Chris@376 304 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 305 cerr << "Note: i == points.end()" << endl;
Chris@376 306 #endif
Chris@376 307 --i;
Chris@376 308 }
Chris@1038 309 while (i != points.begin() && i->frame > frame) --i;
Chris@297 310
Chris@1038 311 sv_frame_t foundFrame = i->frame;
Chris@1038 312 sv_frame_t foundMapFrame = i->mapframe;
Chris@297 313
Chris@1038 314 sv_frame_t followingFrame = foundFrame;
Chris@1038 315 sv_frame_t followingMapFrame = foundMapFrame;
Chris@297 316
Chris@312 317 if (++i != points.end()) {
Chris@376 318 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 319 cerr << "another point available" << endl;
Chris@376 320 #endif
Chris@312 321 followingFrame = i->frame;
Chris@338 322 followingMapFrame = i->mapframe;
Chris@376 323 } else {
Chris@376 324 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 325 cerr << "no other point available" << endl;
Chris@376 326 #endif
Chris@376 327 }
Chris@312 328
Chris@1075 329 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 330 cerr << "foundFrame = " << foundFrame << ", foundMapFrame = " << foundMapFrame
Chris@1075 331 << ", followingFrame = " << followingFrame << ", followingMapFrame = "
Chris@1075 332 << followingMapFrame << endl;
Chris@1075 333 #endif
Chris@1075 334
Chris@338 335 if (foundMapFrame < 0) return 0;
Chris@312 336
Chris@1038 337 sv_frame_t resultFrame = foundMapFrame;
Chris@312 338
Chris@1038 339 if (followingFrame != foundFrame && frame > foundFrame) {
Chris@1038 340 double interp =
Chris@1038 341 double(frame - foundFrame) /
Chris@1038 342 double(followingFrame - foundFrame);
Chris@1038 343 resultFrame += lrint(double(followingMapFrame - foundMapFrame) * interp);
Chris@312 344 }
Chris@312 345
Chris@376 346 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 347 cerr << "AlignmentModel::align: resultFrame = " << resultFrame << endl;
Chris@376 348 #endif
Chris@312 349
Chris@312 350 return resultFrame;
Chris@297 351 }
Chris@407 352
Chris@407 353 void
Chris@1016 354 AlignmentModel::setPathFrom(SparseTimeValueModel *rawpath)
Chris@1016 355 {
Chris@1016 356 if (m_rawPath) m_rawPath->aboutToDelete();
Chris@1016 357 delete m_rawPath;
Chris@1016 358
Chris@1016 359 m_rawPath = rawpath;
Chris@1016 360
Chris@1016 361 connect(m_rawPath, SIGNAL(modelChanged()),
Chris@1016 362 this, SLOT(pathChanged()));
Chris@1016 363
Chris@1046 364 connect(m_rawPath, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
Chris@1046 365 this, SLOT(pathChangedWithin(sv_frame_t, sv_frame_t)));
Chris@1016 366
Chris@1016 367 connect(m_rawPath, SIGNAL(completionChanged()),
Chris@1016 368 this, SLOT(pathCompletionChanged()));
Chris@1016 369
Chris@1016 370 constructPath();
Chris@1016 371 constructReversePath();
Chris@1016 372
Chris@1016 373 if (m_rawPath->isReady()) {
Chris@1016 374 pathCompletionChanged();
Chris@1016 375 }
Chris@1016 376 }
Chris@1016 377
Chris@1016 378 void
Chris@407 379 AlignmentModel::setPath(PathModel *path)
Chris@407 380 {
Chris@407 381 if (m_path) m_path->aboutToDelete();
Chris@407 382 delete m_path;
Chris@407 383 m_path = path;
Chris@662 384 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 385 cerr << "AlignmentModel::setPath: path = " << m_path << endl;
Chris@662 386 #endif
Chris@407 387 constructReversePath();
Chris@662 388 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 389 cerr << "AlignmentModel::setPath: after construction path = "
Chris@687 390 << m_path << ", rpath = " << m_reversePath << endl;
Chris@662 391 #endif
Chris@407 392 }
Chris@297 393
Chris@407 394 void
Chris@407 395 AlignmentModel::toXml(QTextStream &stream,
Chris@407 396 QString indent,
Chris@407 397 QString extraAttributes) const
Chris@407 398 {
Chris@407 399 if (!m_path) {
Chris@690 400 SVDEBUG << "AlignmentModel::toXml: no path" << endl;
Chris@407 401 return;
Chris@407 402 }
Chris@407 403
Chris@407 404 m_path->toXml(stream, indent, "");
Chris@407 405
Chris@407 406 Model::toXml(stream, indent,
Chris@407 407 QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4")
Chris@407 408 .arg(getObjectExportId(m_reference))
Chris@407 409 .arg(getObjectExportId(m_aligned))
Chris@407 410 .arg(getObjectExportId(m_path))
Chris@407 411 .arg(extraAttributes));
Chris@407 412 }
Chris@407 413
Chris@407 414