annotate view/AlignmentView.cpp @ 1614:c6f5c822b10d

Fix potential divide-by-zero (depending on a race elsewhere)
author Chris Cannam
date Tue, 30 Jun 2020 10:56:56 +0100
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