annotate layer/SliceLayer.cpp @ 1399:ba1f0234efa7

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