annotate view/AlignmentView.cpp @ 1620:dc0e47f234a2 tip

Fix some erroneous uses of reference frame where we intended "above"-view frame
author Chris Cannam
date Tue, 18 Aug 2020 16:39:26 +0100
parents 36634b427d61
children
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@1620 208 if (af < m_leftmostAbove) {
Chris@1620 209 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1620 210 SVCERR << "AlignmentView: af " << af << " < m_leftmostAbove " << m_leftmostAbove << endl;
Chris@1620 211 #endif
Chris@1620 212 continue;
Chris@1620 213 }
Chris@1620 214 if (af > m_rightmostAbove) {
Chris@1620 215 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1620 216 SVCERR << "AlignmentView: af " << af << " > m_rightmostAbove " << m_rightmostAbove << endl;
Chris@1620 217 #endif
Chris@1615 218 continue;
Chris@1615 219 }
Chris@1493 220
Chris@1615 221 int ax = m_above->getXForFrame(af);
Chris@1615 222 int bx = m_below->getXForFrame(bf);
Chris@1615 223
Chris@1615 224 if (ax >= 0 || ax < w || bx >= 0 || bx < w) {
Chris@1615 225 paint.drawLine(ax, 0, bx, h);
Chris@1615 226 }
Chris@1615 227 }
Chris@1615 228 } else if (m_reference != nullptr) {
Chris@1615 229 // the below has nothing in common with the above: show things
Chris@1615 230 // in common with the reference instead
Chris@1619 231
Chris@1619 232 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1619 233 SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove
Chris@1619 234 << ", we have no relationship with the pane above us: showing "
Chris@1619 235 << "mappings in relation to the reference instead" << endl;
Chris@1619 236 #endif
Chris@1619 237
Chris@1615 238 for (const auto &km: m_fromReferenceMap) {
Chris@1615 239
Chris@1615 240 sv_frame_t af = km.first;
Chris@1615 241 sv_frame_t bf = km.second;
Chris@1615 242
Chris@1615 243 int ax = m_reference->getXForFrame(af);
Chris@1615 244 int bx = m_below->getXForFrame(bf);
Chris@1615 245
Chris@1615 246 if (ax >= 0 || ax < w || bx >= 0 || bx < w) {
Chris@1615 247 paint.drawLine(ax, 0, bx, h);
Chris@1615 248 }
Chris@1493 249 }
Chris@867 250 }
Chris@867 251
Chris@867 252 paint.end();
Chris@1493 253 }
Chris@867 254
Chris@1493 255 void
Chris@1493 256 AlignmentView::reconnectModels()
Chris@868 257 {
Chris@1493 258 vector<ModelId> toConnect {
Chris@1493 259 getSalientModel(m_above),
Chris@1493 260 getSalientModel(m_below)
Chris@1493 261 };
Chris@868 262
Chris@1493 263 for (auto modelId: toConnect) {
Chris@1493 264 if (auto model = ModelById::get(modelId)) {
Chris@1493 265 auto referenceId = model->getAlignmentReference();
Chris@1493 266 if (!referenceId.isNone()) {
Chris@1493 267 toConnect.push_back(referenceId);
Chris@1475 268 }
Chris@1266 269 }
Chris@868 270 }
Chris@868 271
Chris@1493 272 for (auto modelId: toConnect) {
Chris@1493 273 if (auto model = ModelById::get(modelId)) {
Chris@1493 274 auto ptr = model.get();
Chris@1493 275 disconnect(ptr, 0, this, 0);
Chris@1493 276 connect(ptr, SIGNAL(modelChanged(ModelId)),
Chris@1493 277 this, SLOT(keyFramesChanged()));
Chris@1493 278 connect(ptr, SIGNAL(completionChanged(ModelId)),
Chris@1493 279 this, SLOT(keyFramesChanged()));
Chris@1493 280 connect(ptr, SIGNAL(alignmentCompletionChanged(ModelId)),
Chris@1493 281 this, SLOT(keyFramesChanged()));
Chris@1493 282 }
Chris@1493 283 }
Chris@1493 284 }
Chris@1493 285
Chris@1493 286 void
Chris@1615 287 AlignmentView::buildMaps()
Chris@1493 288 {
Chris@1493 289 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1615 290 SVCERR << "AlignmentView " << getId() << "::buildMaps" << endl;
Chris@1493 291 #endif
Chris@1493 292
Chris@1493 293 sv_frame_t resolution = 1;
Chris@1493 294
Chris@1493 295 set<sv_frame_t> keyFramesBelow;
Chris@1493 296 for (auto f: getKeyFrames(m_below, resolution)) {
Chris@1493 297 keyFramesBelow.insert(f);
Chris@1493 298 }
Chris@1493 299
Chris@1615 300 foreach(sv_frame_t f, keyFramesBelow) {
Chris@1615 301 sv_frame_t rf = m_below->alignToReference(f);
Chris@1615 302 m_fromReferenceMap.insert({ rf, f });
Chris@1615 303 }
Chris@1615 304
Chris@1493 305 vector<sv_frame_t> keyFrames = getKeyFrames(m_above, resolution);
Chris@1493 306
Chris@1615 307 // These are the most extreme leftward and rightward frames in
Chris@1615 308 // "above" that have distinct corresponding frames in
Chris@1615 309 // "below". Anything left of m_leftmostAbove or right of
Chris@1615 310 // m_rightmostAbove maps effectively off one end or the other of
Chris@1615 311 // the below view. (They don't actually map off the ends, they
Chris@1615 312 // just all map to the same first/last destination frame. But we
Chris@1615 313 // don't want to display their mappings, as they're just noise.)
Chris@1615 314 m_leftmostAbove = -1;
Chris@1615 315 m_rightmostAbove = -1;
Chris@1615 316
Chris@1620 317 sv_frame_t prevAf = -1;
Chris@1615 318 sv_frame_t prevBf = -1;
Chris@1615 319
Chris@1620 320 foreach (sv_frame_t af, keyFrames) {
Chris@1493 321
Chris@1620 322 sv_frame_t rf = m_above->alignToReference(af);
Chris@1493 323 sv_frame_t bf = m_below->alignFromReference(rf);
Chris@1493 324
Chris@1615 325 if (prevBf > 0 && bf > prevBf) {
Chris@1620 326 if (m_leftmostAbove < 0) {
Chris@1620 327 m_leftmostAbove = prevAf;
Chris@1615 328 }
Chris@1620 329 m_rightmostAbove = af;
Chris@1615 330 }
Chris@1620 331 prevAf = af;
Chris@1615 332 prevBf = bf;
Chris@1615 333
Chris@1493 334 bool mappedSomething = false;
Chris@1493 335
Chris@1493 336 if (resolution > 1) {
Chris@1493 337 if (keyFramesBelow.find(bf) == keyFramesBelow.end()) {
Chris@1493 338
Chris@1620 339 sv_frame_t af1 = af + resolution;
Chris@1620 340 sv_frame_t rf1 = m_above->alignToReference(af1);
Chris@1493 341 sv_frame_t bf1 = m_below->alignFromReference(rf1);
Chris@1493 342
Chris@1493 343 for (sv_frame_t probe = bf + 1; probe <= bf1; ++probe) {
Chris@1493 344 if (keyFramesBelow.find(probe) != keyFramesBelow.end()) {
Chris@1620 345 m_fromAboveMap.insert({ af, probe });
Chris@1493 346 mappedSomething = true;
Chris@1493 347 }
Chris@1493 348 }
Chris@1493 349 }
Chris@1493 350 }
Chris@1493 351
Chris@1493 352 if (!mappedSomething) {
Chris@1620 353 m_fromAboveMap.insert({ af, bf });
Chris@1493 354 }
Chris@1493 355 }
Chris@1493 356
Chris@1493 357 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1615 358 SVCERR << "AlignmentView " << getId() << "::buildMaps: have "
Chris@1615 359 << m_fromAboveMap.size() << " mappings" << endl;
Chris@1493 360 #endif
Chris@1493 361 }
Chris@1493 362
Chris@1493 363 vector<sv_frame_t>
Chris@1493 364 AlignmentView::getKeyFrames(View *view, sv_frame_t &resolution)
Chris@1493 365 {
Chris@1493 366 resolution = 1;
Chris@1493 367
Chris@1493 368 if (!view) {
Chris@1493 369 return getDefaultKeyFrames();
Chris@1493 370 }
Chris@1493 371
Chris@1493 372 ModelId m = getSalientModel(view);
Chris@1475 373 auto model = ModelById::getAs<SparseOneDimensionalModel>(m);
Chris@1475 374 if (!model) {
Chris@1266 375 return getDefaultKeyFrames();
Chris@868 376 }
Chris@868 377
Chris@1493 378 resolution = model->getResolution();
Chris@1493 379
Chris@976 380 vector<sv_frame_t> keyFrames;
Chris@868 381
Chris@1475 382 EventVector pp = model->getAllEvents();
Chris@1433 383 for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) {
Chris@1433 384 keyFrames.push_back(pi->getFrame());
Chris@868 385 }
Chris@868 386
Chris@868 387 return keyFrames;
Chris@868 388 }
Chris@868 389
Chris@976 390 vector<sv_frame_t>
Chris@868 391 AlignmentView::getDefaultKeyFrames()
Chris@868 392 {
Chris@976 393 vector<sv_frame_t> keyFrames;
Chris@1509 394 return keyFrames;
Chris@868 395
Chris@1509 396 #ifdef NOT_REALLY
Chris@868 397 if (!m_above || !m_manager) return keyFrames;
Chris@868 398
Chris@976 399 sv_samplerate_t rate = m_manager->getMainModelSampleRate();
Chris@868 400 if (rate == 0) return keyFrames;
Chris@868 401
Chris@976 402 for (sv_frame_t f = m_above->getModelsStartFrame();
Chris@1266 403 f <= m_above->getModelsEndFrame();
Chris@1266 404 f += sv_frame_t(rate * 5 + 0.5)) {
Chris@1266 405 keyFrames.push_back(f);
Chris@868 406 }
Chris@868 407
Chris@868 408 return keyFrames;
Chris@1509 409 #endif
Chris@868 410 }
Chris@868 411
Chris@1493 412 ModelId
Chris@1493 413 AlignmentView::getSalientModel(View *view)
Chris@1493 414 {
Chris@1493 415 ModelId m;
Chris@1493 416
Chris@1493 417 // get the topmost such
Chris@1493 418 for (int i = 0; i < view->getLayerCount(); ++i) {
Chris@1493 419 if (qobject_cast<TimeInstantLayer *>(view->getLayer(i))) {
Chris@1493 420 ModelId mm = view->getLayer(i)->getModel();
Chris@1493 421 if (ModelById::isa<SparseOneDimensionalModel>(mm)) {
Chris@1493 422 m = mm;
Chris@1493 423 }
Chris@1493 424 }
Chris@1493 425 }
Chris@1493 426
Chris@1493 427 return m;
Chris@1493 428 }
Chris@1493 429
Chris@1493 430