annotate view/AlignmentView.cpp @ 1619:36634b427d61

Fix wrongly-written test which made the mapping alignments line up wrongly in some cases where adjacent panes were related (but, because of this test, the alignment view thought they were not)
author Chris Cannam
date Tue, 18 Aug 2020 14:49:36 +0100
parents 911330a28a7c
children dc0e47f234a2
rev   line source
Chris@867 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@867 2
Chris@867 3 /*
Chris@867 4 Sonic Visualiser
Chris@867 5 An audio file viewer and annotation editor.
Chris@867 6 Centre for Digital Music, Queen Mary, University of London.
Chris@867 7 This file copyright 2006-2014 Chris Cannam and QMUL.
Chris@867 8
Chris@867 9 This program is free software; you can redistribute it and/or
Chris@867 10 modify it under the terms of the GNU General Public License as
Chris@867 11 published by the Free Software Foundation; either version 2 of the
Chris@867 12 License, or (at your option) any later version. See the file
Chris@867 13 COPYING included with this distribution for more information.
Chris@867 14 */
Chris@867 15
Chris@867 16 #include "AlignmentView.h"
Chris@867 17
Chris@867 18 #include <QPainter>
Chris@867 19
Chris@868 20 #include "data/model/SparseOneDimensionalModel.h"
Chris@868 21
Chris@868 22 #include "layer/TimeInstantLayer.h"
Chris@868 23
Chris@1493 24 //#define DEBUG_ALIGNMENT_VIEW 1
Chris@1493 25
Chris@867 26 using std::vector;
Chris@1493 27 using std::set;
Chris@867 28
Chris@867 29 AlignmentView::AlignmentView(QWidget *w) :
Chris@867 30 View(w, false),
Chris@1408 31 m_above(nullptr),
Chris@1615 32 m_below(nullptr),
Chris@1615 33 m_reference(nullptr),
Chris@1615 34 m_leftmostAbove(-1),
Chris@1615 35 m_rightmostAbove(-1)
Chris@867 36 {
Chris@867 37 setObjectName(tr("AlignmentView"));
Chris@867 38 }
Chris@867 39
Chris@867 40 void
Chris@1493 41 AlignmentView::keyFramesChanged()
Chris@1493 42 {
Chris@1493 43 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 44 SVCERR << "AlignmentView " << getId() << "::keyFramesChanged" << endl;
Chris@1493 45 #endif
Chris@1493 46
Chris@1615 47 // This is just a notification that we need to rebuild - so all we
Chris@1615 48 // do here is clear, and rebuild on demand later
Chris@1615 49 QMutexLocker locker(&m_mapsMutex);
Chris@1615 50 m_fromAboveMap.clear();
Chris@1615 51 m_fromReferenceMap.clear();
Chris@1493 52 }
Chris@1493 53
Chris@1493 54 void
Chris@976 55 AlignmentView::globalCentreFrameChanged(sv_frame_t f)
Chris@867 56 {
Chris@867 57 View::globalCentreFrameChanged(f);
Chris@867 58 update();
Chris@867 59 }
Chris@867 60
Chris@867 61 void
Chris@976 62 AlignmentView::viewCentreFrameChanged(View *v, sv_frame_t f)
Chris@867 63 {
Chris@867 64 View::viewCentreFrameChanged(v, f);
Chris@867 65 if (v == m_above) {
Chris@1266 66 m_centreFrame = f;
Chris@1266 67 update();
Chris@867 68 } else if (v == m_below) {
Chris@1266 69 update();
Chris@867 70 }
Chris@867 71 }
Chris@867 72
Chris@867 73 void
Chris@976 74 AlignmentView::viewManagerPlaybackFrameChanged(sv_frame_t)
Chris@867 75 {
Chris@867 76 update();
Chris@867 77 }
Chris@867 78
Chris@867 79 void
Chris@1183 80 AlignmentView::viewAboveZoomLevelChanged(ZoomLevel level, bool)
Chris@867 81 {
Chris@867 82 m_zoomLevel = level;
Chris@867 83 update();
Chris@867 84 }
Chris@867 85
Chris@867 86 void
Chris@1183 87 AlignmentView::viewBelowZoomLevelChanged(ZoomLevel, bool)
Chris@867 88 {
Chris@867 89 update();
Chris@867 90 }
Chris@867 91
Chris@867 92 void
Chris@1615 93 AlignmentView::setAboveView(View *v)
Chris@867 94 {
Chris@867 95 if (m_above) {
Chris@1408 96 disconnect(m_above, nullptr, this, nullptr);
Chris@867 97 }
Chris@867 98
Chris@867 99 m_above = v;
Chris@867 100
Chris@867 101 if (m_above) {
Chris@1266 102 connect(m_above,
Chris@1183 103 SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
Chris@1266 104 this,
Chris@1183 105 SLOT(viewAboveZoomLevelChanged(ZoomLevel, bool)));
Chris@1493 106 connect(m_above,
Chris@1493 107 SIGNAL(propertyContainerAdded(PropertyContainer *)),
Chris@1493 108 this,
Chris@1493 109 SLOT(keyFramesChanged()));
Chris@1493 110 connect(m_above,
Chris@1493 111 SIGNAL(layerModelChanged()),
Chris@1493 112 this,
Chris@1493 113 SLOT(keyFramesChanged()));
Chris@867 114 }
Chris@1493 115
Chris@1493 116 keyFramesChanged();
Chris@867 117 }
Chris@867 118
Chris@867 119 void
Chris@1615 120 AlignmentView::setBelowView(View *v)
Chris@867 121 {
Chris@867 122 if (m_below) {
Chris@1408 123 disconnect(m_below, nullptr, this, nullptr);
Chris@867 124 }
Chris@867 125
Chris@867 126 m_below = v;
Chris@867 127
Chris@867 128 if (m_below) {
Chris@1266 129 connect(m_below,
Chris@1183 130 SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
Chris@1266 131 this,
Chris@1183 132 SLOT(viewBelowZoomLevelChanged(ZoomLevel, bool)));
Chris@1493 133 connect(m_below,
Chris@1493 134 SIGNAL(propertyContainerAdded(PropertyContainer *)),
Chris@1493 135 this,
Chris@1493 136 SLOT(keyFramesChanged()));
Chris@1493 137 connect(m_below,
Chris@1493 138 SIGNAL(layerModelChanged()),
Chris@1493 139 this,
Chris@1493 140 SLOT(keyFramesChanged()));
Chris@867 141 }
Chris@1493 142
Chris@1493 143 keyFramesChanged();
Chris@867 144 }
Chris@867 145
Chris@867 146 void
Chris@1615 147 AlignmentView::setReferenceView(View *view)
Chris@1615 148 {
Chris@1615 149 m_reference = view;
Chris@1615 150 }
Chris@1615 151
Chris@1615 152 void
Chris@867 153 AlignmentView::paintEvent(QPaintEvent *)
Chris@867 154 {
Chris@1408 155 if (m_above == nullptr || m_below == nullptr || !m_manager) return;
Chris@1493 156
Chris@1493 157 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 158 SVCERR << "AlignmentView " << getId() << "::paintEvent" << endl;
Chris@1493 159 #endif
Chris@1493 160
Chris@867 161 bool darkPalette = false;
Chris@867 162 if (m_manager) darkPalette = m_manager->getGlobalDarkBackground();
Chris@867 163
Chris@881 164 QColor fg, bg;
Chris@881 165 if (darkPalette) {
Chris@881 166 fg = Qt::gray;
Chris@881 167 bg = Qt::black;
Chris@881 168 } else {
Chris@881 169 fg = Qt::black;
Chris@881 170 bg = Qt::gray;
Chris@881 171 }
Chris@867 172
Chris@867 173 QPainter paint(this);
Chris@867 174 paint.setPen(QPen(fg, 2));
Chris@867 175 paint.setBrush(Qt::NoBrush);
Chris@867 176 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@867 177
Chris@867 178 paint.fillRect(rect(), bg);
Chris@867 179
Chris@1615 180 QMutexLocker locker(&m_mapsMutex);
Chris@867 181
Chris@1615 182 if (m_fromAboveMap.empty()) {
Chris@1493 183 reconnectModels();
Chris@1615 184 buildMaps();
Chris@1493 185 }
Chris@1493 186
Chris@1493 187 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 188 SVCERR << "AlignmentView " << getId() << "::paintEvent: painting "
Chris@1615 189 << m_fromAboveMap.size() << " mappings" << endl;
Chris@1493 190 #endif
Chris@1493 191
Chris@1615 192 int w = width();
Chris@1615 193 int h = height();
Chris@1493 194
Chris@1619 195 if (m_leftmostAbove >= 0) {
Chris@1619 196
Chris@1619 197 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1619 198 SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove
Chris@1619 199 << ", we have a relationship with the pane above us: showing "
Chris@1619 200 << "mappings in relation to that" << endl;
Chris@1619 201 #endif
Chris@1619 202
Chris@1615 203 for (const auto &km: m_fromAboveMap) {
Chris@1493 204
Chris@1615 205 sv_frame_t af = km.first;
Chris@1615 206 sv_frame_t bf = km.second;
Chris@1615 207
Chris@1615 208 if (af < m_leftmostAbove || af > m_rightmostAbove) {
Chris@1615 209 continue;
Chris@1615 210 }
Chris@1493 211
Chris@1615 212 int ax = m_above->getXForFrame(af);
Chris@1615 213 int bx = m_below->getXForFrame(bf);
Chris@1615 214
Chris@1615 215 if (ax >= 0 || ax < w || bx >= 0 || bx < w) {
Chris@1615 216 paint.drawLine(ax, 0, bx, h);
Chris@1615 217 }
Chris@1615 218 }
Chris@1615 219 } else if (m_reference != nullptr) {
Chris@1615 220 // the below has nothing in common with the above: show things
Chris@1615 221 // in common with the reference instead
Chris@1619 222
Chris@1619 223 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1619 224 SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove
Chris@1619 225 << ", we have no relationship with the pane above us: showing "
Chris@1619 226 << "mappings in relation to the reference instead" << endl;
Chris@1619 227 #endif
Chris@1619 228
Chris@1615 229 for (const auto &km: m_fromReferenceMap) {
Chris@1615 230
Chris@1615 231 sv_frame_t af = km.first;
Chris@1615 232 sv_frame_t bf = km.second;
Chris@1615 233
Chris@1615 234 int ax = m_reference->getXForFrame(af);
Chris@1615 235 int bx = m_below->getXForFrame(bf);
Chris@1615 236
Chris@1615 237 if (ax >= 0 || ax < w || bx >= 0 || bx < w) {
Chris@1615 238 paint.drawLine(ax, 0, bx, h);
Chris@1615 239 }
Chris@1493 240 }
Chris@867 241 }
Chris@867 242
Chris@867 243 paint.end();
Chris@1493 244 }
Chris@867 245
Chris@1493 246 void
Chris@1493 247 AlignmentView::reconnectModels()
Chris@868 248 {
Chris@1493 249 vector<ModelId> toConnect {
Chris@1493 250 getSalientModel(m_above),
Chris@1493 251 getSalientModel(m_below)
Chris@1493 252 };
Chris@868 253
Chris@1493 254 for (auto modelId: toConnect) {
Chris@1493 255 if (auto model = ModelById::get(modelId)) {
Chris@1493 256 auto referenceId = model->getAlignmentReference();
Chris@1493 257 if (!referenceId.isNone()) {
Chris@1493 258 toConnect.push_back(referenceId);
Chris@1475 259 }
Chris@1266 260 }
Chris@868 261 }
Chris@868 262
Chris@1493 263 for (auto modelId: toConnect) {
Chris@1493 264 if (auto model = ModelById::get(modelId)) {
Chris@1493 265 auto ptr = model.get();
Chris@1493 266 disconnect(ptr, 0, this, 0);
Chris@1493 267 connect(ptr, SIGNAL(modelChanged(ModelId)),
Chris@1493 268 this, SLOT(keyFramesChanged()));
Chris@1493 269 connect(ptr, SIGNAL(completionChanged(ModelId)),
Chris@1493 270 this, SLOT(keyFramesChanged()));
Chris@1493 271 connect(ptr, SIGNAL(alignmentCompletionChanged(ModelId)),
Chris@1493 272 this, SLOT(keyFramesChanged()));
Chris@1493 273 }
Chris@1493 274 }
Chris@1493 275 }
Chris@1493 276
Chris@1493 277 void
Chris@1615 278 AlignmentView::buildMaps()
Chris@1493 279 {
Chris@1493 280 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1615 281 SVCERR << "AlignmentView " << getId() << "::buildMaps" << endl;
Chris@1493 282 #endif
Chris@1493 283
Chris@1493 284 sv_frame_t resolution = 1;
Chris@1493 285
Chris@1493 286 set<sv_frame_t> keyFramesBelow;
Chris@1493 287 for (auto f: getKeyFrames(m_below, resolution)) {
Chris@1493 288 keyFramesBelow.insert(f);
Chris@1493 289 }
Chris@1493 290
Chris@1615 291 foreach(sv_frame_t f, keyFramesBelow) {
Chris@1615 292 sv_frame_t rf = m_below->alignToReference(f);
Chris@1615 293 m_fromReferenceMap.insert({ rf, f });
Chris@1615 294 }
Chris@1615 295
Chris@1493 296 vector<sv_frame_t> keyFrames = getKeyFrames(m_above, resolution);
Chris@1493 297
Chris@1615 298 // These are the most extreme leftward and rightward frames in
Chris@1615 299 // "above" that have distinct corresponding frames in
Chris@1615 300 // "below". Anything left of m_leftmostAbove or right of
Chris@1615 301 // m_rightmostAbove maps effectively off one end or the other of
Chris@1615 302 // the below view. (They don't actually map off the ends, they
Chris@1615 303 // just all map to the same first/last destination frame. But we
Chris@1615 304 // don't want to display their mappings, as they're just noise.)
Chris@1615 305 m_leftmostAbove = -1;
Chris@1615 306 m_rightmostAbove = -1;
Chris@1615 307
Chris@1615 308 sv_frame_t prevRf = -1;
Chris@1615 309 sv_frame_t prevBf = -1;
Chris@1615 310
Chris@1493 311 foreach (sv_frame_t f, keyFrames) {
Chris@1493 312
Chris@1493 313 sv_frame_t rf = m_above->alignToReference(f);
Chris@1493 314 sv_frame_t bf = m_below->alignFromReference(rf);
Chris@1493 315
Chris@1615 316 if (prevBf > 0 && bf > prevBf) {
Chris@1615 317 if (m_leftmostAbove < 0 && prevBf > 0 && bf > prevBf) {
Chris@1615 318 m_leftmostAbove = prevRf;
Chris@1615 319 }
Chris@1615 320 m_rightmostAbove = rf;
Chris@1615 321 }
Chris@1615 322 prevRf = rf;
Chris@1615 323 prevBf = bf;
Chris@1615 324
Chris@1493 325 bool mappedSomething = false;
Chris@1493 326
Chris@1493 327 if (resolution > 1) {
Chris@1493 328 if (keyFramesBelow.find(bf) == keyFramesBelow.end()) {
Chris@1493 329
Chris@1493 330 sv_frame_t f1 = f + resolution;
Chris@1493 331 sv_frame_t rf1 = m_above->alignToReference(f1);
Chris@1493 332 sv_frame_t bf1 = m_below->alignFromReference(rf1);
Chris@1493 333
Chris@1493 334 for (sv_frame_t probe = bf + 1; probe <= bf1; ++probe) {
Chris@1493 335 if (keyFramesBelow.find(probe) != keyFramesBelow.end()) {
Chris@1615 336 m_fromAboveMap.insert({ f, probe });
Chris@1493 337 mappedSomething = true;
Chris@1493 338 }
Chris@1493 339 }
Chris@1493 340 }
Chris@1493 341 }
Chris@1493 342
Chris@1493 343 if (!mappedSomething) {
Chris@1615 344 m_fromAboveMap.insert({ f, bf });
Chris@1493 345 }
Chris@1493 346 }
Chris@1493 347
Chris@1493 348 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1615 349 SVCERR << "AlignmentView " << getId() << "::buildMaps: have "
Chris@1615 350 << m_fromAboveMap.size() << " mappings" << endl;
Chris@1493 351 #endif
Chris@1493 352 }
Chris@1493 353
Chris@1493 354 vector<sv_frame_t>
Chris@1493 355 AlignmentView::getKeyFrames(View *view, sv_frame_t &resolution)
Chris@1493 356 {
Chris@1493 357 resolution = 1;
Chris@1493 358
Chris@1493 359 if (!view) {
Chris@1493 360 return getDefaultKeyFrames();
Chris@1493 361 }
Chris@1493 362
Chris@1493 363 ModelId m = getSalientModel(view);
Chris@1475 364 auto model = ModelById::getAs<SparseOneDimensionalModel>(m);
Chris@1475 365 if (!model) {
Chris@1266 366 return getDefaultKeyFrames();
Chris@868 367 }
Chris@868 368
Chris@1493 369 resolution = model->getResolution();
Chris@1493 370
Chris@976 371 vector<sv_frame_t> keyFrames;
Chris@868 372
Chris@1475 373 EventVector pp = model->getAllEvents();
Chris@1433 374 for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) {
Chris@1433 375 keyFrames.push_back(pi->getFrame());
Chris@868 376 }
Chris@868 377
Chris@868 378 return keyFrames;
Chris@868 379 }
Chris@868 380
Chris@976 381 vector<sv_frame_t>
Chris@868 382 AlignmentView::getDefaultKeyFrames()
Chris@868 383 {
Chris@976 384 vector<sv_frame_t> keyFrames;
Chris@1509 385 return keyFrames;
Chris@868 386
Chris@1509 387 #ifdef NOT_REALLY
Chris@868 388 if (!m_above || !m_manager) return keyFrames;
Chris@868 389
Chris@976 390 sv_samplerate_t rate = m_manager->getMainModelSampleRate();
Chris@868 391 if (rate == 0) return keyFrames;
Chris@868 392
Chris@976 393 for (sv_frame_t f = m_above->getModelsStartFrame();
Chris@1266 394 f <= m_above->getModelsEndFrame();
Chris@1266 395 f += sv_frame_t(rate * 5 + 0.5)) {
Chris@1266 396 keyFrames.push_back(f);
Chris@868 397 }
Chris@868 398
Chris@868 399 return keyFrames;
Chris@1509 400 #endif
Chris@868 401 }
Chris@868 402
Chris@1493 403 ModelId
Chris@1493 404 AlignmentView::getSalientModel(View *view)
Chris@1493 405 {
Chris@1493 406 ModelId m;
Chris@1493 407
Chris@1493 408 // get the topmost such
Chris@1493 409 for (int i = 0; i < view->getLayerCount(); ++i) {
Chris@1493 410 if (qobject_cast<TimeInstantLayer *>(view->getLayer(i))) {
Chris@1493 411 ModelId mm = view->getLayer(i)->getModel();
Chris@1493 412 if (ModelById::isa<SparseOneDimensionalModel>(mm)) {
Chris@1493 413 m = mm;
Chris@1493 414 }
Chris@1493 415 }
Chris@1493 416 }
Chris@1493 417
Chris@1493 418 return m;
Chris@1493 419 }
Chris@1493 420
Chris@1493 421