annotate layer/SliceLayer.cpp @ 299:5c59c433b358

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