annotate layer/SliceLayer.cpp @ 1204:d421df27e184 3.0-integration

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