annotate layer/WaveformLayer.cpp @ 1212:a1ee3108d1d3 3.0-integration

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