Chris@923
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@923
|
2
|
Chris@923
|
3 /*
|
Chris@923
|
4 Sonic Visualiser
|
Chris@923
|
5 An audio file viewer and annotation editor.
|
Chris@923
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@923
|
7
|
Chris@923
|
8 This program is free software; you can redistribute it and/or
|
Chris@923
|
9 modify it under the terms of the GNU General Public License as
|
Chris@923
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@923
|
11 License, or (at your option) any later version. See the file
|
Chris@923
|
12 COPYING included with this distribution for more information.
|
Chris@923
|
13 */
|
Chris@923
|
14
|
Chris@923
|
15 #include "LevelPanWidget.h"
|
Chris@923
|
16
|
Chris@923
|
17 #include <QPainter>
|
Chris@923
|
18 #include <QMouseEvent>
|
Chris@923
|
19 #include <QWheelEvent>
|
Chris@923
|
20
|
Chris@923
|
21 #include "layer/ColourMapper.h"
|
Chris@925
|
22 #include "base/AudioLevel.h"
|
Chris@923
|
23
|
Chris@923
|
24 #include <iostream>
|
Chris@923
|
25
|
Chris@923
|
26 using std::cerr;
|
Chris@923
|
27 using std::endl;
|
Chris@923
|
28
|
Chris@924
|
29 static const int maxLevel = 4;
|
Chris@923
|
30 static const int maxPan = 2; // range is -maxPan to maxPan
|
Chris@923
|
31
|
Chris@923
|
32 LevelPanWidget::LevelPanWidget(QWidget *parent) :
|
Chris@923
|
33 QWidget(parent),
|
Chris@923
|
34 m_level(maxLevel),
|
Chris@923
|
35 m_pan(0),
|
Chris@923
|
36 m_editable(true)
|
Chris@923
|
37 {
|
Chris@923
|
38 }
|
Chris@923
|
39
|
Chris@923
|
40 LevelPanWidget::~LevelPanWidget()
|
Chris@923
|
41 {
|
Chris@923
|
42 }
|
Chris@923
|
43
|
Chris@923
|
44 void
|
Chris@925
|
45 LevelPanWidget::setLevel(float flevel)
|
Chris@923
|
46 {
|
Chris@925
|
47 int level = AudioLevel::multiplier_to_fader
|
Chris@925
|
48 (flevel, maxLevel, AudioLevel::ShortFader);
|
Chris@925
|
49 if (level < 0) level = 0;
|
Chris@925
|
50 if (level > maxLevel) level = maxLevel;
|
Chris@925
|
51 if (level != m_level) {
|
Chris@925
|
52 m_level = level;
|
Chris@925
|
53 float convertsTo = getLevel();
|
Chris@925
|
54 if (fabsf(convertsTo - flevel) > 1e-5) {
|
Chris@925
|
55 emitLevelChanged();
|
Chris@925
|
56 }
|
Chris@925
|
57 update();
|
Chris@925
|
58 }
|
Chris@923
|
59 }
|
Chris@923
|
60
|
Chris@923
|
61 void
|
Chris@923
|
62 LevelPanWidget::setPan(float pan)
|
Chris@923
|
63 {
|
Chris@923
|
64 m_pan = int(round(pan * maxPan));
|
Chris@923
|
65 if (m_pan < -maxPan) m_pan = -maxPan;
|
Chris@923
|
66 if (m_pan > maxPan) m_pan = maxPan;
|
Chris@923
|
67 update();
|
Chris@923
|
68 }
|
Chris@923
|
69
|
Chris@923
|
70 void
|
Chris@923
|
71 LevelPanWidget::setEditable(bool editable)
|
Chris@923
|
72 {
|
Chris@923
|
73 m_editable = editable;
|
Chris@923
|
74 update();
|
Chris@923
|
75 }
|
Chris@923
|
76
|
Chris@923
|
77 float
|
Chris@923
|
78 LevelPanWidget::getLevel() const
|
Chris@923
|
79 {
|
Chris@925
|
80 return float(AudioLevel::fader_to_multiplier
|
Chris@925
|
81 (m_level, maxLevel, AudioLevel::ShortFader));
|
Chris@923
|
82 }
|
Chris@923
|
83
|
Chris@923
|
84 float
|
Chris@923
|
85 LevelPanWidget::getPan() const
|
Chris@923
|
86 {
|
Chris@923
|
87 return float(m_pan) / float(maxPan);
|
Chris@923
|
88 }
|
Chris@923
|
89
|
Chris@923
|
90 void
|
Chris@923
|
91 LevelPanWidget::emitLevelChanged()
|
Chris@923
|
92 {
|
Chris@923
|
93 cerr << "emitting levelChanged(" << getLevel() << ")" << endl;
|
Chris@923
|
94 emit levelChanged(getLevel());
|
Chris@923
|
95 }
|
Chris@923
|
96
|
Chris@923
|
97 void
|
Chris@923
|
98 LevelPanWidget::emitPanChanged()
|
Chris@923
|
99 {
|
Chris@923
|
100 cerr << "emitting panChanged(" << getPan() << ")" << endl;
|
Chris@923
|
101 emit panChanged(getPan());
|
Chris@923
|
102 }
|
Chris@923
|
103
|
Chris@923
|
104 void
|
Chris@923
|
105 LevelPanWidget::mousePressEvent(QMouseEvent *e)
|
Chris@923
|
106 {
|
Chris@923
|
107 mouseMoveEvent(e);
|
Chris@923
|
108 }
|
Chris@923
|
109
|
Chris@923
|
110 void
|
Chris@923
|
111 LevelPanWidget::mouseMoveEvent(QMouseEvent *e)
|
Chris@923
|
112 {
|
Chris@923
|
113 if (!m_editable) return;
|
Chris@923
|
114
|
Chris@923
|
115 int level, pan;
|
Chris@923
|
116 toCell(e->pos(), level, pan);
|
Chris@923
|
117 if (level == m_level && pan == m_pan) {
|
Chris@923
|
118 return;
|
Chris@923
|
119 }
|
Chris@923
|
120 if (level != m_level) {
|
Chris@923
|
121 m_level = level;
|
Chris@923
|
122 emitLevelChanged();
|
Chris@923
|
123 }
|
Chris@923
|
124 if (pan != m_pan) {
|
Chris@923
|
125 m_pan = pan;
|
Chris@923
|
126 emitPanChanged();
|
Chris@923
|
127 }
|
Chris@923
|
128 update();
|
Chris@923
|
129 }
|
Chris@923
|
130
|
Chris@923
|
131 void
|
Chris@923
|
132 LevelPanWidget::mouseReleaseEvent(QMouseEvent *e)
|
Chris@923
|
133 {
|
Chris@923
|
134 mouseMoveEvent(e);
|
Chris@923
|
135 }
|
Chris@923
|
136
|
Chris@923
|
137 void
|
Chris@923
|
138 LevelPanWidget::wheelEvent(QWheelEvent *e)
|
Chris@923
|
139 {
|
Chris@923
|
140 if (e->modifiers() & Qt::ControlModifier) {
|
Chris@923
|
141 if (e->delta() > 0) {
|
Chris@923
|
142 if (m_pan < maxPan) {
|
Chris@923
|
143 ++m_pan;
|
Chris@923
|
144 emitPanChanged();
|
Chris@923
|
145 update();
|
Chris@923
|
146 }
|
Chris@923
|
147 } else {
|
Chris@923
|
148 if (m_pan > -maxPan) {
|
Chris@923
|
149 --m_pan;
|
Chris@923
|
150 emitPanChanged();
|
Chris@923
|
151 update();
|
Chris@923
|
152 }
|
Chris@923
|
153 }
|
Chris@923
|
154 } else {
|
Chris@923
|
155 if (e->delta() > 0) {
|
Chris@923
|
156 if (m_level < maxLevel) {
|
Chris@923
|
157 ++m_level;
|
Chris@923
|
158 emitLevelChanged();
|
Chris@923
|
159 update();
|
Chris@923
|
160 }
|
Chris@923
|
161 } else {
|
Chris@923
|
162 if (m_level > 0) {
|
Chris@923
|
163 --m_level;
|
Chris@923
|
164 emitLevelChanged();
|
Chris@923
|
165 update();
|
Chris@923
|
166 }
|
Chris@923
|
167 }
|
Chris@923
|
168 }
|
Chris@923
|
169 }
|
Chris@923
|
170
|
Chris@923
|
171 void
|
Chris@923
|
172 LevelPanWidget::toCell(QPointF loc, int &level, int &pan) const
|
Chris@923
|
173 {
|
Chris@923
|
174 double w = width(), h = height();
|
Chris@923
|
175 int npan = maxPan * 2 + 1;
|
Chris@924
|
176 int nlevel = maxLevel + 1;
|
Chris@924
|
177 double wcell = w / npan, hcell = h / nlevel;
|
Chris@923
|
178 level = int((h - loc.y()) / hcell) + 1;
|
Chris@924
|
179 if (level < 0) level = 0;
|
Chris@923
|
180 if (level > maxLevel) level = maxLevel;
|
Chris@923
|
181 pan = int(loc.x() / wcell) - maxPan;
|
Chris@923
|
182 if (pan < -maxPan) pan = -maxPan;
|
Chris@923
|
183 if (pan > maxPan) pan = maxPan;
|
Chris@923
|
184 }
|
Chris@923
|
185
|
Chris@923
|
186 QSizeF
|
Chris@923
|
187 LevelPanWidget::cellSize() const
|
Chris@923
|
188 {
|
Chris@923
|
189 double w = width(), h = height();
|
Chris@923
|
190 int npan = maxPan * 2 + 1;
|
Chris@924
|
191 int nlevel = maxLevel + 1;
|
Chris@924
|
192 double wcell = w / npan, hcell = h / nlevel;
|
Chris@923
|
193 return QSizeF(wcell, hcell);
|
Chris@923
|
194 }
|
Chris@923
|
195
|
Chris@923
|
196 QPointF
|
Chris@923
|
197 LevelPanWidget::cellCentre(int level, int pan) const
|
Chris@923
|
198 {
|
Chris@923
|
199 QSizeF cs = cellSize();
|
Chris@923
|
200 return QPointF(cs.width() * (pan + maxPan) + cs.width() / 2.,
|
Chris@924
|
201 height() - cs.height() * (level + 1) + cs.height() / 2.);
|
Chris@923
|
202 }
|
Chris@923
|
203
|
Chris@923
|
204 QSizeF
|
Chris@923
|
205 LevelPanWidget::cellLightSize() const
|
Chris@923
|
206 {
|
Chris@923
|
207 double extent = 3. / 4.;
|
Chris@923
|
208 QSizeF cs = cellSize();
|
Chris@923
|
209 double m = std::min(cs.width(), cs.height());
|
Chris@923
|
210 return QSizeF(m * extent, m * extent);
|
Chris@923
|
211 }
|
Chris@923
|
212
|
Chris@923
|
213 QRectF
|
Chris@923
|
214 LevelPanWidget::cellLightRect(int level, int pan) const
|
Chris@923
|
215 {
|
Chris@923
|
216 QSizeF cls = cellLightSize();
|
Chris@923
|
217 QPointF cc = cellCentre(level, pan);
|
Chris@923
|
218 return QRectF(cc.x() - cls.width() / 2.,
|
Chris@923
|
219 cc.y() - cls.height() / 2.,
|
Chris@923
|
220 cls.width(),
|
Chris@923
|
221 cls.height());
|
Chris@923
|
222 }
|
Chris@923
|
223
|
Chris@923
|
224 double
|
Chris@923
|
225 LevelPanWidget::thinLineWidth() const
|
Chris@923
|
226 {
|
Chris@923
|
227 double tw = ceil(width() / (maxPan * 2. * 10.));
|
Chris@923
|
228 double th = ceil(height() / (maxLevel * 10.));
|
Chris@923
|
229 return std::min(th, tw);
|
Chris@923
|
230 }
|
Chris@923
|
231
|
Chris@923
|
232 void
|
Chris@923
|
233 LevelPanWidget::paintEvent(QPaintEvent *)
|
Chris@923
|
234 {
|
Chris@923
|
235 QPainter paint(this);
|
Chris@923
|
236 ColourMapper mapper(ColourMapper::Sunset, 0, maxLevel);
|
Chris@923
|
237
|
Chris@923
|
238 paint.setRenderHint(QPainter::Antialiasing, true);
|
Chris@923
|
239
|
Chris@923
|
240 QPen pen;
|
Chris@923
|
241
|
Chris@923
|
242 double thin = thinLineWidth();
|
Chris@923
|
243
|
Chris@923
|
244 pen.setColor(QColor(127, 127, 127, 127));
|
Chris@923
|
245 pen.setWidthF(cellLightSize().width() + thin);
|
Chris@923
|
246 pen.setCapStyle(Qt::RoundCap);
|
Chris@923
|
247 paint.setPen(pen);
|
Chris@923
|
248
|
Chris@923
|
249 for (int pan = -maxPan; pan <= maxPan; ++pan) {
|
Chris@924
|
250 paint.drawLine(cellCentre(0, pan), cellCentre(maxLevel, pan));
|
Chris@924
|
251 }
|
Chris@924
|
252
|
Chris@924
|
253 if (isEnabled()) {
|
Chris@924
|
254 pen.setColor(Qt::black);
|
Chris@924
|
255 } else {
|
Chris@924
|
256 pen.setColor(Qt::darkGray);
|
Chris@923
|
257 }
|
Chris@923
|
258
|
Chris@923
|
259 pen.setWidthF(thin);
|
Chris@923
|
260 pen.setCapStyle(Qt::FlatCap);
|
Chris@923
|
261 paint.setPen(pen);
|
Chris@923
|
262
|
Chris@924
|
263 for (int level = 0; level <= m_level; ++level) {
|
Chris@924
|
264 if (isEnabled()) {
|
Chris@924
|
265 paint.setBrush(mapper.map(level));
|
Chris@924
|
266 }
|
Chris@924
|
267 QRectF clr = cellLightRect(level, m_pan);
|
Chris@924
|
268 if (m_level == 0) {
|
Chris@924
|
269 paint.drawLine(clr.topLeft(), clr.bottomRight());
|
Chris@924
|
270 paint.drawLine(clr.bottomLeft(), clr.topRight());
|
Chris@924
|
271 } else {
|
Chris@924
|
272 paint.drawEllipse(clr);
|
Chris@924
|
273 }
|
Chris@923
|
274 }
|
Chris@923
|
275 }
|
Chris@923
|
276
|
Chris@923
|
277
|