annotate layer/WaveformLayer.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 a9dfa2d6d5ac 0895517bb2d1
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "WaveformLayer.h"
Chris@0 17
Chris@0 18 #include "base/AudioLevel.h"
Chris@128 19 #include "view/View.h"
Chris@0 20 #include "base/Profiler.h"
Chris@167 21 #include "base/RangeMapper.h"
Chris@285 22 #include "base/ColourDatabase.h"
Chris@0 23
Chris@0 24 #include <QPainter>
Chris@0 25 #include <QPixmap>
Chris@316 26 #include <QTextStream>
Chris@0 27
Chris@0 28 #include <iostream>
Chris@0 29 #include <cmath>
Chris@0 30
Chris@4 31 //#define DEBUG_WAVEFORM_PAINT 1
Chris@4 32
Chris@0 33 using std::cerr;
Chris@0 34 using std::endl;
Chris@0 35
Chris@44 36 WaveformLayer::WaveformLayer() :
Chris@287 37 SingleColourLayer(),
Chris@0 38 m_model(0),
Chris@0 39 m_gain(1.0f),
Chris@67 40 m_autoNormalize(false),
Chris@0 41 m_showMeans(true),
Chris@0 42 m_greyscale(true),
Chris@0 43 m_channelMode(SeparateChannels),
Chris@0 44 m_channel(-1),
Chris@0 45 m_scale(LinearScale),
Chris@0 46 m_aggressive(false),
Chris@0 47 m_cache(0),
Chris@0 48 m_cacheValid(false)
Chris@0 49 {
Chris@44 50
Chris@0 51 }
Chris@0 52
Chris@0 53 WaveformLayer::~WaveformLayer()
Chris@0 54 {
Chris@0 55 delete m_cache;
Chris@0 56 }
Chris@0 57
Chris@0 58 void
Chris@0 59 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model)
Chris@0 60 {
Chris@69 61 bool channelsChanged = false;
Chris@69 62 if (m_channel == -1) {
Chris@69 63 if (!m_model) {
Chris@69 64 if (model) {
Chris@69 65 channelsChanged = true;
Chris@69 66 }
Chris@69 67 } else {
Chris@69 68 if (model &&
Chris@69 69 m_model->getChannelCount() != model->getChannelCount()) {
Chris@69 70 channelsChanged = true;
Chris@69 71 }
Chris@69 72 }
Chris@69 73 }
Chris@69 74
Chris@0 75 m_model = model;
Chris@0 76 m_cacheValid = false;
Chris@0 77 if (!m_model || !m_model->isOK()) return;
Chris@0 78
Chris@320 79 connectSignals(m_model);
Chris@301 80
Chris@0 81 emit modelReplaced();
Chris@69 82
Chris@69 83 if (channelsChanged) emit layerParametersChanged();
Chris@0 84 }
Chris@0 85
Chris@0 86 Layer::PropertyList
Chris@0 87 WaveformLayer::getProperties() const
Chris@0 88 {
Chris@287 89 PropertyList list = SingleColourLayer::getProperties();
Chris@87 90 list.push_back("Scale");
Chris@87 91 list.push_back("Gain");
Chris@87 92 list.push_back("Normalize Visible Area");
Chris@68 93
Chris@68 94 if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) {
Chris@87 95 list.push_back("Channels");
Chris@68 96 }
Chris@68 97
Chris@0 98 return list;
Chris@0 99 }
Chris@0 100
Chris@87 101 QString
Chris@87 102 WaveformLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 103 {
Chris@87 104 if (name == "Scale") return tr("Scale");
Chris@87 105 if (name == "Gain") return tr("Gain");
Chris@87 106 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 107 if (name == "Channels") return tr("Channels");
Chris@287 108 return SingleColourLayer::getPropertyLabel(name);
Chris@87 109 }
Chris@87 110
Chris@335 111 QString
Chris@335 112 WaveformLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 113 {
Chris@335 114 if (name == "Normalize Visible Area") return "normalise";
Chris@335 115 return "";
Chris@335 116 }
Chris@335 117
Chris@0 118 Layer::PropertyType
Chris@0 119 WaveformLayer::getPropertyType(const PropertyName &name) const
Chris@0 120 {
Chris@87 121 if (name == "Gain") return RangeProperty;
Chris@87 122 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 123 if (name == "Channels") return ValueProperty;
Chris@87 124 if (name == "Scale") return ValueProperty;
Chris@287 125 return SingleColourLayer::getPropertyType(name);
Chris@0 126 }
Chris@0 127
Chris@0 128 QString
Chris@0 129 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 130 {
Chris@87 131 if (name == "Gain" ||
Chris@87 132 name == "Normalize Visible Area" ||
Chris@87 133 name == "Scale") return tr("Scale");
Chris@0 134 return QString();
Chris@0 135 }
Chris@0 136
Chris@0 137 int
Chris@0 138 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 139 int *min, int *max, int *deflt) const
Chris@0 140 {
Chris@216 141 int val = 0;
Chris@0 142
Chris@216 143 int garbage0, garbage1, garbage2;
Chris@56 144 if (!min) min = &garbage0;
Chris@56 145 if (!max) max = &garbage1;
Chris@216 146 if (!deflt) deflt = &garbage2;
Chris@10 147
Chris@87 148 if (name == "Gain") {
Chris@0 149
Chris@0 150 *min = -50;
Chris@0 151 *max = 50;
Chris@216 152 *deflt = 0;
Chris@0 153
Chris@216 154 val = lrint(log10(m_gain) * 20.0);
Chris@216 155 if (val < *min) val = *min;
Chris@216 156 if (val > *max) val = *max;
Chris@0 157
Chris@87 158 } else if (name == "Normalize Visible Area") {
Chris@67 159
Chris@216 160 val = (m_autoNormalize ? 1 : 0);
Chris@216 161 *deflt = 0;
Chris@67 162
Chris@87 163 } else if (name == "Channels") {
Chris@0 164
Chris@67 165 *min = 0;
Chris@67 166 *max = 2;
Chris@216 167 *deflt = 0;
Chris@216 168 if (m_channelMode == MixChannels) val = 1;
Chris@216 169 else if (m_channelMode == MergeChannels) val = 2;
Chris@216 170 else val = 0;
Chris@0 171
Chris@87 172 } else if (name == "Scale") {
Chris@0 173
Chris@0 174 *min = 0;
Chris@0 175 *max = 2;
Chris@216 176 *deflt = 0;
Chris@0 177
Chris@216 178 val = (int)m_scale;
Chris@0 179
Chris@0 180 } else {
Chris@287 181 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 182 }
Chris@0 183
Chris@216 184 return val;
Chris@0 185 }
Chris@0 186
Chris@0 187 QString
Chris@0 188 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 189 int value) const
Chris@0 190 {
Chris@87 191 if (name == "Scale") {
Chris@0 192 switch (value) {
Chris@0 193 default:
Chris@0 194 case 0: return tr("Linear");
Chris@0 195 case 1: return tr("Meter");
Chris@0 196 case 2: return tr("dB");
Chris@0 197 }
Chris@0 198 }
Chris@87 199 if (name == "Channels") {
Chris@67 200 switch (value) {
Chris@67 201 default:
Chris@67 202 case 0: return tr("Separate");
Chris@67 203 case 1: return tr("Mean");
Chris@67 204 case 2: return tr("Butterfly");
Chris@67 205 }
Chris@67 206 }
Chris@287 207 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@0 208 }
Chris@0 209
Chris@167 210 RangeMapper *
Chris@167 211 WaveformLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 212 {
Chris@167 213 if (name == "Gain") {
Chris@167 214 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 215 }
Chris@167 216 return 0;
Chris@167 217 }
Chris@167 218
Chris@0 219 void
Chris@0 220 WaveformLayer::setProperty(const PropertyName &name, int value)
Chris@0 221 {
Chris@87 222 if (name == "Gain") {
Chris@0 223 setGain(pow(10, float(value)/20.0));
Chris@87 224 } else if (name == "Normalize Visible Area") {
Chris@67 225 setAutoNormalize(value ? true : false);
Chris@87 226 } else if (name == "Channels") {
Chris@67 227 if (value == 1) setChannelMode(MixChannels);
Chris@67 228 else if (value == 2) setChannelMode(MergeChannels);
Chris@67 229 else setChannelMode(SeparateChannels);
Chris@87 230 } else if (name == "Scale") {
Chris@0 231 switch (value) {
Chris@0 232 default:
Chris@0 233 case 0: setScale(LinearScale); break;
Chris@0 234 case 1: setScale(MeterScale); break;
Chris@0 235 case 2: setScale(dBScale); break;
Chris@0 236 }
Chris@287 237 } else {
Chris@287 238 SingleColourLayer::setProperty(name, value);
Chris@0 239 }
Chris@0 240 }
Chris@0 241
Chris@0 242 void
Chris@67 243 WaveformLayer::setGain(float gain)
Chris@0 244 {
Chris@0 245 if (m_gain == gain) return;
Chris@0 246 m_gain = gain;
Chris@0 247 m_cacheValid = false;
Chris@0 248 emit layerParametersChanged();
Chris@133 249 emit verticalZoomChanged();
Chris@0 250 }
Chris@0 251
Chris@0 252 void
Chris@67 253 WaveformLayer::setAutoNormalize(bool autoNormalize)
Chris@67 254 {
Chris@67 255 if (m_autoNormalize == autoNormalize) return;
Chris@67 256 m_autoNormalize = autoNormalize;
Chris@67 257 m_cacheValid = false;
Chris@67 258 emit layerParametersChanged();
Chris@67 259 }
Chris@67 260
Chris@67 261 void
Chris@0 262 WaveformLayer::setShowMeans(bool showMeans)
Chris@0 263 {
Chris@0 264 if (m_showMeans == showMeans) return;
Chris@0 265 m_showMeans = showMeans;
Chris@0 266 m_cacheValid = false;
Chris@0 267 emit layerParametersChanged();
Chris@0 268 }
Chris@0 269
Chris@0 270 void
Chris@0 271 WaveformLayer::setUseGreyscale(bool useGreyscale)
Chris@0 272 {
Chris@0 273 if (m_greyscale == useGreyscale) return;
Chris@0 274 m_greyscale = useGreyscale;
Chris@0 275 m_cacheValid = false;
Chris@0 276 emit layerParametersChanged();
Chris@0 277 }
Chris@0 278
Chris@0 279 void
Chris@0 280 WaveformLayer::setChannelMode(ChannelMode channelMode)
Chris@0 281 {
Chris@0 282 if (m_channelMode == channelMode) return;
Chris@0 283 m_channelMode = channelMode;
Chris@0 284 m_cacheValid = false;
Chris@0 285 emit layerParametersChanged();
Chris@0 286 }
Chris@0 287
Chris@0 288 void
Chris@0 289 WaveformLayer::setChannel(int channel)
Chris@0 290 {
Chris@101 291 // std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl;
Chris@0 292
Chris@0 293 if (m_channel == channel) return;
Chris@0 294 m_channel = channel;
Chris@0 295 m_cacheValid = false;
Chris@0 296 emit layerParametersChanged();
Chris@0 297 }
Chris@0 298
Chris@0 299 void
Chris@0 300 WaveformLayer::setScale(Scale scale)
Chris@0 301 {
Chris@0 302 if (m_scale == scale) return;
Chris@0 303 m_scale = scale;
Chris@0 304 m_cacheValid = false;
Chris@0 305 emit layerParametersChanged();
Chris@0 306 }
Chris@0 307
Chris@0 308 void
Chris@0 309 WaveformLayer::setAggressiveCacheing(bool aggressive)
Chris@0 310 {
Chris@0 311 if (m_aggressive == aggressive) return;
Chris@0 312 m_aggressive = aggressive;
Chris@0 313 m_cacheValid = false;
Chris@0 314 emit layerParametersChanged();
Chris@0 315 }
Chris@0 316
Chris@0 317 int
Chris@115 318 WaveformLayer::getCompletion(View *) const
Chris@0 319 {
Chris@0 320 int completion = 100;
Chris@0 321 if (!m_model || !m_model->isOK()) return completion;
Chris@0 322 if (m_model->isReady(&completion)) return 100;
Chris@0 323 return completion;
Chris@0 324 }
Chris@0 325
Chris@79 326 bool
Chris@101 327 WaveformLayer::getValueExtents(float &min, float &max,
Chris@248 328 bool &, QString &unit) const
Chris@79 329 {
Chris@79 330 if (m_scale == LinearScale) {
Chris@79 331 min = 0.0;
Chris@79 332 max = 1.0;
Chris@79 333 unit = "V";
Chris@79 334 } else if (m_scale == MeterScale) {
Chris@79 335 return false; //!!!
Chris@79 336 } else {
Chris@79 337 min = AudioLevel::multiplier_to_dB(0.0);
Chris@79 338 max = AudioLevel::multiplier_to_dB(1.0);
Chris@79 339 unit = "dB";
Chris@79 340 }
Chris@79 341 return true;
Chris@79 342 }
Chris@79 343
Chris@0 344 int
Chris@0 345 WaveformLayer::dBscale(float sample, int m) const
Chris@0 346 {
Chris@67 347 if (sample < 0.0) return dBscale(-sample, m);
Chris@0 348 float dB = AudioLevel::multiplier_to_dB(sample);
Chris@0 349 if (dB < -50.0) return 0;
Chris@0 350 if (dB > 0.0) return m;
Chris@0 351 return int(((dB + 50.0) * m) / 50.0 + 0.1);
Chris@0 352 }
Chris@0 353
Chris@0 354 size_t
Chris@67 355 WaveformLayer::getChannelArrangement(size_t &min, size_t &max,
Chris@67 356 bool &merging, bool &mixing)
Chris@0 357 const
Chris@0 358 {
Chris@0 359 if (!m_model || !m_model->isOK()) return 0;
Chris@0 360
Chris@0 361 size_t channels = m_model->getChannelCount();
Chris@0 362 if (channels == 0) return 0;
Chris@0 363
Chris@0 364 size_t rawChannels = channels;
Chris@0 365
Chris@0 366 if (m_channel == -1) {
Chris@0 367 min = 0;
Chris@67 368 if (m_channelMode == MergeChannels ||
Chris@67 369 m_channelMode == MixChannels) {
Chris@0 370 max = 0;
Chris@0 371 channels = 1;
Chris@0 372 } else {
Chris@0 373 max = channels - 1;
Chris@0 374 }
Chris@0 375 } else {
Chris@0 376 min = m_channel;
Chris@0 377 max = m_channel;
Chris@0 378 rawChannels = 1;
Chris@0 379 channels = 1;
Chris@0 380 }
Chris@0 381
Chris@0 382 merging = (m_channelMode == MergeChannels && rawChannels > 1);
Chris@67 383 mixing = (m_channelMode == MixChannels && rawChannels > 1);
Chris@0 384
Chris@0 385 // std::cerr << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << std::endl;
Chris@0 386
Chris@0 387 return channels;
Chris@0 388 }
Chris@0 389
Chris@67 390 bool
Chris@67 391 WaveformLayer::isLayerScrollable(const View *) const
Chris@67 392 {
Chris@67 393 return !m_autoNormalize;
Chris@67 394 }
Chris@67 395
Chris@68 396 static float meterdbs[] = { -40, -30, -20, -15, -10,
Chris@68 397 -5, -3, -2, -1, -0.5, 0 };
Chris@68 398
Chris@0 399 void
Chris@44 400 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const
Chris@0 401 {
Chris@0 402 if (!m_model || !m_model->isOK()) {
Chris@0 403 return;
Chris@0 404 }
Chris@0 405
Chris@44 406 long startFrame = v->getStartFrame();
Chris@302 407 long endFrame = v->getEndFrame();
Chris@44 408 int zoomLevel = v->getZoomLevel();
Chris@0 409
Chris@2 410 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 411 Profiler profiler("WaveformLayer::paint", true);
Chris@0 412 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 413 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << ", start " << startFrame << std::endl;
Chris@2 414 #endif
Chris@0 415
Chris@0 416 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 417 bool mergingChannels = false, mixingChannels = false;
Chris@0 418
Chris@67 419 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 420 mergingChannels, mixingChannels);
Chris@0 421 if (channels == 0) return;
Chris@0 422
Chris@44 423 int w = v->width();
Chris@44 424 int h = v->height();
Chris@0 425
Chris@0 426 bool ready = m_model->isReady();
Chris@0 427 QPainter *paint;
Chris@0 428
Chris@0 429 if (m_aggressive) {
Chris@0 430
Chris@214 431 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 432 std::cerr << "WaveformLayer::paint: aggressive is true" << std::endl;
Chris@214 433 #endif
Chris@214 434
Chris@0 435 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@0 436 m_cacheValid = false;
Chris@0 437 }
Chris@0 438
Chris@214 439 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@214 440 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 441 if (m_cache) {
Chris@214 442 std::cerr << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << std::endl;
Chris@214 443 }
Chris@214 444 #endif
Chris@214 445 delete m_cache;
Chris@214 446 m_cache = new QPixmap(w, h);
Chris@214 447 m_cacheValid = false;
Chris@214 448 }
Chris@214 449
Chris@0 450 if (m_cacheValid) {
Chris@0 451 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 452 return;
Chris@0 453 }
Chris@0 454
Chris@0 455 paint = new QPainter(m_cache);
Chris@0 456
Chris@0 457 paint->setPen(Qt::NoPen);
Chris@287 458 paint->setBrush(getBackgroundQColor(v));
Chris@0 459 paint->drawRect(rect);
Chris@0 460
Chris@287 461 paint->setPen(getForegroundQColor(v));
Chris@0 462 paint->setBrush(Qt::NoBrush);
Chris@0 463
Chris@0 464 } else {
Chris@0 465 paint = &viewPainter;
Chris@0 466 }
Chris@0 467
Chris@28 468 paint->setRenderHint(QPainter::Antialiasing, false);
Chris@28 469
Chris@0 470 int x0 = 0, x1 = w - 1;
Chris@0 471 int y0 = 0, y1 = h - 1;
Chris@0 472
Chris@0 473 x0 = rect.left();
Chris@0 474 x1 = rect.right();
Chris@0 475 y0 = rect.top();
Chris@0 476 y1 = rect.bottom();
Chris@0 477
Chris@28 478 if (x0 > 0) --x0;
Chris@44 479 if (x1 < v->width()) ++x1;
Chris@28 480
Chris@44 481 long frame0 = v->getFrameForX(x0);
Chris@44 482 long frame1 = v->getFrameForX(x1 + 1);
Chris@302 483
Chris@4 484 #ifdef DEBUG_WAVEFORM_PAINT
Chris@4 485 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl;
Chris@4 486 #endif
Chris@0 487
Chris@200 488 RangeSummarisableTimeValueModel::RangeBlock *ranges =
Chris@200 489 new RangeSummarisableTimeValueModel::RangeBlock;
Chris@200 490
Chris@200 491 RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges = 0;
Chris@0 492 RangeSummarisableTimeValueModel::Range range;
Chris@287 493
Chris@287 494 QColor baseColour = getBaseQColor();
Chris@287 495 std::vector<QColor> greys = getPartialShades(v);
Chris@0 496
Chris@285 497 QColor midColour = baseColour;
Chris@0 498 if (midColour == Qt::black) {
Chris@0 499 midColour = Qt::gray;
Chris@44 500 } else if (v->hasLightBackground()) {
Chris@0 501 midColour = midColour.light(150);
Chris@0 502 } else {
Chris@0 503 midColour = midColour.light(50);
Chris@0 504 }
Chris@0 505
Chris@67 506 while (m_effectiveGains.size() <= maxChannel) {
Chris@67 507 m_effectiveGains.push_back(m_gain);
Chris@67 508 }
Chris@67 509
Chris@302 510 // Although a long for purposes of comparison against the view
Chris@302 511 // start and end frames, these are known to be non-negative
Chris@302 512 long modelStart = long(m_model->getStartFrame());
Chris@302 513 long modelEnd = long(m_model->getEndFrame());
Chris@302 514
Chris@302 515 #ifdef DEBUG_WAVEFORM_PAINT
Chris@302 516 std::cerr << "Model start = " << modelStart << ", end = " << modelEnd << std::endl;
Chris@302 517 #endif
Chris@302 518
Chris@0 519 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 520
Chris@0 521 int prevRangeBottom = -1, prevRangeTop = -1;
Chris@285 522 QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour;
Chris@302 523 size_t rangeStart, rangeEnd;
Chris@0 524
Chris@67 525 m_effectiveGains[ch] = m_gain;
Chris@67 526
Chris@67 527 if (m_autoNormalize) {
Chris@302 528
Chris@302 529 if (startFrame < modelStart) rangeStart = modelStart;
Chris@302 530 else rangeStart = startFrame;
Chris@302 531
Chris@302 532 if (endFrame < 0) rangeEnd = 0;
Chris@302 533 else if (endFrame > modelEnd) rangeEnd = modelEnd;
Chris@302 534 else rangeEnd = endFrame;
Chris@302 535
Chris@302 536 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@302 537
Chris@67 538 RangeSummarisableTimeValueModel::Range range =
Chris@302 539 m_model->getSummary(ch, rangeStart, rangeEnd - rangeStart);
Chris@67 540 if (mergingChannels || mixingChannels) {
Chris@67 541 RangeSummarisableTimeValueModel::Range otherRange =
Chris@302 542 m_model->getSummary(1, rangeStart, rangeEnd - rangeStart);
Chris@67 543 range.max = std::max(range.max, otherRange.max);
Chris@67 544 range.min = std::min(range.min, otherRange.min);
Chris@67 545 range.absmean = std::min(range.absmean, otherRange.absmean);
Chris@67 546 }
Chris@67 547 m_effectiveGains[ch] = 1.0 / std::max(fabsf(range.max),
Chris@67 548 fabsf(range.min));
Chris@67 549 }
Chris@67 550
Chris@67 551 float gain = m_effectiveGains[ch];
Chris@67 552
Chris@68 553 int m = (h / channels) / 2;
Chris@68 554 int my = m + (((ch - minChannel) * h) / channels);
Chris@68 555
Chris@68 556 // std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl;
Chris@68 557
Chris@68 558 if (my - m > y1 || my + m < y0) continue;
Chris@68 559
Chris@68 560 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 561 m_channelMode != MergeChannels) {
Chris@68 562 m = (h / channels);
Chris@68 563 my = m + (((ch - minChannel) * h) / channels);
Chris@68 564 }
Chris@68 565
Chris@295 566 paint->setPen(greys[1]);
Chris@68 567 paint->drawLine(x0, my, x1, my);
Chris@68 568
Chris@68 569 int n = 10;
Chris@68 570 int py = -1;
Chris@68 571
Chris@195 572 if (v->hasLightBackground() &&
Chris@195 573 v->getViewManager() &&
Chris@195 574 v->getViewManager()->shouldShowScaleGuides()) {
Chris@68 575
Chris@68 576 paint->setPen(QColor(240, 240, 240));
Chris@68 577
Chris@68 578 for (int i = 1; i < n; ++i) {
Chris@68 579
Chris@68 580 float val = 0.0, nval = 0.0;
Chris@68 581
Chris@68 582 switch (m_scale) {
Chris@68 583
Chris@68 584 case LinearScale:
Chris@68 585 val = (i * gain) / n;
Chris@68 586 if (i > 0) nval = -val;
Chris@68 587 break;
Chris@68 588
Chris@68 589 case MeterScale:
Chris@68 590 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 591 break;
Chris@68 592
Chris@68 593 case dBScale:
Chris@68 594 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 595 break;
Chris@68 596 }
Chris@68 597
Chris@68 598 if (val < -1.0 || val > 1.0) continue;
Chris@68 599
Chris@274 600 int y = getYForValue(v, val, ch);
Chris@68 601
Chris@68 602 if (py >= 0 && abs(y - py) < 10) continue;
Chris@68 603 else py = y;
Chris@68 604
Chris@68 605 int ny = y;
Chris@68 606 if (nval != 0.0) {
Chris@274 607 ny = getYForValue(v, nval, ch);
Chris@68 608 }
Chris@68 609
Chris@68 610 paint->drawLine(x0, y, x1, y);
Chris@68 611 if (ny != y) {
Chris@68 612 paint->drawLine(x0, ny, x1, ny);
Chris@68 613 }
Chris@68 614 }
Chris@68 615 }
Chris@68 616
Chris@302 617 if (frame1 < modelStart) continue;
Chris@68 618
Chris@68 619 size_t modelZoomLevel = zoomLevel;
Chris@68 620
Chris@302 621 if (frame0 < modelStart) rangeStart = modelStart;
Chris@302 622 else rangeStart = frame0;
Chris@302 623
Chris@302 624 if (frame1 < 0) rangeEnd = 0;
Chris@302 625 else if (frame1 > modelEnd) rangeEnd = modelEnd;
Chris@302 626 else rangeEnd = frame1;
Chris@302 627
Chris@302 628 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@302 629
Chris@302 630 m_model->getSummaries
Chris@302 631 (ch, rangeStart, rangeEnd - rangeStart, *ranges, modelZoomLevel);
Chris@302 632
Chris@302 633 // std::cerr << ranges->size() << " ranges" << std::endl;
Chris@68 634
Chris@68 635 if (mergingChannels || mixingChannels) {
Chris@71 636 if (m_model->getChannelCount() > 1) {
Chris@200 637 if (!otherChannelRanges) {
Chris@200 638 otherChannelRanges =
Chris@200 639 new RangeSummarisableTimeValueModel::RangeBlock;
Chris@200 640 }
Chris@302 641 m_model->getSummaries
Chris@302 642 (1, rangeStart, rangeEnd - rangeStart, *otherChannelRanges,
Chris@200 643 modelZoomLevel);
Chris@71 644 } else {
Chris@200 645 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@71 646 otherChannelRanges = ranges;
Chris@71 647 }
Chris@68 648 }
Chris@68 649
Chris@0 650 for (int x = x0; x <= x1; ++x) {
Chris@0 651
Chris@0 652 range = RangeSummarisableTimeValueModel::Range();
Chris@0 653 size_t index = x - x0;
Chris@0 654 size_t maxIndex = index;
Chris@0 655
Chris@302 656 if (frame0 < modelStart) {
Chris@302 657 if (index < size_t((modelStart - frame0) / zoomLevel)) {
Chris@302 658 continue;
Chris@302 659 } else {
Chris@302 660 index -= ((modelStart - frame0) / zoomLevel);
Chris@302 661 maxIndex = index;
Chris@302 662 }
Chris@302 663 }
Chris@0 664
Chris@0 665 if (int(modelZoomLevel) != zoomLevel) {
Chris@0 666
Chris@0 667 index = size_t((double(index) * zoomLevel) / modelZoomLevel);
Chris@0 668
Chris@0 669 if (int(modelZoomLevel) < zoomLevel) {
Chris@0 670 // Peaks may be missed! The model should avoid
Chris@0 671 // this by rounding zoom levels up rather than
Chris@0 672 // down, but we'd better cope in case it doesn't
Chris@0 673 maxIndex = index;
Chris@0 674 } else {
Chris@0 675 maxIndex = size_t((double(index + 1) * zoomLevel)
Chris@0 676 / modelZoomLevel) - 1;
Chris@0 677 }
Chris@0 678 }
Chris@0 679
Chris@200 680 if (ranges && index < ranges->size()) {
Chris@0 681
Chris@200 682 range = (*ranges)[index];
Chris@0 683
Chris@200 684 if (maxIndex > index && maxIndex < ranges->size()) {
Chris@200 685 range.max = std::max(range.max, (*ranges)[maxIndex].max);
Chris@200 686 range.min = std::min(range.min, (*ranges)[maxIndex].min);
Chris@0 687 range.absmean = (range.absmean +
Chris@200 688 (*ranges)[maxIndex].absmean) / 2;
Chris@0 689 }
Chris@0 690
Chris@0 691 } else {
Chris@0 692 continue;
Chris@0 693 }
Chris@0 694
Chris@0 695 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 696
Chris@0 697 if (mergingChannels) {
Chris@0 698
Chris@200 699 if (otherChannelRanges && index < otherChannelRanges->size()) {
Chris@0 700
Chris@0 701 range.max = fabsf(range.max);
Chris@200 702 range.min = -fabsf((*otherChannelRanges)[index].max);
Chris@0 703 range.absmean = (range.absmean +
Chris@200 704 (*otherChannelRanges)[index].absmean) / 2;
Chris@0 705
Chris@200 706 if (maxIndex > index && maxIndex < otherChannelRanges->size()) {
Chris@0 707 // let's not concern ourselves about the mean
Chris@0 708 range.min = std::min
Chris@0 709 (range.min,
Chris@200 710 -fabsf((*otherChannelRanges)[maxIndex].max));
Chris@0 711 }
Chris@0 712 }
Chris@67 713
Chris@67 714 } else if (mixingChannels) {
Chris@67 715
Chris@200 716 if (otherChannelRanges && index < otherChannelRanges->size()) {
Chris@67 717
Chris@200 718 range.max = (range.max + (*otherChannelRanges)[index].max) / 2;
Chris@200 719 range.min = (range.min + (*otherChannelRanges)[index].min) / 2;
Chris@200 720 range.absmean = (range.absmean + (*otherChannelRanges)[index].absmean) / 2;
Chris@67 721 }
Chris@67 722 }
Chris@0 723
Chris@0 724 int greyLevels = 1;
Chris@0 725 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 726
Chris@0 727 switch (m_scale) {
Chris@0 728
Chris@0 729 case LinearScale:
Chris@67 730 rangeBottom = int( m * greyLevels * range.min * gain);
Chris@67 731 rangeTop = int( m * greyLevels * range.max * gain);
Chris@67 732 meanBottom = int(-m * range.absmean * gain);
Chris@67 733 meanTop = int( m * range.absmean * gain);
Chris@0 734 break;
Chris@0 735
Chris@0 736 case dBScale:
Chris@67 737 if (!mergingChannels) {
Chris@67 738 int db0 = dBscale(range.min * gain, m);
Chris@67 739 int db1 = dBscale(range.max * gain, m);
Chris@67 740 rangeTop = std::max(db0, db1);
Chris@67 741 meanTop = std::min(db0, db1);
Chris@67 742 if (mixingChannels) rangeBottom = meanTop;
Chris@67 743 else rangeBottom = dBscale(range.absmean * gain, m);
Chris@67 744 meanBottom = rangeBottom;
Chris@67 745 } else {
Chris@67 746 rangeBottom = -dBscale(range.min * gain, m * greyLevels);
Chris@67 747 rangeTop = dBscale(range.max * gain, m * greyLevels);
Chris@67 748 meanBottom = -dBscale(range.absmean * gain, m);
Chris@67 749 meanTop = dBscale(range.absmean * gain, m);
Chris@67 750 }
Chris@0 751 break;
Chris@0 752
Chris@0 753 case MeterScale:
Chris@67 754 if (!mergingChannels) {
Chris@67 755 int r0 = abs(AudioLevel::multiplier_to_preview(range.min * gain, m));
Chris@67 756 int r1 = abs(AudioLevel::multiplier_to_preview(range.max * gain, m));
Chris@67 757 rangeTop = std::max(r0, r1);
Chris@67 758 meanTop = std::min(r0, r1);
Chris@67 759 if (mixingChannels) rangeBottom = meanTop;
Chris@67 760 else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 761 meanBottom = rangeBottom;
Chris@67 762 } else {
Chris@324 763 rangeBottom = -AudioLevel::multiplier_to_preview(range.min * gain, m * greyLevels);
Chris@67 764 rangeTop = AudioLevel::multiplier_to_preview(range.max * gain, m * greyLevels);
Chris@67 765 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 766 meanTop = AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 767 }
Chris@67 768 break;
Chris@0 769 }
Chris@0 770
Chris@27 771 rangeBottom = my * greyLevels - rangeBottom;
Chris@27 772 rangeTop = my * greyLevels - rangeTop;
Chris@27 773 meanBottom = my - meanBottom;
Chris@27 774 meanTop = my - meanTop;
Chris@27 775
Chris@27 776 int topFill = (rangeTop % greyLevels);
Chris@27 777 if (topFill > 0) topFill = greyLevels - topFill;
Chris@27 778
Chris@27 779 int bottomFill = (rangeBottom % greyLevels);
Chris@27 780
Chris@0 781 rangeTop = rangeTop / greyLevels;
Chris@0 782 rangeBottom = rangeBottom / greyLevels;
Chris@0 783
Chris@0 784 bool clipped = false;
Chris@27 785
Chris@27 786 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@27 787 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@27 788 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@27 789 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@27 790
Chris@67 791 if (range.max <= -1.0 ||
Chris@67 792 range.max >= 1.0) clipped = true;
Chris@0 793
Chris@0 794 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 795 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 796
Chris@0 797 bool drawMean = m_showMeans;
Chris@0 798 if (meanTop == rangeTop) {
Chris@0 799 if (meanTop < meanBottom) ++meanTop;
Chris@0 800 else drawMean = false;
Chris@0 801 }
Chris@67 802 if (meanBottom == rangeBottom && m_scale == LinearScale) {
Chris@0 803 if (meanBottom > meanTop) --meanBottom;
Chris@0 804 else drawMean = false;
Chris@0 805 }
Chris@0 806
Chris@0 807 if (x != x0 && prevRangeBottom != -1) {
Chris@0 808 if (prevRangeBottom > rangeBottom &&
Chris@0 809 prevRangeTop > rangeBottom) {
Chris@28 810 // paint->setPen(midColour);
Chris@285 811 paint->setPen(baseColour);
Chris@0 812 paint->drawLine(x-1, prevRangeTop, x, rangeBottom);
Chris@28 813 paint->setPen(prevRangeTopColour);
Chris@0 814 paint->drawPoint(x-1, prevRangeTop);
Chris@0 815 } else if (prevRangeBottom < rangeTop &&
Chris@0 816 prevRangeTop < rangeTop) {
Chris@28 817 // paint->setPen(midColour);
Chris@285 818 paint->setPen(baseColour);
Chris@0 819 paint->drawLine(x-1, prevRangeBottom, x, rangeTop);
Chris@28 820 paint->setPen(prevRangeBottomColour);
Chris@0 821 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 822 }
Chris@0 823 }
Chris@0 824
Chris@0 825 if (ready) {
Chris@67 826 if (clipped /*!!! ||
Chris@67 827 range.min * gain <= -1.0 ||
Chris@67 828 range.max * gain >= 1.0 */) {
Chris@285 829 paint->setPen(Qt::red); //!!! getContrastingColour
Chris@0 830 } else {
Chris@285 831 paint->setPen(baseColour);
Chris@0 832 }
Chris@0 833 } else {
Chris@0 834 paint->setPen(midColour);
Chris@0 835 }
Chris@0 836
Chris@0 837 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@0 838
Chris@285 839 prevRangeTopColour = baseColour;
Chris@285 840 prevRangeBottomColour = baseColour;
Chris@28 841
Chris@0 842 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 843 if (!clipped) {
Chris@0 844 if (rangeTop < rangeBottom) {
Chris@0 845 if (topFill > 0 &&
Chris@0 846 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 847 paint->setPen(greys[topFill - 1]);
Chris@27 848 paint->drawPoint(x, rangeTop);
Chris@28 849 prevRangeTopColour = greys[topFill - 1];
Chris@0 850 }
Chris@0 851 if (bottomFill > 0 &&
Chris@0 852 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 853 paint->setPen(greys[bottomFill - 1]);
Chris@27 854 paint->drawPoint(x, rangeBottom);
Chris@28 855 prevRangeBottomColour = greys[bottomFill - 1];
Chris@0 856 }
Chris@0 857 }
Chris@0 858 }
Chris@0 859 }
Chris@0 860
Chris@0 861 if (drawMean) {
Chris@0 862 paint->setPen(midColour);
Chris@0 863 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 864 }
Chris@0 865
Chris@0 866 prevRangeBottom = rangeBottom;
Chris@0 867 prevRangeTop = rangeTop;
Chris@0 868 }
Chris@0 869 }
Chris@0 870
Chris@0 871 if (m_aggressive) {
Chris@41 872
Chris@44 873 if (ready && rect == v->rect()) {
Chris@0 874 m_cacheValid = true;
Chris@0 875 m_cacheZoomLevel = zoomLevel;
Chris@0 876 }
Chris@0 877 paint->end();
Chris@0 878 delete paint;
Chris@0 879 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 880 }
Chris@200 881
Chris@200 882 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@200 883 delete ranges;
Chris@0 884 }
Chris@0 885
Chris@25 886 QString
Chris@44 887 WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 888 {
Chris@25 889 int x = pos.x();
Chris@25 890
Chris@25 891 if (!m_model || !m_model->isOK()) return "";
Chris@25 892
Chris@44 893 long f0 = v->getFrameForX(x);
Chris@44 894 long f1 = v->getFrameForX(x + 1);
Chris@25 895
Chris@25 896 if (f0 < 0) f0 = 0;
Chris@25 897 if (f1 <= f0) return "";
Chris@25 898
Chris@25 899 QString text;
Chris@25 900
Chris@25 901 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 902 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 903
Chris@25 904 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@25 905 text += tr("Time:\t%1 - %2")
Chris@25 906 .arg(rt0.toText(true).c_str())
Chris@25 907 .arg(rt1.toText(true).c_str());
Chris@25 908 } else {
Chris@25 909 text += tr("Time:\t%1")
Chris@25 910 .arg(rt0.toText(true).c_str());
Chris@25 911 }
Chris@25 912
Chris@25 913 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 914 bool mergingChannels = false, mixingChannels = false;
Chris@25 915
Chris@67 916 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 917 mergingChannels, mixingChannels);
Chris@25 918 if (channels == 0) return "";
Chris@25 919
Chris@25 920 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 921
Chris@44 922 size_t blockSize = v->getZoomLevel();
Chris@200 923 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@302 924 m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 925
Chris@25 926 if (ranges.empty()) continue;
Chris@25 927
Chris@25 928 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@25 929
Chris@25 930 QString label = tr("Level:");
Chris@25 931 if (minChannel != maxChannel) {
Chris@25 932 if (ch == 0) label = tr("Left:");
Chris@25 933 else if (ch == 1) label = tr("Right:");
Chris@25 934 else label = tr("Channel %1").arg(ch + 1);
Chris@25 935 }
Chris@25 936
Chris@76 937 bool singleValue = false;
Chris@76 938 float min, max;
Chris@76 939
Chris@76 940 if (fabs(range.min) < 0.01) {
Chris@76 941 min = range.min;
Chris@76 942 max = range.max;
Chris@76 943 singleValue = (min == max);
Chris@76 944 } else {
Chris@76 945 int imin = int(range.min * 1000);
Chris@76 946 int imax = int(range.max * 1000);
Chris@76 947 singleValue = (imin == imax);
Chris@76 948 min = float(imin)/1000;
Chris@76 949 max = float(imax)/1000;
Chris@76 950 }
Chris@76 951
Chris@25 952 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min),
Chris@25 953 fabsf(range.max)))
Chris@25 954 * 100);
Chris@25 955
Chris@76 956 if (!singleValue) {
Chris@25 957 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@76 958 .arg(label).arg(min).arg(max).arg(float(db)/100);
Chris@25 959 } else {
Chris@25 960 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@76 961 .arg(label).arg(min).arg(float(db)/100);
Chris@25 962 }
Chris@25 963 }
Chris@25 964
Chris@25 965 return text;
Chris@25 966 }
Chris@25 967
Chris@0 968 int
Chris@274 969 WaveformLayer::getYForValue(const View *v, float value, size_t channel) const
Chris@68 970 {
Chris@274 971 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 972 bool mergingChannels = false, mixingChannels = false;
Chris@274 973
Chris@274 974 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 975 mergingChannels, mixingChannels);
Chris@274 976
Chris@68 977 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 978
Chris@68 979 int h = v->height();
Chris@68 980 int m = (h / channels) / 2;
Chris@68 981
Chris@68 982 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 983 m_channelMode != MergeChannels) {
Chris@68 984 m = (h / channels);
Chris@68 985 }
Chris@68 986
Chris@274 987 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 988
Chris@68 989 int vy = 0;
Chris@68 990
Chris@274 991 switch (m_scale) {
Chris@68 992
Chris@68 993 case LinearScale:
Chris@68 994 vy = int(m * value);
Chris@68 995 break;
Chris@68 996
Chris@68 997 case MeterScale:
Chris@68 998 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 999 break;
Chris@68 1000
Chris@68 1001 case dBScale:
Chris@68 1002 vy = dBscale(value, m);
Chris@68 1003 break;
Chris@68 1004 }
Chris@68 1005
Chris@324 1006 // std::cerr << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << std::endl;
Chris@324 1007
Chris@68 1008 return my - vy;
Chris@68 1009 }
Chris@68 1010
Chris@261 1011 float
Chris@274 1012 WaveformLayer::getValueForY(const View *v, int y, size_t &channel) const
Chris@261 1013 {
Chris@274 1014 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1015 bool mergingChannels = false, mixingChannels = false;
Chris@274 1016
Chris@274 1017 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1018 mergingChannels, mixingChannels);
Chris@274 1019
Chris@261 1020 if (maxChannel < minChannel) return 0;
Chris@261 1021
Chris@261 1022 int h = v->height();
Chris@261 1023 int m = (h / channels) / 2;
Chris@261 1024
Chris@261 1025 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1026 m_channelMode != MergeChannels) {
Chris@261 1027 m = (h / channels);
Chris@261 1028 }
Chris@274 1029
Chris@274 1030 channel = (y * channels) / h + minChannel;
Chris@261 1031
Chris@261 1032 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1033
Chris@262 1034 int vy = my - y;
Chris@261 1035 float value = 0;
Chris@274 1036 float thresh = -50.f;
Chris@261 1037
Chris@274 1038 switch (m_scale) {
Chris@261 1039
Chris@261 1040 case LinearScale:
Chris@261 1041 value = float(vy) / m;
Chris@261 1042 break;
Chris@261 1043
Chris@261 1044 case MeterScale:
Chris@261 1045 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1046 break;
Chris@261 1047
Chris@261 1048 case dBScale:
Chris@274 1049 value = (-thresh * float(vy)) / m + thresh;
Chris@274 1050 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1051 break;
Chris@261 1052 }
Chris@261 1053
Chris@274 1054 return value / m_gain;
Chris@261 1055 }
Chris@261 1056
Chris@261 1057 bool
Chris@267 1058 WaveformLayer::getYScaleValue(const View *v, int y,
Chris@261 1059 float &value, QString &unit) const
Chris@261 1060 {
Chris@274 1061 size_t channel;
Chris@261 1062
Chris@274 1063 value = getValueForY(v, y, channel);
Chris@261 1064
Chris@274 1065 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1066
Chris@274 1067 float thresh = -50.f;
Chris@274 1068
Chris@274 1069 if (value > 0.f) {
Chris@274 1070 value = 10.f * log10f(value);
Chris@274 1071 if (value < thresh) value = thresh;
Chris@274 1072 } else value = thresh;
Chris@274 1073
Chris@274 1074 unit = "dBV";
Chris@274 1075
Chris@274 1076 } else {
Chris@274 1077 unit = "V";
Chris@274 1078 }
Chris@274 1079
Chris@274 1080 return true;
Chris@274 1081 }
Chris@274 1082
Chris@274 1083 bool
Chris@274 1084 WaveformLayer::getYScaleDifference(const View *v, int y0, int y1,
Chris@274 1085 float &diff, QString &unit) const
Chris@274 1086 {
Chris@274 1087 size_t c0, c1;
Chris@274 1088 float v0 = getValueForY(v, y0, c0);
Chris@274 1089 float v1 = getValueForY(v, y1, c1);
Chris@274 1090
Chris@274 1091 if (c0 != c1) {
Chris@274 1092 // different channels, not comparable
Chris@274 1093 diff = 0.f;
Chris@274 1094 unit = "";
Chris@274 1095 return false;
Chris@274 1096 }
Chris@274 1097
Chris@274 1098 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1099
Chris@274 1100 float thresh = -50.f;
Chris@274 1101
Chris@274 1102 if (v1 == v0) diff = thresh;
Chris@274 1103 else {
Chris@274 1104 if (v1 > v0) diff = v0 / v1;
Chris@274 1105 else diff = v1 / v0;
Chris@274 1106
Chris@274 1107 diff = 10.f * log10f(diff);
Chris@274 1108 if (diff < thresh) diff = thresh;
Chris@274 1109 }
Chris@274 1110
Chris@274 1111 unit = "dBV";
Chris@274 1112
Chris@274 1113 } else {
Chris@274 1114 diff = fabsf(v1 - v0);
Chris@274 1115 unit = "V";
Chris@274 1116 }
Chris@274 1117
Chris@261 1118 return true;
Chris@261 1119 }
Chris@261 1120
Chris@68 1121 int
Chris@248 1122 WaveformLayer::getVerticalScaleWidth(View *, QPainter &paint) const
Chris@0 1123 {
Chris@0 1124 if (m_scale == LinearScale) {
Chris@0 1125 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1126 } else {
Chris@0 1127 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 1128 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 1129 }
Chris@0 1130 }
Chris@0 1131
Chris@0 1132 void
Chris@44 1133 WaveformLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 1134 {
Chris@0 1135 if (!m_model || !m_model->isOK()) {
Chris@0 1136 return;
Chris@0 1137 }
Chris@0 1138
Chris@0 1139 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1140 bool mergingChannels = false, mixingChannels = false;
Chris@0 1141
Chris@67 1142 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1143 mergingChannels, mixingChannels);
Chris@0 1144 if (channels == 0) return;
Chris@0 1145
Chris@0 1146 int h = rect.height(), w = rect.width();
Chris@0 1147 int textHeight = paint.fontMetrics().height();
Chris@0 1148 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1149
Chris@67 1150 float gain = m_gain;
Chris@67 1151
Chris@0 1152 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1153
Chris@68 1154 int lastLabelledY = -1;
Chris@0 1155
Chris@67 1156 if (ch < m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1157
Chris@68 1158 int n = 10;
Chris@0 1159
Chris@68 1160 for (int i = 0; i <= n; ++i) {
Chris@68 1161
Chris@68 1162 float val = 0.0, nval = 0.0;
Chris@0 1163 QString text = "";
Chris@0 1164
Chris@68 1165 switch (m_scale) {
Chris@68 1166
Chris@68 1167 case LinearScale:
Chris@68 1168 val = (i * gain) / n;
Chris@68 1169 text = QString("%1").arg(float(i) / n);
Chris@68 1170 if (i == 0) text = "0.0";
Chris@68 1171 else {
Chris@68 1172 nval = -val;
Chris@68 1173 if (i == n) text = "1.0";
Chris@68 1174 }
Chris@68 1175 break;
Chris@0 1176
Chris@68 1177 case MeterScale:
Chris@68 1178 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 1179 text = QString("%1").arg(meterdbs[i]);
Chris@68 1180 if (i == n) text = tr("0dB");
Chris@68 1181 if (i == 0) {
Chris@68 1182 text = tr("-Inf");
Chris@68 1183 val = 0.0;
Chris@68 1184 }
Chris@68 1185 break;
Chris@0 1186
Chris@68 1187 case dBScale:
Chris@68 1188 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 1189 text = QString("%1").arg(-(10*n) + i * 10);
Chris@68 1190 if (i == n) text = tr("0dB");
Chris@68 1191 if (i == 0) {
Chris@68 1192 text = tr("-Inf");
Chris@68 1193 val = 0.0;
Chris@68 1194 }
Chris@68 1195 break;
Chris@68 1196 }
Chris@0 1197
Chris@68 1198 if (val < -1.0 || val > 1.0) continue;
Chris@0 1199
Chris@274 1200 int y = getYForValue(v, val, ch);
Chris@0 1201
Chris@68 1202 int ny = y;
Chris@68 1203 if (nval != 0.0) {
Chris@274 1204 ny = getYForValue(v, nval, ch);
Chris@68 1205 }
Chris@0 1206
Chris@68 1207 bool spaceForLabel = (i == 0 ||
Chris@68 1208 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1209
Chris@68 1210 if (spaceForLabel) {
Chris@0 1211
Chris@68 1212 int tx = 3;
Chris@68 1213 if (m_scale != LinearScale) {
Chris@68 1214 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1215 }
Chris@68 1216
Chris@68 1217 int ty = y;
Chris@68 1218 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1219 ty = paint.fontMetrics().ascent();
Chris@68 1220 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1221 ty = h - paint.fontMetrics().descent();
Chris@68 1222 } else {
Chris@68 1223 ty += toff;
Chris@68 1224 }
Chris@68 1225 paint.drawText(tx, ty, text);
Chris@0 1226
Chris@68 1227 lastLabelledY = ty - toff;
Chris@67 1228
Chris@68 1229 if (ny != y) {
Chris@68 1230 ty = ny;
Chris@68 1231 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1232 ty = paint.fontMetrics().ascent();
Chris@68 1233 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1234 ty = h - paint.fontMetrics().descent();
Chris@68 1235 } else {
Chris@68 1236 ty += toff;
Chris@68 1237 }
Chris@68 1238 paint.drawText(tx, ty, text);
Chris@68 1239 }
Chris@68 1240
Chris@68 1241 paint.drawLine(w - 7, y, w, y);
Chris@68 1242 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1243
Chris@68 1244 } else {
Chris@68 1245
Chris@68 1246 paint.drawLine(w - 4, y, w, y);
Chris@68 1247 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1248 }
Chris@0 1249 }
Chris@0 1250 }
Chris@0 1251 }
Chris@0 1252
Chris@316 1253 void
Chris@316 1254 WaveformLayer::toXml(QTextStream &stream,
Chris@316 1255 QString indent, QString extraAttributes) const
Chris@6 1256 {
Chris@6 1257 QString s;
Chris@6 1258
Chris@285 1259 QString colourName, colourSpec, darkbg;
Chris@285 1260 ColourDatabase::getInstance()->getStringValues
Chris@285 1261 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1262
Chris@6 1263 s += QString("gain=\"%1\" "
Chris@287 1264 "showMeans=\"%2\" "
Chris@287 1265 "greyscale=\"%3\" "
Chris@287 1266 "channelMode=\"%4\" "
Chris@287 1267 "channel=\"%5\" "
Chris@287 1268 "scale=\"%6\" "
Chris@287 1269 "aggressive=\"%7\" "
Chris@287 1270 "autoNormalize=\"%8\"")
Chris@6 1271 .arg(m_gain)
Chris@6 1272 .arg(m_showMeans)
Chris@6 1273 .arg(m_greyscale)
Chris@6 1274 .arg(m_channelMode)
Chris@287 1275 .arg(m_channel)
Chris@6 1276 .arg(m_scale)
Chris@67 1277 .arg(m_aggressive)
Chris@67 1278 .arg(m_autoNormalize);
Chris@6 1279
Chris@316 1280 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 1281 }
Chris@6 1282
Chris@11 1283 void
Chris@11 1284 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1285 {
Chris@11 1286 bool ok = false;
Chris@11 1287
Chris@287 1288 SingleColourLayer::setProperties(attributes);
Chris@287 1289
Chris@11 1290 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1291 if (ok) setGain(gain);
Chris@11 1292
Chris@11 1293 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 1294 attributes.value("showMeans") == "true");
Chris@11 1295 setShowMeans(showMeans);
Chris@11 1296
Chris@11 1297 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 1298 attributes.value("greyscale") == "true");
Chris@11 1299 setUseGreyscale(greyscale);
Chris@11 1300
Chris@11 1301 ChannelMode channelMode = (ChannelMode)
Chris@11 1302 attributes.value("channelMode").toInt(&ok);
Chris@11 1303 if (ok) setChannelMode(channelMode);
Chris@11 1304
Chris@11 1305 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1306 if (ok) setChannel(channel);
Chris@11 1307
Chris@11 1308 Scale scale = (Scale)
Chris@11 1309 attributes.value("scale").toInt(&ok);
Chris@11 1310 if (ok) setScale(scale);
Chris@11 1311
Chris@11 1312 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 1313 attributes.value("aggressive") == "true");
Chris@11 1314 setUseGreyscale(aggressive);
Chris@67 1315
Chris@67 1316 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1317 attributes.value("autoNormalize") == "true");
Chris@67 1318 setAutoNormalize(autoNormalize);
Chris@11 1319 }
Chris@11 1320
Chris@133 1321 int
Chris@133 1322 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1323 {
Chris@133 1324 defaultStep = 50;
Chris@133 1325 return 100;
Chris@133 1326 }
Chris@0 1327
Chris@133 1328 int
Chris@133 1329 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1330 {
Chris@133 1331 int val = lrint(log10(m_gain) * 20.0) + 50;
Chris@133 1332 if (val < 0) val = 0;
Chris@133 1333 if (val > 100) val = 100;
Chris@133 1334 return val;
Chris@133 1335 }
Chris@133 1336
Chris@133 1337 void
Chris@133 1338 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1339 {
Chris@133 1340 setGain(pow(10, float(step - 50) / 20.0));
Chris@133 1341 }
Chris@133 1342