Chris@0
|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 A waveform viewer and audio annotation editor.
|
Chris@5
|
5 Chris Cannam, Queen Mary University of London, 2005-2006
|
Chris@0
|
6
|
Chris@0
|
7 This is experimental software. Not for distribution.
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 #include "TimeInstantLayer.h"
|
Chris@0
|
11
|
Chris@0
|
12 #include "base/Model.h"
|
Chris@0
|
13 #include "base/RealTime.h"
|
Chris@0
|
14 #include "base/View.h"
|
Chris@0
|
15 #include "base/Profiler.h"
|
Chris@0
|
16
|
Chris@0
|
17 #include "model/SparseOneDimensionalModel.h"
|
Chris@0
|
18
|
Chris@0
|
19 #include <QPainter>
|
Chris@0
|
20
|
Chris@0
|
21 #include <iostream>
|
Chris@0
|
22
|
Chris@0
|
23 TimeInstantLayer::TimeInstantLayer(View *w) :
|
Chris@0
|
24 Layer(w),
|
Chris@0
|
25 m_model(0),
|
Chris@0
|
26 m_colour(QColor(200, 50, 255))
|
Chris@0
|
27 {
|
Chris@0
|
28 m_view->addLayer(this);
|
Chris@0
|
29 }
|
Chris@0
|
30
|
Chris@0
|
31 void
|
Chris@0
|
32 TimeInstantLayer::setModel(SparseOneDimensionalModel *model)
|
Chris@0
|
33 {
|
Chris@0
|
34 if (m_model == model) return;
|
Chris@0
|
35 m_model = model;
|
Chris@0
|
36
|
Chris@0
|
37 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
|
Chris@0
|
38 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
|
Chris@0
|
39 this, SIGNAL(modelChanged(size_t, size_t)));
|
Chris@0
|
40
|
Chris@0
|
41 connect(m_model, SIGNAL(completionChanged()),
|
Chris@0
|
42 this, SIGNAL(modelCompletionChanged()));
|
Chris@0
|
43
|
Chris@0
|
44 std::cerr << "TimeInstantLayer::setModel(" << model << ")" << std::endl;
|
Chris@0
|
45
|
Chris@0
|
46 emit modelReplaced();
|
Chris@0
|
47 }
|
Chris@0
|
48
|
Chris@0
|
49 Layer::PropertyList
|
Chris@0
|
50 TimeInstantLayer::getProperties() const
|
Chris@0
|
51 {
|
Chris@0
|
52 PropertyList list;
|
Chris@0
|
53 list.push_back(tr("Colour"));
|
Chris@0
|
54 return list;
|
Chris@0
|
55 }
|
Chris@0
|
56
|
Chris@0
|
57 Layer::PropertyType
|
Chris@0
|
58 TimeInstantLayer::getPropertyType(const PropertyName &name) const
|
Chris@0
|
59 {
|
Chris@0
|
60 return ValueProperty;
|
Chris@0
|
61 }
|
Chris@0
|
62
|
Chris@0
|
63 int
|
Chris@0
|
64 TimeInstantLayer::getPropertyRangeAndValue(const PropertyName &name,
|
Chris@0
|
65 int *min, int *max) const
|
Chris@0
|
66 {
|
Chris@0
|
67 int deft = 0;
|
Chris@0
|
68
|
Chris@0
|
69 if (name == tr("Colour")) {
|
Chris@0
|
70
|
Chris@0
|
71 *min = 0;
|
Chris@0
|
72 *max = 5;
|
Chris@0
|
73
|
Chris@0
|
74 if (m_colour == Qt::black) deft = 0;
|
Chris@0
|
75 else if (m_colour == Qt::darkRed) deft = 1;
|
Chris@0
|
76 else if (m_colour == Qt::darkBlue) deft = 2;
|
Chris@0
|
77 else if (m_colour == Qt::darkGreen) deft = 3;
|
Chris@0
|
78 else if (m_colour == QColor(200, 50, 255)) deft = 4;
|
Chris@0
|
79 else if (m_colour == QColor(255, 150, 50)) deft = 5;
|
Chris@0
|
80
|
Chris@0
|
81 } else {
|
Chris@0
|
82
|
Chris@0
|
83 deft = Layer::getPropertyRangeAndValue(name, min, max);
|
Chris@0
|
84 }
|
Chris@0
|
85
|
Chris@0
|
86 return deft;
|
Chris@0
|
87 }
|
Chris@0
|
88
|
Chris@0
|
89 QString
|
Chris@0
|
90 TimeInstantLayer::getPropertyValueLabel(const PropertyName &name,
|
Chris@0
|
91 int value) const
|
Chris@0
|
92 {
|
Chris@0
|
93 if (name == tr("Colour")) {
|
Chris@0
|
94 switch (value) {
|
Chris@0
|
95 default:
|
Chris@0
|
96 case 0: return tr("Black");
|
Chris@0
|
97 case 1: return tr("Red");
|
Chris@0
|
98 case 2: return tr("Blue");
|
Chris@0
|
99 case 3: return tr("Green");
|
Chris@0
|
100 case 4: return tr("Purple");
|
Chris@0
|
101 case 5: return tr("Orange");
|
Chris@0
|
102 }
|
Chris@0
|
103 }
|
Chris@0
|
104 return tr("<unknown>");
|
Chris@0
|
105 }
|
Chris@0
|
106
|
Chris@0
|
107 void
|
Chris@0
|
108 TimeInstantLayer::setProperty(const PropertyName &name, int value)
|
Chris@0
|
109 {
|
Chris@0
|
110 if (name == tr("Colour")) {
|
Chris@0
|
111 switch (value) {
|
Chris@0
|
112 default:
|
Chris@0
|
113 case 0: setBaseColour(Qt::black); break;
|
Chris@0
|
114 case 1: setBaseColour(Qt::darkRed); break;
|
Chris@0
|
115 case 2: setBaseColour(Qt::darkBlue); break;
|
Chris@0
|
116 case 3: setBaseColour(Qt::darkGreen); break;
|
Chris@0
|
117 case 4: setBaseColour(QColor(200, 50, 255)); break;
|
Chris@0
|
118 case 5: setBaseColour(QColor(255, 150, 50)); break;
|
Chris@0
|
119 }
|
Chris@0
|
120 }
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 void
|
Chris@0
|
124 TimeInstantLayer::setBaseColour(QColor colour)
|
Chris@0
|
125 {
|
Chris@0
|
126 if (m_colour == colour) return;
|
Chris@0
|
127 m_colour = colour;
|
Chris@0
|
128 emit layerParametersChanged();
|
Chris@0
|
129 }
|
Chris@0
|
130
|
Chris@0
|
131 bool
|
Chris@0
|
132 TimeInstantLayer::isLayerScrollable() const
|
Chris@0
|
133 {
|
Chris@0
|
134 QPoint discard;
|
Chris@0
|
135 return !m_view->shouldIlluminateLocalFeatures(this, discard);
|
Chris@0
|
136 }
|
Chris@0
|
137
|
Chris@0
|
138 QRect
|
Chris@0
|
139 TimeInstantLayer::getFeatureDescriptionRect(QPainter &paint, QPoint pos) const
|
Chris@0
|
140 {
|
Chris@0
|
141 return QRect(0, 0,
|
Chris@0
|
142 std::max(100, paint.fontMetrics().width(tr("No local points"))),
|
Chris@0
|
143 50); //!!! cruddy
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 SparseOneDimensionalModel::PointList
|
Chris@0
|
147 TimeInstantLayer::getLocalPoints(int x) const
|
Chris@0
|
148 {
|
Chris@0
|
149 if (!m_model) return SparseOneDimensionalModel::PointList();
|
Chris@0
|
150
|
Chris@0
|
151 long startFrame = m_view->getStartFrame();
|
Chris@0
|
152 long endFrame = m_view->getEndFrame();
|
Chris@0
|
153 int zoomLevel = m_view->getZoomLevel();
|
Chris@0
|
154 long frame = startFrame + x * zoomLevel;
|
Chris@0
|
155
|
Chris@0
|
156 SparseOneDimensionalModel::PointList onPoints =
|
Chris@0
|
157 m_model->getPoints(frame);
|
Chris@0
|
158
|
Chris@0
|
159 if (!onPoints.empty()) {
|
Chris@0
|
160 return onPoints;
|
Chris@0
|
161 }
|
Chris@0
|
162
|
Chris@0
|
163 SparseOneDimensionalModel::PointList prevPoints =
|
Chris@0
|
164 m_model->getPreviousPoints(frame);
|
Chris@0
|
165 SparseOneDimensionalModel::PointList nextPoints =
|
Chris@0
|
166 m_model->getNextPoints(frame);
|
Chris@0
|
167
|
Chris@0
|
168 SparseOneDimensionalModel::PointList usePoints = prevPoints;
|
Chris@0
|
169
|
Chris@0
|
170 if (prevPoints.empty()) {
|
Chris@0
|
171 usePoints = nextPoints;
|
Chris@0
|
172 } else if (prevPoints.begin()->frame < startFrame &&
|
Chris@0
|
173 !(nextPoints.begin()->frame > endFrame)) {
|
Chris@0
|
174 usePoints = nextPoints;
|
Chris@0
|
175 } else if (nextPoints.begin()->frame - frame <
|
Chris@0
|
176 frame - prevPoints.begin()->frame) {
|
Chris@0
|
177 usePoints = nextPoints;
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@0
|
180 return usePoints;
|
Chris@0
|
181 }
|
Chris@0
|
182
|
Chris@0
|
183 void
|
Chris@0
|
184 TimeInstantLayer::paintLocalFeatureDescription(QPainter &paint, QRect rect,
|
Chris@0
|
185 QPoint pos) const
|
Chris@0
|
186 {
|
Chris@0
|
187 //!!! bleagh
|
Chris@0
|
188
|
Chris@0
|
189 int x = pos.x();
|
Chris@0
|
190
|
Chris@0
|
191 if (!m_model || !m_model->getSampleRate()) return;
|
Chris@0
|
192
|
Chris@0
|
193 SparseOneDimensionalModel::PointList points = getLocalPoints(x);
|
Chris@0
|
194
|
Chris@0
|
195 QFontMetrics metrics = paint.fontMetrics();
|
Chris@0
|
196 int xbase = rect.x() + 5;
|
Chris@0
|
197 int ybase = rect.y() + 5;
|
Chris@0
|
198
|
Chris@0
|
199 if (points.empty()) {
|
Chris@0
|
200 QString label = tr("No local points");
|
Chris@0
|
201 if (!m_model->isReady()) {
|
Chris@0
|
202 label = tr("In progress");
|
Chris@0
|
203 }
|
Chris@0
|
204 paint.drawText(xbase + 5, ybase + 5 + metrics.ascent(), label);
|
Chris@0
|
205 return;
|
Chris@0
|
206 }
|
Chris@0
|
207
|
Chris@0
|
208 long useFrame = points.begin()->frame;
|
Chris@0
|
209
|
Chris@0
|
210 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
|
Chris@2
|
211 QString timeText = QString(tr("Time %1")).arg(rt.toText(true).c_str());
|
Chris@0
|
212
|
Chris@0
|
213 int timewidth = metrics.width(timeText);
|
Chris@0
|
214 int labelwidth = metrics.width(points.begin()->label);
|
Chris@0
|
215
|
Chris@0
|
216 int boxheight = metrics.height() * 2 + 3;
|
Chris@0
|
217 int boxwidth = std::max(timewidth, labelwidth);
|
Chris@0
|
218
|
Chris@0
|
219 paint.drawRect(xbase, ybase, boxwidth + 10,
|
Chris@0
|
220 boxheight + 10 - metrics.descent() + 1);
|
Chris@0
|
221
|
Chris@0
|
222 paint.drawText(xbase + 5, ybase + 5 + metrics.ascent(), timeText);
|
Chris@0
|
223 paint.drawText(xbase + 5, ybase + 7 + metrics.ascent() + metrics.height(),
|
Chris@0
|
224 points.begin()->label);
|
Chris@0
|
225 }
|
Chris@0
|
226
|
Chris@0
|
227 void
|
Chris@0
|
228 TimeInstantLayer::paint(QPainter &paint, QRect rect) const
|
Chris@0
|
229 {
|
Chris@0
|
230 if (!m_model || !m_model->isOK()) return;
|
Chris@0
|
231
|
Chris@0
|
232 // Profiler profiler("TimeInstantLayer::paint", true);
|
Chris@0
|
233
|
Chris@0
|
234 long startFrame = m_view->getStartFrame();
|
Chris@0
|
235 int zoomLevel = m_view->getZoomLevel();
|
Chris@0
|
236
|
Chris@0
|
237 int x0 = rect.left(), x1 = rect.right();
|
Chris@0
|
238 long frame0 = startFrame + x0 * zoomLevel;
|
Chris@0
|
239 long frame1 = startFrame + x1 * zoomLevel;
|
Chris@0
|
240
|
Chris@0
|
241 SparseOneDimensionalModel::PointList points(m_model->getPoints
|
Chris@0
|
242 (frame0, frame1));
|
Chris@0
|
243
|
Chris@0
|
244 paint.setPen(m_colour);
|
Chris@0
|
245
|
Chris@0
|
246 QColor brushColour(m_colour);
|
Chris@0
|
247 brushColour.setAlpha(100);
|
Chris@0
|
248 paint.setBrush(brushColour);
|
Chris@0
|
249
|
Chris@0
|
250 // std::cerr << "TimeInstantLayer::paint: resolution is "
|
Chris@0
|
251 // << m_model->getResolution() << " frames" << std::endl;
|
Chris@0
|
252
|
Chris@0
|
253 QPoint localPos;
|
Chris@0
|
254 long illuminateFrame = -1;
|
Chris@0
|
255
|
Chris@0
|
256 if (m_view->shouldIlluminateLocalFeatures(this, localPos)) {
|
Chris@0
|
257 SparseOneDimensionalModel::PointList localPoints =
|
Chris@0
|
258 getLocalPoints(localPos.x());
|
Chris@0
|
259 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
|
Chris@0
|
260 }
|
Chris@0
|
261
|
Chris@0
|
262 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
|
Chris@0
|
263 i != points.end(); ++i) {
|
Chris@0
|
264
|
Chris@0
|
265 const SparseOneDimensionalModel::Point &p(*i);
|
Chris@0
|
266
|
Chris@0
|
267 int x = (p.frame - startFrame) / zoomLevel;
|
Chris@0
|
268 int w = m_model->getResolution() / zoomLevel;
|
Chris@0
|
269
|
Chris@0
|
270 if (w < 1) w = 1;
|
Chris@0
|
271 if (p.frame == illuminateFrame) {
|
Chris@0
|
272 paint.setPen(Qt::black); //!!!
|
Chris@0
|
273 } else {
|
Chris@0
|
274 paint.setPen(brushColour);
|
Chris@0
|
275 }
|
Chris@0
|
276 paint.drawRect(x, 0, w - 1, m_view->height() - 1);
|
Chris@0
|
277 paint.setPen(m_colour);
|
Chris@0
|
278
|
Chris@0
|
279 if (p.label != "") {
|
Chris@0
|
280
|
Chris@0
|
281 // only draw if there's enough room from here to the next point
|
Chris@0
|
282
|
Chris@0
|
283 int lw = paint.fontMetrics().width(p.label);
|
Chris@0
|
284 bool good = true;
|
Chris@0
|
285
|
Chris@0
|
286 SparseOneDimensionalModel::PointList::const_iterator j = i;
|
Chris@0
|
287 if (++j != points.end()) {
|
Chris@0
|
288 int nx = (j->frame - startFrame) / zoomLevel;
|
Chris@0
|
289 if (nx >= x && nx - x - w - 3 <= lw) good = false;
|
Chris@0
|
290 }
|
Chris@0
|
291
|
Chris@0
|
292 if (good) {
|
Chris@0
|
293 paint.drawText(x + w + 2,
|
Chris@0
|
294 m_view->height() - paint.fontMetrics().height(),
|
Chris@0
|
295 p.label);
|
Chris@0
|
296 }
|
Chris@0
|
297 }
|
Chris@0
|
298 }
|
Chris@0
|
299 }
|
Chris@0
|
300
|
Chris@0
|
301
|
Chris@0
|
302 #ifdef INCLUDE_MOCFILES
|
Chris@0
|
303 #include "TimeInstantLayer.moc.cpp"
|
Chris@0
|
304 #endif
|
Chris@0
|
305
|