annotate layer/WaveformLayer.cpp @ 1330:c1f719094c25 zoom

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