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