AlignmentView.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006-2014 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "AlignmentView.h"
17 
18 #include <QPainter>
19 
20 #include "data/model/SparseOneDimensionalModel.h"
21 
22 #include "layer/TimeInstantLayer.h"
23 
24 //#define DEBUG_ALIGNMENT_VIEW 1
25 
26 using std::vector;
27 using std::set;
28 
30  View(w, false),
31  m_above(nullptr),
32  m_below(nullptr),
33  m_reference(nullptr),
34  m_leftmostAbove(-1),
35  m_rightmostAbove(-1)
36 {
37  setObjectName(tr("AlignmentView"));
38 }
39 
40 void
42 {
43 #ifdef DEBUG_ALIGNMENT_VIEW
44  SVCERR << "AlignmentView " << getId() << "::keyFramesChanged" << endl;
45 #endif
46 
47  // This is just a notification that we need to rebuild - so all we
48  // do here is clear, and rebuild on demand later
49  QMutexLocker locker(&m_mapsMutex);
50  m_fromAboveMap.clear();
51  m_fromReferenceMap.clear();
52 }
53 
54 void
56 {
58  update();
59 }
60 
61 void
63 {
65  if (v == m_above) {
66  m_centreFrame = f;
67  update();
68  } else if (v == m_below) {
69  update();
70  }
71 }
72 
73 void
75 {
76  update();
77 }
78 
79 void
81 {
82  m_zoomLevel = level;
83  update();
84 }
85 
86 void
88 {
89  update();
90 }
91 
92 void
94 {
95  if (m_above) {
96  disconnect(m_above, nullptr, this, nullptr);
97  }
98 
99  m_above = v;
100 
101  if (m_above) {
102  connect(m_above,
103  SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
104  this,
105  SLOT(viewAboveZoomLevelChanged(ZoomLevel, bool)));
106  connect(m_above,
107  SIGNAL(propertyContainerAdded(PropertyContainer *)),
108  this,
109  SLOT(keyFramesChanged()));
110  connect(m_above,
111  SIGNAL(layerModelChanged()),
112  this,
113  SLOT(keyFramesChanged()));
114  }
115 
117 }
118 
119 void
121 {
122  if (m_below) {
123  disconnect(m_below, nullptr, this, nullptr);
124  }
125 
126  m_below = v;
127 
128  if (m_below) {
129  connect(m_below,
130  SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
131  this,
132  SLOT(viewBelowZoomLevelChanged(ZoomLevel, bool)));
133  connect(m_below,
134  SIGNAL(propertyContainerAdded(PropertyContainer *)),
135  this,
136  SLOT(keyFramesChanged()));
137  connect(m_below,
138  SIGNAL(layerModelChanged()),
139  this,
140  SLOT(keyFramesChanged()));
141  }
142 
144 }
145 
146 void
148 {
149  m_reference = view;
150 }
151 
152 void
154 {
155  if (m_above == nullptr || m_below == nullptr || !m_manager) return;
156 
157 #ifdef DEBUG_ALIGNMENT_VIEW
158  SVCERR << "AlignmentView " << getId() << "::paintEvent" << endl;
159 #endif
160 
161  bool darkPalette = false;
162  if (m_manager) darkPalette = m_manager->getGlobalDarkBackground();
163 
164  QColor fg, bg;
165  if (darkPalette) {
166  fg = Qt::gray;
167  bg = Qt::black;
168  } else {
169  fg = Qt::black;
170  bg = Qt::gray;
171  }
172 
173  QPainter paint(this);
174  paint.setPen(QPen(fg, 2));
175  paint.setBrush(Qt::NoBrush);
176  paint.setRenderHint(QPainter::Antialiasing, true);
177 
178  paint.fillRect(rect(), bg);
179 
180  QMutexLocker locker(&m_mapsMutex);
181 
182  if (m_fromAboveMap.empty()) {
183  reconnectModels();
184  buildMaps();
185  }
186 
187 #ifdef DEBUG_ALIGNMENT_VIEW
188  SVCERR << "AlignmentView " << getId() << "::paintEvent: painting "
189  << m_fromAboveMap.size() << " mappings" << endl;
190 #endif
191 
192  int w = width();
193  int h = height();
194 
195  if (m_leftmostAbove >= 0) {
196 
197 #ifdef DEBUG_ALIGNMENT_VIEW
198  SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove
199  << ", we have a relationship with the pane above us: showing "
200  << "mappings in relation to that" << endl;
201 #endif
202 
203  for (const auto &km: m_fromAboveMap) {
204 
205  sv_frame_t af = km.first;
206  sv_frame_t bf = km.second;
207 
208  if (af < m_leftmostAbove) {
209 #ifdef DEBUG_ALIGNMENT_VIEW
210  SVCERR << "AlignmentView: af " << af << " < m_leftmostAbove " << m_leftmostAbove << endl;
211 #endif
212  continue;
213  }
214  if (af > m_rightmostAbove) {
215 #ifdef DEBUG_ALIGNMENT_VIEW
216  SVCERR << "AlignmentView: af " << af << " > m_rightmostAbove " << m_rightmostAbove << endl;
217 #endif
218  continue;
219  }
220 
221  int ax = m_above->getXForFrame(af);
222  int bx = m_below->getXForFrame(bf);
223 
224  if (ax >= 0 || ax < w || bx >= 0 || bx < w) {
225  paint.drawLine(ax, 0, bx, h);
226  }
227  }
228  } else if (m_reference != nullptr) {
229  // the below has nothing in common with the above: show things
230  // in common with the reference instead
231 
232 #ifdef DEBUG_ALIGNMENT_VIEW
233  SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove
234  << ", we have no relationship with the pane above us: showing "
235  << "mappings in relation to the reference instead" << endl;
236 #endif
237 
238  for (const auto &km: m_fromReferenceMap) {
239 
240  sv_frame_t af = km.first;
241  sv_frame_t bf = km.second;
242 
243  int ax = m_reference->getXForFrame(af);
244  int bx = m_below->getXForFrame(bf);
245 
246  if (ax >= 0 || ax < w || bx >= 0 || bx < w) {
247  paint.drawLine(ax, 0, bx, h);
248  }
249  }
250  }
251 
252  paint.end();
253 }
254 
255 void
257 {
258  vector<ModelId> toConnect {
261  };
262 
263  for (auto modelId: toConnect) {
264  if (auto model = ModelById::get(modelId)) {
265  auto referenceId = model->getAlignmentReference();
266  if (!referenceId.isNone()) {
267  toConnect.push_back(referenceId);
268  }
269  }
270  }
271 
272  for (auto modelId: toConnect) {
273  if (auto model = ModelById::get(modelId)) {
274  auto ptr = model.get();
275  disconnect(ptr, 0, this, 0);
276  connect(ptr, SIGNAL(modelChanged(ModelId)),
277  this, SLOT(keyFramesChanged()));
278  connect(ptr, SIGNAL(completionChanged(ModelId)),
279  this, SLOT(keyFramesChanged()));
280  connect(ptr, SIGNAL(alignmentCompletionChanged(ModelId)),
281  this, SLOT(keyFramesChanged()));
282  }
283  }
284 }
285 
286 void
288 {
289 #ifdef DEBUG_ALIGNMENT_VIEW
290  SVCERR << "AlignmentView " << getId() << "::buildMaps" << endl;
291 #endif
292 
293  sv_frame_t resolution = 1;
294 
295  set<sv_frame_t> keyFramesBelow;
296  for (auto f: getKeyFrames(m_below, resolution)) {
297  keyFramesBelow.insert(f);
298  }
299 
300  foreach(sv_frame_t f, keyFramesBelow) {
301  sv_frame_t rf = m_below->alignToReference(f);
302  m_fromReferenceMap.insert({ rf, f });
303  }
304 
305  vector<sv_frame_t> keyFrames = getKeyFrames(m_above, resolution);
306 
307  // These are the most extreme leftward and rightward frames in
308  // "above" that have distinct corresponding frames in
309  // "below". Anything left of m_leftmostAbove or right of
310  // m_rightmostAbove maps effectively off one end or the other of
311  // the below view. (They don't actually map off the ends, they
312  // just all map to the same first/last destination frame. But we
313  // don't want to display their mappings, as they're just noise.)
314  m_leftmostAbove = -1;
315  m_rightmostAbove = -1;
316 
317  sv_frame_t prevAf = -1;
318  sv_frame_t prevBf = -1;
319 
320  foreach (sv_frame_t af, keyFrames) {
321 
322  sv_frame_t rf = m_above->alignToReference(af);
323  sv_frame_t bf = m_below->alignFromReference(rf);
324 
325  if (prevBf > 0 && bf > prevBf) {
326  if (m_leftmostAbove < 0) {
327  m_leftmostAbove = prevAf;
328  }
329  m_rightmostAbove = af;
330  }
331  prevAf = af;
332  prevBf = bf;
333 
334  bool mappedSomething = false;
335 
336  if (resolution > 1) {
337  if (keyFramesBelow.find(bf) == keyFramesBelow.end()) {
338 
339  sv_frame_t af1 = af + resolution;
340  sv_frame_t rf1 = m_above->alignToReference(af1);
341  sv_frame_t bf1 = m_below->alignFromReference(rf1);
342 
343  for (sv_frame_t probe = bf + 1; probe <= bf1; ++probe) {
344  if (keyFramesBelow.find(probe) != keyFramesBelow.end()) {
345  m_fromAboveMap.insert({ af, probe });
346  mappedSomething = true;
347  }
348  }
349  }
350  }
351 
352  if (!mappedSomething) {
353  m_fromAboveMap.insert({ af, bf });
354  }
355  }
356 
357 #ifdef DEBUG_ALIGNMENT_VIEW
358  SVCERR << "AlignmentView " << getId() << "::buildMaps: have "
359  << m_fromAboveMap.size() << " mappings" << endl;
360 #endif
361 }
362 
363 vector<sv_frame_t>
364 AlignmentView::getKeyFrames(View *view, sv_frame_t &resolution)
365 {
366  resolution = 1;
367 
368  if (!view) {
369  return getDefaultKeyFrames();
370  }
371 
372  ModelId m = getSalientModel(view);
373  auto model = ModelById::getAs<SparseOneDimensionalModel>(m);
374  if (!model) {
375  return getDefaultKeyFrames();
376  }
377 
378  resolution = model->getResolution();
379 
380  vector<sv_frame_t> keyFrames;
381 
382  EventVector pp = model->getAllEvents();
383  for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) {
384  keyFrames.push_back(pi->getFrame());
385  }
386 
387  return keyFrames;
388 }
389 
390 vector<sv_frame_t>
392 {
393  vector<sv_frame_t> keyFrames;
394  return keyFrames;
395 
396 #ifdef NOT_REALLY
397  if (!m_above || !m_manager) return keyFrames;
398 
399  sv_samplerate_t rate = m_manager->getMainModelSampleRate();
400  if (rate == 0) return keyFrames;
401 
402  for (sv_frame_t f = m_above->getModelsStartFrame();
403  f <= m_above->getModelsEndFrame();
404  f += sv_frame_t(rate * 5 + 0.5)) {
405  keyFrames.push_back(f);
406  }
407 
408  return keyFrames;
409 #endif
410 }
411 
412 ModelId
414 {
415  ModelId m;
416 
417  // get the topmost such
418  for (int i = 0; i < view->getLayerCount(); ++i) {
419  if (qobject_cast<TimeInstantLayer *>(view->getLayer(i))) {
420  ModelId mm = view->getLayer(i)->getModel();
421  if (ModelById::isa<SparseOneDimensionalModel>(mm)) {
422  m = mm;
423  }
424  }
425  }
426 
427  return m;
428 }
429 
430 
ZoomLevel m_zoomLevel
Definition: View.h:541
sv_frame_t alignFromReference(sv_frame_t) const
Definition: View.cpp:1597
sv_frame_t m_leftmostAbove
Definition: AlignmentView.h:64
void viewManagerPlaybackFrameChanged(sv_frame_t) override
bool getGlobalDarkBackground() const
virtual void viewAboveZoomLevelChanged(ZoomLevel, bool)
int getId() const override
Retrieve the id of this object.
Definition: View.h:72
virtual int getLayerCount() const
Return the number of layers, regardless of whether visible or dormant, i.e.
Definition: View.h:197
virtual void modelChanged(ModelId)
Definition: View.cpp:1104
View * m_reference
Definition: AlignmentView.h:59
sv_frame_t alignToReference(sv_frame_t) const
Definition: View.cpp:1606
sv_samplerate_t getMainModelSampleRate() const
The sample rate of the current main model.
Definition: ViewManager.h:190
void propertyContainerAdded(PropertyContainer *pc)
QMutex m_mapsMutex
Definition: AlignmentView.h:61
ViewManager * m_manager
Definition: View.h:586
void viewCentreFrameChanged(View *, sv_frame_t) override
void keyFramesChanged()
void setBelowView(View *view)
sv_frame_t m_centreFrame
Definition: View.h:540
std::multimap< sv_frame_t, sv_frame_t > m_fromReferenceMap
Definition: AlignmentView.h:63
virtual Layer * getLayer(int n)
Return the nth layer, counted in stacking order.
Definition: View.h:205
ModelId getSalientModel(View *)
void globalCentreFrameChanged(sv_frame_t) override
sv_frame_t getModelsStartFrame() const override
Definition: View.cpp:1451
AlignmentView(QWidget *parent=0)
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
Definition: View.h:55
void setReferenceView(View *view)
sv_frame_t m_rightmostAbove
Definition: AlignmentView.h:65
virtual ModelId getModel() const =0
Return the ID of the model represented in this layer.
sv_frame_t getModelsEndFrame() const override
Definition: View.cpp:1475
void paintEvent(QPaintEvent *e) override
virtual void viewCentreFrameChanged(View *, sv_frame_t)
Definition: View.cpp:1263
void layerModelChanged()
std::vector< sv_frame_t > getKeyFrames(View *, sv_frame_t &resolution)
std::multimap< sv_frame_t, sv_frame_t > m_fromAboveMap
Definition: AlignmentView.h:62
void zoomLevelChanged(ZoomLevel level, bool locked)
void setAboveView(View *view)
virtual void globalCentreFrameChanged(sv_frame_t)
Definition: View.cpp:1250
std::vector< sv_frame_t > getDefaultKeyFrames()
void reconnectModels()
int getXForFrame(sv_frame_t frame) const override
Return the pixel x-coordinate corresponding to a given sample frame.
Definition: View.cpp:527
virtual void viewBelowZoomLevelChanged(ZoomLevel, bool)