comparison layer/SliceLayer.cpp @ 193:57c2350a8c40

* Add slice layers (so you can display a slice of a colour 3d plot as if it were a spectrum) * Make spectrum layer a subclass of slice layer
author Chris Cannam
date Fri, 26 Jan 2007 16:59:57 +0000
parents layer/SpectrumLayer.cpp@42118892f428
children d13e209bfa94
comparison
equal deleted inserted replaced
192:fcc043f75c41 193:57c2350a8c40
1
2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
3
4 /*
5 Sonic Visualiser
6 An audio file viewer and annotation editor.
7 Centre for Digital Music, Queen Mary, University of London.
8 This file copyright 2006 QMUL.
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of the
13 License, or (at your option) any later version. See the file
14 COPYING included with this distribution for more information.
15 */
16
17 #include "SliceLayer.h"
18
19 #include "view/View.h"
20 #include "base/AudioLevel.h"
21 #include "base/RangeMapper.h"
22
23 #include <QPainter>
24 #include <QPainterPath>
25
26 SliceLayer::SliceLayer() :
27 m_sliceableModel(0),
28 m_colour(Qt::darkBlue),
29 m_energyScale(dBScale),
30 m_samplingMode(SamplePeak),
31 m_plotStyle(PlotLines),
32 m_binScale(LinearBins),
33 m_normalize(false),
34 m_bias(false);
35 m_gain(1.0)
36 {
37 }
38
39 SliceLayer::~SliceLayer()
40 {
41
42 }
43
44 void
45 SliceLayer::setSliceableModel(const Model *model)
46 {
47 const DenseThreeDimensionalModel *sliceable =
48 dynamic_cast<const DenseThreeDimensionalModel *>(model);
49
50 if (model && !sliceable) {
51 std::cerr << "WARNING: SliceLayer::setSliceableModel(" << model
52 << "): model is not a DenseThreeDimensionalModel" << std::endl;
53 }
54
55 if (m_sliceableModel == sliceable) return;
56
57 m_sliceableModel = sliceable;
58
59 connect(m_sliceableModel, SIGNAL(modelChanged()),
60 this, SIGNAL(modelChanged()));
61
62 connect(m_sliceableModel, SIGNAL(modelChanged(size_t, size_t)),
63 this, SIGNAL(modelChanged(size_t, size_t)));
64
65 connect(m_sliceableModel, SIGNAL(completionChanged()),
66 this, SIGNAL(modelCompletionChanged()));
67
68 emit modelReplaced();
69 }
70
71 void
72 SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement)
73 {
74 std::cerr << "SliceLayer::sliceableModelReplaced(" << orig << ", " << replacement << ")" << std::endl;
75
76 if (orig == m_sliceableModel) {
77 setSliceableModel
78 (dynamic_cast<const DenseThreeDimensionalModel *>(replacement));
79 }
80 }
81
82 void
83 SliceLayer::modelAboutToBeDeleted(Model *m)
84 {
85 std::cerr << "SliceLayer::modelAboutToBeDeleted(" << m << ")" << std::endl;
86
87 if (m == m_sliceableModel) {
88 setSliceableModel(0);
89 }
90 }
91
92 void
93 SliceLayer::paint(View *v, QPainter &paint, QRect rect) const
94 {
95 if (!m_sliceableModel) return;
96
97 int w = (v->width() * 2) / 3;
98 int xorigin = (v->width() / 2) - (w / 2);
99
100 int h = (v->height() * 2) / 3;
101 int yorigin = (v->height() / 2) + (h / 2);
102
103 paint.save();
104 paint.setPen(m_colour);
105 paint.setRenderHint(QPainter::Antialiasing, false);
106
107 QPainterPath path;
108 float thresh = -80.f;
109
110 int mh = m_sliceableModel->getHeight();
111
112 float *values = new float[mh];
113 int divisor = 0;
114
115 for (size_t bin = 0; bin < mh; ++bin) {
116 values[bin] = 0.f;
117 }
118
119 size_t f0 = v->getCentreFrame();
120 int f0x = v->getXForFrame(f0);
121 size_t f1 = v->getFrameForX(f0x + 1);
122
123 size_t col0 = f0 / m_sliceableModel->getResolution();
124 size_t col1 = col0;
125 if (m_samplingMode != NearestSample) {
126 col1 = f1 / m_sliceableModel->getResolution();
127 }
128 if (col1 <= col0) col1 = col0 + 1;
129
130 for (size_t col = col0; col < col1; ++col) {
131 for (size_t bin = 0; bin < mh; ++bin) {
132 float value = m_sliceableModel->getValueAt(col, bin);
133 if (m_bias) value *= bin + 1;
134 if (m_samplingMode == SamplePeak) {
135 if (value > values[bin]) values[bin] = value;
136 } else {
137 values[bin] += value;
138 }
139 }
140 ++divisor;
141 }
142
143 float max = 0.f;
144 for (size_t bin = 0; bin < mh; ++bin) {
145 if (m_samplingMode == SampleMean) values[bin] /= divisor;
146 if (values[bin] > max) max = values[bin];
147 }
148 if (max != 0.f && m_normalize) {
149 for (size_t bin = 0; bin < mh; ++bin) {
150 values[bin] /= max;
151 }
152 }
153
154 float py = 0;
155 float nx = xorigin;
156
157 for (size_t bin = 0; bin < mh; ++bin) {
158
159 float x;
160
161 switch (m_binScale) {
162
163 case LinearBins:
164 x = nx;
165 nx = xorigin + (float(w) * (bin + 1)) / mh;
166 break;
167
168 case LogBins:
169 x = nx;
170 nx = xorigin + (float(w) * (log10f(bin + 2) - log10f(1))) /
171 (log10f(mh + 1) - log10f(1));
172 break;
173
174 case InvertedLogBins:
175 x = nx;
176 nx = xorigin + w - (float(w) * (log10f(mh - bin) - log10f(1))) /
177 (log10f(mh) - log10f(1));
178 break;
179 }
180
181 float value = values[bin];
182
183 value *= m_gain;
184 float y = 0.f;
185
186 switch (m_energyScale) {
187
188 case dBScale:
189 {
190 float db = thresh;
191 if (value > 0.f) db = 10.f * log10f(value);
192 if (db < thresh) db = thresh;
193 float val = (db - thresh) / -thresh;
194 y = yorigin - (float(h) * val);
195 break;
196 }
197
198 case MeterScale:
199 y = yorigin - AudioLevel::multiplier_to_preview(value, h);
200 break;
201
202 default:
203 y = yorigin - (float(h) * value);
204 break;
205 }
206
207 if (m_plotStyle == PlotLines) {
208
209 if (bin == 0) {
210 path.moveTo(x, y);
211 } else {
212 path.lineTo(x, y);
213 }
214
215 } else if (m_plotStyle == PlotSteps) {
216
217 if (bin == 0) {
218 path.moveTo(x, y);
219 } else {
220 path.lineTo(x, y);
221 }
222 path.lineTo(nx, y);
223
224 } else if (m_plotStyle == PlotBlocks) {
225
226 path.moveTo(x, yorigin);
227 path.lineTo(x, y);
228 path.lineTo(nx, y);
229 path.lineTo(nx, yorigin);
230 path.lineTo(x, yorigin);
231 }
232
233 py = y;
234 }
235
236 paint.drawPath(path);
237 paint.restore();
238
239 }
240
241 Layer::PropertyList
242 SliceLayer::getProperties() const
243 {
244 PropertyList list;
245 list.push_back("Colour");
246 list.push_back("Plot Type");
247 list.push_back("Sampling Mode");
248 list.push_back("Scale");
249 list.push_back("Normalize");
250 list.push_back("Gain");
251 list.push_back("Bin Scale");
252
253 return list;
254 }
255
256 QString
257 SliceLayer::getPropertyLabel(const PropertyName &name) const
258 {
259 if (name == "Colour") return tr("Colour");
260 if (name == "Plot Type") return tr("Plot Type");
261 if (name == "Energy Scale") return tr("Scale");
262 if (name == "Normalize") return tr("Normalize");
263 if (name == "Gain") return tr("Gain");
264 if (name == "Sampling Mode") return tr("Sampling Mode");
265 if (name == "Bin Scale") return tr("Plot X Scale");
266 return "";
267 }
268
269 Layer::PropertyType
270 SliceLayer::getPropertyType(const PropertyName &name) const
271 {
272 if (name == "Gain") return RangeProperty;
273 if (name == "Normalize") return ToggleProperty;
274 return ValueProperty;
275 }
276
277 QString
278 SliceLayer::getPropertyGroupName(const PropertyName &name) const
279 {
280 if (name == "Scale" ||
281 name == "Normalize" ||
282 name == "Sampling Mode" ||
283 name == "Gain") return tr("Scale");
284 if (name == "Plot Type" ||
285 name == "Bin Scale") return tr("Plot Type");
286 return QString();
287 }
288
289 int
290 SliceLayer::getPropertyRangeAndValue(const PropertyName &name,
291 int *min, int *max) const
292 {
293 int deft = 0;
294
295 int garbage0, garbage1;
296 if (!min) min = &garbage0;
297 if (!max) max = &garbage1;
298
299 if (name == "Gain") {
300
301 *min = -50;
302 *max = 50;
303
304 std::cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << std::endl;
305
306 deft = lrint(log10(m_gain) * 20.0);
307 if (deft < *min) deft = *min;
308 if (deft > *max) deft = *max;
309
310 } else if (name == "Normalize") {
311
312 deft = (m_normalize ? 1 : 0);
313
314 } else if (name == "Colour") {
315
316 *min = 0;
317 *max = 5;
318
319 if (m_colour == Qt::black) deft = 0;
320 else if (m_colour == Qt::darkRed) deft = 1;
321 else if (m_colour == Qt::darkBlue) deft = 2;
322 else if (m_colour == Qt::darkGreen) deft = 3;
323 else if (m_colour == QColor(200, 50, 255)) deft = 4;
324 else if (m_colour == QColor(255, 150, 50)) deft = 5;
325
326 } else if (name == "Scale") {
327
328 *min = 0;
329 *max = 2;
330
331 deft = (int)m_energyScale;
332
333 } else if (name == "Sampling Mode") {
334
335 *min = 0;
336 *max = 2;
337
338 deft = (int)m_samplingMode;
339
340 } else if (name == "Plot Type") {
341
342 *min = 0;
343 *max = 2;
344
345 deft = (int)m_plotStyle;
346
347 } else if (name == "Bin Scale") {
348
349 *min = 0;
350 *max = 2;
351
352 deft = (int)m_binScale;
353
354 } else {
355 deft = Layer::getPropertyRangeAndValue(name, min, max);
356 }
357
358 return deft;
359 }
360
361 QString
362 SliceLayer::getPropertyValueLabel(const PropertyName &name,
363 int value) const
364 {
365 if (name == "Colour") {
366 switch (value) {
367 default:
368 case 0: return tr("Black");
369 case 1: return tr("Red");
370 case 2: return tr("Blue");
371 case 3: return tr("Green");
372 case 4: return tr("Purple");
373 case 5: return tr("Orange");
374 }
375 }
376 if (name == "Scale") {
377 switch (value) {
378 default:
379 case 0: return tr("Linear");
380 case 1: return tr("Meter");
381 case 2: return tr("dB");
382 }
383 }
384 if (name == "Sampling Mode") {
385 switch (value) {
386 default:
387 case 0: return tr("Any");
388 case 1: return tr("Mean");
389 case 2: return tr("Peak");
390 }
391 }
392 if (name == "Plot Type") {
393 switch (value) {
394 default:
395 case 0: return tr("Lines");
396 case 1: return tr("Steps");
397 case 2: return tr("Blocks");
398 }
399 }
400 if (name == "Bin Scale") {
401 switch (value) {
402 default:
403 case 0: return tr("Linear");
404 case 1: return tr("Log");
405 case 2: return tr("Rev Log");
406 }
407 }
408 return tr("<unknown>");
409 }
410
411 RangeMapper *
412 SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const
413 {
414 if (name == "Gain") {
415 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
416 }
417 return 0;
418 }
419
420 void
421 SliceLayer::setProperty(const PropertyName &name, int value)
422 {
423 if (name == "Gain") {
424 setGain(pow(10, float(value)/20.0));
425 } else if (name == "Colour") {
426 switch (value) {
427 default:
428 case 0: setBaseColour(Qt::black); break;
429 case 1: setBaseColour(Qt::darkRed); break;
430 case 2: setBaseColour(Qt::darkBlue); break;
431 case 3: setBaseColour(Qt::darkGreen); break;
432 case 4: setBaseColour(QColor(200, 50, 255)); break;
433 case 5: setBaseColour(QColor(255, 150, 50)); break;
434 }
435 } else if (name == "Scale") {
436 switch (value) {
437 default:
438 case 0: setEnergyScale(LinearScale); break;
439 case 1: setEnergyScale(MeterScale); break;
440 case 2: setEnergyScale(dBScale); break;
441 }
442 } else if (name == "Plot Type") {
443 setPlotStyle(PlotStyle(value));
444 } else if (name == "Sampling Mode") {
445 switch (value) {
446 default:
447 case 0: setSamplingMode(NearestSample); break;
448 case 1: setSamplingMode(SampleMean); break;
449 case 2: setSamplingMode(SamplePeak); break;
450 }
451 } else if (name == "Bin Scale") {
452 switch (value) {
453 default:
454 case 0: setBinScale(LinearBins); break;
455 case 1: setBinScale(LogBins); break;
456 case 2: setBinScale(InvertedLogBins); break;
457 }
458 } else if (name == "Normalize") {
459 setNormalize(value ? true : false);
460 }
461 }
462
463 void
464 SliceLayer::setBaseColour(QColor colour)
465 {
466 if (m_colour == colour) return;
467 m_colour = colour;
468 emit layerParametersChanged();
469 }
470
471 void
472 SliceLayer::setEnergyScale(EnergyScale scale)
473 {
474 if (m_energyScale == scale) return;
475 m_energyScale = scale;
476 emit layerParametersChanged();
477 }
478
479 void
480 SliceLayer::setSamplingMode(SamplingMode mode)
481 {
482 if (m_samplingMode == mode) return;
483 m_samplingMode = mode;
484 emit layerParametersChanged();
485 }
486
487 void
488 SliceLayer::setPlotStyle(PlotStyle style)
489 {
490 if (m_plotStyle == style) return;
491 m_plotStyle = style;
492 emit layerParametersChanged();
493 }
494
495 void
496 SliceLayer::setBinScale(BinScale scale)
497 {
498 if (m_binScale == scale) return;
499 m_binScale = scale;
500 emit layerParametersChanged();
501 }
502
503 void
504 SliceLayer::setNormalize(bool n)
505 {
506 if (m_normalize == n) return;
507 m_normalize = n;
508 emit layerParametersChanged();
509 }
510
511 void
512 SliceLayer::setGain(float gain)
513 {
514 if (m_gain == gain) return;
515 m_gain = gain;
516 emit layerParametersChanged();
517 }
518
519 QString
520 SliceLayer::toXmlString(QString indent, QString extraAttributes) const
521 {
522 QString s;
523
524 s += QString("colour=\"%1\" "
525 "energyScale=\"%2\" "
526 "samplingMode=\"%3\" "
527 "gain=\"%4\" "
528 "normalize=\"%5\"")
529 .arg(encodeColour(m_colour))
530 .arg(m_energyScale)
531 .arg(m_samplingMode)
532 .arg(m_gain)
533 .arg(m_normalize ? "true" : "false");
534
535 return Layer::toXmlString(indent, extraAttributes + " " + s);
536 }
537
538 void
539 SliceLayer::setProperties(const QXmlAttributes &attributes)
540 {
541 bool ok = false;
542
543 QString colourSpec = attributes.value("colour");
544 if (colourSpec != "") {
545 QColor colour(colourSpec);
546 if (colour.isValid()) {
547 setBaseColour(QColor(colourSpec));
548 }
549 }
550
551 EnergyScale scale = (EnergyScale)
552 attributes.value("energyScale").toInt(&ok);
553 if (ok) setEnergyScale(scale);
554
555 SamplingMode mode = (SamplingMode)
556 attributes.value("samplingMode").toInt(&ok);
557 if (ok) setSamplingMode(mode);
558
559 float gain = attributes.value("gain").toFloat(&ok);
560 if (ok) setGain(gain);
561
562 bool normalize = (attributes.value("normalize").trimmed() == "true");
563 setNormalize(normalize);
564 }
565
566 bool
567 SliceLayer::getValueExtents(float &min, float &max, bool &logarithmic,
568 QString &units) const
569 {
570 return false;
571 }
572