annotate layer/SliceLayer.cpp @ 349:369a197737c7

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