Mercurial > hg > svgui
comparison widgets/LevelPanWidget.cpp @ 946:36cddc3de023 alignment_view
Merge from default branch
author | Chris Cannam |
---|---|
date | Mon, 20 Apr 2015 09:19:52 +0100 |
parents | d6acb8e36605 |
children | 125748a569fa |
comparison
equal
deleted
inserted
replaced
897:499b637f2a26 | 946:36cddc3de023 |
---|---|
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 | |
8 This program is free software; you can redistribute it and/or | |
9 modify it under the terms of the GNU General Public License as | |
10 published by the Free Software Foundation; either version 2 of the | |
11 License, or (at your option) any later version. See the file | |
12 COPYING included with this distribution for more information. | |
13 */ | |
14 | |
15 #include "LevelPanWidget.h" | |
16 | |
17 #include <QPainter> | |
18 #include <QMouseEvent> | |
19 #include <QWheelEvent> | |
20 | |
21 #include "layer/ColourMapper.h" | |
22 #include "base/AudioLevel.h" | |
23 | |
24 #include <iostream> | |
25 #include <cmath> | |
26 #include <cassert> | |
27 | |
28 using std::cerr; | |
29 using std::endl; | |
30 | |
31 static const int maxLevel = 4; // min is 0, may be mute or not depending on m_includeMute | |
32 static const int maxPan = 2; // range is -maxPan to maxPan | |
33 | |
34 LevelPanWidget::LevelPanWidget(QWidget *parent) : | |
35 QWidget(parent), | |
36 m_level(maxLevel), | |
37 m_pan(0), | |
38 m_editable(true), | |
39 m_includeMute(true) | |
40 { | |
41 } | |
42 | |
43 LevelPanWidget::~LevelPanWidget() | |
44 { | |
45 } | |
46 | |
47 QSize | |
48 LevelPanWidget::sizeHint() const | |
49 { | |
50 static double ratio = 0.0; | |
51 if (ratio == 0.0) { | |
52 double baseEm; | |
53 #ifdef Q_OS_MAC | |
54 baseEm = 17.0; | |
55 #else | |
56 baseEm = 15.0; | |
57 #endif | |
58 double em = QFontMetrics(QFont()).height(); | |
59 ratio = em / baseEm; | |
60 } | |
61 | |
62 int pixels = 40; | |
63 int scaled = int(pixels * ratio + 0.5); | |
64 if (pixels != 0 && scaled == 0) scaled = 1; | |
65 return QSize(scaled, scaled); | |
66 } | |
67 | |
68 static int | |
69 db_to_level(double db) | |
70 { | |
71 // Only if !m_includeMute, otherwise AudioLevel is used. | |
72 // Levels are: +6 0 -6 -12 -20 | |
73 assert(maxLevel == 4); | |
74 if (db > 3.) return 4; | |
75 else if (db > -3.) return 3; | |
76 else if (db > -9.) return 2; | |
77 else if (db > -16.) return 1; | |
78 else return 0; | |
79 } | |
80 | |
81 static double | |
82 level_to_db(int level) | |
83 { | |
84 // Only if !m_includeMute, otherwise AudioLevel is used. | |
85 // Levels are: +6 0 -6 -12 -20 | |
86 assert(maxLevel == 4); | |
87 if (level >= 4) return 6.; | |
88 else if (level == 3) return 0.; | |
89 else if (level == 2) return -6.; | |
90 else if (level == 1) return -12.; | |
91 else return -20.; | |
92 } | |
93 | |
94 void | |
95 LevelPanWidget::setLevel(float flevel) | |
96 { | |
97 int level; | |
98 if (m_includeMute) { | |
99 level = AudioLevel::multiplier_to_fader | |
100 (flevel, maxLevel, AudioLevel::ShortFader); | |
101 } else { | |
102 level = db_to_level(AudioLevel::multiplier_to_dB(flevel)); | |
103 } | |
104 if (level < 0) level = 0; | |
105 if (level > maxLevel) level = maxLevel; | |
106 if (level != m_level) { | |
107 m_level = level; | |
108 float convertsTo = getLevel(); | |
109 if (fabsf(convertsTo - flevel) > 1e-5) { | |
110 emitLevelChanged(); | |
111 } | |
112 update(); | |
113 } | |
114 } | |
115 | |
116 float | |
117 LevelPanWidget::getLevel() const | |
118 { | |
119 if (m_includeMute) { | |
120 return float(AudioLevel::fader_to_multiplier | |
121 (m_level, maxLevel, AudioLevel::ShortFader)); | |
122 } else { | |
123 return float(AudioLevel::dB_to_multiplier(level_to_db(m_level))); | |
124 } | |
125 } | |
126 | |
127 void | |
128 LevelPanWidget::setPan(float pan) | |
129 { | |
130 m_pan = int(round(pan * maxPan)); | |
131 if (m_pan < -maxPan) m_pan = -maxPan; | |
132 if (m_pan > maxPan) m_pan = maxPan; | |
133 update(); | |
134 } | |
135 | |
136 bool | |
137 LevelPanWidget::isEditable() const | |
138 { | |
139 return m_editable; | |
140 } | |
141 | |
142 bool | |
143 LevelPanWidget::includesMute() const | |
144 { | |
145 return m_includeMute; | |
146 } | |
147 | |
148 void | |
149 LevelPanWidget::setEditable(bool editable) | |
150 { | |
151 m_editable = editable; | |
152 update(); | |
153 } | |
154 | |
155 void | |
156 LevelPanWidget::setIncludeMute(bool include) | |
157 { | |
158 m_includeMute = include; | |
159 emitLevelChanged(); | |
160 update(); | |
161 } | |
162 | |
163 float | |
164 LevelPanWidget::getPan() const | |
165 { | |
166 return float(m_pan) / float(maxPan); | |
167 } | |
168 | |
169 void | |
170 LevelPanWidget::emitLevelChanged() | |
171 { | |
172 cerr << "emitting levelChanged(" << getLevel() << ")" << endl; | |
173 emit levelChanged(getLevel()); | |
174 } | |
175 | |
176 void | |
177 LevelPanWidget::emitPanChanged() | |
178 { | |
179 cerr << "emitting panChanged(" << getPan() << ")" << endl; | |
180 emit panChanged(getPan()); | |
181 } | |
182 | |
183 void | |
184 LevelPanWidget::mousePressEvent(QMouseEvent *e) | |
185 { | |
186 mouseMoveEvent(e); | |
187 } | |
188 | |
189 void | |
190 LevelPanWidget::mouseMoveEvent(QMouseEvent *e) | |
191 { | |
192 if (!m_editable) return; | |
193 | |
194 int level, pan; | |
195 toCell(rect(), e->pos(), level, pan); | |
196 if (level == m_level && pan == m_pan) { | |
197 return; | |
198 } | |
199 if (level != m_level) { | |
200 m_level = level; | |
201 emitLevelChanged(); | |
202 } | |
203 if (pan != m_pan) { | |
204 m_pan = pan; | |
205 emitPanChanged(); | |
206 } | |
207 update(); | |
208 } | |
209 | |
210 void | |
211 LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) | |
212 { | |
213 mouseMoveEvent(e); | |
214 } | |
215 | |
216 void | |
217 LevelPanWidget::wheelEvent(QWheelEvent *e) | |
218 { | |
219 if (e->modifiers() & Qt::ControlModifier) { | |
220 if (e->delta() > 0) { | |
221 if (m_pan < maxPan) { | |
222 ++m_pan; | |
223 emitPanChanged(); | |
224 update(); | |
225 } | |
226 } else { | |
227 if (m_pan > -maxPan) { | |
228 --m_pan; | |
229 emitPanChanged(); | |
230 update(); | |
231 } | |
232 } | |
233 } else { | |
234 if (e->delta() > 0) { | |
235 if (m_level < maxLevel) { | |
236 ++m_level; | |
237 emitLevelChanged(); | |
238 update(); | |
239 } | |
240 } else { | |
241 if (m_level > 0) { | |
242 --m_level; | |
243 emitLevelChanged(); | |
244 update(); | |
245 } | |
246 } | |
247 } | |
248 } | |
249 | |
250 void | |
251 LevelPanWidget::toCell(QRectF rect, QPointF loc, int &level, int &pan) const | |
252 { | |
253 double w = rect.width(), h = rect.height(); | |
254 | |
255 int npan = maxPan * 2 + 1; | |
256 int nlevel = maxLevel + 1; | |
257 | |
258 double wcell = w / npan, hcell = h / nlevel; | |
259 | |
260 level = int((h - (loc.y() - rect.y())) / hcell); | |
261 if (level < 0) level = 0; | |
262 if (level > maxLevel) level = maxLevel; | |
263 | |
264 pan = int((loc.x() - rect.x()) / wcell) - maxPan; | |
265 if (pan < -maxPan) pan = -maxPan; | |
266 if (pan > maxPan) pan = maxPan; | |
267 } | |
268 | |
269 QSizeF | |
270 LevelPanWidget::cellSize(QRectF rect) const | |
271 { | |
272 double w = rect.width(), h = rect.height(); | |
273 int npan = maxPan * 2 + 1; | |
274 int nlevel = maxLevel + 1; | |
275 double wcell = w / npan, hcell = h / nlevel; | |
276 return QSizeF(wcell, hcell); | |
277 } | |
278 | |
279 QPointF | |
280 LevelPanWidget::cellCentre(QRectF rect, int level, int pan) const | |
281 { | |
282 QSizeF cs = cellSize(rect); | |
283 return QPointF(rect.x() + cs.width() * (pan + maxPan) + cs.width() / 2., | |
284 rect.y() + rect.height() - cs.height() * (level + 1) + cs.height() / 2.); | |
285 } | |
286 | |
287 QSizeF | |
288 LevelPanWidget::cellLightSize(QRectF rect) const | |
289 { | |
290 double extent = 3. / 4.; | |
291 QSizeF cs = cellSize(rect); | |
292 double m = std::min(cs.width(), cs.height()); | |
293 return QSizeF(m * extent, m * extent); | |
294 } | |
295 | |
296 QRectF | |
297 LevelPanWidget::cellLightRect(QRectF rect, int level, int pan) const | |
298 { | |
299 QSizeF cls = cellLightSize(rect); | |
300 QPointF cc = cellCentre(rect, level, pan); | |
301 return QRectF(cc.x() - cls.width() / 2., | |
302 cc.y() - cls.height() / 2., | |
303 cls.width(), | |
304 cls.height()); | |
305 } | |
306 | |
307 double | |
308 LevelPanWidget::thinLineWidth(QRectF rect) const | |
309 { | |
310 double tw = ceil(rect.width() / (maxPan * 2. * 10.)); | |
311 double th = ceil(rect.height() / (maxLevel * 10.)); | |
312 return std::min(th, tw); | |
313 } | |
314 | |
315 static QColor | |
316 level_to_colour(int level) | |
317 { | |
318 assert(maxLevel == 4); | |
319 if (level == 0) return Qt::black; | |
320 else if (level == 1) return QColor(80, 0, 0); | |
321 else if (level == 2) return QColor(160, 0, 0); | |
322 else if (level == 3) return QColor(255, 0, 0); | |
323 else return QColor(255, 255, 0); | |
324 } | |
325 | |
326 void | |
327 LevelPanWidget::renderTo(QPaintDevice *dev, QRectF rect, bool asIfEditable) const | |
328 { | |
329 QPainter paint(dev); | |
330 | |
331 paint.setRenderHint(QPainter::Antialiasing, true); | |
332 | |
333 QPen pen; | |
334 | |
335 double thin = thinLineWidth(rect); | |
336 | |
337 pen.setColor(QColor(127, 127, 127, 127)); | |
338 pen.setWidthF(cellLightSize(rect).width() + thin); | |
339 pen.setCapStyle(Qt::RoundCap); | |
340 paint.setPen(pen); | |
341 | |
342 for (int pan = -maxPan; pan <= maxPan; ++pan) { | |
343 paint.drawLine(cellCentre(rect, 0, pan), cellCentre(rect, maxLevel, pan)); | |
344 } | |
345 | |
346 if (isEnabled()) { | |
347 pen.setColor(Qt::black); | |
348 } else { | |
349 pen.setColor(Qt::darkGray); | |
350 } | |
351 | |
352 if (!asIfEditable && m_includeMute && m_level == 0) { | |
353 pen.setWidthF(thin * 2); | |
354 pen.setCapStyle(Qt::RoundCap); | |
355 paint.setPen(pen); | |
356 paint.drawLine(cellCentre(rect, 0, -maxPan), | |
357 cellCentre(rect, maxLevel, maxPan)); | |
358 paint.drawLine(cellCentre(rect, maxLevel, -maxPan), | |
359 cellCentre(rect, 0, maxPan)); | |
360 return; | |
361 } | |
362 | |
363 pen.setWidthF(thin); | |
364 pen.setCapStyle(Qt::FlatCap); | |
365 paint.setPen(pen); | |
366 | |
367 for (int level = 0; level <= m_level; ++level) { | |
368 if (isEnabled()) { | |
369 paint.setBrush(level_to_colour(level)); | |
370 } | |
371 QRectF clr = cellLightRect(rect, level, m_pan); | |
372 if (m_includeMute && m_level == 0) { | |
373 paint.drawLine(clr.topLeft(), clr.bottomRight()); | |
374 paint.drawLine(clr.bottomLeft(), clr.topRight()); | |
375 } else { | |
376 paint.drawEllipse(clr); | |
377 } | |
378 } | |
379 } | |
380 | |
381 void | |
382 LevelPanWidget::paintEvent(QPaintEvent *) | |
383 { | |
384 renderTo(this, rect(), m_editable); | |
385 } | |
386 | |
387 | |
388 |