annotate layer/SliceLayer.cpp @ 1245:f0e291fa7b9c

Use Range01 normalisation in Colour 3D Plot. This gives us the same column normalisation behaviour as in 2.5 (better than the Max1 option).
author Chris Cannam
date Tue, 28 Feb 2017 14:06:24 +0000
parents 4d0ca1ab4cd0
children 9e1559b08f0d
rev   line source
Chris@133 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@133 2
Chris@133 3 /*
Chris@133 4 Sonic Visualiser
Chris@133 5 An audio file viewer and annotation editor.
Chris@133 6 Centre for Digital Music, Queen Mary, University of London.
Chris@195 7 This file copyright 2006-2007 QMUL.
Chris@133 8
Chris@133 9 This program is free software; you can redistribute it and/or
Chris@133 10 modify it under the terms of the GNU General Public License as
Chris@133 11 published by the Free Software Foundation; either version 2 of the
Chris@133 12 License, or (at your option) any later version. See the file
Chris@133 13 COPYING included with this distribution for more information.
Chris@133 14 */
Chris@133 15
Chris@193 16 #include "SliceLayer.h"
Chris@133 17
Chris@133 18 #include "view/View.h"
Chris@153 19 #include "base/AudioLevel.h"
Chris@167 20 #include "base/RangeMapper.h"
Chris@195 21 #include "base/RealTime.h"
Chris@376 22 #include "ColourMapper.h"
Chris@376 23 #include "ColourDatabase.h"
Chris@195 24
Chris@195 25 #include "PaintAssistant.h"
Chris@133 26
Chris@133 27 #include <QPainter>
Chris@133 28 #include <QPainterPath>
Chris@316 29 #include <QTextStream>
Chris@316 30
Chris@133 31
Chris@193 32 SliceLayer::SliceLayer() :
Chris@193 33 m_sliceableModel(0),
Chris@197 34 m_colourMap(0),
Chris@153 35 m_energyScale(dBScale),
Chris@198 36 m_samplingMode(SampleMean),
Chris@1233 37 m_plotStyle(PlotLines),
Chris@193 38 m_binScale(LinearBins),
Chris@153 39 m_normalize(false),
Chris@284 40 m_threshold(0.0),
Chris@284 41 m_initialThreshold(0.0),
Chris@198 42 m_gain(1.0),
Chris@1238 43 m_minbin(0),
Chris@1238 44 m_maxbin(0),
Chris@198 45 m_currentf0(0),
Chris@198 46 m_currentf1(0)
Chris@133 47 {
Chris@133 48 }
Chris@133 49
Chris@193 50 SliceLayer::~SliceLayer()
Chris@133 51 {
Chris@193 52
Chris@133 53 }
Chris@133 54
Chris@133 55 void
Chris@193 56 SliceLayer::setSliceableModel(const Model *model)
Chris@133 57 {
Chris@193 58 const DenseThreeDimensionalModel *sliceable =
Chris@193 59 dynamic_cast<const DenseThreeDimensionalModel *>(model);
Chris@193 60
Chris@193 61 if (model && !sliceable) {
Chris@682 62 cerr << "WARNING: SliceLayer::setSliceableModel(" << model
Chris@682 63 << "): model is not a DenseThreeDimensionalModel" << endl;
Chris@193 64 }
Chris@193 65
Chris@193 66 if (m_sliceableModel == sliceable) return;
Chris@193 67
Chris@193 68 m_sliceableModel = sliceable;
Chris@193 69
Chris@320 70 connectSignals(m_sliceableModel);
Chris@193 71
Chris@1238 72 m_minbin = 0;
Chris@1238 73 m_maxbin = m_sliceableModel->getHeight();
Chris@1238 74
Chris@193 75 emit modelReplaced();
Chris@1238 76 emit layerParametersChanged();
Chris@153 77 }
Chris@153 78
Chris@153 79 void
Chris@193 80 SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement)
Chris@153 81 {
Chris@587 82 SVDEBUG << "SliceLayer::sliceableModelReplaced(" << orig << ", " << replacement << ")" << endl;
Chris@153 83
Chris@193 84 if (orig == m_sliceableModel) {
Chris@193 85 setSliceableModel
Chris@193 86 (dynamic_cast<const DenseThreeDimensionalModel *>(replacement));
Chris@153 87 }
Chris@153 88 }
Chris@153 89
Chris@153 90 void
Chris@193 91 SliceLayer::modelAboutToBeDeleted(Model *m)
Chris@153 92 {
Chris@587 93 SVDEBUG << "SliceLayer::modelAboutToBeDeleted(" << m << ")" << endl;
Chris@153 94
Chris@193 95 if (m == m_sliceableModel) {
Chris@193 96 setSliceableModel(0);
Chris@153 97 }
Chris@133 98 }
Chris@133 99
Chris@198 100 QString
Chris@918 101 SliceLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const
Chris@198 102 {
Chris@199 103 int minbin, maxbin, range;
Chris@805 104 return getFeatureDescriptionAux(v, p, true, minbin, maxbin, range);
Chris@199 105 }
Chris@199 106
Chris@199 107 QString
Chris@918 108 SliceLayer::getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &p,
Chris@805 109 bool includeBinDescription,
Chris@805 110 int &minbin, int &maxbin, int &range) const
Chris@199 111 {
Chris@199 112 minbin = 0;
Chris@199 113 maxbin = 0;
Chris@198 114 if (!m_sliceableModel) return "";
Chris@198 115
Chris@1238 116 minbin = int(round(getBinForX(v, p.x())));
Chris@1238 117 maxbin = int(round(getBinForX(v, p.x() + 1)));
Chris@1238 118
Chris@198 119 int mh = m_sliceableModel->getHeight();
Chris@199 120 if (minbin >= mh) minbin = mh - 1;
Chris@199 121 if (maxbin >= mh) maxbin = mh - 1;
Chris@199 122 if (minbin < 0) minbin = 0;
Chris@199 123 if (maxbin < 0) maxbin = 0;
Chris@198 124
Chris@906 125 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate();
Chris@198 126
Chris@906 127 sv_frame_t f0 = m_currentf0;
Chris@906 128 sv_frame_t f1 = m_currentf1;
Chris@198 129
Chris@198 130 RealTime rt0 = RealTime::frame2RealTime(f0, sampleRate);
Chris@198 131 RealTime rt1 = RealTime::frame2RealTime(f1, sampleRate);
Chris@198 132
Chris@906 133 range = int(f1 - f0 + 1);
Chris@198 134
Chris@280 135 QString rtrangestr = QString("%1 s").arg((rt1 - rt0).toText().c_str());
Chris@280 136
Chris@199 137 if (includeBinDescription) {
Chris@198 138
Chris@1238 139 int i0 = minbin - m_minbin;
Chris@1238 140 int i1 = maxbin - m_minbin;
Chris@1238 141
Chris@906 142 float minvalue = 0.0;
Chris@1238 143 if (in_range_for(m_values, i0)) minvalue = m_values[i0];
Chris@198 144
Chris@199 145 float maxvalue = minvalue;
Chris@1238 146 if (in_range_for(m_values, i1)) maxvalue = m_values[i1];
Chris@1238 147
Chris@199 148 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
Chris@199 149
Chris@199 150 QString binstr;
Chris@199 151 if (maxbin != minbin) {
Chris@199 152 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1);
Chris@199 153 } else {
Chris@199 154 binstr = QString("%1").arg(minbin+1);
Chris@199 155 }
Chris@199 156
Chris@199 157 QString valuestr;
Chris@199 158 if (maxvalue != minvalue) {
Chris@199 159 valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue);
Chris@199 160 } else {
Chris@199 161 valuestr = QString("%1").arg(minvalue);
Chris@199 162 }
Chris@199 163
Chris@280 164 QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples (%4)\nBin:\t%5\n%6 value:\t%7")
Chris@199 165 .arg(QString::fromStdString(rt0.toText(true)))
Chris@199 166 .arg(QString::fromStdString(rt1.toText(true)))
Chris@199 167 .arg(range)
Chris@280 168 .arg(rtrangestr)
Chris@199 169 .arg(binstr)
Chris@199 170 .arg(m_samplingMode == NearestSample ? tr("First") :
Chris@199 171 m_samplingMode == SampleMean ? tr("Mean") : tr("Peak"))
Chris@199 172 .arg(valuestr);
Chris@199 173
Chris@199 174 return description;
Chris@199 175
Chris@199 176 } else {
Chris@199 177
Chris@280 178 QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples (%4)")
Chris@199 179 .arg(QString::fromStdString(rt0.toText(true)))
Chris@199 180 .arg(QString::fromStdString(rt1.toText(true)))
Chris@280 181 .arg(range)
Chris@280 182 .arg(rtrangestr);
Chris@199 183
Chris@199 184 return description;
Chris@199 185 }
Chris@198 186 }
Chris@198 187
Chris@906 188 double
Chris@1238 189 SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const
Chris@198 190 {
Chris@906 191 double x = 0;
Chris@198 192
Chris@1238 193 bin -= m_minbin;
Chris@1238 194 if (bin < 0) bin = 0;
Chris@1238 195
Chris@1238 196 double count = m_maxbin - m_minbin;
Chris@1238 197 if (count < 0) count = 0;
Chris@1238 198
Chris@1238 199 int pw = v->getPaintWidth();
Chris@1238 200 int origin = m_xorigins[v->getId()];
Chris@1238 201 int w = pw - origin;
Chris@1238 202 if (w < 1) w = 1;
Chris@1238 203
Chris@198 204 switch (m_binScale) {
Chris@198 205
Chris@198 206 case LinearBins:
Chris@906 207 x = (w * bin) / count;
Chris@198 208 break;
Chris@198 209
Chris@198 210 case LogBins:
Chris@906 211 x = (w * log10(bin + 1)) / log10(count + 1);
Chris@198 212 break;
Chris@198 213
Chris@198 214 case InvertedLogBins:
Chris@906 215 x = w - (w * log10(count - bin - 1)) / log10(count);
Chris@198 216 break;
Chris@198 217 }
Chris@1238 218
Chris@1238 219 return x + origin;
Chris@198 220 }
Chris@198 221
Chris@1238 222 double
Chris@1238 223 SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const
Chris@198 224 {
Chris@1238 225 double bin = 0;
Chris@198 226
Chris@1238 227 double count = m_maxbin - m_minbin;
Chris@1238 228 if (count < 0) count = 0;
Chris@1238 229
Chris@1238 230 int pw = v->getPaintWidth();
Chris@1238 231 int origin = m_xorigins[v->getId()];
Chris@1238 232
Chris@1238 233 int w = pw - origin;
Chris@1238 234 if (w < 1) w = 1;
Chris@1238 235
Chris@1238 236 x = x - origin;
Chris@1238 237 if (x < 0) x = 0;
Chris@1238 238
Chris@198 239 switch (m_binScale) {
Chris@198 240
Chris@198 241 case LinearBins:
Chris@198 242 bin = int((x * count) / w + 0.0001);
Chris@198 243 break;
Chris@198 244
Chris@198 245 case LogBins:
Chris@906 246 bin = int(pow(10.0, (x * log10(count + 1)) / w) - 1 + 0.0001);
Chris@198 247 break;
Chris@198 248
Chris@198 249 case InvertedLogBins:
Chris@906 250 bin = count + 1 - int(pow(10.0, (log10(count) * (w - x)) / double(w)) + 0.0001);
Chris@198 251 break;
Chris@198 252 }
Chris@198 253
Chris@1238 254 return bin + m_minbin;
Chris@198 255 }
Chris@198 256
Chris@906 257 double
Chris@1238 258 SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const
Chris@274 259 {
Chris@906 260 norm = 0.0;
Chris@274 261
Chris@1238 262 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return 0;
Chris@274 263
Chris@274 264 value *= m_gain;
Chris@274 265
Chris@1238 266 int yorigin = m_yorigins[v->getId()];
Chris@1238 267 int h = m_heights[v->getId()];
Chris@906 268 double thresh = getThresholdDb();
Chris@274 269
Chris@906 270 double y = 0.0;
Chris@274 271
Chris@274 272 if (h <= 0) return y;
Chris@274 273
Chris@274 274 switch (m_energyScale) {
Chris@274 275
Chris@274 276 case dBScale:
Chris@274 277 {
Chris@906 278 double db = thresh;
Chris@906 279 if (value > 0.0) db = 10.0 * log10(fabs(value));
Chris@274 280 if (db < thresh) db = thresh;
Chris@274 281 norm = (db - thresh) / -thresh;
Chris@906 282 y = yorigin - (double(h) * norm);
Chris@274 283 break;
Chris@274 284 }
Chris@274 285
Chris@274 286 case MeterScale:
Chris@274 287 y = AudioLevel::multiplier_to_preview(value, h);
Chris@906 288 norm = double(y) / double(h);
Chris@274 289 y = yorigin - y;
Chris@274 290 break;
Chris@274 291
Chris@538 292 case AbsoluteScale:
Chris@906 293 value = fabs(value);
Chris@538 294 // and fall through
Chris@538 295
Chris@805 296 case LinearScale:
Chris@274 297 default:
Chris@538 298 norm = (value - m_threshold);
Chris@284 299 if (norm < 0) norm = 0;
Chris@906 300 y = yorigin - (double(h) * norm);
Chris@274 301 break;
Chris@274 302 }
Chris@274 303
Chris@274 304 return y;
Chris@274 305 }
Chris@274 306
Chris@906 307 double
Chris@1238 308 SliceLayer::getValueForY(const LayerGeometryProvider *v, double y) const
Chris@274 309 {
Chris@906 310 double value = 0.0;
Chris@274 311
Chris@1238 312 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return value;
Chris@274 313
Chris@1238 314 int yorigin = m_yorigins[v->getId()];
Chris@1238 315 int h = m_heights[v->getId()];
Chris@906 316 double thresh = getThresholdDb();
Chris@274 317
Chris@274 318 if (h <= 0) return value;
Chris@274 319
Chris@274 320 y = yorigin - y;
Chris@274 321
Chris@274 322 switch (m_energyScale) {
Chris@274 323
Chris@274 324 case dBScale:
Chris@274 325 {
Chris@906 326 double db = ((y / h) * -thresh) + thresh;
Chris@906 327 value = pow(10.0, db/10.0);
Chris@274 328 break;
Chris@274 329 }
Chris@274 330
Chris@274 331 case MeterScale:
Chris@906 332 value = AudioLevel::preview_to_multiplier(int(lrint(y)), h);
Chris@274 333 break;
Chris@805 334
Chris@805 335 case LinearScale:
Chris@805 336 case AbsoluteScale:
Chris@274 337 default:
Chris@284 338 value = y / h + m_threshold;
Chris@274 339 }
Chris@274 340
Chris@274 341 return value / m_gain;
Chris@274 342 }
Chris@274 343
Chris@133 344 void
Chris@916 345 SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@133 346 {
Chris@254 347 if (!m_sliceableModel || !m_sliceableModel->isOK() ||
Chris@254 348 !m_sliceableModel->isReady()) return;
Chris@133 349
Chris@195 350 paint.save();
Chris@195 351 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@538 352 paint.setBrush(Qt::NoBrush);
Chris@195 353
Chris@195 354 if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) {
Chris@195 355 if (!m_scalePoints.empty()) {
Chris@195 356 paint.setPen(QColor(240, 240, 240)); //!!! and dark background?
Chris@805 357 for (int i = 0; i < (int)m_scalePoints.size(); ++i) {
Chris@195 358 paint.drawLine(0, m_scalePoints[i], rect.width(), m_scalePoints[i]);
Chris@195 359 }
Chris@195 360 }
Chris@195 361 }
Chris@195 362
Chris@1233 363 if (m_plotStyle == PlotBlocks) {
Chris@1233 364 // Must use actual zero-width pen, too slow otherwise
Chris@1233 365 paint.setPen(QPen(getBaseQColor(), 0));
Chris@1233 366 } else {
Chris@1233 367 paint.setPen(PaintAssistant::scalePen(getBaseQColor()));
Chris@1233 368 }
Chris@195 369
Chris@607 370 int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
Chris@918 371 int w = v->getPaintWidth() - xorigin - 1;
Chris@198 372
Chris@1238 373 m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription
Chris@133 374
Chris@918 375 int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7;
Chris@195 376 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@133 377
Chris@1238 378 m_yorigins[v->getId()] = yorigin; // for getYForValue etc
Chris@1238 379 m_heights[v->getId()] = h;
Chris@274 380
Chris@274 381 if (h <= 0) return;
Chris@274 382
Chris@133 383 QPainterPath path;
Chris@133 384
Chris@805 385 int mh = m_sliceableModel->getHeight();
Chris@1238 386 int bin0 = 0;
Chris@133 387
Chris@1238 388 if (m_maxbin > m_minbin) {
Chris@1238 389 mh = m_maxbin - m_minbin;
Chris@1238 390 bin0 = m_minbin;
Chris@1238 391 }
Chris@1238 392
Chris@193 393 int divisor = 0;
Chris@193 394
Chris@198 395 m_values.clear();
Chris@805 396 for (int bin = 0; bin < mh; ++bin) {
Chris@906 397 m_values.push_back(0.0);
Chris@193 398 }
Chris@193 399
Chris@906 400 sv_frame_t f0 = v->getCentreFrame();
Chris@193 401 int f0x = v->getXForFrame(f0);
Chris@195 402 f0 = v->getFrameForX(f0x);
Chris@906 403 sv_frame_t f1 = v->getFrameForX(f0x + 1);
Chris@195 404 if (f1 > f0) --f1;
Chris@193 405
Chris@682 406 // cerr << "centre frame " << v->getCentreFrame() << ", x " << f0x << ", f0 " << f0 << ", f1 " << f1 << endl;
Chris@274 407
Chris@805 408 int res = m_sliceableModel->getResolution();
Chris@906 409 int col0 = int(f0 / res);
Chris@805 410 int col1 = col0;
Chris@906 411 if (m_samplingMode != NearestSample) col1 = int(f1 / res);
Chris@274 412 f0 = col0 * res;
Chris@274 413 f1 = (col1 + 1) * res - 1;
Chris@274 414
Chris@682 415 // cerr << "resolution " << res << ", col0 " << col0 << ", col1 " << col1 << ", f0 " << f0 << ", f1 " << f1 << endl;
Chris@193 416
Chris@198 417 m_currentf0 = f0;
Chris@198 418 m_currentf1 = f1;
Chris@198 419
Chris@254 420 BiasCurve curve;
Chris@254 421 getBiasCurve(curve);
Chris@906 422 int cs = int(curve.size());
Chris@254 423
Chris@805 424 for (int col = col0; col <= col1; ++col) {
Chris@805 425 for (int bin = 0; bin < mh; ++bin) {
Chris@1238 426 float value = m_sliceableModel->getValueAt(col, bin0 + bin);
Chris@254 427 if (bin < cs) value *= curve[bin];
Chris@193 428 if (m_samplingMode == SamplePeak) {
Chris@198 429 if (value > m_values[bin]) m_values[bin] = value;
Chris@193 430 } else {
Chris@198 431 m_values[bin] += value;
Chris@193 432 }
Chris@153 433 }
Chris@193 434 ++divisor;
Chris@193 435 }
Chris@193 436
Chris@906 437 float max = 0.0;
Chris@805 438 for (int bin = 0; bin < mh; ++bin) {
Chris@847 439 if (m_samplingMode == SampleMean && divisor > 0) {
Chris@906 440 m_values[bin] /= float(divisor);
Chris@847 441 }
Chris@198 442 if (m_values[bin] > max) max = m_values[bin];
Chris@193 443 }
Chris@906 444 if (max != 0.0 && m_normalize) {
Chris@805 445 for (int bin = 0; bin < mh; ++bin) {
Chris@198 446 m_values[bin] /= max;
Chris@193 447 }
Chris@193 448 }
Chris@193 449
Chris@1238 450 double nx = getXForBin(v, bin0);
Chris@193 451
Chris@197 452 ColourMapper mapper(m_colourMap, 0, 1);
Chris@197 453
Chris@805 454 for (int bin = 0; bin < mh; ++bin) {
Chris@193 455
Chris@906 456 double x = nx;
Chris@1238 457 nx = getXForBin(v, bin + bin0 + 1);
Chris@193 458
Chris@906 459 double value = m_values[bin];
Chris@906 460 double norm = 0.0;
Chris@1238 461 double y = getYForValue(v, value, norm);
Chris@133 462
Chris@193 463 if (m_plotStyle == PlotLines) {
Chris@193 464
Chris@193 465 if (bin == 0) {
Chris@1233 466 path.moveTo((x + nx) / 2, y);
Chris@193 467 } else {
Chris@1233 468 path.lineTo((x + nx) / 2, y);
Chris@193 469 }
Chris@193 470
Chris@193 471 } else if (m_plotStyle == PlotSteps) {
Chris@193 472
Chris@193 473 if (bin == 0) {
Chris@193 474 path.moveTo(x, y);
Chris@193 475 } else {
Chris@193 476 path.lineTo(x, y);
Chris@193 477 }
Chris@193 478 path.lineTo(nx, y);
Chris@193 479
Chris@193 480 } else if (m_plotStyle == PlotBlocks) {
Chris@193 481
Chris@193 482 path.moveTo(x, yorigin);
Chris@133 483 path.lineTo(x, y);
Chris@193 484 path.lineTo(nx, y);
Chris@193 485 path.lineTo(nx, yorigin);
Chris@193 486 path.lineTo(x, yorigin);
Chris@197 487
Chris@197 488 } else if (m_plotStyle == PlotFilledBlocks) {
Chris@197 489
Chris@197 490 paint.fillRect(QRectF(x, y, nx - x, yorigin - y), mapper.map(norm));
Chris@133 491 }
Chris@193 492
Chris@133 493 }
Chris@133 494
Chris@197 495 if (m_plotStyle != PlotFilledBlocks) {
Chris@197 496 paint.drawPath(path);
Chris@197 497 }
Chris@133 498 paint.restore();
Chris@195 499 }
Chris@195 500
Chris@195 501 int
Chris@918 502 SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@195 503 {
Chris@1238 504 int width;
Chris@538 505 if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) {
Chris@1238 506 width = std::max(paint.fontMetrics().width("0.0") + 13,
Chris@1238 507 paint.fontMetrics().width("x10-10"));
Chris@195 508 } else {
Chris@1238 509 width = std::max(paint.fontMetrics().width(tr("0dB")),
Chris@1238 510 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@195 511 }
Chris@1238 512 return width;
Chris@195 513 }
Chris@195 514
Chris@195 515 void
Chris@918 516 SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@195 517 {
Chris@906 518 double thresh = m_threshold;
Chris@538 519 if (m_energyScale != LinearScale && m_energyScale != AbsoluteScale) {
Chris@284 520 thresh = AudioLevel::dB_to_multiplier(getThresholdDb());
Chris@195 521 }
Chris@195 522
Chris@195 523 // int h = (rect.height() * 3) / 4;
Chris@195 524 // int y = (rect.height() / 2) - (h / 2);
Chris@195 525
Chris@918 526 int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 6;
Chris@195 527 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@195 528 if (h < 0) return;
Chris@195 529
Chris@195 530 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h);
Chris@195 531
Chris@220 532 int mult = 1;
Chris@220 533
Chris@195 534 PaintAssistant::paintVerticalLevelScale
Chris@195 535 (paint, actual, thresh, 1.0 / m_gain,
Chris@195 536 PaintAssistant::Scale(m_energyScale),
Chris@220 537 mult,
Chris@195 538 const_cast<std::vector<int> *>(&m_scalePoints));
Chris@220 539
Chris@220 540 if (mult != 1 && mult != 0) {
Chris@906 541 int log = int(lrint(log10(mult)));
Chris@220 542 QString a = tr("x10");
Chris@220 543 QString b = QString("%1").arg(-log);
Chris@220 544 paint.drawText(3, 8 + paint.fontMetrics().ascent(), a);
Chris@220 545 paint.drawText(3 + paint.fontMetrics().width(a),
Chris@220 546 3 + paint.fontMetrics().ascent(), b);
Chris@220 547 }
Chris@133 548 }
Chris@133 549
Chris@153 550 Layer::PropertyList
Chris@193 551 SliceLayer::getProperties() const
Chris@153 552 {
Chris@287 553 PropertyList list = SingleColourLayer::getProperties();
Chris@538 554 list.push_back("Bin Scale");
Chris@193 555 list.push_back("Plot Type");
Chris@153 556 list.push_back("Scale");
Chris@153 557 list.push_back("Normalize");
Chris@284 558 list.push_back("Threshold");
Chris@153 559 list.push_back("Gain");
Chris@153 560
Chris@153 561 return list;
Chris@153 562 }
Chris@153 563
Chris@153 564 QString
Chris@193 565 SliceLayer::getPropertyLabel(const PropertyName &name) const
Chris@153 566 {
Chris@193 567 if (name == "Plot Type") return tr("Plot Type");
Chris@290 568 if (name == "Scale") return tr("Scale");
Chris@153 569 if (name == "Normalize") return tr("Normalize");
Chris@284 570 if (name == "Threshold") return tr("Threshold");
Chris@153 571 if (name == "Gain") return tr("Gain");
Chris@193 572 if (name == "Sampling Mode") return tr("Sampling Mode");
Chris@538 573 if (name == "Bin Scale") return tr("Bin Scale");
Chris@287 574 return SingleColourLayer::getPropertyLabel(name);
Chris@153 575 }
Chris@153 576
Chris@335 577 QString
Chris@335 578 SliceLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 579 {
Chris@335 580 if (name == "Normalize") return "normalise";
Chris@335 581 return "";
Chris@335 582 }
Chris@335 583
Chris@153 584 Layer::PropertyType
Chris@193 585 SliceLayer::getPropertyType(const PropertyName &name) const
Chris@153 586 {
Chris@153 587 if (name == "Gain") return RangeProperty;
Chris@153 588 if (name == "Normalize") return ToggleProperty;
Chris@284 589 if (name == "Threshold") return RangeProperty;
Chris@287 590 if (name == "Plot Type") return ValueProperty;
Chris@290 591 if (name == "Scale") return ValueProperty;
Chris@287 592 if (name == "Sampling Mode") return ValueProperty;
Chris@287 593 if (name == "Bin Scale") return ValueProperty;
Chris@287 594 if (name == "Colour" && m_plotStyle == PlotFilledBlocks) return ValueProperty;
Chris@287 595 return SingleColourLayer::getPropertyType(name);
Chris@153 596 }
Chris@153 597
Chris@153 598 QString
Chris@193 599 SliceLayer::getPropertyGroupName(const PropertyName &name) const
Chris@153 600 {
Chris@153 601 if (name == "Scale" ||
Chris@153 602 name == "Normalize" ||
Chris@193 603 name == "Sampling Mode" ||
Chris@284 604 name == "Threshold" ||
Chris@193 605 name == "Gain") return tr("Scale");
Chris@193 606 if (name == "Plot Type" ||
Chris@538 607 name == "Bin Scale") return tr("Bins");
Chris@287 608 return SingleColourLayer::getPropertyGroupName(name);
Chris@153 609 }
Chris@153 610
Chris@153 611 int
Chris@193 612 SliceLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 613 int *min, int *max, int *deflt) const
Chris@153 614 {
Chris@216 615 int val = 0;
Chris@153 616
Chris@216 617 int garbage0, garbage1, garbage2;
Chris@153 618 if (!min) min = &garbage0;
Chris@153 619 if (!max) max = &garbage1;
Chris@248 620 if (!deflt) deflt = &garbage2;
Chris@153 621
Chris@153 622 if (name == "Gain") {
Chris@153 623
Chris@153 624 *min = -50;
Chris@153 625 *max = 50;
Chris@216 626 *deflt = 0;
Chris@153 627
Chris@1238 628 // cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl;
Chris@193 629
Chris@906 630 val = int(lrint(log10(m_gain) * 20.0));
Chris@216 631 if (val < *min) val = *min;
Chris@216 632 if (val > *max) val = *max;
Chris@153 633
Chris@284 634 } else if (name == "Threshold") {
Chris@284 635
Chris@284 636 *min = -80;
Chris@284 637 *max = 0;
Chris@284 638
Chris@906 639 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold)));
Chris@284 640 if (*deflt < *min) *deflt = *min;
Chris@284 641 if (*deflt > *max) *deflt = *max;
Chris@284 642
Chris@906 643 val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold)));
Chris@284 644 if (val < *min) val = *min;
Chris@284 645 if (val > *max) val = *max;
Chris@284 646
Chris@153 647 } else if (name == "Normalize") {
Chris@153 648
Chris@216 649 val = (m_normalize ? 1 : 0);
Chris@216 650 *deflt = 0;
Chris@153 651
Chris@287 652 } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
Chris@197 653
Chris@287 654 *min = 0;
Chris@287 655 *max = ColourMapper::getColourMapCount() - 1;
Chris@287 656 *deflt = 0;
Chris@287 657
Chris@287 658 val = m_colourMap;
Chris@153 659
Chris@153 660 } else if (name == "Scale") {
Chris@153 661
Chris@153 662 *min = 0;
Chris@538 663 *max = 3;
Chris@216 664 *deflt = (int)dBScale;
Chris@153 665
Chris@216 666 val = (int)m_energyScale;
Chris@153 667
Chris@193 668 } else if (name == "Sampling Mode") {
Chris@153 669
Chris@153 670 *min = 0;
Chris@193 671 *max = 2;
Chris@216 672 *deflt = (int)SampleMean;
Chris@193 673
Chris@216 674 val = (int)m_samplingMode;
Chris@153 675
Chris@193 676 } else if (name == "Plot Type") {
Chris@193 677
Chris@193 678 *min = 0;
Chris@197 679 *max = 3;
Chris@216 680 *deflt = (int)PlotSteps;
Chris@193 681
Chris@216 682 val = (int)m_plotStyle;
Chris@193 683
Chris@193 684 } else if (name == "Bin Scale") {
Chris@193 685
Chris@193 686 *min = 0;
Chris@198 687 *max = 2;
Chris@216 688 *deflt = (int)LinearBins;
Chris@198 689 // *max = 1; // I don't think we really do want to offer inverted log
Chris@193 690
Chris@216 691 val = (int)m_binScale;
Chris@193 692
Chris@153 693 } else {
Chris@287 694 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@153 695 }
Chris@153 696
Chris@216 697 return val;
Chris@153 698 }
Chris@153 699
Chris@153 700 QString
Chris@193 701 SliceLayer::getPropertyValueLabel(const PropertyName &name,
Chris@153 702 int value) const
Chris@153 703 {
Chris@287 704 if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
Chris@287 705 return ColourMapper::getColourMapName(value);
Chris@153 706 }
Chris@153 707 if (name == "Scale") {
Chris@153 708 switch (value) {
Chris@153 709 default:
Chris@153 710 case 0: return tr("Linear");
Chris@153 711 case 1: return tr("Meter");
Chris@538 712 case 2: return tr("Log");
Chris@538 713 case 3: return tr("Absolute");
Chris@153 714 }
Chris@153 715 }
Chris@193 716 if (name == "Sampling Mode") {
Chris@153 717 switch (value) {
Chris@153 718 default:
Chris@193 719 case 0: return tr("Any");
Chris@193 720 case 1: return tr("Mean");
Chris@193 721 case 2: return tr("Peak");
Chris@193 722 }
Chris@193 723 }
Chris@193 724 if (name == "Plot Type") {
Chris@193 725 switch (value) {
Chris@193 726 default:
Chris@193 727 case 0: return tr("Lines");
Chris@193 728 case 1: return tr("Steps");
Chris@193 729 case 2: return tr("Blocks");
Chris@197 730 case 3: return tr("Colours");
Chris@193 731 }
Chris@193 732 }
Chris@193 733 if (name == "Bin Scale") {
Chris@193 734 switch (value) {
Chris@193 735 default:
Chris@538 736 case 0: return tr("Linear");
Chris@538 737 case 1: return tr("Log");
Chris@538 738 case 2: return tr("Rev Log");
Chris@153 739 }
Chris@153 740 }
Chris@287 741 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@153 742 }
Chris@153 743
Chris@167 744 RangeMapper *
Chris@193 745 SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 746 {
Chris@167 747 if (name == "Gain") {
Chris@167 748 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 749 }
Chris@284 750 if (name == "Threshold") {
Chris@284 751 return new LinearRangeMapper(-80, 0, -80, 0, tr("dB"));
Chris@284 752 }
Chris@287 753 return SingleColourLayer::getNewPropertyRangeMapper(name);
Chris@167 754 }
Chris@167 755
Chris@133 756 void
Chris@193 757 SliceLayer::setProperty(const PropertyName &name, int value)
Chris@133 758 {
Chris@153 759 if (name == "Gain") {
Chris@906 760 setGain(powf(10, float(value)/20.0f));
Chris@284 761 } else if (name == "Threshold") {
Chris@906 762 if (value == -80) setThreshold(0.0f);
Chris@906 763 else setThreshold(float(AudioLevel::dB_to_multiplier(value)));
Chris@287 764 } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
Chris@287 765 setFillColourMap(value);
Chris@153 766 } else if (name == "Scale") {
Chris@153 767 switch (value) {
Chris@153 768 default:
Chris@153 769 case 0: setEnergyScale(LinearScale); break;
Chris@153 770 case 1: setEnergyScale(MeterScale); break;
Chris@153 771 case 2: setEnergyScale(dBScale); break;
Chris@538 772 case 3: setEnergyScale(AbsoluteScale); break;
Chris@153 773 }
Chris@193 774 } else if (name == "Plot Type") {
Chris@193 775 setPlotStyle(PlotStyle(value));
Chris@193 776 } else if (name == "Sampling Mode") {
Chris@193 777 switch (value) {
Chris@193 778 default:
Chris@193 779 case 0: setSamplingMode(NearestSample); break;
Chris@193 780 case 1: setSamplingMode(SampleMean); break;
Chris@193 781 case 2: setSamplingMode(SamplePeak); break;
Chris@193 782 }
Chris@193 783 } else if (name == "Bin Scale") {
Chris@193 784 switch (value) {
Chris@193 785 default:
Chris@193 786 case 0: setBinScale(LinearBins); break;
Chris@193 787 case 1: setBinScale(LogBins); break;
Chris@193 788 case 2: setBinScale(InvertedLogBins); break;
Chris@193 789 }
Chris@153 790 } else if (name == "Normalize") {
Chris@153 791 setNormalize(value ? true : false);
Chris@287 792 } else {
Chris@287 793 SingleColourLayer::setProperty(name, value);
Chris@153 794 }
Chris@153 795 }
Chris@153 796
Chris@153 797 void
Chris@197 798 SliceLayer::setFillColourMap(int map)
Chris@197 799 {
Chris@197 800 if (m_colourMap == map) return;
Chris@197 801 m_colourMap = map;
Chris@197 802 emit layerParametersChanged();
Chris@197 803 }
Chris@197 804
Chris@197 805 void
Chris@193 806 SliceLayer::setEnergyScale(EnergyScale scale)
Chris@153 807 {
Chris@153 808 if (m_energyScale == scale) return;
Chris@153 809 m_energyScale = scale;
Chris@153 810 emit layerParametersChanged();
Chris@153 811 }
Chris@153 812
Chris@153 813 void
Chris@193 814 SliceLayer::setSamplingMode(SamplingMode mode)
Chris@153 815 {
Chris@193 816 if (m_samplingMode == mode) return;
Chris@193 817 m_samplingMode = mode;
Chris@153 818 emit layerParametersChanged();
Chris@153 819 }
Chris@153 820
Chris@153 821 void
Chris@193 822 SliceLayer::setPlotStyle(PlotStyle style)
Chris@153 823 {
Chris@193 824 if (m_plotStyle == style) return;
Chris@197 825 bool colourTypeChanged = (style == PlotFilledBlocks ||
Chris@197 826 m_plotStyle == PlotFilledBlocks);
Chris@193 827 m_plotStyle = style;
Chris@197 828 if (colourTypeChanged) {
Chris@197 829 emit layerParameterRangesChanged();
Chris@197 830 }
Chris@153 831 emit layerParametersChanged();
Chris@153 832 }
Chris@153 833
Chris@153 834 void
Chris@193 835 SliceLayer::setBinScale(BinScale scale)
Chris@153 836 {
Chris@193 837 if (m_binScale == scale) return;
Chris@193 838 m_binScale = scale;
Chris@153 839 emit layerParametersChanged();
Chris@153 840 }
Chris@153 841
Chris@153 842 void
Chris@193 843 SliceLayer::setNormalize(bool n)
Chris@153 844 {
Chris@153 845 if (m_normalize == n) return;
Chris@153 846 m_normalize = n;
Chris@153 847 emit layerParametersChanged();
Chris@153 848 }
Chris@153 849
Chris@153 850 void
Chris@284 851 SliceLayer::setThreshold(float thresh)
Chris@284 852 {
Chris@284 853 if (m_threshold == thresh) return;
Chris@284 854 m_threshold = thresh;
Chris@284 855 emit layerParametersChanged();
Chris@284 856 }
Chris@284 857
Chris@284 858 void
Chris@193 859 SliceLayer::setGain(float gain)
Chris@153 860 {
Chris@153 861 if (m_gain == gain) return;
Chris@153 862 m_gain = gain;
Chris@153 863 emit layerParametersChanged();
Chris@153 864 }
Chris@153 865
Chris@284 866 float
Chris@284 867 SliceLayer::getThresholdDb() const
Chris@284 868 {
Chris@284 869 if (m_threshold == 0.0) return -80.f;
Chris@906 870 float db = float(AudioLevel::multiplier_to_dB(m_threshold));
Chris@284 871 return db;
Chris@284 872 }
Chris@284 873
Chris@287 874 int
Chris@287 875 SliceLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 876 {
Chris@287 877 impose = false;
Chris@287 878 return ColourDatabase::getInstance()->getColourIndex
Chris@287 879 (QString(darkbg ? "Bright Blue" : "Blue"));
Chris@287 880 }
Chris@287 881
Chris@316 882 void
Chris@316 883 SliceLayer::toXml(QTextStream &stream,
Chris@316 884 QString indent, QString extraAttributes) const
Chris@153 885 {
Chris@153 886 QString s;
Chris@153 887
Chris@287 888 s += QString("colourScheme=\"%1\" "
Chris@287 889 "energyScale=\"%2\" "
Chris@287 890 "samplingMode=\"%3\" "
Chris@598 891 "plotStyle=\"%4\" "
Chris@598 892 "binScale=\"%5\" "
Chris@598 893 "gain=\"%6\" "
Chris@598 894 "threshold=\"%7\" "
Chris@1238 895 "normalize=\"%8\" %9")
Chris@197 896 .arg(m_colourMap)
Chris@153 897 .arg(m_energyScale)
Chris@193 898 .arg(m_samplingMode)
Chris@598 899 .arg(m_plotStyle)
Chris@598 900 .arg(m_binScale)
Chris@153 901 .arg(m_gain)
Chris@598 902 .arg(m_threshold)
Chris@1238 903 .arg(m_normalize ? "true" : "false")
Chris@1238 904 .arg(QString("minbin=\"%1\" "
Chris@1238 905 "maxbin=\"%2\"")
Chris@1238 906 .arg(m_minbin)
Chris@1238 907 .arg(m_maxbin));
Chris@153 908
Chris@316 909 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@153 910 }
Chris@153 911
Chris@153 912 void
Chris@193 913 SliceLayer::setProperties(const QXmlAttributes &attributes)
Chris@153 914 {
Chris@153 915 bool ok = false;
Chris@153 916
Chris@287 917 SingleColourLayer::setProperties(attributes);
Chris@153 918
Chris@153 919 EnergyScale scale = (EnergyScale)
Chris@153 920 attributes.value("energyScale").toInt(&ok);
Chris@153 921 if (ok) setEnergyScale(scale);
Chris@153 922
Chris@193 923 SamplingMode mode = (SamplingMode)
Chris@193 924 attributes.value("samplingMode").toInt(&ok);
Chris@193 925 if (ok) setSamplingMode(mode);
Chris@153 926
Chris@197 927 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 928 if (ok) setFillColourMap(colourMap);
Chris@197 929
Chris@598 930 PlotStyle s = (PlotStyle)
Chris@598 931 attributes.value("plotStyle").toInt(&ok);
Chris@598 932 if (ok) setPlotStyle(s);
Chris@598 933
Chris@598 934 BinScale b = (BinScale)
Chris@598 935 attributes.value("binScale").toInt(&ok);
Chris@598 936 if (ok) setBinScale(b);
Chris@598 937
Chris@153 938 float gain = attributes.value("gain").toFloat(&ok);
Chris@153 939 if (ok) setGain(gain);
Chris@153 940
Chris@598 941 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@598 942 if (ok) setThreshold(threshold);
Chris@598 943
Chris@153 944 bool normalize = (attributes.value("normalize").trimmed() == "true");
Chris@153 945 setNormalize(normalize);
Chris@1238 946
Chris@1238 947 bool alsoOk = false;
Chris@1238 948
Chris@1238 949 float min = attributes.value("minbin").toFloat(&ok);
Chris@1238 950 float max = attributes.value("maxbin").toFloat(&alsoOk);
Chris@1238 951 if (ok && alsoOk) setDisplayExtents(min, max);
Chris@133 952 }
Chris@133 953
Chris@133 954 bool
Chris@1238 955 SliceLayer::getValueExtents(double &min, double &max, bool &logarithmic,
Chris@1238 956 QString &unit) const
Chris@133 957 {
Chris@1238 958 if (!m_sliceableModel) return false;
Chris@1238 959
Chris@1238 960 min = 0;
Chris@1238 961 max = double(m_sliceableModel->getHeight());
Chris@1238 962
Chris@1238 963 logarithmic = (m_binScale == BinScale::LogBins);
Chris@1238 964 unit = "";
Chris@1238 965
Chris@1238 966 return true;
Chris@133 967 }
Chris@133 968
Chris@1238 969 bool
Chris@1238 970 SliceLayer::getDisplayExtents(double &min, double &max) const
Chris@1238 971 {
Chris@1238 972 if (!m_sliceableModel) return false;
Chris@1238 973
Chris@1238 974 double hmax = double(m_sliceableModel->getHeight());
Chris@1238 975
Chris@1238 976 min = m_minbin;
Chris@1238 977 max = m_maxbin;
Chris@1238 978 if (max <= min) {
Chris@1238 979 min = 0;
Chris@1238 980 max = hmax;
Chris@1238 981 }
Chris@1238 982 if (min < 0) min = 0;
Chris@1238 983 if (max > hmax) max = hmax;
Chris@1238 984
Chris@1238 985 return true;
Chris@1238 986 }
Chris@1238 987
Chris@1238 988 bool
Chris@1238 989 SliceLayer::setDisplayExtents(double min, double max)
Chris@1238 990 {
Chris@1238 991 if (!m_sliceableModel) return false;
Chris@1238 992
Chris@1238 993 m_minbin = int(lrint(min));
Chris@1238 994 m_maxbin = int(lrint(max));
Chris@1238 995
Chris@1238 996 emit layerParametersChanged();
Chris@1238 997 return true;
Chris@1238 998 }
Chris@1238 999
Chris@1238 1000 int
Chris@1238 1001 SliceLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@1238 1002 {
Chris@1238 1003 if (!m_sliceableModel) return 0;
Chris@1238 1004
Chris@1238 1005 defaultStep = 0;
Chris@1238 1006 int h = m_sliceableModel->getHeight();
Chris@1238 1007 return h;
Chris@1238 1008 }
Chris@1238 1009
Chris@1238 1010 int
Chris@1238 1011 SliceLayer::getCurrentVerticalZoomStep() const
Chris@1238 1012 {
Chris@1238 1013 if (!m_sliceableModel) return 0;
Chris@1238 1014
Chris@1238 1015 double min, max;
Chris@1238 1016 getDisplayExtents(min, max);
Chris@1238 1017 return m_sliceableModel->getHeight() - int(lrint(max - min));
Chris@1238 1018 }
Chris@1238 1019
Chris@1238 1020 void
Chris@1238 1021 SliceLayer::setVerticalZoomStep(int step)
Chris@1238 1022 {
Chris@1238 1023 if (!m_sliceableModel) return;
Chris@1238 1024
Chris@1238 1025 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): before: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl;
Chris@1238 1026
Chris@1238 1027 int dist = m_sliceableModel->getHeight() - step;
Chris@1238 1028 if (dist < 1) dist = 1;
Chris@1238 1029 double centre = m_minbin + (m_maxbin - m_minbin) / 2.0;
Chris@1238 1030 m_minbin = int(lrint(centre - dist/2.0));
Chris@1238 1031 if (m_minbin < 0) m_minbin = 0;
Chris@1238 1032 m_maxbin = m_minbin + dist;
Chris@1238 1033 if (m_maxbin > m_sliceableModel->getHeight()) m_maxbin = m_sliceableModel->getHeight();
Chris@1238 1034
Chris@1238 1035 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): after: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl;
Chris@1238 1036
Chris@1238 1037 emit layerParametersChanged();
Chris@1238 1038 }
Chris@1238 1039
Chris@1238 1040 RangeMapper *
Chris@1238 1041 SliceLayer::getNewVerticalZoomRangeMapper() const
Chris@1238 1042 {
Chris@1238 1043 if (!m_sliceableModel) return 0;
Chris@1238 1044
Chris@1238 1045 return new LinearRangeMapper(0, m_sliceableModel->getHeight(),
Chris@1238 1046 0, m_sliceableModel->getHeight(), "");
Chris@1238 1047 }