annotate layer/SliceLayer.cpp @ 1269:f2894944c6b8

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