annotate view/AlignmentView.cpp @ 1617:cbbb411da977

Merge
author Chris Cannam
date Tue, 21 Jul 2020 14:02:12 +0100
parents 911330a28a7c
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