annotate data/model/AlignmentModel.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents f19711ab7074
children edc2d0e635dc
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@1735 22 AlignmentModel::AlignmentModel(ModelId reference,
Chris@1735 23 ModelId aligned,
Chris@1735 24 ModelId pathSource) :
Chris@297 25 m_reference(reference),
Chris@297 26 m_aligned(aligned),
Chris@1735 27 m_pathSource(pathSource),
Chris@1582 28 m_path(nullptr),
Chris@1582 29 m_reversePath(nullptr),
Chris@323 30 m_pathBegun(false),
Chris@1774 31 m_pathComplete(false),
Chris@1774 32 m_relativePitch(0)
Chris@297 33 {
Chris@1737 34 setPathFrom(pathSource);
Chris@1696 35
Chris@1696 36 if (m_reference == m_aligned) {
Chris@1696 37 // Trivial alignment, e.g. of main model to itself, which we
Chris@1696 38 // record so that we can distinguish the reference model for
Chris@1696 39 // alignments from an unaligned model. No path required
Chris@1696 40 m_pathComplete = true;
Chris@1696 41 }
Chris@297 42 }
Chris@297 43
Chris@297 44 AlignmentModel::~AlignmentModel()
Chris@297 45 {
Chris@1691 46 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1691 47 SVCERR << "AlignmentModel(" << this << ")::~AlignmentModel()" << endl;
Chris@1691 48 #endif
Chris@297 49 }
Chris@297 50
Chris@297 51 bool
Chris@297 52 AlignmentModel::isOK() const
Chris@297 53 {
Chris@1705 54 if (m_error != "") return false;
Chris@1737 55 if (m_pathSource.isNone()) return true;
Chris@1737 56 auto pathSourceModel =
Chris@1737 57 ModelById::getAs<SparseTimeValueModel>(m_pathSource);
Chris@1737 58 if (pathSourceModel) {
Chris@1737 59 return pathSourceModel->isOK();
Chris@1737 60 }
Chris@1705 61 return true;
Chris@297 62 }
Chris@297 63
Chris@1038 64 sv_frame_t
Chris@297 65 AlignmentModel::getStartFrame() const
Chris@297 66 {
Chris@1737 67 auto reference = ModelById::get(m_reference);
Chris@1737 68 auto aligned = ModelById::get(m_aligned);
Chris@1737 69
Chris@1737 70 if (reference && aligned) {
Chris@1737 71 sv_frame_t a = reference->getStartFrame();
Chris@1737 72 sv_frame_t b = aligned->getStartFrame();
Chris@1737 73 return std::min(a, b);
Chris@1737 74 } else {
Chris@1737 75 return 0;
Chris@1737 76 }
Chris@297 77 }
Chris@297 78
Chris@1038 79 sv_frame_t
Chris@1725 80 AlignmentModel::getTrueEndFrame() const
Chris@297 81 {
Chris@1737 82 auto reference = ModelById::get(m_reference);
Chris@1737 83 auto aligned = ModelById::get(m_aligned);
Chris@1737 84
Chris@1737 85 if (reference && aligned) {
Chris@1737 86 sv_frame_t a = reference->getEndFrame();
Chris@1737 87 sv_frame_t b = aligned->getEndFrame();
Chris@1737 88 return std::max(a, b);
Chris@1737 89 } else {
Chris@1737 90 return 0;
Chris@1737 91 }
Chris@297 92 }
Chris@297 93
Chris@1040 94 sv_samplerate_t
Chris@297 95 AlignmentModel::getSampleRate() const
Chris@297 96 {
Chris@1737 97 auto reference = ModelById::get(m_reference);
Chris@1737 98 if (reference) {
Chris@1737 99 return reference->getSampleRate();
Chris@1737 100 } else {
Chris@1737 101 return 0;
Chris@1737 102 }
Chris@297 103 }
Chris@297 104
Chris@297 105 bool
Chris@297 106 AlignmentModel::isReady(int *completion) const
Chris@297 107 {
Chris@1737 108 if (!m_pathBegun && !m_pathSource.isNone()) {
Chris@338 109 if (completion) *completion = 0;
Chris@1561 110 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1696 111 SVCERR << "AlignmentModel::isReady: path not begun" << endl;
Chris@1561 112 #endif
Chris@323 113 return false;
Chris@323 114 }
Chris@1016 115 if (m_pathComplete) {
Chris@338 116 if (completion) *completion = 100;
Chris@1561 117 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1696 118 SVCERR << "AlignmentModel::isReady: path complete" << endl;
Chris@1561 119 #endif
Chris@338 120 return true;
Chris@338 121 }
Chris@1737 122 if (m_pathSource.isNone()) {
Chris@1016 123 // lack of raw path could mean path is complete (in which case
Chris@1737 124 // m_pathComplete true above) or else no path source has been
Chris@1016 125 // set at all yet (this case)
Chris@1016 126 if (completion) *completion = 0;
Chris@1561 127 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1696 128 SVCERR << "AlignmentModel::isReady: no raw path" << endl;
Chris@1561 129 #endif
Chris@1016 130 return false;
Chris@1016 131 }
Chris@1737 132 auto pathSourceModel =
Chris@1737 133 ModelById::getAs<SparseTimeValueModel>(m_pathSource);
Chris@1737 134 if (pathSourceModel) {
Chris@1737 135 return pathSourceModel->isReady(completion);
Chris@1737 136 } else {
Chris@1737 137 return true; // there is no meaningful answer here
Chris@1737 138 }
Chris@297 139 }
Chris@297 140
Chris@297 141 const ZoomConstraint *
Chris@297 142 AlignmentModel::getZoomConstraint() const
Chris@297 143 {
Chris@1582 144 return nullptr;
Chris@297 145 }
Chris@297 146
Chris@1737 147 ModelId
Chris@297 148 AlignmentModel::getReferenceModel() const
Chris@297 149 {
Chris@297 150 return m_reference;
Chris@297 151 }
Chris@297 152
Chris@1737 153 ModelId
Chris@297 154 AlignmentModel::getAlignedModel() const
Chris@297 155 {
Chris@297 156 return m_aligned;
Chris@297 157 }
Chris@297 158
Chris@1038 159 sv_frame_t
Chris@1038 160 AlignmentModel::toReference(sv_frame_t frame) const
Chris@297 161 {
Chris@376 162 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 163 cerr << "AlignmentModel::toReference(" << frame << ")" << endl;
Chris@376 164 #endif
Chris@371 165 if (!m_path) {
Chris@1738 166 if (m_pathSource.isNone()) {
Chris@1738 167 return frame;
Chris@1738 168 }
Chris@371 169 constructPath();
Chris@371 170 }
Chris@1738 171 if (!m_path) {
Chris@1738 172 return frame;
Chris@1738 173 }
Chris@1738 174
Chris@1738 175 return performAlignment(*m_path, frame);
Chris@297 176 }
Chris@297 177
Chris@1038 178 sv_frame_t
Chris@1038 179 AlignmentModel::fromReference(sv_frame_t frame) const
Chris@297 180 {
Chris@376 181 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 182 cerr << "AlignmentModel::fromReference(" << frame << ")" << endl;
Chris@376 183 #endif
Chris@371 184 if (!m_reversePath) {
Chris@1738 185 if (m_pathSource.isNone()) {
Chris@1738 186 return frame;
Chris@1738 187 }
Chris@371 188 constructReversePath();
Chris@371 189 }
Chris@1738 190 if (!m_reversePath) {
Chris@1738 191 return frame;
Chris@1738 192 }
Chris@297 193
Chris@1738 194 return performAlignment(*m_reversePath, frame);
Chris@297 195 }
Chris@297 196
Chris@297 197 void
Chris@1752 198 AlignmentModel::pathSourceChangedWithin(ModelId, sv_frame_t, sv_frame_t)
Chris@297 199 {
Chris@297 200 if (!m_pathComplete) return;
Chris@338 201 constructPath();
Chris@297 202 constructReversePath();
Chris@297 203 }
Chris@297 204
Chris@297 205 void
Chris@1752 206 AlignmentModel::pathSourceCompletionChanged(ModelId)
Chris@297 207 {
Chris@1737 208 auto pathSourceModel =
Chris@1737 209 ModelById::getAs<SparseTimeValueModel>(m_pathSource);
Chris@1737 210 if (!pathSourceModel) return;
Chris@1737 211
Chris@323 212 m_pathBegun = true;
Chris@323 213
Chris@297 214 if (!m_pathComplete) {
Chris@338 215
Chris@297 216 int completion = 0;
Chris@1737 217 pathSourceModel->isReady(&completion);
Chris@338 218
Chris@376 219 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1666 220 SVCERR << "AlignmentModel::pathCompletionChanged: completion = "
Chris@1666 221 << completion << endl;
Chris@376 222 #endif
Chris@338 223
Chris@323 224 m_pathComplete = (completion == 100);
Chris@338 225
Chris@297 226 if (m_pathComplete) {
Chris@338 227
Chris@338 228 constructPath();
Chris@297 229 constructReversePath();
Chris@1666 230
Chris@1691 231 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1691 232 SVCERR << "AlignmentModel: path complete" << endl;
Chris@1691 233 #endif
Chris@297 234 }
Chris@297 235 }
Chris@323 236
Chris@1752 237 emit completionChanged(getId());
Chris@297 238 }
Chris@297 239
Chris@297 240 void
Chris@338 241 AlignmentModel::constructPath() const
Chris@297 242 {
Chris@1737 243 auto alignedModel = ModelById::get(m_aligned);
Chris@1737 244 if (!alignedModel) return;
Chris@1737 245
Chris@1737 246 auto pathSourceModel =
Chris@1737 247 ModelById::getAs<SparseTimeValueModel>(m_pathSource);
Chris@338 248 if (!m_path) {
Chris@1760 249 if (!pathSourceModel) {
Chris@843 250 cerr << "ERROR: AlignmentModel::constructPath: "
Chris@1760 251 << "No raw path available (id is " << m_pathSource
Chris@1760 252 << ")" << endl;
Chris@338 253 return;
Chris@338 254 }
Chris@1738 255 m_path.reset(new Path
Chris@1737 256 (pathSourceModel->getSampleRate(),
Chris@1738 257 pathSourceModel->getResolution()));
Chris@338 258 } else {
Chris@1737 259 if (!pathSourceModel) return;
Chris@297 260 }
Chris@297 261
Chris@338 262 m_path->clear();
Chris@297 263
Chris@1737 264 EventVector points = pathSourceModel->getAllEvents();
Chris@1651 265
Chris@1651 266 for (const auto &p: points) {
Chris@1651 267 sv_frame_t frame = p.getFrame();
Chris@1651 268 double value = p.getValue();
Chris@1737 269 sv_frame_t rframe = lrint(value * alignedModel->getSampleRate());
Chris@1662 270 m_path->add(PathPoint(frame, rframe));
Chris@297 271 }
Chris@297 272
Chris@376 273 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 274 cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 275 #endif
Chris@338 276 }
Chris@338 277
Chris@338 278 void
Chris@338 279 AlignmentModel::constructReversePath() const
Chris@338 280 {
Chris@338 281 if (!m_reversePath) {
Chris@407 282 if (!m_path) {
Chris@843 283 cerr << "ERROR: AlignmentModel::constructReversePath: "
Chris@843 284 << "No forward path available" << endl;
Chris@407 285 return;
Chris@407 286 }
Chris@1738 287 m_reversePath.reset(new Path
Chris@1737 288 (m_path->getSampleRate(),
Chris@1738 289 m_path->getResolution()));
Chris@338 290 } else {
Chris@407 291 if (!m_path) return;
Chris@338 292 }
Chris@338 293
Chris@338 294 m_reversePath->clear();
Chris@407 295
Chris@1738 296 Path::Points points = m_path->getPoints();
Chris@407 297
Chris@1738 298 for (auto p: points) {
Chris@1738 299 sv_frame_t frame = p.frame;
Chris@1738 300 sv_frame_t rframe = p.mapframe;
Chris@1662 301 m_reversePath->add(PathPoint(rframe, frame));
Chris@407 302 }
Chris@338 303
Chris@376 304 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 305 cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
Chris@376 306 #endif
Chris@297 307 }
Chris@297 308
Chris@1038 309 sv_frame_t
Chris@1738 310 AlignmentModel::performAlignment(const Path &path, sv_frame_t frame) const
Chris@297 311 {
Chris@338 312 // The path consists of a series of points, each with frame equal
Chris@338 313 // to the frame on the source model and mapframe equal to the
Chris@338 314 // frame on the target model. Both should be monotonically
Chris@338 315 // increasing.
Chris@297 316
Chris@1738 317 const Path::Points &points = path.getPoints();
Chris@297 318
Chris@297 319 if (points.empty()) {
Chris@379 320 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 321 cerr << "AlignmentModel::align: No points" << endl;
Chris@379 322 #endif
Chris@297 323 return frame;
Chris@297 324 }
Chris@297 325
Chris@376 326 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 327 cerr << "AlignmentModel::align: frame " << frame << " requested" << endl;
Chris@376 328 #endif
Chris@376 329
Chris@1662 330 PathPoint point(frame);
Chris@1738 331 Path::Points::const_iterator i = points.lower_bound(point);
Chris@376 332 if (i == points.end()) {
Chris@376 333 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 334 cerr << "Note: i == points.end()" << endl;
Chris@376 335 #endif
Chris@376 336 --i;
Chris@376 337 }
Chris@1738 338 while (i != points.begin() && i->frame > frame) {
Chris@1738 339 --i;
Chris@1738 340 }
Chris@297 341
Chris@1038 342 sv_frame_t foundFrame = i->frame;
Chris@1038 343 sv_frame_t foundMapFrame = i->mapframe;
Chris@297 344
Chris@1038 345 sv_frame_t followingFrame = foundFrame;
Chris@1038 346 sv_frame_t followingMapFrame = foundMapFrame;
Chris@297 347
Chris@312 348 if (++i != points.end()) {
Chris@376 349 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 350 cerr << "another point available" << endl;
Chris@376 351 #endif
Chris@312 352 followingFrame = i->frame;
Chris@338 353 followingMapFrame = i->mapframe;
Chris@376 354 } else {
Chris@376 355 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@843 356 cerr << "no other point available" << endl;
Chris@376 357 #endif
Chris@376 358 }
Chris@312 359
Chris@1075 360 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 361 cerr << "foundFrame = " << foundFrame << ", foundMapFrame = " << foundMapFrame
Chris@1075 362 << ", followingFrame = " << followingFrame << ", followingMapFrame = "
Chris@1075 363 << followingMapFrame << endl;
Chris@1075 364 #endif
Chris@1075 365
Chris@1738 366 if (foundMapFrame < 0) {
Chris@1738 367 return 0;
Chris@1738 368 }
Chris@312 369
Chris@1038 370 sv_frame_t resultFrame = foundMapFrame;
Chris@312 371
Chris@1038 372 if (followingFrame != foundFrame && frame > foundFrame) {
Chris@1038 373 double interp =
Chris@1038 374 double(frame - foundFrame) /
Chris@1038 375 double(followingFrame - foundFrame);
Chris@1038 376 resultFrame += lrint(double(followingMapFrame - foundMapFrame) * interp);
Chris@312 377 }
Chris@312 378
Chris@376 379 #ifdef DEBUG_ALIGNMENT_MODEL
Chris@1075 380 cerr << "AlignmentModel::align: resultFrame = " << resultFrame << endl;
Chris@376 381 #endif
Chris@312 382
Chris@312 383 return resultFrame;
Chris@297 384 }
Chris@407 385
Chris@407 386 void
Chris@1737 387 AlignmentModel::setPathFrom(ModelId pathSource)
Chris@1016 388 {
Chris@1737 389 m_pathSource = pathSource;
Chris@1737 390
Chris@1737 391 auto pathSourceModel =
Chris@1737 392 ModelById::getAs<SparseTimeValueModel>(m_pathSource);
Chris@1737 393
Chris@1737 394 if (pathSourceModel) {
Chris@1016 395
Chris@1737 396 connect(pathSourceModel.get(),
Chris@1752 397 SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
Chris@1752 398 this, SLOT(pathSourceChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
Chris@1737 399
Chris@1752 400 connect(pathSourceModel.get(), SIGNAL(completionChanged(ModelId)),
Chris@1752 401 this, SLOT(pathSourceCompletionChanged(ModelId)));
Chris@1737 402
Chris@1737 403 constructPath();
Chris@1737 404 constructReversePath();
Chris@1737 405
Chris@1737 406 if (pathSourceModel->isReady()) {
Chris@1752 407 pathSourceCompletionChanged(m_pathSource);
Chris@1737 408 }
Chris@1712 409 }
Chris@1016 410 }
Chris@1016 411
Chris@1016 412 void
Chris@1738 413 AlignmentModel::setPath(const Path &path)
Chris@407 414 {
Chris@1738 415 m_path.reset(new Path(path));
Chris@1560 416 m_pathComplete = true;
Chris@407 417 constructReversePath();
Chris@407 418 }
Chris@297 419
Chris@407 420 void
Chris@407 421 AlignmentModel::toXml(QTextStream &stream,
Chris@407 422 QString indent,
Chris@407 423 QString extraAttributes) const
Chris@407 424 {
Chris@407 425 if (!m_path) {
Chris@690 426 SVDEBUG << "AlignmentModel::toXml: no path" << endl;
Chris@407 427 return;
Chris@407 428 }
Chris@407 429
Chris@407 430 m_path->toXml(stream, indent, "");
Chris@407 431
Chris@407 432 Model::toXml(stream, indent,
Chris@407 433 QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4")
Chris@1738 434 .arg(ModelById::getExportId(m_reference))
Chris@1738 435 .arg(ModelById::getExportId(m_aligned))
Chris@1677 436 .arg(m_path->getExportId())
Chris@407 437 .arg(extraAttributes));
Chris@407 438 }