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@868
|
310
|
Chris@868
|
311 if (!m_above || !m_manager) return keyFrames;
|
Chris@868
|
312
|
Chris@976
|
313 sv_samplerate_t rate = m_manager->getMainModelSampleRate();
|
Chris@868
|
314 if (rate == 0) return keyFrames;
|
Chris@868
|
315
|
Chris@976
|
316 for (sv_frame_t f = m_above->getModelsStartFrame();
|
Chris@1266
|
317 f <= m_above->getModelsEndFrame();
|
Chris@1266
|
318 f += sv_frame_t(rate * 5 + 0.5)) {
|
Chris@1266
|
319 keyFrames.push_back(f);
|
Chris@868
|
320 }
|
Chris@868
|
321
|
Chris@868
|
322 return keyFrames;
|
Chris@868
|
323 }
|
Chris@868
|
324
|
Chris@1493
|
325 ModelId
|
Chris@1493
|
326 AlignmentView::getSalientModel(View *view)
|
Chris@1493
|
327 {
|
Chris@1493
|
328 ModelId m;
|
Chris@1493
|
329
|
Chris@1493
|
330 // get the topmost such
|
Chris@1493
|
331 for (int i = 0; i < view->getLayerCount(); ++i) {
|
Chris@1493
|
332 if (qobject_cast<TimeInstantLayer *>(view->getLayer(i))) {
|
Chris@1493
|
333 ModelId mm = view->getLayer(i)->getModel();
|
Chris@1493
|
334 if (ModelById::isa<SparseOneDimensionalModel>(mm)) {
|
Chris@1493
|
335 m = mm;
|
Chris@1493
|
336 }
|
Chris@1493
|
337 }
|
Chris@1493
|
338 }
|
Chris@1493
|
339
|
Chris@1493
|
340 return m;
|
Chris@1493
|
341 }
|
Chris@1493
|
342
|
Chris@1493
|
343
|