annotate view/Overview.cpp @ 1212:a1ee3108d1d3 3.0-integration

Make the colour 3d plot renderer able to support more than one level of peak cache; introduce a second "peak" cache for the spectrogram layer that actually has a 1-1 column relationship with the underlying FFT model, and use it in addition to the existing peak cache if memory is plentiful. Makes spectrograms appear much faster in many common situations.
author Chris Cannam
date Thu, 05 Jan 2017 14:02:54 +0000
parents 97d0c798c2ac
children a34a2a25907c
rev   line source
Chris@173 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@173 2
Chris@173 3 /*
Chris@173 4 Sonic Visualiser
Chris@173 5 An audio file viewer and annotation editor.
Chris@173 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@173 8
Chris@173 9 This program is free software; you can redistribute it and/or
Chris@173 10 modify it under the terms of the GNU General Public License as
Chris@173 11 published by the Free Software Foundation; either version 2 of the
Chris@173 12 License, or (at your option) any later version. See the file
Chris@173 13 COPYING included with this distribution for more information.
Chris@173 14 */
Chris@173 15
Chris@173 16 #include "Overview.h"
Chris@173 17 #include "layer/Layer.h"
Chris@173 18 #include "data/model/Model.h"
Chris@173 19 #include "base/ZoomConstraint.h"
Chris@173 20
Chris@173 21 #include <QPaintEvent>
Chris@173 22 #include <QPainter>
Chris@173 23 #include <iostream>
Chris@173 24
Chris@643 25 //#define DEBUG_OVERVIEW 1
Chris@642 26
Chris@682 27
Chris@173 28 Overview::Overview(QWidget *w) :
Chris@173 29 View(w, false),
Chris@854 30 m_clickedInRange(false),
Chris@854 31 m_dragCentreFrame(0)
Chris@173 32 {
Chris@173 33 setObjectName(tr("Overview"));
Chris@173 34 m_followPan = false;
Chris@173 35 m_followZoom = false;
Chris@211 36 setPlaybackFollow(PlaybackIgnore);
Chris@274 37 m_modelTestTime.start();
Chris@965 38
Chris@965 39 bool light = hasLightBackground();
Chris@965 40 if (light) m_boxColour = Qt::darkGray;
Chris@965 41 else m_boxColour = Qt::lightGray;
Chris@173 42 }
Chris@173 43
Chris@173 44 void
Chris@908 45 Overview::modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame)
Chris@173 46 {
Chris@253 47 bool zoomChanged = false;
Chris@253 48
Chris@908 49 sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame();
Chris@908 50 int zoomLevel = int(frameCount / width());
Chris@253 51 if (zoomLevel < 1) zoomLevel = 1;
Chris@253 52 zoomLevel = getZoomConstraintBlockSize(zoomLevel,
Chris@253 53 ZoomConstraint::RoundUp);
Chris@253 54 if (zoomLevel != m_zoomLevel) {
Chris@253 55 zoomChanged = true;
Chris@253 56 }
Chris@253 57
Chris@253 58 if (!zoomChanged) {
Chris@274 59 if (m_modelTestTime.elapsed() < 1000) {
Chris@835 60 for (LayerList::const_iterator i = m_layerStack.begin();
Chris@835 61 i != m_layerStack.end(); ++i) {
Chris@274 62 if ((*i)->getModel() &&
Chris@389 63 (!(*i)->getModel()->isOK() ||
Chris@389 64 !(*i)->getModel()->isReady())) {
Chris@274 65 return;
Chris@274 66 }
Chris@253 67 }
Chris@274 68 } else {
Chris@274 69 m_modelTestTime.restart();
Chris@253 70 }
Chris@253 71 }
Chris@253 72
Chris@806 73 View::modelChangedWithin(startFrame, endFrame);
Chris@173 74 }
Chris@173 75
Chris@173 76 void
Chris@173 77 Overview::modelReplaced()
Chris@173 78 {
Chris@339 79 m_playPointerFrame = getAlignedPlaybackFrame();
Chris@173 80 View::modelReplaced();
Chris@173 81 }
Chris@173 82
Chris@173 83 void
Chris@211 84 Overview::registerView(View *view)
Chris@173 85 {
Chris@211 86 m_views.insert(view);
Chris@173 87 update();
Chris@173 88 }
Chris@173 89
Chris@173 90 void
Chris@211 91 Overview::unregisterView(View *view)
Chris@173 92 {
Chris@211 93 m_views.erase(view);
Chris@173 94 update();
Chris@173 95 }
Chris@173 96
Chris@173 97 void
Chris@908 98 Overview::globalCentreFrameChanged(sv_frame_t
Chris@806 99 #ifdef DEBUG_OVERVIEW
Chris@806 100 f
Chris@806 101 #endif
Chris@806 102 )
Chris@173 103 {
Chris@642 104 #ifdef DEBUG_OVERVIEW
Chris@682 105 cerr << "Overview::globalCentreFrameChanged: " << f << endl;
Chris@642 106 #endif
Chris@211 107 update();
Chris@211 108 }
Chris@173 109
Chris@211 110 void
Chris@908 111 Overview::viewCentreFrameChanged(View *v, sv_frame_t
Chris@806 112 #ifdef DEBUG_OVERVIEW
Chris@806 113 f
Chris@806 114 #endif
Chris@806 115 )
Chris@211 116 {
Chris@642 117 #ifdef DEBUG_OVERVIEW
Chris@682 118 cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl;
Chris@642 119 #endif
Chris@211 120 if (m_views.find(v) != m_views.end()) {
Chris@173 121 update();
Chris@173 122 }
Chris@211 123 }
Chris@173 124
Chris@173 125 void
Chris@806 126 Overview::viewZoomLevelChanged(View *v, int, bool)
Chris@173 127 {
Chris@222 128 if (v == this) return;
Chris@211 129 if (m_views.find(v) != m_views.end()) {
Chris@173 130 update();
Chris@173 131 }
Chris@173 132 }
Chris@173 133
Chris@173 134 void
Chris@908 135 Overview::viewManagerPlaybackFrameChanged(sv_frame_t f)
Chris@173 136 {
Chris@642 137 #ifdef DEBUG_OVERVIEW
Chris@682 138 cerr << "Overview[" << this << "]::viewManagerPlaybackFrameChanged(" << f << "): " << f << endl;
Chris@642 139 #endif
Chris@642 140
Chris@173 141 bool changed = false;
Chris@173 142
Chris@339 143 f = getAlignedPlaybackFrame();
Chris@339 144
Chris@173 145 if (getXForFrame(m_playPointerFrame) != getXForFrame(f)) changed = true;
Chris@173 146 m_playPointerFrame = f;
Chris@173 147
Chris@173 148 if (changed) update();
Chris@173 149 }
Chris@173 150
Chris@871 151 QColor
Chris@871 152 Overview::getFillWithin() const
Chris@871 153 {
Chris@871 154 return Qt::transparent;
Chris@871 155 }
Chris@871 156
Chris@871 157 QColor
Chris@871 158 Overview::getFillWithout() const
Chris@871 159 {
Chris@871 160 QColor c = palette().window().color();
Chris@871 161 c.setAlpha(100);
Chris@871 162 return c;
Chris@871 163 }
Chris@871 164
Chris@173 165 void
Chris@965 166 Overview::setBoxColour(QColor c)
Chris@965 167 {
Chris@965 168 m_boxColour = c;
Chris@965 169 }
Chris@965 170
Chris@965 171 void
Chris@173 172 Overview::paintEvent(QPaintEvent *e)
Chris@173 173 {
Chris@173 174 // Recalculate zoom in case the size of the widget has changed.
Chris@173 175
Chris@642 176 #ifdef DEBUG_OVERVIEW
Chris@682 177 cerr << "Overview::paintEvent: width is " << width() << ", centre frame " << m_centreFrame << endl;
Chris@642 178 #endif
Chris@214 179
Chris@908 180 sv_frame_t startFrame = getModelsStartFrame();
Chris@908 181 sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame();
Chris@908 182 int zoomLevel = int(frameCount / width());
Chris@173 183 if (zoomLevel < 1) zoomLevel = 1;
Chris@173 184 zoomLevel = getZoomConstraintBlockSize(zoomLevel,
Chris@173 185 ZoomConstraint::RoundUp);
Chris@173 186 if (zoomLevel != m_zoomLevel) {
Chris@173 187 m_zoomLevel = zoomLevel;
Chris@222 188 emit zoomLevelChanged(m_zoomLevel, m_followZoom);
Chris@173 189 }
Chris@253 190
Chris@908 191 sv_frame_t centreFrame = startFrame + m_zoomLevel * (width() / 2);
Chris@173 192 if (centreFrame > (startFrame + getModelsEndFrame())/2) {
Chris@173 193 centreFrame = (startFrame + getModelsEndFrame())/2;
Chris@173 194 }
Chris@173 195 if (centreFrame != m_centreFrame) {
Chris@642 196 #ifdef DEBUG_OVERVIEW
Chris@682 197 cerr << "Overview::paintEvent: Centre frame changed from "
Chris@642 198 << m_centreFrame << " to " << centreFrame << " and thus start frame from " << getStartFrame();
Chris@642 199 #endif
Chris@173 200 m_centreFrame = centreFrame;
Chris@642 201 #ifdef DEBUG_OVERVIEW
Chris@682 202 cerr << " to " << getStartFrame() << endl;
Chris@642 203 #endif
Chris@211 204 emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore);
Chris@173 205 }
Chris@173 206
Chris@173 207 View::paintEvent(e);
Chris@173 208
Chris@173 209 QPainter paint;
Chris@173 210 paint.begin(this);
Chris@871 211 paint.setClipRegion(e->region());
Chris@871 212 paint.setRenderHints(QPainter::Antialiasing);
Chris@871 213
Chris@871 214 // We paint a rounded rect for each distinct set of view extents,
Chris@871 215 // and we colour in the inside and outside of the rect that
Chris@871 216 // corresponds to the current view. (One small caveat -- we don't
Chris@871 217 // know which rect that is yet. We'll have to figure it out
Chris@871 218 // somehow...)
Chris@173 219
Chris@871 220 std::set<std::pair<int, int> > extents;
Chris@871 221 std::vector<QRect> rects;
Chris@871 222 QRect primary;
Chris@173 223
Chris@173 224 int y = 0;
Chris@173 225
Chris@211 226 for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
Chris@173 227 if (!*i) continue;
Chris@173 228
Chris@173 229 View *w = (View *)*i;
Chris@173 230
Chris@908 231 sv_frame_t f0 = w->getFrameForX(0);
Chris@908 232 sv_frame_t f1 = w->getFrameForX(w->width());
Chris@173 233
Chris@339 234 if (f0 >= 0) {
Chris@908 235 sv_frame_t rf0 = w->alignToReference(f0);
Chris@339 236 f0 = alignFromReference(rf0);
Chris@339 237 }
Chris@339 238 if (f1 >= 0) {
Chris@908 239 sv_frame_t rf1 = w->alignToReference(f1);
Chris@339 240 f1 = alignFromReference(rf1);
Chris@339 241 }
Chris@339 242
Chris@173 243 int x0 = getXForFrame(f0);
Chris@173 244 int x1 = getXForFrame(f1);
Chris@173 245
Chris@871 246 if (x1 <= x0) x1 = x0 + 1;
Chris@871 247
Chris@871 248 std::pair<int, int> extent(x0, x1);
Chris@871 249
Chris@871 250 if (extents.find(extent) == extents.end()) {
Chris@871 251
matthiasm@935 252 y += height() / 10 + 1;
Chris@871 253 extents.insert(extent);
Chris@173 254
Chris@871 255 QRect vr(x0, y, x1 - x0, height() - 2 * y);
Chris@871 256 rects.push_back(vr);
Chris@871 257 primary = vr; //!!! for now
Chris@871 258 }
Chris@871 259 }
Chris@871 260
Chris@871 261 QPainterPath without;
matthiasm@935 262 without.addRoundedRect(primary, 4, 4);
Chris@871 263 without.addRect(rect());
Chris@871 264 paint.setPen(Qt::NoPen);
Chris@871 265 paint.setBrush(getFillWithout());
Chris@871 266 paint.drawPath(without);
Chris@871 267
Chris@871 268 paint.setBrush(getFillWithin());
matthiasm@935 269 paint.drawRoundedRect(primary, 4, 4);
Chris@871 270
Chris@871 271 foreach (QRect vr, rects) {
Chris@871 272 paint.setBrush(Qt::NoBrush);
Chris@965 273 paint.setPen(QPen(m_boxColour, 2));
matthiasm@935 274 paint.drawRoundedRect(vr, 4, 4);
Chris@173 275 }
Chris@173 276
Chris@173 277 paint.end();
Chris@173 278 }
Chris@173 279
Chris@173 280 void
Chris@173 281 Overview::mousePressEvent(QMouseEvent *e)
Chris@173 282 {
Chris@173 283 m_clickPos = e->pos();
Chris@908 284 sv_frame_t clickFrame = getFrameForX(m_clickPos.x());
Chris@339 285 if (clickFrame > 0) m_dragCentreFrame = clickFrame;
Chris@339 286 else m_dragCentreFrame = 0;
Chris@339 287 m_clickedInRange = true;
Chris@339 288
Chris@211 289 for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
Chris@339 290 if (*i && (*i)->getAligningModel() == getAligningModel()) {
Chris@339 291 m_dragCentreFrame = (*i)->getCentreFrame();
Chris@339 292 break;
Chris@339 293 }
Chris@173 294 }
Chris@173 295 }
Chris@173 296
Chris@173 297 void
Chris@173 298 Overview::mouseReleaseEvent(QMouseEvent *e)
Chris@173 299 {
Chris@173 300 if (m_clickedInRange) {
Chris@173 301 mouseMoveEvent(e);
Chris@173 302 }
Chris@173 303 m_clickedInRange = false;
Chris@173 304 }
Chris@173 305
Chris@173 306 void
Chris@173 307 Overview::mouseMoveEvent(QMouseEvent *e)
Chris@173 308 {
Chris@173 309 if (!m_clickedInRange) return;
Chris@173 310
Chris@806 311 int xoff = int(e->x()) - int(m_clickPos.x());
Chris@908 312 sv_frame_t frameOff = xoff * m_zoomLevel;
Chris@173 313
Chris@908 314 sv_frame_t newCentreFrame = m_dragCentreFrame;
Chris@173 315 if (frameOff > 0) {
Chris@173 316 newCentreFrame += frameOff;
Chris@908 317 } else if (newCentreFrame >= -frameOff) {
Chris@173 318 newCentreFrame += frameOff;
Chris@173 319 } else {
Chris@173 320 newCentreFrame = 0;
Chris@173 321 }
Chris@173 322
Chris@173 323 if (newCentreFrame >= getModelsEndFrame()) {
Chris@173 324 newCentreFrame = getModelsEndFrame();
Chris@173 325 if (newCentreFrame > 0) --newCentreFrame;
Chris@173 326 }
Chris@173 327
Chris@173 328 if (std::max(m_centreFrame, newCentreFrame) -
Chris@908 329 std::min(m_centreFrame, newCentreFrame) > m_zoomLevel) {
Chris@908 330 sv_frame_t rf = alignToReference(newCentreFrame);
Chris@643 331 #ifdef DEBUG_OVERVIEW
Chris@682 332 cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl;
Chris@643 333 #endif
Chris@817 334 if (m_followPlay == PlaybackScrollContinuous ||
Chris@817 335 m_followPlay == PlaybackScrollPageWithCentre) {
Chris@817 336 emit centreFrameChanged(rf, true, PlaybackScrollContinuous);
Chris@817 337 } else {
Chris@817 338 emit centreFrameChanged(rf, true, PlaybackIgnore);
Chris@817 339 }
Chris@173 340 }
Chris@173 341 }
Chris@173 342
Chris@189 343 void
Chris@189 344 Overview::mouseDoubleClickEvent(QMouseEvent *e)
Chris@189 345 {
Chris@908 346 sv_frame_t frame = getFrameForX(e->x());
Chris@908 347 sv_frame_t rf = 0;
Chris@339 348 if (frame > 0) rf = alignToReference(frame);
Chris@643 349 #ifdef DEBUG_OVERVIEW
Chris@682 350 cerr << "Overview::mouseDoubleClickEvent: frame " << frame << " -> rf " << rf << endl;
Chris@643 351 #endif
Chris@643 352 m_clickedInRange = false; // we're not starting a drag with the second click
Chris@339 353 emit centreFrameChanged(rf, true, PlaybackScrollContinuous);
Chris@189 354 }
Chris@173 355
Chris@189 356 void
Chris@189 357 Overview::enterEvent(QEvent *)
Chris@189 358 {
Chris@189 359 emit contextHelpChanged(tr("Click and drag to navigate; double-click to jump"));
Chris@189 360 }
Chris@189 361
Chris@189 362 void
Chris@189 363 Overview::leaveEvent(QEvent *)
Chris@189 364 {
Chris@189 365 emit contextHelpChanged("");
Chris@189 366 }
Chris@189 367
Chris@189 368