annotate layer/SliceLayer.cpp @ 1394:4a36f6130056 spectrogramparam

Various tweaks & fixes to log-scale handling in spectrum. We can't easily preserve the nice behaviour where peaks stay in place as fft size changes, without potentially losing a bit of low-frequency information
author Chris Cannam
date Wed, 14 Nov 2018 14:17:06 +0000
parents 1eb560b363e7
children ba1f0234efa7
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@1383 27 #include "base/Profiler.h"
Chris@1383 28
Chris@133 29 #include <QPainter>
Chris@133 30 #include <QPainterPath>
Chris@316 31 #include <QTextStream>
Chris@316 32
Chris@133 33
Chris@193 34 SliceLayer::SliceLayer() :
Chris@193 35 m_sliceableModel(0),
Chris@1281 36 m_colourMap(int(ColourMapper::Ice)),
Chris@1362 37 m_colourInverted(false),
Chris@153 38 m_energyScale(dBScale),
Chris@198 39 m_samplingMode(SampleMean),
Chris@1233 40 m_plotStyle(PlotLines),
Chris@193 41 m_binScale(LinearBins),
Chris@153 42 m_normalize(false),
Chris@284 43 m_threshold(0.0),
Chris@284 44 m_initialThreshold(0.0),
Chris@198 45 m_gain(1.0),
Chris@1238 46 m_minbin(0),
Chris@1238 47 m_maxbin(0),
Chris@198 48 m_currentf0(0),
Chris@198 49 m_currentf1(0)
Chris@133 50 {
Chris@133 51 }
Chris@133 52
Chris@193 53 SliceLayer::~SliceLayer()
Chris@133 54 {
Chris@193 55
Chris@133 56 }
Chris@133 57
Chris@133 58 void
Chris@193 59 SliceLayer::setSliceableModel(const Model *model)
Chris@133 60 {
Chris@193 61 const DenseThreeDimensionalModel *sliceable =
Chris@193 62 dynamic_cast<const DenseThreeDimensionalModel *>(model);
Chris@193 63
Chris@193 64 if (model && !sliceable) {
Chris@682 65 cerr << "WARNING: SliceLayer::setSliceableModel(" << model
Chris@682 66 << "): model is not a DenseThreeDimensionalModel" << endl;
Chris@193 67 }
Chris@193 68
Chris@193 69 if (m_sliceableModel == sliceable) return;
Chris@193 70
Chris@193 71 m_sliceableModel = sliceable;
Chris@193 72
Chris@1255 73 if (!m_sliceableModel) return;
Chris@1255 74
Chris@320 75 connectSignals(m_sliceableModel);
Chris@193 76
Chris@1389 77 if (m_minbin == 0 && m_maxbin == 0) {
Chris@1389 78 m_minbin = 0;
Chris@1389 79 m_maxbin = m_sliceableModel->getHeight();
Chris@1389 80 }
Chris@1238 81
Chris@193 82 emit modelReplaced();
Chris@1238 83 emit layerParametersChanged();
Chris@153 84 }
Chris@153 85
Chris@153 86 void
Chris@193 87 SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement)
Chris@153 88 {
Chris@587 89 SVDEBUG << "SliceLayer::sliceableModelReplaced(" << orig << ", " << replacement << ")" << endl;
Chris@153 90
Chris@193 91 if (orig == m_sliceableModel) {
Chris@193 92 setSliceableModel
Chris@193 93 (dynamic_cast<const DenseThreeDimensionalModel *>(replacement));
Chris@153 94 }
Chris@153 95 }
Chris@153 96
Chris@153 97 void
Chris@193 98 SliceLayer::modelAboutToBeDeleted(Model *m)
Chris@153 99 {
Chris@587 100 SVDEBUG << "SliceLayer::modelAboutToBeDeleted(" << m << ")" << endl;
Chris@153 101
Chris@193 102 if (m == m_sliceableModel) {
Chris@193 103 setSliceableModel(0);
Chris@153 104 }
Chris@133 105 }
Chris@133 106
Chris@198 107 QString
Chris@918 108 SliceLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const
Chris@198 109 {
Chris@199 110 int minbin, maxbin, range;
Chris@805 111 return getFeatureDescriptionAux(v, p, true, minbin, maxbin, range);
Chris@199 112 }
Chris@199 113
Chris@199 114 QString
Chris@918 115 SliceLayer::getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &p,
Chris@805 116 bool includeBinDescription,
Chris@805 117 int &minbin, int &maxbin, int &range) const
Chris@199 118 {
Chris@199 119 minbin = 0;
Chris@199 120 maxbin = 0;
Chris@198 121 if (!m_sliceableModel) return "";
Chris@198 122
Chris@1256 123 minbin = int(getBinForX(v, p.x()));
Chris@1256 124 maxbin = int(getBinForX(v, p.x() + 1));
Chris@1238 125
Chris@198 126 int mh = m_sliceableModel->getHeight();
Chris@199 127 if (minbin >= mh) minbin = mh - 1;
Chris@199 128 if (maxbin >= mh) maxbin = mh - 1;
Chris@199 129 if (minbin < 0) minbin = 0;
Chris@199 130 if (maxbin < 0) maxbin = 0;
Chris@198 131
Chris@906 132 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate();
Chris@198 133
Chris@906 134 sv_frame_t f0 = m_currentf0;
Chris@906 135 sv_frame_t f1 = m_currentf1;
Chris@198 136
Chris@198 137 RealTime rt0 = RealTime::frame2RealTime(f0, sampleRate);
Chris@198 138 RealTime rt1 = RealTime::frame2RealTime(f1, sampleRate);
Chris@198 139
Chris@906 140 range = int(f1 - f0 + 1);
Chris@198 141
Chris@280 142 QString rtrangestr = QString("%1 s").arg((rt1 - rt0).toText().c_str());
Chris@280 143
Chris@199 144 if (includeBinDescription) {
Chris@198 145
Chris@1238 146 int i0 = minbin - m_minbin;
Chris@1238 147 int i1 = maxbin - m_minbin;
Chris@1238 148
Chris@906 149 float minvalue = 0.0;
Chris@1238 150 if (in_range_for(m_values, i0)) minvalue = m_values[i0];
Chris@198 151
Chris@199 152 float maxvalue = minvalue;
Chris@1238 153 if (in_range_for(m_values, i1)) maxvalue = m_values[i1];
Chris@1238 154
Chris@199 155 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
Chris@199 156
Chris@199 157 QString binstr;
Chris@199 158 if (maxbin != minbin) {
Chris@199 159 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1);
Chris@199 160 } else {
Chris@199 161 binstr = QString("%1").arg(minbin+1);
Chris@199 162 }
Chris@199 163
Chris@199 164 QString valuestr;
Chris@199 165 if (maxvalue != minvalue) {
Chris@199 166 valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue);
Chris@199 167 } else {
Chris@199 168 valuestr = QString("%1").arg(minvalue);
Chris@199 169 }
Chris@199 170
Chris@280 171 QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples (%4)\nBin:\t%5\n%6 value:\t%7")
Chris@199 172 .arg(QString::fromStdString(rt0.toText(true)))
Chris@199 173 .arg(QString::fromStdString(rt1.toText(true)))
Chris@199 174 .arg(range)
Chris@280 175 .arg(rtrangestr)
Chris@199 176 .arg(binstr)
Chris@199 177 .arg(m_samplingMode == NearestSample ? tr("First") :
Chris@199 178 m_samplingMode == SampleMean ? tr("Mean") : tr("Peak"))
Chris@199 179 .arg(valuestr);
Chris@199 180
Chris@199 181 return description;
Chris@199 182
Chris@199 183 } else {
Chris@199 184
Chris@280 185 QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples (%4)")
Chris@199 186 .arg(QString::fromStdString(rt0.toText(true)))
Chris@199 187 .arg(QString::fromStdString(rt1.toText(true)))
Chris@280 188 .arg(range)
Chris@280 189 .arg(rtrangestr);
Chris@199 190
Chris@199 191 return description;
Chris@199 192 }
Chris@198 193 }
Chris@198 194
Chris@906 195 double
Chris@1238 196 SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const
Chris@198 197 {
Chris@1386 198 return getXForScalePoint(v, bin, m_minbin, m_maxbin);
Chris@1386 199 }
Chris@1386 200
Chris@1386 201 double
Chris@1386 202 SliceLayer::getXForScalePoint(const LayerGeometryProvider *v,
Chris@1386 203 double p, double pmin, double pmax) const
Chris@1386 204 {
Chris@906 205 double x = 0;
Chris@198 206
Chris@1238 207 int pw = v->getPaintWidth();
Chris@1238 208 int origin = m_xorigins[v->getId()];
Chris@1238 209 int w = pw - origin;
Chris@1238 210 if (w < 1) w = 1;
Chris@1238 211
Chris@1394 212 if (p < pmin) p = pmin;
Chris@1394 213 if (p > pmax) p = pmax;
Chris@1394 214
Chris@1394 215 if (m_binScale == LinearBins) {
Chris@1394 216 x = (w * (p - pmin)) / (pmax - pmin);
Chris@1394 217 } else {
Chris@198 218
Chris@1394 219 if (m_binScale == InvertedLogBins) {
Chris@1394 220 // stoopid
Chris@1394 221 p = pmax - p;
Chris@1394 222 }
Chris@198 223
Chris@1281 224 // The 0.8 here is an awkward compromise. Our x-coord is
Chris@1281 225 // proportional to log of bin number, with the x-coord "of a
Chris@1281 226 // bin" being that of the left edge of the bin range. We can't
Chris@1281 227 // start counting bins from 0, as that would give us x = -Inf
Chris@1281 228 // and hide the first bin entirely. But if we start from 1, we
Chris@1281 229 // are giving a lot of space to the first bin, which in most
Chris@1281 230 // display modes won't be used because the "point" location
Chris@1281 231 // for that bin is in the middle of it. Yet in some modes
Chris@1281 232 // we'll still want it. A compromise is to count our first bin
Chris@1281 233 // as "a bit less than 1", so that most of it is visible but a
Chris@1281 234 // bit is tactfully cropped at the left edge so it doesn't
Chris@1281 235 // take up so much space.
Chris@1394 236 const double origin = 0.8;
Chris@198 237
Chris@1394 238 // sometimes we are called with a pmin/pmax range that begins
Chris@1394 239 // before 0: in that situation, we shift everything along by
Chris@1394 240 // the difference between 0 and pmin before doing any other
Chris@1394 241 // calculations
Chris@1394 242 double reqdshift = 0.0;
Chris@1394 243 if (pmin < 0) reqdshift = -pmin;
Chris@1394 244
Chris@1394 245 double pminlog = log10(pmin + reqdshift + origin);
Chris@1394 246 double pmaxlog = log10(pmax + reqdshift + origin);
Chris@1394 247 double plog = log10(p + reqdshift + origin);
Chris@1394 248 x = (w * (plog - pminlog)) / (pmaxlog - pminlog);
Chris@1394 249
Chris@1394 250 /*
Chris@1394 251 cerr << "getXForScalePoint(" << p << "): pmin = " << pmin
Chris@1394 252 << ", pmax = " << pmax << ", w = " << w
Chris@1394 253 << ", reqdshift = " << reqdshift
Chris@1394 254 << ", pminlog = " << pminlog << ", pmaxlog = " << pmaxlog
Chris@1394 255 << ", plog = " << plog
Chris@1394 256 << " -> x = " << x << endl;
Chris@1394 257 */
Chris@1394 258
Chris@1394 259 if (m_binScale == InvertedLogBins) {
Chris@1394 260 // still stoopid
Chris@1394 261 x = w - x;
Chris@1394 262 }
Chris@198 263 }
Chris@1238 264
Chris@1238 265 return x + origin;
Chris@198 266 }
Chris@198 267
Chris@1238 268 double
Chris@1238 269 SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const
Chris@198 270 {
Chris@1386 271 return getScalePointForX(v, x, m_minbin, m_maxbin);
Chris@1386 272 }
Chris@198 273
Chris@1386 274 double
Chris@1386 275 SliceLayer::getScalePointForX(const LayerGeometryProvider *v,
Chris@1386 276 double x, double pmin, double pmax) const
Chris@1386 277 {
Chris@1386 278 double p = 0;
Chris@1386 279
Chris@1238 280 int pw = v->getPaintWidth();
Chris@1238 281 int origin = m_xorigins[v->getId()];
Chris@1238 282
Chris@1238 283 int w = pw - origin;
Chris@1238 284 if (w < 1) w = 1;
Chris@1238 285
Chris@1238 286 x = x - origin;
Chris@1238 287 if (x < 0) x = 0;
Chris@1256 288
Chris@1256 289 double eps = 1e-10;
Chris@198 290
Chris@1394 291 if (m_binScale == LinearBins) {
Chris@1394 292 p = pmin + eps + (x * (pmax - pmin)) / w;
Chris@1394 293 } else {
Chris@198 294
Chris@1394 295 if (m_binScale == InvertedLogBins) {
Chris@1394 296 x = w - x;
Chris@1394 297 }
Chris@1394 298
Chris@1394 299 // See comments in getXForScalePoint
Chris@1394 300
Chris@1394 301 const double origin = 0.8;
Chris@1394 302 double reqdshift = 0.0;
Chris@1394 303 if (pmin < 0) reqdshift = -pmin;
Chris@1394 304
Chris@1394 305 double pminlog = log10(pmin + reqdshift + origin);
Chris@1394 306 double pmaxlog = log10(pmax + reqdshift + origin);
Chris@1394 307
Chris@1394 308 double plog = pminlog + eps + (x * (pmaxlog - pminlog)) / w;
Chris@1394 309 p = pow(10.0, plog) - reqdshift - origin;
Chris@1394 310
Chris@1394 311 if (m_binScale == InvertedLogBins) {
Chris@1394 312 p = pmax - p;
Chris@1394 313 }
Chris@198 314 }
Chris@198 315
Chris@1394 316 return p;
Chris@198 317 }
Chris@198 318
Chris@906 319 double
Chris@1238 320 SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const
Chris@274 321 {
Chris@906 322 norm = 0.0;
Chris@274 323
Chris@1238 324 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return 0;
Chris@274 325
Chris@274 326 value *= m_gain;
Chris@274 327
Chris@1238 328 int yorigin = m_yorigins[v->getId()];
Chris@1238 329 int h = m_heights[v->getId()];
Chris@906 330 double thresh = getThresholdDb();
Chris@274 331
Chris@906 332 double y = 0.0;
Chris@274 333
Chris@274 334 if (h <= 0) return y;
Chris@274 335
Chris@274 336 switch (m_energyScale) {
Chris@274 337
Chris@274 338 case dBScale:
Chris@274 339 {
Chris@906 340 double db = thresh;
Chris@906 341 if (value > 0.0) db = 10.0 * log10(fabs(value));
Chris@274 342 if (db < thresh) db = thresh;
Chris@274 343 norm = (db - thresh) / -thresh;
Chris@906 344 y = yorigin - (double(h) * norm);
Chris@274 345 break;
Chris@274 346 }
Chris@274 347
Chris@274 348 case MeterScale:
Chris@274 349 y = AudioLevel::multiplier_to_preview(value, h);
Chris@906 350 norm = double(y) / double(h);
Chris@274 351 y = yorigin - y;
Chris@274 352 break;
Chris@274 353
Chris@538 354 case AbsoluteScale:
Chris@906 355 value = fabs(value);
Chris@1264 356 #if (__GNUC__ >= 7)
Chris@1263 357 __attribute__ ((fallthrough));
Chris@1263 358 #endif
Chris@538 359
Chris@805 360 case LinearScale:
Chris@274 361 default:
Chris@538 362 norm = (value - m_threshold);
Chris@284 363 if (norm < 0) norm = 0;
Chris@906 364 y = yorigin - (double(h) * norm);
Chris@274 365 break;
Chris@274 366 }
Chris@274 367
Chris@274 368 return y;
Chris@274 369 }
Chris@274 370
Chris@906 371 double
Chris@1238 372 SliceLayer::getValueForY(const LayerGeometryProvider *v, double y) const
Chris@274 373 {
Chris@906 374 double value = 0.0;
Chris@274 375
Chris@1238 376 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return value;
Chris@274 377
Chris@1238 378 int yorigin = m_yorigins[v->getId()];
Chris@1238 379 int h = m_heights[v->getId()];
Chris@906 380 double thresh = getThresholdDb();
Chris@274 381
Chris@274 382 if (h <= 0) return value;
Chris@274 383
Chris@274 384 y = yorigin - y;
Chris@274 385
Chris@274 386 switch (m_energyScale) {
Chris@274 387
Chris@274 388 case dBScale:
Chris@274 389 {
Chris@906 390 double db = ((y / h) * -thresh) + thresh;
Chris@906 391 value = pow(10.0, db/10.0);
Chris@274 392 break;
Chris@274 393 }
Chris@274 394
Chris@274 395 case MeterScale:
Chris@906 396 value = AudioLevel::preview_to_multiplier(int(lrint(y)), h);
Chris@274 397 break;
Chris@805 398
Chris@805 399 case LinearScale:
Chris@805 400 case AbsoluteScale:
Chris@274 401 default:
Chris@284 402 value = y / h + m_threshold;
Chris@274 403 }
Chris@274 404
Chris@274 405 return value / m_gain;
Chris@274 406 }
Chris@274 407
Chris@133 408 void
Chris@916 409 SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@133 410 {
Chris@1383 411 if (!m_sliceableModel ||
Chris@1383 412 !m_sliceableModel->isOK() ||
Chris@254 413 !m_sliceableModel->isReady()) return;
Chris@133 414
Chris@1383 415 Profiler profiler("SliceLayer::paint()");
Chris@1383 416
Chris@195 417 paint.save();
Chris@1383 418 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@538 419 paint.setBrush(Qt::NoBrush);
Chris@195 420
Chris@195 421 if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) {
Chris@195 422 if (!m_scalePoints.empty()) {
Chris@195 423 paint.setPen(QColor(240, 240, 240)); //!!! and dark background?
Chris@1284 424 int ratio = int(round(double(v->getPaintHeight()) /
Chris@1284 425 m_scalePaintHeight));
Chris@805 426 for (int i = 0; i < (int)m_scalePoints.size(); ++i) {
Chris@1284 427 paint.drawLine(0, m_scalePoints[i] * ratio,
Chris@1284 428 rect.width(), m_scalePoints[i] * ratio);
Chris@195 429 }
Chris@195 430 }
Chris@195 431 }
Chris@195 432
Chris@1387 433 int mh = m_sliceableModel->getHeight();
Chris@1389 434 int bin0 = 0;
Chris@1389 435 if (m_maxbin > m_minbin) {
Chris@1389 436 mh = m_maxbin - m_minbin;
Chris@1389 437 bin0 = m_minbin;
Chris@1389 438 }
Chris@1387 439
Chris@1233 440 if (m_plotStyle == PlotBlocks) {
Chris@1233 441 // Must use actual zero-width pen, too slow otherwise
Chris@1233 442 paint.setPen(QPen(getBaseQColor(), 0));
Chris@1233 443 } else {
Chris@1389 444 // Similarly, if there are very many bins here, we use a
Chris@1389 445 // thinner pen
Chris@1389 446 QPen pen(getBaseQColor(), 1);
Chris@1389 447 if (mh < 10000) {
Chris@1389 448 pen = PaintAssistant::scalePen(pen);
Chris@1387 449 }
Chris@1389 450 paint.setPen(pen);
Chris@1233 451 }
Chris@195 452
Chris@607 453 int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
Chris@1238 454 m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription
Chris@133 455
Chris@1383 456 int yorigin = v->getPaintHeight() - getHorizontalScaleHeight(v, paint) -
Chris@1383 457 paint.fontMetrics().height();
Chris@195 458 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@133 459
Chris@1238 460 m_yorigins[v->getId()] = yorigin; // for getYForValue etc
Chris@1238 461 m_heights[v->getId()] = h;
Chris@274 462
Chris@274 463 if (h <= 0) return;
Chris@274 464
Chris@133 465 QPainterPath path;
Chris@1238 466
Chris@193 467 int divisor = 0;
Chris@193 468
Chris@198 469 m_values.clear();
Chris@805 470 for (int bin = 0; bin < mh; ++bin) {
Chris@906 471 m_values.push_back(0.0);
Chris@193 472 }
Chris@193 473
Chris@906 474 sv_frame_t f0 = v->getCentreFrame();
Chris@193 475 int f0x = v->getXForFrame(f0);
Chris@195 476 f0 = v->getFrameForX(f0x);
Chris@906 477 sv_frame_t f1 = v->getFrameForX(f0x + 1);
Chris@195 478 if (f1 > f0) --f1;
Chris@193 479
Chris@682 480 // cerr << "centre frame " << v->getCentreFrame() << ", x " << f0x << ", f0 " << f0 << ", f1 " << f1 << endl;
Chris@274 481
Chris@805 482 int res = m_sliceableModel->getResolution();
Chris@906 483 int col0 = int(f0 / res);
Chris@805 484 int col1 = col0;
Chris@906 485 if (m_samplingMode != NearestSample) col1 = int(f1 / res);
Chris@274 486 f0 = col0 * res;
Chris@274 487 f1 = (col1 + 1) * res - 1;
Chris@274 488
Chris@682 489 // cerr << "resolution " << res << ", col0 " << col0 << ", col1 " << col1 << ", f0 " << f0 << ", f1 " << f1 << endl;
Chris@1383 490 // cerr << "mh = " << mh << endl;
Chris@193 491
Chris@198 492 m_currentf0 = f0;
Chris@198 493 m_currentf1 = f1;
Chris@198 494
Chris@254 495 BiasCurve curve;
Chris@254 496 getBiasCurve(curve);
Chris@906 497 int cs = int(curve.size());
Chris@254 498
Chris@805 499 for (int col = col0; col <= col1; ++col) {
Chris@1383 500 DenseThreeDimensionalModel::Column column =
Chris@1383 501 m_sliceableModel->getColumn(col);
Chris@805 502 for (int bin = 0; bin < mh; ++bin) {
Chris@1383 503 float value = column[bin0 + bin];
Chris@254 504 if (bin < cs) value *= curve[bin];
Chris@193 505 if (m_samplingMode == SamplePeak) {
Chris@198 506 if (value > m_values[bin]) m_values[bin] = value;
Chris@193 507 } else {
Chris@198 508 m_values[bin] += value;
Chris@193 509 }
Chris@153 510 }
Chris@193 511 ++divisor;
Chris@193 512 }
Chris@193 513
Chris@906 514 float max = 0.0;
Chris@805 515 for (int bin = 0; bin < mh; ++bin) {
Chris@847 516 if (m_samplingMode == SampleMean && divisor > 0) {
Chris@906 517 m_values[bin] /= float(divisor);
Chris@847 518 }
Chris@198 519 if (m_values[bin] > max) max = m_values[bin];
Chris@193 520 }
Chris@906 521 if (max != 0.0 && m_normalize) {
Chris@805 522 for (int bin = 0; bin < mh; ++bin) {
Chris@198 523 m_values[bin] /= max;
Chris@193 524 }
Chris@193 525 }
Chris@193 526
Chris@1238 527 double nx = getXForBin(v, bin0);
Chris@193 528
Chris@1362 529 ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1);
Chris@197 530
Chris@1383 531 double ytop = 0, ybottom = 0;
Chris@1383 532 bool firstBinOfPixel = true;
Chris@1384 533
Chris@1384 534 QColor prevColour = v->getBackground();
Chris@1384 535 double prevPx = 0;
Chris@1384 536 double prevYtop = 0;
Chris@1383 537
Chris@805 538 for (int bin = 0; bin < mh; ++bin) {
Chris@193 539
Chris@906 540 double x = nx;
Chris@1238 541 nx = getXForBin(v, bin + bin0 + 1);
Chris@193 542
Chris@906 543 double value = m_values[bin];
Chris@906 544 double norm = 0.0;
Chris@1238 545 double y = getYForValue(v, value, norm);
Chris@133 546
Chris@1383 547 if (y < ytop || firstBinOfPixel) {
Chris@1383 548 ytop = y;
Chris@1383 549 }
Chris@1383 550 if (y > ybottom || firstBinOfPixel) {
Chris@1383 551 ybottom = y;
Chris@1383 552 }
Chris@193 553
Chris@1383 554 if (int(nx) != int(x) || bin+1 == mh) {
Chris@1383 555
Chris@1383 556 if (m_plotStyle == PlotLines) {
Chris@1383 557
Chris@1384 558 double px = (x + nx) / 2;
Chris@1383 559
Chris@1383 560 if (bin == 0) {
Chris@1383 561 path.moveTo(px, y);
Chris@1383 562 } else {
Chris@1383 563 if (ytop != ybottom) {
Chris@1383 564 path.lineTo(px, ybottom);
Chris@1383 565 path.lineTo(px, ytop);
Chris@1383 566 path.moveTo(px, ybottom);
Chris@1383 567 } else {
Chris@1383 568 path.lineTo(px, ytop);
Chris@1383 569 }
Chris@1383 570 }
Chris@1383 571
Chris@1383 572 } else if (m_plotStyle == PlotSteps) {
Chris@1383 573
Chris@1383 574 if (bin == 0) {
Chris@1383 575 path.moveTo(x, y);
Chris@1383 576 } else {
Chris@1383 577 path.lineTo(x, ytop);
Chris@1383 578 }
Chris@1383 579 path.lineTo(nx, ytop);
Chris@1383 580
Chris@1383 581 } else if (m_plotStyle == PlotBlocks) {
Chris@1383 582
Chris@1384 583 // work in pixel coords here, as we don't want the
Chris@1384 584 // vertical edges to be antialiased
Chris@1384 585
Chris@1384 586 path.moveTo(QPoint(int(x), int(yorigin)));
Chris@1384 587 path.lineTo(QPoint(int(x), int(ytop)));
Chris@1384 588 path.lineTo(QPoint(int(nx), int(ytop)));
Chris@1384 589 path.lineTo(QPoint(int(nx), int(yorigin)));
Chris@1384 590 path.lineTo(QPoint(int(x), int(yorigin)));
Chris@1383 591
Chris@1383 592 } else if (m_plotStyle == PlotFilledBlocks) {
Chris@1383 593
Chris@1384 594 QColor c = mapper.map(norm);
Chris@1384 595 paint.setPen(Qt::NoPen);
Chris@1384 596
Chris@1384 597 // work in pixel coords here, as we don't want the
Chris@1384 598 // vertical edges to be antialiased
Chris@1384 599
Chris@1384 600 if (nx > x + 1) {
Chris@1384 601
Chris@1384 602 double px = (x + nx) / 2;
Chris@1384 603
Chris@1384 604 QVector<QPoint> pp;
Chris@1384 605
Chris@1384 606 if (bin > 0) {
Chris@1384 607 paint.setBrush(prevColour);
Chris@1384 608 pp.clear();
Chris@1384 609 pp << QPoint(int(prevPx), int(yorigin));
Chris@1384 610 pp << QPoint(int(prevPx), int(prevYtop));
Chris@1384 611 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 612 int((ytop + prevYtop) / 2));
Chris@1384 613 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 614 int(yorigin));
Chris@1384 615 paint.drawConvexPolygon(QPolygon(pp));
Chris@1384 616
Chris@1384 617 paint.setBrush(c);
Chris@1384 618 pp.clear();
Chris@1384 619 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 620 int(yorigin));
Chris@1384 621 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 622 int((ytop + prevYtop) / 2));
Chris@1384 623 pp << QPoint(int(px), int(ytop));
Chris@1384 624 pp << QPoint(int(px), int(yorigin));
Chris@1384 625 paint.drawConvexPolygon(QPolygon(pp));
Chris@1384 626 }
Chris@1384 627
Chris@1384 628 prevPx = px;
Chris@1384 629 prevColour = c;
Chris@1384 630 prevYtop = ytop;
Chris@1384 631
Chris@1384 632 } else {
Chris@1384 633
Chris@1384 634 paint.fillRect(QRect(int(x), int(ytop),
Chris@1384 635 int(nx) - int(x),
Chris@1384 636 int(yorigin) - int(ytop)),
Chris@1384 637 c);
Chris@1384 638 }
Chris@193 639 }
Chris@193 640
Chris@1383 641 firstBinOfPixel = true;
Chris@193 642
Chris@1383 643 } else {
Chris@1383 644 firstBinOfPixel = false;
Chris@133 645 }
Chris@133 646 }
Chris@133 647
Chris@197 648 if (m_plotStyle != PlotFilledBlocks) {
Chris@197 649 paint.drawPath(path);
Chris@197 650 }
Chris@133 651 paint.restore();
Chris@195 652 }
Chris@195 653
Chris@195 654 int
Chris@918 655 SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@195 656 {
Chris@1238 657 int width;
Chris@538 658 if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) {
Chris@1266 659 width = std::max(paint.fontMetrics().width("0.0") + 13,
Chris@1238 660 paint.fontMetrics().width("x10-10"));
Chris@195 661 } else {
Chris@1238 662 width = std::max(paint.fontMetrics().width(tr("0dB")),
Chris@1238 663 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@195 664 }
Chris@1238 665 return width;
Chris@195 666 }
Chris@195 667
Chris@195 668 void
Chris@918 669 SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@195 670 {
Chris@906 671 double thresh = m_threshold;
Chris@538 672 if (m_energyScale != LinearScale && m_energyScale != AbsoluteScale) {
Chris@284 673 thresh = AudioLevel::dB_to_multiplier(getThresholdDb());
Chris@195 674 }
Chris@195 675
Chris@195 676 // int h = (rect.height() * 3) / 4;
Chris@195 677 // int y = (rect.height() / 2) - (h / 2);
Chris@195 678
Chris@1383 679 int yorigin = v->getPaintHeight() - getHorizontalScaleHeight(v, paint) -
Chris@1383 680 paint.fontMetrics().height();
Chris@195 681 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@195 682 if (h < 0) return;
Chris@195 683
Chris@195 684 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h);
Chris@195 685
Chris@220 686 int mult = 1;
Chris@220 687
Chris@195 688 PaintAssistant::paintVerticalLevelScale
Chris@195 689 (paint, actual, thresh, 1.0 / m_gain,
Chris@195 690 PaintAssistant::Scale(m_energyScale),
Chris@220 691 mult,
Chris@195 692 const_cast<std::vector<int> *>(&m_scalePoints));
Chris@220 693
Chris@1284 694 // Ugly hack (but then everything about this scale drawing is a
Chris@1284 695 // bit ugly). In pixel-doubling hi-dpi scenarios, the scale is
Chris@1284 696 // painted at pixel-doubled resolution but we do explicit
Chris@1284 697 // pixel-doubling ourselves when painting the layer content. We
Chris@1284 698 // make a note of this here so that we can compare with the
Chris@1284 699 // equivalent dimension in the paint method when deciding where to
Chris@1284 700 // place scale continuation lines.
Chris@1284 701 m_scalePaintHeight = v->getPaintHeight();
Chris@1284 702
Chris@220 703 if (mult != 1 && mult != 0) {
Chris@906 704 int log = int(lrint(log10(mult)));
Chris@220 705 QString a = tr("x10");
Chris@220 706 QString b = QString("%1").arg(-log);
Chris@220 707 paint.drawText(3, 8 + paint.fontMetrics().ascent(), a);
Chris@220 708 paint.drawText(3 + paint.fontMetrics().width(a),
Chris@220 709 3 + paint.fontMetrics().ascent(), b);
Chris@220 710 }
Chris@133 711 }
Chris@133 712
Chris@1281 713 bool
Chris@1281 714 SliceLayer::hasLightBackground() const
Chris@1281 715 {
Chris@1281 716 if (usesSolidColour()) {
Chris@1362 717 ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1);
Chris@1281 718 return mapper.hasLightBackground();
Chris@1281 719 } else {
Chris@1281 720 return SingleColourLayer::hasLightBackground();
Chris@1281 721 }
Chris@1281 722 }
Chris@1281 723
Chris@153 724 Layer::PropertyList
Chris@193 725 SliceLayer::getProperties() const
Chris@153 726 {
Chris@287 727 PropertyList list = SingleColourLayer::getProperties();
Chris@538 728 list.push_back("Bin Scale");
Chris@193 729 list.push_back("Plot Type");
Chris@153 730 list.push_back("Scale");
Chris@153 731 list.push_back("Normalize");
Chris@284 732 list.push_back("Threshold");
Chris@153 733 list.push_back("Gain");
Chris@153 734
Chris@153 735 return list;
Chris@153 736 }
Chris@153 737
Chris@153 738 QString
Chris@193 739 SliceLayer::getPropertyLabel(const PropertyName &name) const
Chris@153 740 {
Chris@193 741 if (name == "Plot Type") return tr("Plot Type");
Chris@290 742 if (name == "Scale") return tr("Scale");
Chris@153 743 if (name == "Normalize") return tr("Normalize");
Chris@284 744 if (name == "Threshold") return tr("Threshold");
Chris@153 745 if (name == "Gain") return tr("Gain");
Chris@193 746 if (name == "Sampling Mode") return tr("Sampling Mode");
Chris@538 747 if (name == "Bin Scale") return tr("Bin Scale");
Chris@287 748 return SingleColourLayer::getPropertyLabel(name);
Chris@153 749 }
Chris@153 750
Chris@335 751 QString
Chris@335 752 SliceLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 753 {
Chris@335 754 if (name == "Normalize") return "normalise";
Chris@335 755 return "";
Chris@335 756 }
Chris@335 757
Chris@153 758 Layer::PropertyType
Chris@193 759 SliceLayer::getPropertyType(const PropertyName &name) const
Chris@153 760 {
Chris@153 761 if (name == "Gain") return RangeProperty;
Chris@153 762 if (name == "Normalize") return ToggleProperty;
Chris@284 763 if (name == "Threshold") return RangeProperty;
Chris@287 764 if (name == "Plot Type") return ValueProperty;
Chris@290 765 if (name == "Scale") return ValueProperty;
Chris@287 766 if (name == "Sampling Mode") return ValueProperty;
Chris@287 767 if (name == "Bin Scale") return ValueProperty;
Chris@1281 768 if (name == "Colour" && usesSolidColour()) return ColourMapProperty;
Chris@287 769 return SingleColourLayer::getPropertyType(name);
Chris@153 770 }
Chris@153 771
Chris@153 772 QString
Chris@193 773 SliceLayer::getPropertyGroupName(const PropertyName &name) const
Chris@153 774 {
Chris@153 775 if (name == "Scale" ||
Chris@153 776 name == "Normalize" ||
Chris@193 777 name == "Sampling Mode" ||
Chris@284 778 name == "Threshold" ||
Chris@193 779 name == "Gain") return tr("Scale");
Chris@193 780 if (name == "Plot Type" ||
Chris@538 781 name == "Bin Scale") return tr("Bins");
Chris@287 782 return SingleColourLayer::getPropertyGroupName(name);
Chris@153 783 }
Chris@153 784
Chris@153 785 int
Chris@193 786 SliceLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 787 int *min, int *max, int *deflt) const
Chris@153 788 {
Chris@216 789 int val = 0;
Chris@153 790
Chris@216 791 int garbage0, garbage1, garbage2;
Chris@153 792 if (!min) min = &garbage0;
Chris@153 793 if (!max) max = &garbage1;
Chris@248 794 if (!deflt) deflt = &garbage2;
Chris@153 795
Chris@153 796 if (name == "Gain") {
Chris@153 797
Chris@1266 798 *min = -50;
Chris@1266 799 *max = 50;
Chris@216 800 *deflt = 0;
Chris@153 801
Chris@1238 802 // cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl;
Chris@193 803
Chris@1266 804 val = int(lrint(log10(m_gain) * 20.0));
Chris@1266 805 if (val < *min) val = *min;
Chris@1266 806 if (val > *max) val = *max;
Chris@153 807
Chris@284 808 } else if (name == "Threshold") {
Chris@284 809
Chris@1266 810 *min = -80;
Chris@1266 811 *max = 0;
Chris@284 812
Chris@906 813 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold)));
Chris@1266 814 if (*deflt < *min) *deflt = *min;
Chris@1266 815 if (*deflt > *max) *deflt = *max;
Chris@284 816
Chris@1266 817 val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold)));
Chris@1266 818 if (val < *min) val = *min;
Chris@1266 819 if (val > *max) val = *max;
Chris@284 820
Chris@153 821 } else if (name == "Normalize") {
Chris@1266 822
Chris@1266 823 val = (m_normalize ? 1 : 0);
Chris@216 824 *deflt = 0;
Chris@153 825
Chris@1281 826 } else if (name == "Colour" && usesSolidColour()) {
Chris@197 827
Chris@287 828 *min = 0;
Chris@287 829 *max = ColourMapper::getColourMapCount() - 1;
Chris@1281 830 *deflt = int(ColourMapper::Ice);
Chris@287 831
Chris@287 832 val = m_colourMap;
Chris@153 833
Chris@153 834 } else if (name == "Scale") {
Chris@153 835
Chris@1266 836 *min = 0;
Chris@1266 837 *max = 3;
Chris@216 838 *deflt = (int)dBScale;
Chris@153 839
Chris@1266 840 val = (int)m_energyScale;
Chris@153 841
Chris@193 842 } else if (name == "Sampling Mode") {
Chris@153 843
Chris@1266 844 *min = 0;
Chris@1266 845 *max = 2;
Chris@216 846 *deflt = (int)SampleMean;
Chris@193 847
Chris@1266 848 val = (int)m_samplingMode;
Chris@153 849
Chris@193 850 } else if (name == "Plot Type") {
Chris@193 851
Chris@193 852 *min = 0;
Chris@197 853 *max = 3;
Chris@216 854 *deflt = (int)PlotSteps;
Chris@193 855
Chris@216 856 val = (int)m_plotStyle;
Chris@193 857
Chris@193 858 } else if (name == "Bin Scale") {
Chris@193 859
Chris@193 860 *min = 0;
Chris@198 861 *max = 2;
Chris@216 862 *deflt = (int)LinearBins;
Chris@198 863 // *max = 1; // I don't think we really do want to offer inverted log
Chris@193 864
Chris@216 865 val = (int)m_binScale;
Chris@193 866
Chris@153 867 } else {
Chris@1266 868 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@153 869 }
Chris@153 870
Chris@216 871 return val;
Chris@153 872 }
Chris@153 873
Chris@153 874 QString
Chris@193 875 SliceLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1281 876 int value) const
Chris@153 877 {
Chris@1281 878 if (name == "Colour" && usesSolidColour()) {
Chris@1362 879 return ColourMapper::getColourMapLabel(value);
Chris@153 880 }
Chris@153 881 if (name == "Scale") {
Chris@1266 882 switch (value) {
Chris@1266 883 default:
Chris@1266 884 case 0: return tr("Linear");
Chris@1266 885 case 1: return tr("Meter");
Chris@1266 886 case 2: return tr("Log");
Chris@1266 887 case 3: return tr("Absolute");
Chris@1266 888 }
Chris@153 889 }
Chris@193 890 if (name == "Sampling Mode") {
Chris@1266 891 switch (value) {
Chris@1266 892 default:
Chris@1266 893 case 0: return tr("Any");
Chris@1266 894 case 1: return tr("Mean");
Chris@1266 895 case 2: return tr("Peak");
Chris@1266 896 }
Chris@193 897 }
Chris@193 898 if (name == "Plot Type") {
Chris@1266 899 switch (value) {
Chris@1266 900 default:
Chris@1266 901 case 0: return tr("Lines");
Chris@1266 902 case 1: return tr("Steps");
Chris@1266 903 case 2: return tr("Blocks");
Chris@1266 904 case 3: return tr("Colours");
Chris@1266 905 }
Chris@193 906 }
Chris@193 907 if (name == "Bin Scale") {
Chris@1266 908 switch (value) {
Chris@1266 909 default:
Chris@1266 910 case 0: return tr("Linear");
Chris@1266 911 case 1: return tr("Log");
Chris@1266 912 case 2: return tr("Rev Log");
Chris@1266 913 }
Chris@153 914 }
Chris@287 915 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@153 916 }
Chris@153 917
Chris@167 918 RangeMapper *
Chris@193 919 SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 920 {
Chris@167 921 if (name == "Gain") {
Chris@167 922 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 923 }
Chris@284 924 if (name == "Threshold") {
Chris@284 925 return new LinearRangeMapper(-80, 0, -80, 0, tr("dB"));
Chris@284 926 }
Chris@287 927 return SingleColourLayer::getNewPropertyRangeMapper(name);
Chris@167 928 }
Chris@167 929
Chris@133 930 void
Chris@193 931 SliceLayer::setProperty(const PropertyName &name, int value)
Chris@133 932 {
Chris@153 933 if (name == "Gain") {
Chris@1266 934 setGain(powf(10, float(value)/20.0f));
Chris@284 935 } else if (name == "Threshold") {
Chris@1266 936 if (value == -80) setThreshold(0.0f);
Chris@1266 937 else setThreshold(float(AudioLevel::dB_to_multiplier(value)));
Chris@1281 938 } else if (name == "Colour" && usesSolidColour()) {
Chris@287 939 setFillColourMap(value);
Chris@153 940 } else if (name == "Scale") {
Chris@1266 941 switch (value) {
Chris@1266 942 default:
Chris@1266 943 case 0: setEnergyScale(LinearScale); break;
Chris@1266 944 case 1: setEnergyScale(MeterScale); break;
Chris@1266 945 case 2: setEnergyScale(dBScale); break;
Chris@1266 946 case 3: setEnergyScale(AbsoluteScale); break;
Chris@1266 947 }
Chris@193 948 } else if (name == "Plot Type") {
Chris@1266 949 setPlotStyle(PlotStyle(value));
Chris@193 950 } else if (name == "Sampling Mode") {
Chris@1266 951 switch (value) {
Chris@1266 952 default:
Chris@1266 953 case 0: setSamplingMode(NearestSample); break;
Chris@1266 954 case 1: setSamplingMode(SampleMean); break;
Chris@1266 955 case 2: setSamplingMode(SamplePeak); break;
Chris@1266 956 }
Chris@193 957 } else if (name == "Bin Scale") {
Chris@1266 958 switch (value) {
Chris@1266 959 default:
Chris@1266 960 case 0: setBinScale(LinearBins); break;
Chris@1266 961 case 1: setBinScale(LogBins); break;
Chris@1266 962 case 2: setBinScale(InvertedLogBins); break;
Chris@1266 963 }
Chris@153 964 } else if (name == "Normalize") {
Chris@1266 965 setNormalize(value ? true : false);
Chris@287 966 } else {
Chris@287 967 SingleColourLayer::setProperty(name, value);
Chris@153 968 }
Chris@153 969 }
Chris@153 970
Chris@153 971 void
Chris@197 972 SliceLayer::setFillColourMap(int map)
Chris@197 973 {
Chris@197 974 if (m_colourMap == map) return;
Chris@197 975 m_colourMap = map;
Chris@197 976 emit layerParametersChanged();
Chris@197 977 }
Chris@197 978
Chris@197 979 void
Chris@193 980 SliceLayer::setEnergyScale(EnergyScale scale)
Chris@153 981 {
Chris@153 982 if (m_energyScale == scale) return;
Chris@153 983 m_energyScale = scale;
Chris@153 984 emit layerParametersChanged();
Chris@153 985 }
Chris@153 986
Chris@153 987 void
Chris@193 988 SliceLayer::setSamplingMode(SamplingMode mode)
Chris@153 989 {
Chris@193 990 if (m_samplingMode == mode) return;
Chris@193 991 m_samplingMode = mode;
Chris@153 992 emit layerParametersChanged();
Chris@153 993 }
Chris@153 994
Chris@153 995 void
Chris@193 996 SliceLayer::setPlotStyle(PlotStyle style)
Chris@153 997 {
Chris@193 998 if (m_plotStyle == style) return;
Chris@197 999 bool colourTypeChanged = (style == PlotFilledBlocks ||
Chris@197 1000 m_plotStyle == PlotFilledBlocks);
Chris@193 1001 m_plotStyle = style;
Chris@197 1002 if (colourTypeChanged) {
Chris@197 1003 emit layerParameterRangesChanged();
Chris@197 1004 }
Chris@153 1005 emit layerParametersChanged();
Chris@153 1006 }
Chris@153 1007
Chris@153 1008 void
Chris@193 1009 SliceLayer::setBinScale(BinScale scale)
Chris@153 1010 {
Chris@193 1011 if (m_binScale == scale) return;
Chris@193 1012 m_binScale = scale;
Chris@153 1013 emit layerParametersChanged();
Chris@153 1014 }
Chris@153 1015
Chris@153 1016 void
Chris@193 1017 SliceLayer::setNormalize(bool n)
Chris@153 1018 {
Chris@153 1019 if (m_normalize == n) return;
Chris@153 1020 m_normalize = n;
Chris@153 1021 emit layerParametersChanged();
Chris@153 1022 }
Chris@153 1023
Chris@153 1024 void
Chris@284 1025 SliceLayer::setThreshold(float thresh)
Chris@284 1026 {
Chris@284 1027 if (m_threshold == thresh) return;
Chris@284 1028 m_threshold = thresh;
Chris@284 1029 emit layerParametersChanged();
Chris@284 1030 }
Chris@284 1031
Chris@284 1032 void
Chris@193 1033 SliceLayer::setGain(float gain)
Chris@153 1034 {
Chris@153 1035 if (m_gain == gain) return;
Chris@153 1036 m_gain = gain;
Chris@153 1037 emit layerParametersChanged();
Chris@153 1038 }
Chris@153 1039
Chris@284 1040 float
Chris@284 1041 SliceLayer::getThresholdDb() const
Chris@284 1042 {
Chris@284 1043 if (m_threshold == 0.0) return -80.f;
Chris@906 1044 float db = float(AudioLevel::multiplier_to_dB(m_threshold));
Chris@284 1045 return db;
Chris@284 1046 }
Chris@284 1047
Chris@287 1048 int
Chris@287 1049 SliceLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 1050 {
Chris@287 1051 impose = false;
Chris@287 1052 return ColourDatabase::getInstance()->getColourIndex
Chris@287 1053 (QString(darkbg ? "Bright Blue" : "Blue"));
Chris@287 1054 }
Chris@287 1055
Chris@316 1056 void
Chris@316 1057 SliceLayer::toXml(QTextStream &stream,
Chris@316 1058 QString indent, QString extraAttributes) const
Chris@153 1059 {
Chris@153 1060 QString s;
Chris@153 1061
Chris@1362 1062 s += QString("energyScale=\"%1\" "
Chris@1362 1063 "samplingMode=\"%2\" "
Chris@1362 1064 "plotStyle=\"%3\" "
Chris@1362 1065 "binScale=\"%4\" "
Chris@1362 1066 "gain=\"%5\" "
Chris@1362 1067 "threshold=\"%6\" "
Chris@1362 1068 "normalize=\"%7\" %8 ")
Chris@1266 1069 .arg(m_energyScale)
Chris@193 1070 .arg(m_samplingMode)
Chris@598 1071 .arg(m_plotStyle)
Chris@598 1072 .arg(m_binScale)
Chris@153 1073 .arg(m_gain)
Chris@598 1074 .arg(m_threshold)
Chris@1238 1075 .arg(m_normalize ? "true" : "false")
Chris@1238 1076 .arg(QString("minbin=\"%1\" "
Chris@1238 1077 "maxbin=\"%2\"")
Chris@1238 1078 .arg(m_minbin)
Chris@1238 1079 .arg(m_maxbin));
Chris@153 1080
Chris@1362 1081 // New-style colour map attribute, by string id rather than by
Chris@1362 1082 // number
Chris@1362 1083
Chris@1362 1084 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1085 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1086
Chris@1362 1087 // Old-style colour map attribute
Chris@1362 1088
Chris@1362 1089 s += QString("colourScheme=\"%1\" ")
Chris@1362 1090 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1091
Chris@316 1092 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@153 1093 }
Chris@153 1094
Chris@153 1095 void
Chris@193 1096 SliceLayer::setProperties(const QXmlAttributes &attributes)
Chris@153 1097 {
Chris@153 1098 bool ok = false;
Chris@153 1099
Chris@287 1100 SingleColourLayer::setProperties(attributes);
Chris@153 1101
Chris@153 1102 EnergyScale scale = (EnergyScale)
Chris@1266 1103 attributes.value("energyScale").toInt(&ok);
Chris@153 1104 if (ok) setEnergyScale(scale);
Chris@153 1105
Chris@193 1106 SamplingMode mode = (SamplingMode)
Chris@1266 1107 attributes.value("samplingMode").toInt(&ok);
Chris@193 1108 if (ok) setSamplingMode(mode);
Chris@153 1109
Chris@1362 1110 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1111 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1112 if (colourMap >= 0) {
Chris@1362 1113 setFillColourMap(colourMap);
Chris@1362 1114 } else {
Chris@1362 1115 colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@1362 1116 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1117 setFillColourMap(colourMap);
Chris@1362 1118 }
Chris@1362 1119 }
Chris@197 1120
Chris@598 1121 PlotStyle s = (PlotStyle)
Chris@1266 1122 attributes.value("plotStyle").toInt(&ok);
Chris@598 1123 if (ok) setPlotStyle(s);
Chris@598 1124
Chris@598 1125 BinScale b = (BinScale)
Chris@1266 1126 attributes.value("binScale").toInt(&ok);
Chris@598 1127 if (ok) setBinScale(b);
Chris@598 1128
Chris@153 1129 float gain = attributes.value("gain").toFloat(&ok);
Chris@153 1130 if (ok) setGain(gain);
Chris@153 1131
Chris@598 1132 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@598 1133 if (ok) setThreshold(threshold);
Chris@598 1134
Chris@153 1135 bool normalize = (attributes.value("normalize").trimmed() == "true");
Chris@153 1136 setNormalize(normalize);
Chris@1238 1137
Chris@1238 1138 bool alsoOk = false;
Chris@1238 1139
Chris@1238 1140 float min = attributes.value("minbin").toFloat(&ok);
Chris@1238 1141 float max = attributes.value("maxbin").toFloat(&alsoOk);
Chris@1238 1142 if (ok && alsoOk) setDisplayExtents(min, max);
Chris@133 1143 }
Chris@133 1144
Chris@133 1145 bool
Chris@1238 1146 SliceLayer::getValueExtents(double &min, double &max, bool &logarithmic,
Chris@1238 1147 QString &unit) const
Chris@133 1148 {
Chris@1238 1149 if (!m_sliceableModel) return false;
Chris@1238 1150
Chris@1238 1151 min = 0;
Chris@1238 1152 max = double(m_sliceableModel->getHeight());
Chris@1238 1153
Chris@1238 1154 logarithmic = (m_binScale == BinScale::LogBins);
Chris@1238 1155 unit = "";
Chris@1238 1156
Chris@1238 1157 return true;
Chris@133 1158 }
Chris@133 1159
Chris@1238 1160 bool
Chris@1238 1161 SliceLayer::getDisplayExtents(double &min, double &max) const
Chris@1238 1162 {
Chris@1238 1163 if (!m_sliceableModel) return false;
Chris@1238 1164
Chris@1238 1165 double hmax = double(m_sliceableModel->getHeight());
Chris@1238 1166
Chris@1238 1167 min = m_minbin;
Chris@1238 1168 max = m_maxbin;
Chris@1238 1169 if (max <= min) {
Chris@1238 1170 min = 0;
Chris@1238 1171 max = hmax;
Chris@1238 1172 }
Chris@1238 1173 if (min < 0) min = 0;
Chris@1238 1174 if (max > hmax) max = hmax;
Chris@1238 1175
Chris@1238 1176 return true;
Chris@1238 1177 }
Chris@1238 1178
Chris@1238 1179 bool
Chris@1238 1180 SliceLayer::setDisplayExtents(double min, double max)
Chris@1238 1181 {
Chris@1238 1182 if (!m_sliceableModel) return false;
Chris@1238 1183
Chris@1238 1184 m_minbin = int(lrint(min));
Chris@1238 1185 m_maxbin = int(lrint(max));
Chris@1238 1186
Chris@1238 1187 emit layerParametersChanged();
Chris@1238 1188 return true;
Chris@1238 1189 }
Chris@1238 1190
Chris@1238 1191 int
Chris@1238 1192 SliceLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@1238 1193 {
Chris@1238 1194 if (!m_sliceableModel) return 0;
Chris@1238 1195
Chris@1238 1196 defaultStep = 0;
Chris@1238 1197 int h = m_sliceableModel->getHeight();
Chris@1238 1198 return h;
Chris@1238 1199 }
Chris@1238 1200
Chris@1238 1201 int
Chris@1238 1202 SliceLayer::getCurrentVerticalZoomStep() const
Chris@1238 1203 {
Chris@1238 1204 if (!m_sliceableModel) return 0;
Chris@1238 1205
Chris@1238 1206 double min, max;
Chris@1238 1207 getDisplayExtents(min, max);
Chris@1238 1208 return m_sliceableModel->getHeight() - int(lrint(max - min));
Chris@1238 1209 }
Chris@1238 1210
Chris@1238 1211 void
Chris@1238 1212 SliceLayer::setVerticalZoomStep(int step)
Chris@1238 1213 {
Chris@1238 1214 if (!m_sliceableModel) return;
Chris@1238 1215
Chris@1238 1216 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): before: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl;
Chris@1238 1217
Chris@1238 1218 int dist = m_sliceableModel->getHeight() - step;
Chris@1238 1219 if (dist < 1) dist = 1;
Chris@1238 1220 double centre = m_minbin + (m_maxbin - m_minbin) / 2.0;
Chris@1238 1221 m_minbin = int(lrint(centre - dist/2.0));
Chris@1238 1222 if (m_minbin < 0) m_minbin = 0;
Chris@1238 1223 m_maxbin = m_minbin + dist;
Chris@1238 1224 if (m_maxbin > m_sliceableModel->getHeight()) m_maxbin = m_sliceableModel->getHeight();
Chris@1238 1225
Chris@1238 1226 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): after: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl;
Chris@1238 1227
Chris@1238 1228 emit layerParametersChanged();
Chris@1238 1229 }
Chris@1238 1230
Chris@1238 1231 RangeMapper *
Chris@1238 1232 SliceLayer::getNewVerticalZoomRangeMapper() const
Chris@1238 1233 {
Chris@1238 1234 if (!m_sliceableModel) return 0;
Chris@1238 1235
Chris@1238 1236 return new LinearRangeMapper(0, m_sliceableModel->getHeight(),
Chris@1238 1237 0, m_sliceableModel->getHeight(), "");
Chris@1238 1238 }
Chris@1389 1239
Chris@1389 1240 void
Chris@1389 1241 SliceLayer::zoomToRegion(const LayerGeometryProvider *v, QRect rect)
Chris@1389 1242 {
Chris@1389 1243 double bin0 = getBinForX(v, rect.x());
Chris@1389 1244 double bin1 = getBinForX(v, rect.x() + rect.width());
Chris@1389 1245
Chris@1389 1246 // ignore y for now...
Chris@1389 1247
Chris@1389 1248 SVDEBUG << "SliceLayer::zoomToRegion: zooming to bin range "
Chris@1389 1249 << bin0 << " -> " << bin1 << endl;
Chris@1389 1250
Chris@1389 1251 setDisplayExtents(floor(bin0), ceil(bin1));
Chris@1389 1252 }
Chris@1389 1253