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