annotate view/AlignmentView.cpp @ 1615:911330a28a7c

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