annotate layer/SliceLayer.cpp @ 607:5b72899d692b

Give a dedicated key to toggling the centre line, and move it out of the overlay level setting -- reducing number of overlay levels to 3. Introduce two distinct vertical scale types (so that we can hide the spectrogram colour scale part easily)
author Chris Cannam
date Mon, 30 Jan 2012 16:01:59 +0000
parents 3803f6dcf361
children 1a0dfcbffaf1
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@193 60 std::cerr << "WARNING: SliceLayer::setSliceableModel(" << model
Chris@193 61 << "): model is not a DenseThreeDimensionalModel" << std::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@198 95 SliceLayer::getFeatureDescription(View *v, QPoint &p) const
Chris@198 96 {
Chris@199 97 int minbin, maxbin, range;
Chris@199 98 return getFeatureDescription(v, p, true, minbin, maxbin, range);
Chris@199 99 }
Chris@199 100
Chris@199 101 QString
Chris@199 102 SliceLayer::getFeatureDescription(View *v, QPoint &p,
Chris@199 103 bool includeBinDescription,
Chris@199 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@198 111 int w = v->width() - 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@198 122 int sampleRate = m_sliceableModel->getSampleRate();
Chris@198 123
Chris@198 124 size_t f0 = m_currentf0;
Chris@198 125 size_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@199 130 range = 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@199 136 float minvalue = 0.f;
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@198 182 float
Chris@198 183 SliceLayer::getXForBin(int bin, int count, float w) const
Chris@198 184 {
Chris@198 185 float x = 0;
Chris@198 186
Chris@198 187 switch (m_binScale) {
Chris@198 188
Chris@198 189 case LinearBins:
Chris@198 190 x = (float(w) * bin) / count;
Chris@198 191 break;
Chris@198 192
Chris@198 193 case LogBins:
Chris@198 194 x = (float(w) * log10f(bin + 1)) / log10f(count + 1);
Chris@198 195 break;
Chris@198 196
Chris@198 197 case InvertedLogBins:
Chris@198 198 x = w - (float(w) * log10f(count - bin - 1)) / log10f(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@198 206 SliceLayer::getBinForX(float x, int count, float 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@198 217 bin = int(powf(10.f, (x * log10f(count + 1)) / w) - 1 + 0.0001);
Chris@198 218 break;
Chris@198 219
Chris@198 220 case InvertedLogBins:
Chris@198 221 bin = count + 1 - int(powf(10.f, (log10f(count) * (w - x)) / float(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@274 228 float
Chris@274 229 SliceLayer::getYForValue(float value, const View *v, float &norm) const
Chris@274 230 {
Chris@274 231 norm = 0.f;
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@284 239 float thresh = getThresholdDb();
Chris@274 240
Chris@274 241 float y = 0.f;
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@274 249 float db = thresh;
Chris@538 250 if (value > 0.f) db = 10.f * log10f(fabsf(value));
Chris@274 251 if (db < thresh) db = thresh;
Chris@274 252 norm = (db - thresh) / -thresh;
Chris@274 253 y = yorigin - (float(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@274 259 norm = float(y) / float(h);
Chris@274 260 y = yorigin - y;
Chris@274 261 break;
Chris@274 262
Chris@538 263 case AbsoluteScale:
Chris@538 264 value = fabsf(value);
Chris@538 265 // and fall through
Chris@538 266
Chris@274 267 default:
Chris@538 268 norm = (value - m_threshold);
Chris@284 269 if (norm < 0) norm = 0;
Chris@284 270 y = yorigin - (float(h) * norm);
Chris@274 271 break;
Chris@274 272 }
Chris@274 273
Chris@274 274 return y;
Chris@274 275 }
Chris@274 276
Chris@274 277 float
Chris@274 278 SliceLayer::getValueForY(float y, const View *v) const
Chris@274 279 {
Chris@274 280 float value = 0.f;
Chris@274 281
Chris@274 282 if (m_yorigins.find(v) == m_yorigins.end()) return value;
Chris@274 283
Chris@274 284 int yorigin = m_yorigins[v];
Chris@274 285 int h = m_heights[v];
Chris@284 286 float thresh = getThresholdDb();
Chris@274 287
Chris@274 288 if (h <= 0) return value;
Chris@274 289
Chris@274 290 y = yorigin - y;
Chris@274 291
Chris@274 292 switch (m_energyScale) {
Chris@274 293
Chris@274 294 case dBScale:
Chris@274 295 {
Chris@274 296 float db = ((y / h) * -thresh) + thresh;
Chris@274 297 value = powf(10.f, db/10.f);
Chris@274 298 break;
Chris@274 299 }
Chris@274 300
Chris@274 301 case MeterScale:
Chris@274 302 value = AudioLevel::preview_to_multiplier(lrintf(y), h);
Chris@274 303 break;
Chris@538 304
Chris@274 305 default:
Chris@284 306 value = y / h + m_threshold;
Chris@274 307 }
Chris@274 308
Chris@274 309 return value / m_gain;
Chris@274 310 }
Chris@274 311
Chris@133 312 void
Chris@193 313 SliceLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@133 314 {
Chris@254 315 if (!m_sliceableModel || !m_sliceableModel->isOK() ||
Chris@254 316 !m_sliceableModel->isReady()) return;
Chris@133 317
Chris@195 318 paint.save();
Chris@195 319 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@538 320 paint.setBrush(Qt::NoBrush);
Chris@195 321
Chris@195 322 if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) {
Chris@195 323 if (!m_scalePoints.empty()) {
Chris@195 324 paint.setPen(QColor(240, 240, 240)); //!!! and dark background?
Chris@195 325 for (size_t i = 0; i < m_scalePoints.size(); ++i) {
Chris@195 326 paint.drawLine(0, m_scalePoints[i], rect.width(), m_scalePoints[i]);
Chris@195 327 }
Chris@195 328 }
Chris@195 329 }
Chris@195 330
Chris@287 331 paint.setPen(getBaseQColor());
Chris@195 332
Chris@607 333 int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
Chris@195 334 int w = v->width() - xorigin - 1;
Chris@198 335
Chris@198 336 m_xorigins[v] = xorigin; // for use in getFeatureDescription
Chris@133 337
Chris@195 338 int yorigin = v->height() - 20 - paint.fontMetrics().height() - 7;
Chris@195 339 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@133 340
Chris@274 341 m_yorigins[v] = yorigin; // for getYForValue etc
Chris@274 342 m_heights[v] = h;
Chris@274 343
Chris@274 344 if (h <= 0) return;
Chris@274 345
Chris@133 346 QPainterPath path;
Chris@133 347
Chris@248 348 size_t mh = m_sliceableModel->getHeight();
Chris@133 349
Chris@193 350 int divisor = 0;
Chris@193 351
Chris@198 352 m_values.clear();
Chris@193 353 for (size_t bin = 0; bin < mh; ++bin) {
Chris@198 354 m_values.push_back(0.f);
Chris@193 355 }
Chris@193 356
Chris@193 357 size_t f0 = v->getCentreFrame();
Chris@193 358 int f0x = v->getXForFrame(f0);
Chris@195 359 f0 = v->getFrameForX(f0x);
Chris@193 360 size_t f1 = v->getFrameForX(f0x + 1);
Chris@195 361 if (f1 > f0) --f1;
Chris@193 362
Chris@284 363 // std::cerr << "centre frame " << v->getCentreFrame() << ", x " << f0x << ", f0 " << f0 << ", f1 " << f1 << std::endl;
Chris@274 364
Chris@274 365 size_t res = m_sliceableModel->getResolution();
Chris@274 366 size_t col0 = f0 / res;
Chris@193 367 size_t col1 = col0;
Chris@274 368 if (m_samplingMode != NearestSample) col1 = f1 / res;
Chris@274 369 f0 = col0 * res;
Chris@274 370 f1 = (col1 + 1) * res - 1;
Chris@274 371
Chris@284 372 // std::cerr << "resolution " << res << ", col0 " << col0 << ", col1 " << col1 << ", f0 " << f0 << ", f1 " << f1 << std::endl;
Chris@193 373
Chris@198 374 m_currentf0 = f0;
Chris@198 375 m_currentf1 = f1;
Chris@198 376
Chris@254 377 BiasCurve curve;
Chris@254 378 getBiasCurve(curve);
Chris@254 379 size_t cs = curve.size();
Chris@254 380
Chris@195 381 for (size_t col = col0; col <= col1; ++col) {
Chris@193 382 for (size_t bin = 0; bin < mh; ++bin) {
Chris@193 383 float value = m_sliceableModel->getValueAt(col, bin);
Chris@254 384 if (bin < cs) value *= curve[bin];
Chris@193 385 if (m_samplingMode == SamplePeak) {
Chris@198 386 if (value > m_values[bin]) m_values[bin] = value;
Chris@193 387 } else {
Chris@198 388 m_values[bin] += value;
Chris@193 389 }
Chris@153 390 }
Chris@193 391 ++divisor;
Chris@193 392 }
Chris@193 393
Chris@193 394 float max = 0.f;
Chris@193 395 for (size_t bin = 0; bin < mh; ++bin) {
Chris@198 396 if (m_samplingMode == SampleMean) m_values[bin] /= divisor;
Chris@198 397 if (m_values[bin] > max) max = m_values[bin];
Chris@193 398 }
Chris@193 399 if (max != 0.f && m_normalize) {
Chris@193 400 for (size_t bin = 0; bin < mh; ++bin) {
Chris@198 401 m_values[bin] /= max;
Chris@193 402 }
Chris@193 403 }
Chris@193 404
Chris@193 405 float py = 0;
Chris@193 406 float nx = xorigin;
Chris@193 407
Chris@197 408 ColourMapper mapper(m_colourMap, 0, 1);
Chris@197 409
Chris@193 410 for (size_t bin = 0; bin < mh; ++bin) {
Chris@193 411
Chris@198 412 float x = nx;
Chris@198 413 nx = xorigin + getXForBin(bin + 1, mh, w);
Chris@193 414
Chris@198 415 float value = m_values[bin];
Chris@197 416 float norm = 0.f;
Chris@274 417 float y = getYForValue(value, v, norm);
Chris@133 418
Chris@193 419 if (m_plotStyle == PlotLines) {
Chris@193 420
Chris@193 421 if (bin == 0) {
Chris@193 422 path.moveTo(x, y);
Chris@193 423 } else {
Chris@193 424 path.lineTo(x, y);
Chris@193 425 }
Chris@193 426
Chris@193 427 } else if (m_plotStyle == PlotSteps) {
Chris@193 428
Chris@193 429 if (bin == 0) {
Chris@193 430 path.moveTo(x, y);
Chris@193 431 } else {
Chris@193 432 path.lineTo(x, y);
Chris@193 433 }
Chris@193 434 path.lineTo(nx, y);
Chris@193 435
Chris@193 436 } else if (m_plotStyle == PlotBlocks) {
Chris@193 437
Chris@193 438 path.moveTo(x, yorigin);
Chris@133 439 path.lineTo(x, y);
Chris@193 440 path.lineTo(nx, y);
Chris@193 441 path.lineTo(nx, yorigin);
Chris@193 442 path.lineTo(x, yorigin);
Chris@197 443
Chris@197 444 } else if (m_plotStyle == PlotFilledBlocks) {
Chris@197 445
Chris@197 446 paint.fillRect(QRectF(x, y, nx - x, yorigin - y), mapper.map(norm));
Chris@133 447 }
Chris@193 448
Chris@193 449 py = y;
Chris@133 450 }
Chris@133 451
Chris@197 452 if (m_plotStyle != PlotFilledBlocks) {
Chris@197 453 paint.drawPath(path);
Chris@197 454 }
Chris@133 455 paint.restore();
Chris@198 456 /*
Chris@197 457 QPoint discard;
Chris@197 458
Chris@197 459 if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount() &&
Chris@197 460 v->shouldIlluminateLocalFeatures(this, discard)) {
Chris@195 461
Chris@195 462 int sampleRate = m_sliceableModel->getSampleRate();
Chris@195 463
Chris@195 464 QString startText = QString("%1 / %2")
Chris@195 465 .arg(QString::fromStdString
Chris@195 466 (RealTime::frame2RealTime
Chris@195 467 (f0, sampleRate).toText(true)))
Chris@195 468 .arg(f0);
Chris@195 469
Chris@195 470 QString endText = QString(" %1 / %2")
Chris@195 471 .arg(QString::fromStdString
Chris@195 472 (RealTime::frame2RealTime
Chris@195 473 (f1, sampleRate).toText(true)))
Chris@195 474 .arg(f1);
Chris@195 475
Chris@195 476 QString durationText = QString("(%1 / %2) ")
Chris@195 477 .arg(QString::fromStdString
Chris@195 478 (RealTime::frame2RealTime
Chris@195 479 (f1 - f0 + 1, sampleRate).toText(true)))
Chris@195 480 .arg(f1 - f0 + 1);
Chris@195 481
Chris@195 482 v->drawVisibleText
Chris@195 483 (paint, xorigin + 5,
Chris@195 484 paint.fontMetrics().ascent() + 5,
Chris@195 485 startText, View::OutlinedText);
Chris@195 486
Chris@195 487 v->drawVisibleText
Chris@195 488 (paint, xorigin + 5,
Chris@195 489 paint.fontMetrics().ascent() + paint.fontMetrics().height() + 10,
Chris@195 490 endText, View::OutlinedText);
Chris@195 491
Chris@195 492 v->drawVisibleText
Chris@195 493 (paint, xorigin + 5,
Chris@195 494 paint.fontMetrics().ascent() + 2*paint.fontMetrics().height() + 15,
Chris@195 495 durationText, View::OutlinedText);
Chris@195 496 }
Chris@195 497 */
Chris@195 498 }
Chris@195 499
Chris@195 500 int
Chris@607 501 SliceLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
Chris@195 502 {
Chris@538 503 if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) {
Chris@220 504 return std::max(paint.fontMetrics().width("0.0") + 13,
Chris@220 505 paint.fontMetrics().width("x10-10"));
Chris@195 506 } else {
Chris@195 507 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@195 508 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@195 509 }
Chris@195 510 }
Chris@195 511
Chris@195 512 void
Chris@607 513 SliceLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
Chris@195 514 {
Chris@284 515 float thresh = m_threshold;
Chris@538 516 if (m_energyScale != LinearScale && m_energyScale != AbsoluteScale) {
Chris@284 517 thresh = AudioLevel::dB_to_multiplier(getThresholdDb());
Chris@195 518 }
Chris@195 519
Chris@195 520 // int h = (rect.height() * 3) / 4;
Chris@195 521 // int y = (rect.height() / 2) - (h / 2);
Chris@195 522
Chris@195 523 int yorigin = v->height() - 20 - paint.fontMetrics().height() - 6;
Chris@195 524 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@195 525 if (h < 0) return;
Chris@195 526
Chris@195 527 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h);
Chris@195 528
Chris@220 529 int mult = 1;
Chris@220 530
Chris@195 531 PaintAssistant::paintVerticalLevelScale
Chris@195 532 (paint, actual, thresh, 1.0 / m_gain,
Chris@195 533 PaintAssistant::Scale(m_energyScale),
Chris@220 534 mult,
Chris@195 535 const_cast<std::vector<int> *>(&m_scalePoints));
Chris@220 536
Chris@220 537 if (mult != 1 && mult != 0) {
Chris@220 538 int log = lrintf(log10f(mult));
Chris@220 539 QString a = tr("x10");
Chris@220 540 QString b = QString("%1").arg(-log);
Chris@220 541 paint.drawText(3, 8 + paint.fontMetrics().ascent(), a);
Chris@220 542 paint.drawText(3 + paint.fontMetrics().width(a),
Chris@220 543 3 + paint.fontMetrics().ascent(), b);
Chris@220 544 }
Chris@133 545 }
Chris@133 546
Chris@153 547 Layer::PropertyList
Chris@193 548 SliceLayer::getProperties() const
Chris@153 549 {
Chris@287 550 PropertyList list = SingleColourLayer::getProperties();
Chris@538 551 list.push_back("Bin Scale");
Chris@193 552 list.push_back("Plot Type");
Chris@153 553 list.push_back("Scale");
Chris@153 554 list.push_back("Normalize");
Chris@284 555 list.push_back("Threshold");
Chris@153 556 list.push_back("Gain");
Chris@153 557
Chris@153 558 return list;
Chris@153 559 }
Chris@153 560
Chris@153 561 QString
Chris@193 562 SliceLayer::getPropertyLabel(const PropertyName &name) const
Chris@153 563 {
Chris@193 564 if (name == "Plot Type") return tr("Plot Type");
Chris@290 565 if (name == "Scale") return tr("Scale");
Chris@153 566 if (name == "Normalize") return tr("Normalize");
Chris@284 567 if (name == "Threshold") return tr("Threshold");
Chris@153 568 if (name == "Gain") return tr("Gain");
Chris@193 569 if (name == "Sampling Mode") return tr("Sampling Mode");
Chris@538 570 if (name == "Bin Scale") return tr("Bin Scale");
Chris@287 571 return SingleColourLayer::getPropertyLabel(name);
Chris@153 572 }
Chris@153 573
Chris@335 574 QString
Chris@335 575 SliceLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 576 {
Chris@335 577 if (name == "Normalize") return "normalise";
Chris@335 578 return "";
Chris@335 579 }
Chris@335 580
Chris@153 581 Layer::PropertyType
Chris@193 582 SliceLayer::getPropertyType(const PropertyName &name) const
Chris@153 583 {
Chris@153 584 if (name == "Gain") return RangeProperty;
Chris@153 585 if (name == "Normalize") return ToggleProperty;
Chris@284 586 if (name == "Threshold") return RangeProperty;
Chris@287 587 if (name == "Plot Type") return ValueProperty;
Chris@290 588 if (name == "Scale") return ValueProperty;
Chris@287 589 if (name == "Sampling Mode") return ValueProperty;
Chris@287 590 if (name == "Bin Scale") return ValueProperty;
Chris@287 591 if (name == "Colour" && m_plotStyle == PlotFilledBlocks) return ValueProperty;
Chris@287 592 return SingleColourLayer::getPropertyType(name);
Chris@153 593 }
Chris@153 594
Chris@153 595 QString
Chris@193 596 SliceLayer::getPropertyGroupName(const PropertyName &name) const
Chris@153 597 {
Chris@153 598 if (name == "Scale" ||
Chris@153 599 name == "Normalize" ||
Chris@193 600 name == "Sampling Mode" ||
Chris@284 601 name == "Threshold" ||
Chris@193 602 name == "Gain") return tr("Scale");
Chris@193 603 if (name == "Plot Type" ||
Chris@538 604 name == "Bin Scale") return tr("Bins");
Chris@287 605 return SingleColourLayer::getPropertyGroupName(name);
Chris@153 606 }
Chris@153 607
Chris@153 608 int
Chris@193 609 SliceLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 610 int *min, int *max, int *deflt) const
Chris@153 611 {
Chris@216 612 int val = 0;
Chris@153 613
Chris@216 614 int garbage0, garbage1, garbage2;
Chris@153 615 if (!min) min = &garbage0;
Chris@153 616 if (!max) max = &garbage1;
Chris@248 617 if (!deflt) deflt = &garbage2;
Chris@153 618
Chris@153 619 if (name == "Gain") {
Chris@153 620
Chris@153 621 *min = -50;
Chris@153 622 *max = 50;
Chris@216 623 *deflt = 0;
Chris@153 624
Chris@193 625 std::cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << std::endl;
Chris@193 626
Chris@216 627 val = lrint(log10(m_gain) * 20.0);
Chris@216 628 if (val < *min) val = *min;
Chris@216 629 if (val > *max) val = *max;
Chris@153 630
Chris@284 631 } else if (name == "Threshold") {
Chris@284 632
Chris@284 633 *min = -80;
Chris@284 634 *max = 0;
Chris@284 635
Chris@284 636 *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold));
Chris@284 637 if (*deflt < *min) *deflt = *min;
Chris@284 638 if (*deflt > *max) *deflt = *max;
Chris@284 639
Chris@284 640 val = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
Chris@284 641 if (val < *min) val = *min;
Chris@284 642 if (val > *max) val = *max;
Chris@284 643
Chris@153 644 } else if (name == "Normalize") {
Chris@153 645
Chris@216 646 val = (m_normalize ? 1 : 0);
Chris@216 647 *deflt = 0;
Chris@153 648
Chris@287 649 } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
Chris@197 650
Chris@287 651 *min = 0;
Chris@287 652 *max = ColourMapper::getColourMapCount() - 1;
Chris@287 653 *deflt = 0;
Chris@287 654
Chris@287 655 val = m_colourMap;
Chris@153 656
Chris@153 657 } else if (name == "Scale") {
Chris@153 658
Chris@153 659 *min = 0;
Chris@538 660 *max = 3;
Chris@216 661 *deflt = (int)dBScale;
Chris@153 662
Chris@216 663 val = (int)m_energyScale;
Chris@153 664
Chris@193 665 } else if (name == "Sampling Mode") {
Chris@153 666
Chris@153 667 *min = 0;
Chris@193 668 *max = 2;
Chris@216 669 *deflt = (int)SampleMean;
Chris@193 670
Chris@216 671 val = (int)m_samplingMode;
Chris@153 672
Chris@193 673 } else if (name == "Plot Type") {
Chris@193 674
Chris@193 675 *min = 0;
Chris@197 676 *max = 3;
Chris@216 677 *deflt = (int)PlotSteps;
Chris@193 678
Chris@216 679 val = (int)m_plotStyle;
Chris@193 680
Chris@193 681 } else if (name == "Bin Scale") {
Chris@193 682
Chris@193 683 *min = 0;
Chris@198 684 *max = 2;
Chris@216 685 *deflt = (int)LinearBins;
Chris@198 686 // *max = 1; // I don't think we really do want to offer inverted log
Chris@193 687
Chris@216 688 val = (int)m_binScale;
Chris@193 689
Chris@153 690 } else {
Chris@287 691 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@153 692 }
Chris@153 693
Chris@216 694 return val;
Chris@153 695 }
Chris@153 696
Chris@153 697 QString
Chris@193 698 SliceLayer::getPropertyValueLabel(const PropertyName &name,
Chris@153 699 int value) const
Chris@153 700 {
Chris@287 701 if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
Chris@287 702 return ColourMapper::getColourMapName(value);
Chris@153 703 }
Chris@153 704 if (name == "Scale") {
Chris@153 705 switch (value) {
Chris@153 706 default:
Chris@153 707 case 0: return tr("Linear");
Chris@153 708 case 1: return tr("Meter");
Chris@538 709 case 2: return tr("Log");
Chris@538 710 case 3: return tr("Absolute");
Chris@153 711 }
Chris@153 712 }
Chris@193 713 if (name == "Sampling Mode") {
Chris@153 714 switch (value) {
Chris@153 715 default:
Chris@193 716 case 0: return tr("Any");
Chris@193 717 case 1: return tr("Mean");
Chris@193 718 case 2: return tr("Peak");
Chris@193 719 }
Chris@193 720 }
Chris@193 721 if (name == "Plot Type") {
Chris@193 722 switch (value) {
Chris@193 723 default:
Chris@193 724 case 0: return tr("Lines");
Chris@193 725 case 1: return tr("Steps");
Chris@193 726 case 2: return tr("Blocks");
Chris@197 727 case 3: return tr("Colours");
Chris@193 728 }
Chris@193 729 }
Chris@193 730 if (name == "Bin Scale") {
Chris@193 731 switch (value) {
Chris@193 732 default:
Chris@538 733 case 0: return tr("Linear");
Chris@538 734 case 1: return tr("Log");
Chris@538 735 case 2: return tr("Rev Log");
Chris@153 736 }
Chris@153 737 }
Chris@287 738 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@153 739 }
Chris@153 740
Chris@167 741 RangeMapper *
Chris@193 742 SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 743 {
Chris@167 744 if (name == "Gain") {
Chris@167 745 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 746 }
Chris@284 747 if (name == "Threshold") {
Chris@284 748 return new LinearRangeMapper(-80, 0, -80, 0, tr("dB"));
Chris@284 749 }
Chris@287 750 return SingleColourLayer::getNewPropertyRangeMapper(name);
Chris@167 751 }
Chris@167 752
Chris@133 753 void
Chris@193 754 SliceLayer::setProperty(const PropertyName &name, int value)
Chris@133 755 {
Chris@153 756 if (name == "Gain") {
Chris@153 757 setGain(pow(10, float(value)/20.0));
Chris@284 758 } else if (name == "Threshold") {
Chris@284 759 if (value == -80) setThreshold(0.0);
Chris@284 760 else setThreshold(AudioLevel::dB_to_multiplier(value));
Chris@287 761 } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
Chris@287 762 setFillColourMap(value);
Chris@153 763 } else if (name == "Scale") {
Chris@153 764 switch (value) {
Chris@153 765 default:
Chris@153 766 case 0: setEnergyScale(LinearScale); break;
Chris@153 767 case 1: setEnergyScale(MeterScale); break;
Chris@153 768 case 2: setEnergyScale(dBScale); break;
Chris@538 769 case 3: setEnergyScale(AbsoluteScale); break;
Chris@153 770 }
Chris@193 771 } else if (name == "Plot Type") {
Chris@193 772 setPlotStyle(PlotStyle(value));
Chris@193 773 } else if (name == "Sampling Mode") {
Chris@193 774 switch (value) {
Chris@193 775 default:
Chris@193 776 case 0: setSamplingMode(NearestSample); break;
Chris@193 777 case 1: setSamplingMode(SampleMean); break;
Chris@193 778 case 2: setSamplingMode(SamplePeak); break;
Chris@193 779 }
Chris@193 780 } else if (name == "Bin Scale") {
Chris@193 781 switch (value) {
Chris@193 782 default:
Chris@193 783 case 0: setBinScale(LinearBins); break;
Chris@193 784 case 1: setBinScale(LogBins); break;
Chris@193 785 case 2: setBinScale(InvertedLogBins); break;
Chris@193 786 }
Chris@153 787 } else if (name == "Normalize") {
Chris@153 788 setNormalize(value ? true : false);
Chris@287 789 } else {
Chris@287 790 SingleColourLayer::setProperty(name, value);
Chris@153 791 }
Chris@153 792 }
Chris@153 793
Chris@153 794 void
Chris@197 795 SliceLayer::setFillColourMap(int map)
Chris@197 796 {
Chris@197 797 if (m_colourMap == map) return;
Chris@197 798 m_colourMap = map;
Chris@197 799 emit layerParametersChanged();
Chris@197 800 }
Chris@197 801
Chris@197 802 void
Chris@193 803 SliceLayer::setEnergyScale(EnergyScale scale)
Chris@153 804 {
Chris@153 805 if (m_energyScale == scale) return;
Chris@153 806 m_energyScale = scale;
Chris@153 807 emit layerParametersChanged();
Chris@153 808 }
Chris@153 809
Chris@153 810 void
Chris@193 811 SliceLayer::setSamplingMode(SamplingMode mode)
Chris@153 812 {
Chris@193 813 if (m_samplingMode == mode) return;
Chris@193 814 m_samplingMode = mode;
Chris@153 815 emit layerParametersChanged();
Chris@153 816 }
Chris@153 817
Chris@153 818 void
Chris@193 819 SliceLayer::setPlotStyle(PlotStyle style)
Chris@153 820 {
Chris@193 821 if (m_plotStyle == style) return;
Chris@197 822 bool colourTypeChanged = (style == PlotFilledBlocks ||
Chris@197 823 m_plotStyle == PlotFilledBlocks);
Chris@193 824 m_plotStyle = style;
Chris@197 825 if (colourTypeChanged) {
Chris@197 826 emit layerParameterRangesChanged();
Chris@197 827 }
Chris@153 828 emit layerParametersChanged();
Chris@153 829 }
Chris@153 830
Chris@153 831 void
Chris@193 832 SliceLayer::setBinScale(BinScale scale)
Chris@153 833 {
Chris@193 834 if (m_binScale == scale) return;
Chris@193 835 m_binScale = scale;
Chris@153 836 emit layerParametersChanged();
Chris@153 837 }
Chris@153 838
Chris@153 839 void
Chris@193 840 SliceLayer::setNormalize(bool n)
Chris@153 841 {
Chris@153 842 if (m_normalize == n) return;
Chris@153 843 m_normalize = n;
Chris@153 844 emit layerParametersChanged();
Chris@153 845 }
Chris@153 846
Chris@153 847 void
Chris@284 848 SliceLayer::setThreshold(float thresh)
Chris@284 849 {
Chris@284 850 if (m_threshold == thresh) return;
Chris@284 851 m_threshold = thresh;
Chris@284 852 emit layerParametersChanged();
Chris@284 853 }
Chris@284 854
Chris@284 855 void
Chris@193 856 SliceLayer::setGain(float gain)
Chris@153 857 {
Chris@153 858 if (m_gain == gain) return;
Chris@153 859 m_gain = gain;
Chris@153 860 emit layerParametersChanged();
Chris@153 861 }
Chris@153 862
Chris@284 863 float
Chris@284 864 SliceLayer::getThresholdDb() const
Chris@284 865 {
Chris@284 866 if (m_threshold == 0.0) return -80.f;
Chris@284 867 float db = AudioLevel::multiplier_to_dB(m_threshold);
Chris@284 868 return db;
Chris@284 869 }
Chris@284 870
Chris@287 871 int
Chris@287 872 SliceLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 873 {
Chris@287 874 impose = false;
Chris@287 875 return ColourDatabase::getInstance()->getColourIndex
Chris@287 876 (QString(darkbg ? "Bright Blue" : "Blue"));
Chris@287 877 }
Chris@287 878
Chris@316 879 void
Chris@316 880 SliceLayer::toXml(QTextStream &stream,
Chris@316 881 QString indent, QString extraAttributes) const
Chris@153 882 {
Chris@153 883 QString s;
Chris@153 884
Chris@287 885 s += QString("colourScheme=\"%1\" "
Chris@287 886 "energyScale=\"%2\" "
Chris@287 887 "samplingMode=\"%3\" "
Chris@598 888 "plotStyle=\"%4\" "
Chris@598 889 "binScale=\"%5\" "
Chris@598 890 "gain=\"%6\" "
Chris@598 891 "threshold=\"%7\" "
Chris@598 892 "normalize=\"%8\"")
Chris@197 893 .arg(m_colourMap)
Chris@153 894 .arg(m_energyScale)
Chris@193 895 .arg(m_samplingMode)
Chris@598 896 .arg(m_plotStyle)
Chris@598 897 .arg(m_binScale)
Chris@153 898 .arg(m_gain)
Chris@598 899 .arg(m_threshold)
Chris@153 900 .arg(m_normalize ? "true" : "false");
Chris@153 901
Chris@316 902 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@153 903 }
Chris@153 904
Chris@153 905 void
Chris@193 906 SliceLayer::setProperties(const QXmlAttributes &attributes)
Chris@153 907 {
Chris@153 908 bool ok = false;
Chris@153 909
Chris@287 910 SingleColourLayer::setProperties(attributes);
Chris@153 911
Chris@153 912 EnergyScale scale = (EnergyScale)
Chris@153 913 attributes.value("energyScale").toInt(&ok);
Chris@153 914 if (ok) setEnergyScale(scale);
Chris@153 915
Chris@193 916 SamplingMode mode = (SamplingMode)
Chris@193 917 attributes.value("samplingMode").toInt(&ok);
Chris@193 918 if (ok) setSamplingMode(mode);
Chris@153 919
Chris@197 920 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 921 if (ok) setFillColourMap(colourMap);
Chris@197 922
Chris@598 923 PlotStyle s = (PlotStyle)
Chris@598 924 attributes.value("plotStyle").toInt(&ok);
Chris@598 925 if (ok) setPlotStyle(s);
Chris@598 926
Chris@598 927 BinScale b = (BinScale)
Chris@598 928 attributes.value("binScale").toInt(&ok);
Chris@598 929 if (ok) setBinScale(b);
Chris@598 930
Chris@153 931 float gain = attributes.value("gain").toFloat(&ok);
Chris@153 932 if (ok) setGain(gain);
Chris@153 933
Chris@598 934 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@598 935 if (ok) setThreshold(threshold);
Chris@598 936
Chris@153 937 bool normalize = (attributes.value("normalize").trimmed() == "true");
Chris@153 938 setNormalize(normalize);
Chris@133 939 }
Chris@133 940
Chris@133 941 bool
Chris@248 942 SliceLayer::getValueExtents(float &, float &, bool &, QString &) const
Chris@133 943 {
Chris@133 944 return false;
Chris@133 945 }
Chris@133 946