annotate view/AlignmentView.cpp @ 1553:76e4302a3fc2

Fix note numbering - ensure stable across whole track (as it used to be, but without scanning all notes in paint in order to do that)
author Chris Cannam
date Fri, 22 Nov 2019 14:12:50 +0000
parents 8145a9c4c253
children 911330a28a7c
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@1408 32 m_below(nullptr)
Chris@867 33 {
Chris@867 34 setObjectName(tr("AlignmentView"));
Chris@867 35 }
Chris@867 36
Chris@867 37 void
Chris@1493 38 AlignmentView::keyFramesChanged()
Chris@1493 39 {
Chris@1493 40 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 41 SVCERR << "AlignmentView " << getId() << "::keyFramesChanged" << endl;
Chris@1493 42 #endif
Chris@1493 43
Chris@1493 44 // This is just a notification that we need to rebuild it - so all
Chris@1493 45 // we do here is clear it, and it'll be rebuilt on demand later
Chris@1493 46 QMutexLocker locker(&m_keyFrameMutex);
Chris@1493 47 m_keyFrameMap.clear();
Chris@1493 48 }
Chris@1493 49
Chris@1493 50 void
Chris@976 51 AlignmentView::globalCentreFrameChanged(sv_frame_t f)
Chris@867 52 {
Chris@867 53 View::globalCentreFrameChanged(f);
Chris@867 54 update();
Chris@867 55 }
Chris@867 56
Chris@867 57 void
Chris@976 58 AlignmentView::viewCentreFrameChanged(View *v, sv_frame_t f)
Chris@867 59 {
Chris@867 60 View::viewCentreFrameChanged(v, f);
Chris@867 61 if (v == m_above) {
Chris@1266 62 m_centreFrame = f;
Chris@1266 63 update();
Chris@867 64 } else if (v == m_below) {
Chris@1266 65 update();
Chris@867 66 }
Chris@867 67 }
Chris@867 68
Chris@867 69 void
Chris@976 70 AlignmentView::viewManagerPlaybackFrameChanged(sv_frame_t)
Chris@867 71 {
Chris@867 72 update();
Chris@867 73 }
Chris@867 74
Chris@867 75 void
Chris@1183 76 AlignmentView::viewAboveZoomLevelChanged(ZoomLevel level, bool)
Chris@867 77 {
Chris@867 78 m_zoomLevel = level;
Chris@867 79 update();
Chris@867 80 }
Chris@867 81
Chris@867 82 void
Chris@1183 83 AlignmentView::viewBelowZoomLevelChanged(ZoomLevel, bool)
Chris@867 84 {
Chris@867 85 update();
Chris@867 86 }
Chris@867 87
Chris@867 88 void
Chris@867 89 AlignmentView::setViewAbove(View *v)
Chris@867 90 {
Chris@867 91 if (m_above) {
Chris@1408 92 disconnect(m_above, nullptr, this, nullptr);
Chris@867 93 }
Chris@867 94
Chris@867 95 m_above = v;
Chris@867 96
Chris@867 97 if (m_above) {
Chris@1266 98 connect(m_above,
Chris@1183 99 SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
Chris@1266 100 this,
Chris@1183 101 SLOT(viewAboveZoomLevelChanged(ZoomLevel, bool)));
Chris@1493 102 connect(m_above,
Chris@1493 103 SIGNAL(propertyContainerAdded(PropertyContainer *)),
Chris@1493 104 this,
Chris@1493 105 SLOT(keyFramesChanged()));
Chris@1493 106 connect(m_above,
Chris@1493 107 SIGNAL(layerModelChanged()),
Chris@1493 108 this,
Chris@1493 109 SLOT(keyFramesChanged()));
Chris@867 110 }
Chris@1493 111
Chris@1493 112 keyFramesChanged();
Chris@867 113 }
Chris@867 114
Chris@867 115 void
Chris@867 116 AlignmentView::setViewBelow(View *v)
Chris@867 117 {
Chris@867 118 if (m_below) {
Chris@1408 119 disconnect(m_below, nullptr, this, nullptr);
Chris@867 120 }
Chris@867 121
Chris@867 122 m_below = v;
Chris@867 123
Chris@867 124 if (m_below) {
Chris@1266 125 connect(m_below,
Chris@1183 126 SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
Chris@1266 127 this,
Chris@1183 128 SLOT(viewBelowZoomLevelChanged(ZoomLevel, bool)));
Chris@1493 129 connect(m_below,
Chris@1493 130 SIGNAL(propertyContainerAdded(PropertyContainer *)),
Chris@1493 131 this,
Chris@1493 132 SLOT(keyFramesChanged()));
Chris@1493 133 connect(m_below,
Chris@1493 134 SIGNAL(layerModelChanged()),
Chris@1493 135 this,
Chris@1493 136 SLOT(keyFramesChanged()));
Chris@867 137 }
Chris@1493 138
Chris@1493 139 keyFramesChanged();
Chris@867 140 }
Chris@867 141
Chris@867 142 void
Chris@867 143 AlignmentView::paintEvent(QPaintEvent *)
Chris@867 144 {
Chris@1408 145 if (m_above == nullptr || m_below == nullptr || !m_manager) return;
Chris@1493 146
Chris@1493 147 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 148 SVCERR << "AlignmentView " << getId() << "::paintEvent" << endl;
Chris@1493 149 #endif
Chris@1493 150
Chris@867 151 bool darkPalette = false;
Chris@867 152 if (m_manager) darkPalette = m_manager->getGlobalDarkBackground();
Chris@867 153
Chris@881 154 QColor fg, bg;
Chris@881 155 if (darkPalette) {
Chris@881 156 fg = Qt::gray;
Chris@881 157 bg = Qt::black;
Chris@881 158 } else {
Chris@881 159 fg = Qt::black;
Chris@881 160 bg = Qt::gray;
Chris@881 161 }
Chris@867 162
Chris@867 163 QPainter paint(this);
Chris@867 164 paint.setPen(QPen(fg, 2));
Chris@867 165 paint.setBrush(Qt::NoBrush);
Chris@867 166 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@867 167
Chris@867 168 paint.fillRect(rect(), bg);
Chris@867 169
Chris@1493 170 QMutexLocker locker(&m_keyFrameMutex);
Chris@867 171
Chris@1493 172 if (m_keyFrameMap.empty()) {
Chris@1493 173 reconnectModels();
Chris@1493 174 buildKeyFrameMap();
Chris@1493 175 }
Chris@1493 176
Chris@1493 177 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 178 SVCERR << "AlignmentView " << getId() << "::paintEvent: painting "
Chris@1493 179 << m_keyFrameMap.size() << " mappings" << endl;
Chris@1493 180 #endif
Chris@1493 181
Chris@1493 182 for (const auto &km: m_keyFrameMap) {
Chris@1493 183
Chris@1493 184 sv_frame_t af = km.first;
Chris@1493 185 sv_frame_t bf = km.second;
Chris@1493 186
Chris@1493 187 int ax = m_above->getXForFrame(af);
Chris@1266 188 int bx = m_below->getXForFrame(bf);
Chris@1493 189
Chris@1493 190 if (ax >= 0 || ax < width() || bx >= 0 || bx < width()) {
Chris@1493 191 paint.drawLine(ax, 0, bx, height());
Chris@1493 192 }
Chris@867 193 }
Chris@867 194
Chris@867 195 paint.end();
Chris@1493 196 }
Chris@867 197
Chris@1493 198 void
Chris@1493 199 AlignmentView::reconnectModels()
Chris@868 200 {
Chris@1493 201 vector<ModelId> toConnect {
Chris@1493 202 getSalientModel(m_above),
Chris@1493 203 getSalientModel(m_below)
Chris@1493 204 };
Chris@868 205
Chris@1493 206 for (auto modelId: toConnect) {
Chris@1493 207 if (auto model = ModelById::get(modelId)) {
Chris@1493 208 auto referenceId = model->getAlignmentReference();
Chris@1493 209 if (!referenceId.isNone()) {
Chris@1493 210 toConnect.push_back(referenceId);
Chris@1475 211 }
Chris@1266 212 }
Chris@868 213 }
Chris@868 214
Chris@1493 215 for (auto modelId: toConnect) {
Chris@1493 216 if (auto model = ModelById::get(modelId)) {
Chris@1493 217 auto ptr = model.get();
Chris@1493 218 disconnect(ptr, 0, this, 0);
Chris@1493 219 connect(ptr, SIGNAL(modelChanged(ModelId)),
Chris@1493 220 this, SLOT(keyFramesChanged()));
Chris@1493 221 connect(ptr, SIGNAL(completionChanged(ModelId)),
Chris@1493 222 this, SLOT(keyFramesChanged()));
Chris@1493 223 connect(ptr, SIGNAL(alignmentCompletionChanged(ModelId)),
Chris@1493 224 this, SLOT(keyFramesChanged()));
Chris@1493 225 }
Chris@1493 226 }
Chris@1493 227 }
Chris@1493 228
Chris@1493 229 void
Chris@1493 230 AlignmentView::buildKeyFrameMap()
Chris@1493 231 {
Chris@1493 232 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 233 SVCERR << "AlignmentView " << getId() << "::buildKeyFrameMap" << endl;
Chris@1493 234 #endif
Chris@1493 235
Chris@1493 236 sv_frame_t resolution = 1;
Chris@1493 237
Chris@1493 238 set<sv_frame_t> keyFramesBelow;
Chris@1493 239 for (auto f: getKeyFrames(m_below, resolution)) {
Chris@1493 240 keyFramesBelow.insert(f);
Chris@1493 241 }
Chris@1493 242
Chris@1493 243 vector<sv_frame_t> keyFrames = getKeyFrames(m_above, resolution);
Chris@1493 244
Chris@1493 245 foreach (sv_frame_t f, keyFrames) {
Chris@1493 246
Chris@1493 247 sv_frame_t rf = m_above->alignToReference(f);
Chris@1493 248 sv_frame_t bf = m_below->alignFromReference(rf);
Chris@1493 249
Chris@1493 250 bool mappedSomething = false;
Chris@1493 251
Chris@1493 252 if (resolution > 1) {
Chris@1493 253 if (keyFramesBelow.find(bf) == keyFramesBelow.end()) {
Chris@1493 254
Chris@1493 255 sv_frame_t f1 = f + resolution;
Chris@1493 256 sv_frame_t rf1 = m_above->alignToReference(f1);
Chris@1493 257 sv_frame_t bf1 = m_below->alignFromReference(rf1);
Chris@1493 258
Chris@1493 259 for (sv_frame_t probe = bf + 1; probe <= bf1; ++probe) {
Chris@1493 260 if (keyFramesBelow.find(probe) != keyFramesBelow.end()) {
Chris@1493 261 m_keyFrameMap.insert({ f, probe });
Chris@1493 262 mappedSomething = true;
Chris@1493 263 }
Chris@1493 264 }
Chris@1493 265 }
Chris@1493 266 }
Chris@1493 267
Chris@1493 268 if (!mappedSomething) {
Chris@1493 269 m_keyFrameMap.insert({ f, bf });
Chris@1493 270 }
Chris@1493 271 }
Chris@1493 272
Chris@1493 273 #ifdef DEBUG_ALIGNMENT_VIEW
Chris@1493 274 SVCERR << "AlignmentView " << getId() << "::buildKeyFrameMap: have "
Chris@1493 275 << m_keyFrameMap.size() << " mappings" << endl;
Chris@1493 276 #endif
Chris@1493 277 }
Chris@1493 278
Chris@1493 279 vector<sv_frame_t>
Chris@1493 280 AlignmentView::getKeyFrames(View *view, sv_frame_t &resolution)
Chris@1493 281 {
Chris@1493 282 resolution = 1;
Chris@1493 283
Chris@1493 284 if (!view) {
Chris@1493 285 return getDefaultKeyFrames();
Chris@1493 286 }
Chris@1493 287
Chris@1493 288 ModelId m = getSalientModel(view);
Chris@1475 289 auto model = ModelById::getAs<SparseOneDimensionalModel>(m);
Chris@1475 290 if (!model) {
Chris@1266 291 return getDefaultKeyFrames();
Chris@868 292 }
Chris@868 293
Chris@1493 294 resolution = model->getResolution();
Chris@1493 295
Chris@976 296 vector<sv_frame_t> keyFrames;
Chris@868 297
Chris@1475 298 EventVector pp = model->getAllEvents();
Chris@1433 299 for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) {
Chris@1433 300 keyFrames.push_back(pi->getFrame());
Chris@868 301 }
Chris@868 302
Chris@868 303 return keyFrames;
Chris@868 304 }
Chris@868 305
Chris@976 306 vector<sv_frame_t>
Chris@868 307 AlignmentView::getDefaultKeyFrames()
Chris@868 308 {
Chris@976 309 vector<sv_frame_t> keyFrames;
Chris@1509 310 return keyFrames;
Chris@868 311
Chris@1509 312 #ifdef NOT_REALLY
Chris@868 313 if (!m_above || !m_manager) return keyFrames;
Chris@868 314
Chris@976 315 sv_samplerate_t rate = m_manager->getMainModelSampleRate();
Chris@868 316 if (rate == 0) return keyFrames;
Chris@868 317
Chris@976 318 for (sv_frame_t f = m_above->getModelsStartFrame();
Chris@1266 319 f <= m_above->getModelsEndFrame();
Chris@1266 320 f += sv_frame_t(rate * 5 + 0.5)) {
Chris@1266 321 keyFrames.push_back(f);
Chris@868 322 }
Chris@868 323
Chris@868 324 return keyFrames;
Chris@1509 325 #endif
Chris@868 326 }
Chris@868 327
Chris@1493 328 ModelId
Chris@1493 329 AlignmentView::getSalientModel(View *view)
Chris@1493 330 {
Chris@1493 331 ModelId m;
Chris@1493 332
Chris@1493 333 // get the topmost such
Chris@1493 334 for (int i = 0; i < view->getLayerCount(); ++i) {
Chris@1493 335 if (qobject_cast<TimeInstantLayer *>(view->getLayer(i))) {
Chris@1493 336 ModelId mm = view->getLayer(i)->getModel();
Chris@1493 337 if (ModelById::isa<SparseOneDimensionalModel>(mm)) {
Chris@1493 338 m = mm;
Chris@1493 339 }
Chris@1493 340 }
Chris@1493 341 }
Chris@1493 342
Chris@1493 343 return m;
Chris@1493 344 }
Chris@1493 345
Chris@1493 346