annotate layer/WaveformLayer.cpp @ 1534:bfd8b22fd67c

Fix #1904 Scrolling colour 3d plot does not always work when in View normalisation mode. We shouldn't imagine we've just invalidated the cache if the truth is that we've only just created the renderer
author Chris Cannam
date Wed, 09 Oct 2019 13:45:17 +0100
parents 1ccb64bfb22b
children 10fe8124dc17
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@1340 26
Chris@1340 27 #include "data/model/WaveformOversampler.h"
Chris@0 28
Chris@0 29 #include <QPainter>
Chris@0 30 #include <QPixmap>
Chris@316 31 #include <QTextStream>
Chris@0 32
Chris@0 33 #include <iostream>
Chris@0 34 #include <cmath>
Chris@0 35
Chris@1352 36 //#define DEBUG_WAVEFORM_PAINT 1
Chris@1338 37 //#define DEBUG_WAVEFORM_PAINT_BY_PIXEL 1
Chris@4 38
Chris@1333 39 using std::vector;
Chris@682 40
Chris@0 41
Chris@44 42 WaveformLayer::WaveformLayer() :
Chris@287 43 SingleColourLayer(),
Chris@0 44 m_gain(1.0f),
Chris@67 45 m_autoNormalize(false),
Chris@0 46 m_showMeans(true),
Chris@0 47 m_channelMode(SeparateChannels),
Chris@0 48 m_channel(-1),
Chris@0 49 m_scale(LinearScale),
Chris@709 50 m_middleLineHeight(0.5),
Chris@0 51 m_aggressive(false),
Chris@1408 52 m_cache(nullptr),
Chris@1325 53 m_cacheValid(false)
Chris@0 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@1470 62 const ZoomConstraint *
Chris@1470 63 WaveformLayer::getZoomConstraint() const
Chris@1470 64 {
Chris@1470 65 auto model = ModelById::get(m_model);
Chris@1474 66 if (model) return model->getZoomConstraint();
Chris@1470 67 else return nullptr;
Chris@1470 68 }
Chris@1470 69
Chris@0 70 void
Chris@1470 71 WaveformLayer::setModel(ModelId modelId)
Chris@0 72 {
Chris@1470 73 auto oldModel = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1470 74 auto newModel = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId);
Chris@1470 75
Chris@1471 76 if (!modelId.isNone() && !newModel) {
Chris@1471 77 throw std::logic_error("Not a RangeSummarisableTimeValueModel");
Chris@1470 78 }
Chris@1470 79
Chris@1471 80 if (m_model == modelId) return;
Chris@1471 81 m_model = modelId;
Chris@1471 82
Chris@1471 83 m_cacheValid = false;
Chris@1471 84
Chris@69 85 bool channelsChanged = false;
Chris@69 86 if (m_channel == -1) {
Chris@1470 87 if (!oldModel) {
Chris@1470 88 if (newModel) {
Chris@69 89 channelsChanged = true;
Chris@69 90 }
Chris@69 91 } else {
Chris@1470 92 if (newModel &&
Chris@1470 93 oldModel->getChannelCount() != newModel->getChannelCount()) {
Chris@69 94 channelsChanged = true;
Chris@69 95 }
Chris@69 96 }
Chris@69 97 }
Chris@69 98
Chris@1471 99 if (newModel) {
Chris@1471 100 connectSignals(m_model);
Chris@1471 101 }
Chris@1471 102
Chris@0 103 emit modelReplaced();
Chris@69 104
Chris@69 105 if (channelsChanged) emit layerParametersChanged();
Chris@0 106 }
Chris@0 107
Chris@0 108 Layer::PropertyList
Chris@0 109 WaveformLayer::getProperties() const
Chris@0 110 {
Chris@287 111 PropertyList list = SingleColourLayer::getProperties();
Chris@87 112 list.push_back("Scale");
Chris@87 113 list.push_back("Gain");
Chris@87 114 list.push_back("Normalize Visible Area");
Chris@68 115
Chris@1474 116 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 117 if (model && model->getChannelCount() > 1 && m_channel == -1) {
Chris@87 118 list.push_back("Channels");
Chris@68 119 }
Chris@68 120
Chris@0 121 return list;
Chris@0 122 }
Chris@0 123
Chris@87 124 QString
Chris@87 125 WaveformLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 126 {
Chris@87 127 if (name == "Scale") return tr("Scale");
Chris@87 128 if (name == "Gain") return tr("Gain");
Chris@87 129 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 130 if (name == "Channels") return tr("Channels");
Chris@287 131 return SingleColourLayer::getPropertyLabel(name);
Chris@87 132 }
Chris@87 133
Chris@335 134 QString
Chris@335 135 WaveformLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 136 {
Chris@335 137 if (name == "Normalize Visible Area") return "normalise";
Chris@335 138 return "";
Chris@335 139 }
Chris@335 140
Chris@0 141 Layer::PropertyType
Chris@0 142 WaveformLayer::getPropertyType(const PropertyName &name) const
Chris@0 143 {
Chris@87 144 if (name == "Gain") return RangeProperty;
Chris@87 145 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 146 if (name == "Channels") return ValueProperty;
Chris@87 147 if (name == "Scale") return ValueProperty;
Chris@287 148 return SingleColourLayer::getPropertyType(name);
Chris@0 149 }
Chris@0 150
Chris@0 151 QString
Chris@0 152 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 153 {
Chris@87 154 if (name == "Gain" ||
Chris@87 155 name == "Normalize Visible Area" ||
Chris@1266 156 name == "Scale") return tr("Scale");
Chris@0 157 return QString();
Chris@0 158 }
Chris@0 159
Chris@0 160 int
Chris@0 161 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 162 int *min, int *max, int *deflt) const
Chris@0 163 {
Chris@216 164 int val = 0;
Chris@0 165
Chris@216 166 int garbage0, garbage1, garbage2;
Chris@56 167 if (!min) min = &garbage0;
Chris@56 168 if (!max) max = &garbage1;
Chris@216 169 if (!deflt) deflt = &garbage2;
Chris@10 170
Chris@87 171 if (name == "Gain") {
Chris@0 172
Chris@1266 173 *min = -50;
Chris@1266 174 *max = 50;
Chris@216 175 *deflt = 0;
Chris@0 176
Chris@1266 177 val = int(lrint(log10(m_gain) * 20.0));
Chris@1266 178 if (val < *min) val = *min;
Chris@1266 179 if (val > *max) val = *max;
Chris@0 180
Chris@87 181 } else if (name == "Normalize Visible Area") {
Chris@67 182
Chris@216 183 val = (m_autoNormalize ? 1 : 0);
Chris@216 184 *deflt = 0;
Chris@67 185
Chris@87 186 } else if (name == "Channels") {
Chris@0 187
Chris@67 188 *min = 0;
Chris@67 189 *max = 2;
Chris@216 190 *deflt = 0;
Chris@216 191 if (m_channelMode == MixChannels) val = 1;
Chris@216 192 else if (m_channelMode == MergeChannels) val = 2;
Chris@216 193 else val = 0;
Chris@0 194
Chris@87 195 } else if (name == "Scale") {
Chris@0 196
Chris@1266 197 *min = 0;
Chris@1266 198 *max = 2;
Chris@216 199 *deflt = 0;
Chris@0 200
Chris@1266 201 val = (int)m_scale;
Chris@0 202
Chris@0 203 } else {
Chris@1266 204 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 205 }
Chris@0 206
Chris@216 207 return val;
Chris@0 208 }
Chris@0 209
Chris@0 210 QString
Chris@0 211 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1266 212 int value) const
Chris@0 213 {
Chris@87 214 if (name == "Scale") {
Chris@1266 215 switch (value) {
Chris@1266 216 default:
Chris@1266 217 case 0: return tr("Linear");
Chris@1266 218 case 1: return tr("Meter");
Chris@1266 219 case 2: return tr("dB");
Chris@1266 220 }
Chris@0 221 }
Chris@87 222 if (name == "Channels") {
Chris@67 223 switch (value) {
Chris@67 224 default:
Chris@67 225 case 0: return tr("Separate");
Chris@67 226 case 1: return tr("Mean");
Chris@67 227 case 2: return tr("Butterfly");
Chris@67 228 }
Chris@67 229 }
Chris@287 230 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@0 231 }
Chris@0 232
Chris@167 233 RangeMapper *
Chris@167 234 WaveformLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 235 {
Chris@167 236 if (name == "Gain") {
Chris@167 237 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 238 }
Chris@1408 239 return nullptr;
Chris@167 240 }
Chris@167 241
Chris@0 242 void
Chris@0 243 WaveformLayer::setProperty(const PropertyName &name, int value)
Chris@0 244 {
Chris@87 245 if (name == "Gain") {
Chris@1266 246 setGain(float(pow(10, float(value)/20.0)));
Chris@87 247 } else if (name == "Normalize Visible Area") {
Chris@67 248 setAutoNormalize(value ? true : false);
Chris@87 249 } else if (name == "Channels") {
Chris@67 250 if (value == 1) setChannelMode(MixChannels);
Chris@67 251 else if (value == 2) setChannelMode(MergeChannels);
Chris@67 252 else setChannelMode(SeparateChannels);
Chris@87 253 } else if (name == "Scale") {
Chris@1266 254 switch (value) {
Chris@1266 255 default:
Chris@1266 256 case 0: setScale(LinearScale); break;
Chris@1266 257 case 1: setScale(MeterScale); break;
Chris@1266 258 case 2: setScale(dBScale); break;
Chris@1266 259 }
Chris@287 260 } else {
Chris@287 261 SingleColourLayer::setProperty(name, value);
Chris@0 262 }
Chris@0 263 }
Chris@0 264
Chris@0 265 void
Chris@67 266 WaveformLayer::setGain(float gain)
Chris@0 267 {
Chris@0 268 if (m_gain == gain) return;
Chris@0 269 m_gain = gain;
Chris@0 270 m_cacheValid = false;
Chris@0 271 emit layerParametersChanged();
Chris@133 272 emit verticalZoomChanged();
Chris@0 273 }
Chris@0 274
Chris@0 275 void
Chris@67 276 WaveformLayer::setAutoNormalize(bool autoNormalize)
Chris@67 277 {
Chris@67 278 if (m_autoNormalize == autoNormalize) return;
Chris@67 279 m_autoNormalize = autoNormalize;
Chris@67 280 m_cacheValid = false;
Chris@67 281 emit layerParametersChanged();
Chris@67 282 }
Chris@67 283
Chris@67 284 void
Chris@0 285 WaveformLayer::setShowMeans(bool showMeans)
Chris@0 286 {
Chris@0 287 if (m_showMeans == showMeans) return;
Chris@0 288 m_showMeans = showMeans;
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::setChannelMode(ChannelMode channelMode)
Chris@0 295 {
Chris@0 296 if (m_channelMode == channelMode) return;
Chris@0 297 m_channelMode = channelMode;
Chris@0 298 m_cacheValid = false;
Chris@0 299 emit layerParametersChanged();
Chris@0 300 }
Chris@0 301
Chris@0 302 void
Chris@0 303 WaveformLayer::setChannel(int channel)
Chris@0 304 {
Chris@587 305 // SVDEBUG << "WaveformLayer::setChannel(" << channel << ")" << endl;
Chris@0 306
Chris@0 307 if (m_channel == channel) return;
Chris@0 308 m_channel = channel;
Chris@0 309 m_cacheValid = false;
Chris@0 310 emit layerParametersChanged();
Chris@0 311 }
Chris@0 312
Chris@0 313 void
Chris@0 314 WaveformLayer::setScale(Scale scale)
Chris@0 315 {
Chris@0 316 if (m_scale == scale) return;
Chris@0 317 m_scale = scale;
Chris@0 318 m_cacheValid = false;
Chris@0 319 emit layerParametersChanged();
Chris@0 320 }
Chris@0 321
Chris@0 322 void
Chris@908 323 WaveformLayer::setMiddleLineHeight(double height)
Chris@709 324 {
Chris@709 325 if (m_middleLineHeight == height) return;
Chris@709 326 m_middleLineHeight = height;
Chris@709 327 m_cacheValid = false;
Chris@709 328 emit layerParametersChanged();
Chris@709 329 }
Chris@709 330
Chris@709 331 void
Chris@0 332 WaveformLayer::setAggressiveCacheing(bool aggressive)
Chris@0 333 {
Chris@0 334 if (m_aggressive == aggressive) return;
Chris@0 335 m_aggressive = aggressive;
Chris@0 336 m_cacheValid = false;
Chris@0 337 emit layerParametersChanged();
Chris@0 338 }
Chris@0 339
Chris@0 340 int
Chris@918 341 WaveformLayer::getCompletion(LayerGeometryProvider *) const
Chris@0 342 {
Chris@0 343 int completion = 100;
Chris@1474 344 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 345 if (!model || !model->isOK()) return completion;
Chris@1474 346 if (model->isReady(&completion)) return 100;
Chris@0 347 return completion;
Chris@0 348 }
Chris@0 349
Chris@79 350 bool
Chris@908 351 WaveformLayer::getValueExtents(double &min, double &max,
Chris@248 352 bool &, QString &unit) const
Chris@79 353 {
Chris@79 354 if (m_scale == LinearScale) {
Chris@79 355 min = 0.0;
Chris@79 356 max = 1.0;
Chris@79 357 unit = "V";
Chris@79 358 } else if (m_scale == MeterScale) {
Chris@79 359 return false; //!!!
Chris@79 360 } else {
Chris@79 361 min = AudioLevel::multiplier_to_dB(0.0);
Chris@79 362 max = AudioLevel::multiplier_to_dB(1.0);
Chris@79 363 unit = "dB";
Chris@79 364 }
Chris@79 365 return true;
Chris@79 366 }
Chris@79 367
Chris@1367 368 double
Chris@908 369 WaveformLayer::dBscale(double sample, int m) const
Chris@0 370 {
Chris@67 371 if (sample < 0.0) return dBscale(-sample, m);
Chris@908 372 double dB = AudioLevel::multiplier_to_dB(sample);
Chris@0 373 if (dB < -50.0) return 0;
Chris@0 374 if (dB > 0.0) return m;
Chris@1367 375 return ((dB + 50.0) * m) / 50.0;
Chris@0 376 }
Chris@0 377
Chris@805 378 int
Chris@805 379 WaveformLayer::getChannelArrangement(int &min, int &max,
Chris@67 380 bool &merging, bool &mixing)
Chris@0 381 const
Chris@0 382 {
Chris@1474 383 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 384 if (!model || !model->isOK()) return 0;
Chris@0 385
Chris@1474 386 int channels = model->getChannelCount();
Chris@0 387 if (channels == 0) return 0;
Chris@0 388
Chris@805 389 int rawChannels = channels;
Chris@0 390
Chris@0 391 if (m_channel == -1) {
Chris@1266 392 min = 0;
Chris@1266 393 if (m_channelMode == MergeChannels ||
Chris@67 394 m_channelMode == MixChannels) {
Chris@1266 395 max = 0;
Chris@1266 396 channels = 1;
Chris@1266 397 } else {
Chris@1266 398 max = channels - 1;
Chris@1266 399 }
Chris@0 400 } else {
Chris@1266 401 min = m_channel;
Chris@1266 402 max = m_channel;
Chris@1266 403 rawChannels = 1;
Chris@1266 404 channels = 1;
Chris@0 405 }
Chris@0 406
Chris@1523 407 // "Merging" -> "butterfly mode" - use +ve side of "waveform" for
Chris@1523 408 // channel 0 and -ve side for channel 1. If we only have one
Chris@1523 409 // channel, we still do this but just duplicate channel 0 onto
Chris@1523 410 // channel 1 - this is the only way to get a classic-looking
Chris@1523 411 // waveform with meter or db scale from a single-channel file,
Chris@1523 412 // although it isn't currently exposed in the SV UI
Chris@1523 413 merging = (m_channelMode == MergeChannels);
Chris@1523 414
Chris@1523 415 // "Mixing" -> produce a single waveform from the mean of the
Chris@1523 416 // channels. Unlike merging, this really does only make sense if
Chris@1523 417 // we have >1 channel.
Chris@67 418 mixing = (m_channelMode == MixChannels && rawChannels > 1);
Chris@0 419
Chris@587 420 // SVDEBUG << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << endl;
Chris@0 421
Chris@0 422 return channels;
Chris@0 423 }
Chris@0 424
Chris@67 425 bool
Chris@918 426 WaveformLayer::isLayerScrollable(const LayerGeometryProvider *) const
Chris@67 427 {
Chris@67 428 return !m_autoNormalize;
Chris@67 429 }
Chris@67 430
Chris@68 431 static float meterdbs[] = { -40, -30, -20, -15, -10,
Chris@68 432 -5, -3, -2, -1, -0.5, 0 };
Chris@68 433
Chris@365 434 bool
Chris@1336 435 WaveformLayer::getSourceFramesForX(LayerGeometryProvider *v,
Chris@1336 436 int x, int modelZoomLevel,
Chris@908 437 sv_frame_t &f0, sv_frame_t &f1) const
Chris@365 438 {
Chris@1474 439 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 440 if (!model) return false;
Chris@1474 441
Chris@908 442 sv_frame_t viewFrame = v->getFrameForX(x);
Chris@365 443 if (viewFrame < 0) {
Chris@365 444 f0 = 0;
Chris@365 445 f1 = 0;
Chris@365 446 return false;
Chris@365 447 }
Chris@365 448
Chris@365 449 f0 = viewFrame;
Chris@365 450 f0 = f0 / modelZoomLevel;
Chris@365 451 f0 = f0 * modelZoomLevel;
Chris@365 452
Chris@1341 453 if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
Chris@1341 454 f1 = f0 + 1;
Chris@1341 455 } else {
Chris@1341 456 viewFrame = v->getFrameForX(x + 1);
Chris@1341 457 f1 = viewFrame;
Chris@1341 458 f1 = f1 / modelZoomLevel;
Chris@1341 459 f1 = f1 * modelZoomLevel;
Chris@1341 460 }
Chris@365 461
Chris@1474 462 return (f0 < model->getEndFrame());
Chris@365 463 }
Chris@365 464
Chris@365 465 float
Chris@918 466 WaveformLayer::getNormalizeGain(LayerGeometryProvider *v, int channel) const
Chris@365 467 {
Chris@1474 468 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 469 if (!model) return 0.f;
Chris@1474 470
Chris@908 471 sv_frame_t startFrame = v->getStartFrame();
Chris@908 472 sv_frame_t endFrame = v->getEndFrame();
Chris@365 473
Chris@1474 474 sv_frame_t modelStart = model->getStartFrame();
Chris@1474 475 sv_frame_t modelEnd = model->getEndFrame();
Chris@365 476
Chris@908 477 sv_frame_t rangeStart, rangeEnd;
Chris@365 478
Chris@365 479 if (startFrame < modelStart) rangeStart = modelStart;
Chris@365 480 else rangeStart = startFrame;
Chris@365 481
Chris@365 482 if (endFrame < 0) rangeEnd = 0;
Chris@365 483 else if (endFrame > modelEnd) rangeEnd = modelEnd;
Chris@365 484 else rangeEnd = endFrame;
Chris@365 485
Chris@365 486 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@365 487
Chris@365 488 RangeSummarisableTimeValueModel::Range range =
Chris@1474 489 model->getSummary(channel, rangeStart, rangeEnd - rangeStart);
Chris@365 490
Chris@805 491 int minChannel = 0, maxChannel = 0;
Chris@365 492 bool mergingChannels = false, mixingChannels = false;
Chris@365 493
Chris@859 494 (void)getChannelArrangement(minChannel, maxChannel,
Chris@859 495 mergingChannels, mixingChannels);
Chris@365 496
Chris@365 497 if (mergingChannels || mixingChannels) {
Chris@1523 498 if (model->getChannelCount() > 1) {
Chris@1523 499 RangeSummarisableTimeValueModel::Range otherRange =
Chris@1523 500 model->getSummary(1, rangeStart, rangeEnd - rangeStart);
Chris@1523 501 range.setMax(std::max(range.max(), otherRange.max()));
Chris@1523 502 range.setMin(std::min(range.min(), otherRange.min()));
Chris@1523 503 range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
Chris@1523 504 }
Chris@365 505 }
Chris@365 506
Chris@908 507 return float(1.0 / std::max(fabs(range.max()), fabs(range.min())));
Chris@365 508 }
Chris@365 509
Chris@0 510 void
Chris@916 511 WaveformLayer::paint(LayerGeometryProvider *v, QPainter &viewPainter, QRect rect) const
Chris@0 512 {
Chris@1474 513 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 514 if (!model || !model->isOK()) {
Chris@1266 515 return;
Chris@0 516 }
Chris@0 517
Chris@1325 518 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@0 519
Chris@2 520 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 521 Profiler profiler("WaveformLayer::paint", true);
Chris@1338 522 SVCERR << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@1266 523 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl;
Chris@2 524 #endif
Chris@0 525
Chris@805 526 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 527 bool mergingChannels = false, mixingChannels = false;
Chris@0 528
Chris@67 529 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 530 mergingChannels, mixingChannels);
Chris@0 531 if (channels == 0) return;
Chris@0 532
Chris@915 533 int w = v->getPaintWidth();
Chris@915 534 int h = v->getPaintHeight();
Chris@0 535
Chris@0 536 QPainter *paint;
Chris@0 537
Chris@0 538 if (m_aggressive) {
Chris@0 539
Chris@214 540 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 541 SVCERR << "WaveformLayer::paint: aggressive is true" << endl;
Chris@214 542 #endif
Chris@214 543
Chris@1325 544 using namespace std::rel_ops;
Chris@1325 545
Chris@1266 546 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@1266 547 m_cacheValid = false;
Chris@1266 548 }
Chris@0 549
Chris@1266 550 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@214 551 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 552 if (m_cache) {
Chris@1338 553 SVCERR << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << endl;
Chris@214 554 }
Chris@214 555 #endif
Chris@1266 556 delete m_cache;
Chris@1266 557 m_cache = new QPixmap(w, h);
Chris@214 558 m_cacheValid = false;
Chris@1266 559 }
Chris@214 560
Chris@1266 561 if (m_cacheValid) {
Chris@1266 562 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@1266 563 return;
Chris@1266 564 }
Chris@0 565
Chris@1266 566 paint = new QPainter(m_cache);
Chris@0 567
Chris@1266 568 paint->setPen(Qt::NoPen);
Chris@1266 569 paint->setBrush(getBackgroundQColor(v));
Chris@1266 570 paint->drawRect(rect);
Chris@0 571
Chris@1266 572 paint->setPen(getForegroundQColor(v));
Chris@1266 573 paint->setBrush(Qt::NoBrush);
Chris@0 574
Chris@0 575 } else {
Chris@1266 576 paint = &viewPainter;
Chris@0 577 }
Chris@0 578
Chris@1367 579 paint->setRenderHint(QPainter::Antialiasing, true);
Chris@28 580
Chris@709 581 if (m_middleLineHeight != 0.5) {
Chris@709 582 paint->save();
Chris@908 583 double space = m_middleLineHeight * 2;
Chris@709 584 if (space > 1.0) space = 2.0 - space;
Chris@908 585 double yt = h * (m_middleLineHeight - space/2);
Chris@709 586 paint->translate(QPointF(0, yt));
Chris@709 587 paint->scale(1.0, space);
Chris@709 588 }
Chris@709 589
Chris@0 590 int x0 = 0, x1 = w - 1;
Chris@0 591
Chris@0 592 x0 = rect.left();
Chris@0 593 x1 = rect.right();
Chris@0 594
Chris@1334 595 if (x0 > 0) {
Chris@1334 596 rect.adjust(-1, 0, 0, 0);
Chris@1334 597 x0 = rect.left();
Chris@1334 598 }
Chris@1334 599
Chris@1334 600 if (x1 < w) {
Chris@1334 601 rect.adjust(0, 0, 1, 0);
Chris@1334 602 x1 = rect.right();
Chris@1334 603 }
Chris@28 604
Chris@365 605 // Our zoom level may differ from that at which the underlying
Chris@365 606 // model has its blocks.
Chris@302 607
Chris@365 608 // Each pixel within our visible range must always draw from
Chris@365 609 // exactly the same set of underlying audio frames, no matter what
Chris@365 610 // the range being drawn is. And that set of underlying frames
Chris@365 611 // must remain the same when we scroll one or more pixels left or
Chris@365 612 // right.
Chris@1325 613
Chris@1325 614 int desiredBlockSize = 1;
Chris@1325 615 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1325 616 desiredBlockSize = zoomLevel.level;
Chris@1325 617 }
Chris@1474 618 int blockSize = model->getSummaryBlockSize(desiredBlockSize);
Chris@365 619
Chris@908 620 sv_frame_t frame0;
Chris@908 621 sv_frame_t frame1;
Chris@908 622 sv_frame_t spare;
Chris@365 623
Chris@1325 624 getSourceFramesForX(v, x0, blockSize, frame0, spare);
Chris@1325 625 getSourceFramesForX(v, x1, blockSize, spare, frame1);
Chris@365 626
Chris@4 627 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 628 SVCERR << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << blockSize << ")" << endl;
Chris@4 629 #endif
Chris@0 630
Chris@1338 631 m_effectiveGains.clear();
Chris@805 632 while ((int)m_effectiveGains.size() <= maxChannel) {
Chris@67 633 m_effectiveGains.push_back(m_gain);
Chris@67 634 }
Chris@1337 635 if (m_autoNormalize) {
Chris@1337 636 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1337 637 m_effectiveGains[ch] = getNormalizeGain(v, ch);
Chris@1337 638 }
Chris@1336 639 }
Chris@67 640
Chris@1338 641 RangeVec ranges;
Chris@1338 642
Chris@1336 643 if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
Chris@1338 644 getSummaryRanges(minChannel, maxChannel,
Chris@1338 645 mixingChannels || mergingChannels,
Chris@1338 646 frame0, frame1,
Chris@1338 647 blockSize, ranges);
Chris@1338 648 } else {
Chris@1338 649 getOversampledRanges(minChannel, maxChannel,
Chris@1338 650 mixingChannels || mergingChannels,
Chris@1338 651 frame0, frame1,
Chris@1338 652 v->getZoomLevel().level, ranges);
Chris@1338 653 }
Chris@1333 654
Chris@1343 655 if (!ranges.empty()) {
Chris@1343 656 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1343 657 paintChannel(v, paint, rect, ch, ranges, blockSize,
Chris@1343 658 frame0, frame1);
Chris@1343 659 }
Chris@1333 660 }
Chris@1333 661
Chris@709 662 if (m_middleLineHeight != 0.5) {
Chris@709 663 paint->restore();
Chris@709 664 }
Chris@709 665
Chris@0 666 if (m_aggressive) {
Chris@1474 667 if (model->isReady() && rect == v->getPaintRect()) {
Chris@1266 668 m_cacheValid = true;
Chris@1266 669 m_cacheZoomLevel = zoomLevel;
Chris@1266 670 }
Chris@1266 671 paint->end();
Chris@1266 672 delete paint;
Chris@1266 673 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 674 }
Chris@0 675 }
Chris@0 676
Chris@1332 677 void
Chris@1338 678 WaveformLayer::getSummaryRanges(int minChannel, int maxChannel,
Chris@1338 679 bool mixingOrMerging,
Chris@1338 680 sv_frame_t frame0, sv_frame_t frame1,
Chris@1338 681 int blockSize, RangeVec &ranges)
Chris@1338 682 const
Chris@1338 683 {
Chris@1474 684 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 685 if (!model) return;
Chris@1474 686
Chris@1338 687 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1338 688 ranges.push_back({});
Chris@1474 689 model->getSummaries(ch, frame0, frame1 - frame0,
Chris@1338 690 ranges[ch - minChannel], blockSize);
Chris@1338 691 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 692 SVCERR << "channel " << ch << ": " << ranges[ch - minChannel].size() << " ranges from " << frame0 << " to " << frame1 << " at zoom level " << blockSize << endl;
Chris@1338 693 #endif
Chris@1338 694 }
Chris@1338 695
Chris@1338 696 if (mixingOrMerging) {
Chris@1338 697 if (minChannel != 0 || maxChannel != 0) {
Chris@1366 698 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
Chris@1474 699 } else if (model->getChannelCount() > 1) {
Chris@1338 700 ranges.push_back({});
Chris@1474 701 model->getSummaries
Chris@1338 702 (1, frame0, frame1 - frame0, ranges[1], blockSize);
Chris@1523 703 } else {
Chris@1523 704 ranges.push_back(ranges[0]);
Chris@1338 705 }
Chris@1338 706 }
Chris@1338 707 }
Chris@1338 708
Chris@1338 709 void
Chris@1338 710 WaveformLayer::getOversampledRanges(int minChannel, int maxChannel,
Chris@1366 711 bool mixingOrMerging,
Chris@1338 712 sv_frame_t frame0, sv_frame_t frame1,
Chris@1338 713 int oversampleBy, RangeVec &ranges)
Chris@1338 714 const
Chris@1338 715 {
Chris@1474 716 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 717 if (!model) return;
Chris@1474 718
Chris@1366 719 if (mixingOrMerging) {
Chris@1366 720 if (minChannel != 0 || maxChannel != 0) {
Chris@1366 721 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
Chris@1366 722 }
Chris@1474 723 if (model->getChannelCount() > 1) {
Chris@1366 724 // call back on self for the individual channels with
Chris@1366 725 // mixingOrMerging false
Chris@1366 726 getOversampledRanges
Chris@1366 727 (0, 1, false, frame0, frame1, oversampleBy, ranges);
Chris@1366 728 return;
Chris@1523 729 } else {
Chris@1523 730 // call back on self for a single channel, then duplicate
Chris@1523 731 getOversampledRanges
Chris@1523 732 (0, 0, false, frame0, frame1, oversampleBy, ranges);
Chris@1523 733 ranges.push_back(ranges[0]);
Chris@1523 734 return;
Chris@1366 735 }
Chris@1366 736 }
Chris@1366 737
Chris@1338 738 // These frame values, tail length, etc variables are at the model
Chris@1338 739 // sample rate, not the oversampled rate
Chris@1338 740
Chris@1338 741 sv_frame_t tail = 16;
Chris@1474 742 sv_frame_t startFrame = model->getStartFrame();
Chris@1474 743 sv_frame_t endFrame = model->getEndFrame();
Chris@1338 744
Chris@1338 745 sv_frame_t rf0 = frame0 - tail;
Chris@1338 746 if (rf0 < startFrame) {
Chris@1338 747 rf0 = 0;
Chris@1338 748 }
Chris@1338 749
Chris@1338 750 sv_frame_t rf1 = frame1 + tail;
Chris@1338 751 if (rf1 >= endFrame) {
Chris@1338 752 rf1 = endFrame - 1;
Chris@1338 753 }
Chris@1338 754 if (rf1 <= rf0) {
Chris@1338 755 SVCERR << "WARNING: getOversampledRanges: rf1 (" << rf1 << ") <= rf0 ("
Chris@1338 756 << rf0 << ")" << endl;
Chris@1338 757 return;
Chris@1338 758 }
Chris@1338 759
Chris@1338 760 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1339 761 floatvec_t oversampled = WaveformOversampler::getOversampledData
Chris@1474 762 (*model, ch, frame0, frame1 - frame0, oversampleBy);
Chris@1338 763 RangeSummarisableTimeValueModel::RangeBlock rr;
Chris@1339 764 for (float v: oversampled) {
Chris@1338 765 RangeSummarisableTimeValueModel::Range r;
Chris@1339 766 r.sample(v);
Chris@1338 767 rr.push_back(r);
Chris@1338 768 }
Chris@1338 769 ranges.push_back(rr);
Chris@1338 770
Chris@1338 771 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 772 SVCERR << "getOversampledRanges: " << frame0 << " -> " << frame1
Chris@1338 773 << " (" << frame1 - frame0 << "-frame range) at ratio "
Chris@1338 774 << oversampleBy << " with tail " << tail
Chris@1338 775 << " -> got " << oversampled.size()
Chris@1338 776 << " oversampled values for channel " << ch
Chris@1338 777 << ", from which returning " << rr.size() << " ranges" << endl;
Chris@1338 778 #endif
Chris@1338 779 }
Chris@1338 780
Chris@1338 781 return;
Chris@1338 782 }
Chris@1338 783
Chris@1338 784 void
Chris@1338 785 WaveformLayer::paintChannel(LayerGeometryProvider *v,
Chris@1338 786 QPainter *paint,
Chris@1338 787 QRect rect, int ch,
Chris@1338 788 const RangeVec &ranges,
Chris@1338 789 int blockSize,
Chris@1352 790 sv_frame_t frame0,
Chris@1352 791 sv_frame_t frame1)
Chris@1332 792 const
Chris@1332 793 {
Chris@1474 794 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 795 if (!model) return;
Chris@1474 796
Chris@1334 797 int x0 = rect.left();
Chris@1334 798 int y0 = rect.top();
Chris@1334 799
Chris@1334 800 int x1 = rect.right();
Chris@1334 801 int y1 = rect.bottom();
Chris@1334 802
Chris@1332 803 int h = v->getPaintHeight();
Chris@1332 804
Chris@1332 805 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@1332 806 bool mergingChannels = false, mixingChannels = false;
Chris@1332 807
Chris@1332 808 channels = getChannelArrangement(minChannel, maxChannel,
Chris@1332 809 mergingChannels, mixingChannels);
Chris@1332 810 if (channels == 0) return;
Chris@1332 811
Chris@1332 812 QColor baseColour = getBaseQColor();
Chris@1332 813 QColor midColour = baseColour;
Chris@1367 814
Chris@1332 815 if (midColour == Qt::black) {
Chris@1332 816 midColour = Qt::gray;
Chris@1332 817 } else if (v->hasLightBackground()) {
Chris@1474 818 midColour = midColour.lighter(150);
Chris@1332 819 } else {
Chris@1474 820 midColour = midColour.lighter(50);
Chris@1332 821 }
Chris@1332 822
Chris@1332 823 double gain = m_effectiveGains[ch];
Chris@1332 824
Chris@1332 825 int m = (h / channels) / 2;
Chris@1332 826 int my = m + (((ch - minChannel) * h) / channels);
Chris@1332 827
Chris@1332 828 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 829 SVCERR << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl;
Chris@1332 830 #endif
Chris@1332 831
Chris@1332 832 if (my - m > y1 || my + m < y0) return;
Chris@1332 833
Chris@1332 834 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@1332 835 m_channelMode != MergeChannels) {
Chris@1332 836 m = (h / channels);
Chris@1332 837 my = m + (((ch - minChannel) * h) / channels);
Chris@1332 838 }
Chris@1332 839
Chris@1367 840 // Horizontal axis along middle
Chris@1367 841 paint->setPen(QPen(midColour, 0));
Chris@1393 842 paint->drawLine(QPointF(x0, my + 0.5), QPointF(x1, my + 0.5));
Chris@1332 843
Chris@1335 844 paintChannelScaleGuides(v, paint, rect, ch);
Chris@1332 845
Chris@1333 846 int rangeix = ch - minChannel;
Chris@1338 847
Chris@1338 848 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 849 SVCERR << "paint channel " << ch << ": frame0 = " << frame0 << ", frame1 = " << frame1 << ", blockSize = " << blockSize << ", have " << ranges.size() << " range blocks of which ours is index " << rangeix << " with " << ranges[rangeix].size() << " ranges in it" << endl;
Chris@1352 850 #else
Chris@1352 851 (void)frame1; // not actually used
Chris@1338 852 #endif
Chris@1338 853
Chris@1367 854 QPainterPath waveformPath;
Chris@1367 855 QPainterPath meanPath;
Chris@1367 856 QPainterPath clipPath;
Chris@1367 857 vector<QPointF> individualSamplePoints;
Chris@1367 858
Chris@1367 859 bool firstPoint = true;
Chris@1372 860 double prevRangeBottom = 0, prevRangeTop = 0;
Chris@1367 861
Chris@1332 862 for (int x = x0; x <= x1; ++x) {
Chris@1332 863
Chris@1332 864 sv_frame_t f0, f1;
Chris@1338 865 sv_frame_t i0, i1;
Chris@1338 866
Chris@1338 867 bool showIndividualSample = false;
Chris@1338 868
Chris@1338 869 if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
Chris@1338 870 if (!getSourceFramesForX(v, x, blockSize, f0, f1)) {
Chris@1338 871 continue;
Chris@1338 872 }
Chris@1338 873 f1 = f1 - 1;
Chris@1338 874 i0 = (f0 - frame0) / blockSize;
Chris@1338 875 i1 = (f1 - frame0) / blockSize;
Chris@1338 876 } else {
Chris@1338 877 int oversampleBy = v->getZoomLevel().level;
Chris@1338 878 f0 = f1 = v->getFrameForX(x);
Chris@1338 879 int xf0 = v->getXForFrame(f0);
Chris@1338 880 showIndividualSample = (x == xf0);
Chris@1338 881 i0 = i1 = (f0 - frame0) * oversampleBy + (x - xf0);
Chris@1338 882 }
Chris@1332 883
Chris@1332 884 if (f0 < frame0) {
Chris@1375 885 // Not an error, this simply occurs when painting the
Chris@1375 886 // start of a signal in PixelsPerFrame zone
Chris@1332 887 continue;
Chris@1332 888 }
Chris@1332 889
Chris@1338 890 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL
Chris@1338 891 SVCERR << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl;
Chris@1332 892 #endif
Chris@1332 893
Chris@1332 894 if (i1 > i0 + 1) {
Chris@1338 895 SVCERR << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << v->getZoomLevel() << ", model zoom = " << blockSize << ")" << endl;
Chris@1332 896 }
Chris@1332 897
Chris@1333 898 const auto &r = ranges[rangeix];
Chris@1333 899 RangeSummarisableTimeValueModel::Range range;
Chris@1333 900
Chris@1333 901 if (in_range_for(r, i0)) {
Chris@1332 902
Chris@1333 903 range = r[i0];
Chris@1332 904
Chris@1333 905 if (i1 > i0 && in_range_for(r, i1)) {
Chris@1333 906 range.setMax(std::max(range.max(), r[i1].max()));
Chris@1333 907 range.setMin(std::min(range.min(), r[i1].min()));
Chris@1333 908 range.setAbsmean((range.absmean() + r[i1].absmean()) / 2);
Chris@1332 909 }
Chris@1332 910
Chris@1332 911 } else {
Chris@1332 912 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 913 SVCERR << "No (or not enough) ranges for index i0 = " << i0 << " (there are " << r.size() << " range(s))" << endl;
Chris@1332 914 #endif
Chris@1332 915 continue;
Chris@1332 916 }
Chris@1332 917
Chris@1367 918 double rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@1332 919
Chris@1333 920 if (mergingChannels && ranges.size() > 1) {
Chris@1332 921
Chris@1333 922 const auto &other = ranges[1];
Chris@1333 923
Chris@1333 924 if (in_range_for(other, i0)) {
Chris@1332 925
Chris@1332 926 range.setMax(fabsf(range.max()));
Chris@1333 927 range.setMin(-fabsf(other[i0].max()));
Chris@1332 928 range.setAbsmean
Chris@1333 929 ((range.absmean() + other[i0].absmean()) / 2);
Chris@1332 930
Chris@1333 931 if (i1 > i0 && in_range_for(other, i1)) {
Chris@1332 932 // let's not concern ourselves about the mean
Chris@1333 933 range.setMin(std::min(range.min(),
Chris@1333 934 -fabsf(other[i1].max())));
Chris@1332 935 }
Chris@1332 936 }
Chris@1332 937
Chris@1333 938 } else if (mixingChannels && ranges.size() > 1) {
Chris@1332 939
Chris@1333 940 const auto &other = ranges[1];
Chris@1333 941
Chris@1333 942 if (in_range_for(other, i0)) {
Chris@1332 943
Chris@1333 944 range.setMax((range.max() + other[i0].max()) / 2);
Chris@1333 945 range.setMin((range.min() + other[i0].min()) / 2);
Chris@1333 946 range.setAbsmean((range.absmean() + other[i0].absmean()) / 2);
Chris@1332 947 }
Chris@1332 948 }
Chris@1332 949
Chris@1332 950 switch (m_scale) {
Chris@1332 951
Chris@1332 952 case LinearScale:
Chris@1367 953 rangeBottom = range.min() * gain * m;
Chris@1367 954 rangeTop = range.max() * gain * m;
Chris@1367 955 meanBottom = range.absmean() * gain * (-m);
Chris@1367 956 meanTop = range.absmean() * gain * m;
Chris@1332 957 break;
Chris@1332 958
Chris@1332 959 case dBScale:
Chris@1332 960 if (!mergingChannels) {
Chris@1367 961 double db0 = dBscale(range.min() * gain, m);
Chris@1367 962 double db1 = dBscale(range.max() * gain, m);
Chris@1367 963 rangeTop = std::max(db0, db1);
Chris@1367 964 meanTop = std::min(db0, db1);
Chris@1332 965 if (mixingChannels) rangeBottom = meanTop;
Chris@1332 966 else rangeBottom = dBscale(range.absmean() * gain, m);
Chris@1367 967 meanBottom = rangeBottom;
Chris@1332 968 } else {
Chris@1367 969 rangeBottom = -dBscale(range.min() * gain, m);
Chris@1367 970 rangeTop = dBscale(range.max() * gain, m);
Chris@1367 971 meanBottom = -dBscale(range.absmean() * gain, m);
Chris@1367 972 meanTop = dBscale(range.absmean() * gain, m);
Chris@1332 973 }
Chris@1332 974 break;
Chris@1332 975
Chris@1332 976 case MeterScale:
Chris@1332 977 if (!mergingChannels) {
Chris@1405 978 double r0 = std::abs(AudioLevel::multiplier_to_preview
Chris@1367 979 (range.min() * gain, m));
Chris@1405 980 double r1 = std::abs(AudioLevel::multiplier_to_preview
Chris@1367 981 (range.max() * gain, m));
Chris@1367 982 rangeTop = std::max(r0, r1);
Chris@1367 983 meanTop = std::min(r0, r1);
Chris@1332 984 if (mixingChannels) rangeBottom = meanTop;
Chris@1367 985 else rangeBottom = AudioLevel::multiplier_to_preview
Chris@1367 986 (range.absmean() * gain, m);
Chris@1332 987 meanBottom = rangeBottom;
Chris@1332 988 } else {
Chris@1367 989 rangeBottom = -AudioLevel::multiplier_to_preview
Chris@1367 990 (range.min() * gain, m);
Chris@1367 991 rangeTop = AudioLevel::multiplier_to_preview
Chris@1367 992 (range.max() * gain, m);
Chris@1367 993 meanBottom = -AudioLevel::multiplier_to_preview
Chris@1367 994 (range.absmean() * gain, m);
Chris@1367 995 meanTop = AudioLevel::multiplier_to_preview
Chris@1367 996 (range.absmean() * gain, m);
Chris@1332 997 }
Chris@1332 998 break;
Chris@1332 999 }
Chris@1332 1000
Chris@1367 1001 rangeBottom = my - rangeBottom;
Chris@1367 1002 rangeTop = my - rangeTop;
Chris@1367 1003 meanBottom = my - meanBottom;
Chris@1367 1004 meanTop = my - meanTop;
Chris@1332 1005
Chris@1332 1006 bool clipped = false;
Chris@1332 1007
Chris@1332 1008 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@1332 1009 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@1332 1010 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@1332 1011 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@1332 1012
Chris@1367 1013 if (range.max() <= -1.0 || range.max() >= 1.0) {
Chris@1367 1014 clipped = true;
Chris@1367 1015 }
Chris@1332 1016
Chris@1367 1017 bool drawMean = m_showMeans;
Chris@1332 1018
Chris@1367 1019 meanTop = meanTop - 0.5;
Chris@1367 1020 meanBottom = meanBottom + 0.5;
Chris@1367 1021
Chris@1367 1022 if (meanTop <= rangeTop + 1.0) {
Chris@1367 1023 meanTop = rangeTop + 1.0;
Chris@1332 1024 }
Chris@1367 1025 if (meanBottom >= rangeBottom - 1.0 && m_scale == LinearScale) {
Chris@1367 1026 meanBottom = rangeBottom - 1.0;
Chris@1332 1027 }
Chris@1367 1028 if (meanTop > meanBottom - 1.0) {
Chris@1367 1029 drawMean = false;
Chris@1332 1030 }
Chris@1332 1031
Chris@1338 1032 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL
Chris@1338 1033 SVCERR << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
Chris@1332 1034 #endif
Chris@1332 1035
Chris@1367 1036 double rangeMiddle = (rangeTop + rangeBottom) / 2.0;
Chris@1367 1037 bool trivialRange = (fabs(rangeTop - rangeBottom) < 1.0);
Chris@1367 1038 double px = x + 0.5;
Chris@1367 1039
Chris@1367 1040 if (showIndividualSample) {
Chris@1367 1041 individualSamplePoints.push_back(QPointF(px, rangeTop));
Chris@1367 1042 if (!trivialRange) {
Chris@1367 1043 // common e.g. in "butterfly" merging mode
Chris@1367 1044 individualSamplePoints.push_back(QPointF(px, rangeBottom));
Chris@1332 1045 }
Chris@1332 1046 }
Chris@1372 1047
Chris@1372 1048 bool contiguous = true;
Chris@1372 1049 if (rangeTop > prevRangeBottom + 0.5 ||
Chris@1372 1050 rangeBottom < prevRangeTop - 0.5) {
Chris@1372 1051 contiguous = false;
Chris@1372 1052 }
Chris@1332 1053
Chris@1372 1054 if (firstPoint || (contiguous && !trivialRange)) {
Chris@1372 1055 waveformPath.moveTo(QPointF(px, rangeTop));
Chris@1372 1056 waveformPath.lineTo(QPointF(px, rangeBottom));
Chris@1372 1057 waveformPath.moveTo(QPointF(px, rangeMiddle));
Chris@1367 1058 } else {
Chris@1367 1059 waveformPath.lineTo(QPointF(px, rangeMiddle));
Chris@1372 1060 if (!trivialRange) {
Chris@1372 1061 waveformPath.lineTo(QPointF(px, rangeTop));
Chris@1372 1062 waveformPath.lineTo(QPointF(px, rangeBottom));
Chris@1372 1063 waveformPath.lineTo(QPointF(px, rangeMiddle));
Chris@1372 1064 }
Chris@1367 1065 }
Chris@1367 1066
Chris@1372 1067 firstPoint = false;
Chris@1372 1068 prevRangeTop = rangeTop;
Chris@1372 1069 prevRangeBottom = rangeBottom;
Chris@1372 1070
Chris@1332 1071 if (drawMean) {
Chris@1367 1072 meanPath.moveTo(QPointF(px, meanBottom));
Chris@1367 1073 meanPath.lineTo(QPointF(px, meanTop));
Chris@1332 1074 }
Chris@1367 1075
Chris@1367 1076 if (clipped) {
Chris@1367 1077 if (trivialRange) {
Chris@1367 1078 clipPath.moveTo(QPointF(px, rangeMiddle));
Chris@1367 1079 clipPath.lineTo(QPointF(px+1, rangeMiddle));
Chris@1367 1080 } else {
Chris@1367 1081 clipPath.moveTo(QPointF(px, rangeBottom));
Chris@1367 1082 clipPath.lineTo(QPointF(px, rangeTop));
Chris@1367 1083 }
Chris@1367 1084 }
Chris@1367 1085 }
Chris@1367 1086
Chris@1367 1087 double penWidth = 1.0;
Chris@1418 1088 if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
Chris@1418 1089 penWidth = 0.0;
Chris@1418 1090 }
Chris@1367 1091
Chris@1474 1092 if (model->isReady()) {
Chris@1367 1093 paint->setPen(QPen(baseColour, penWidth));
Chris@1367 1094 } else {
Chris@1367 1095 paint->setPen(QPen(midColour, penWidth));
Chris@1367 1096 }
Chris@1367 1097 paint->drawPath(waveformPath);
Chris@1367 1098
Chris@1367 1099 if (!clipPath.isEmpty()) {
Chris@1367 1100 paint->save();
Chris@1367 1101 paint->setPen(QPen(ColourDatabase::getInstance()->
Chris@1367 1102 getContrastingColour(m_colour), penWidth));
Chris@1367 1103 paint->drawPath(clipPath);
Chris@1367 1104 paint->restore();
Chris@1367 1105 }
Chris@1367 1106
Chris@1367 1107 if (!meanPath.isEmpty()) {
Chris@1367 1108 paint->save();
Chris@1367 1109 paint->setPen(QPen(midColour, penWidth));
Chris@1367 1110 paint->drawPath(meanPath);
Chris@1367 1111 paint->restore();
Chris@1367 1112 }
Chris@1367 1113
Chris@1367 1114 if (!individualSamplePoints.empty()) {
Chris@1401 1115 double sz = v->scaleSize(2.0);
Chris@1371 1116 if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
Chris@1371 1117 if (v->getZoomLevel().level < 10) {
Chris@1401 1118 sz = v->scaleSize(1.2);
Chris@1371 1119 }
Chris@1371 1120 }
Chris@1367 1121 paint->save();
Chris@1367 1122 paint->setPen(QPen(baseColour, penWidth));
Chris@1367 1123 for (QPointF p: individualSamplePoints) {
Chris@1367 1124 paint->drawRect(QRectF(p.x() - sz/2, p.y() - sz/2, sz, sz));
Chris@1367 1125 }
Chris@1367 1126 paint->restore();
Chris@1332 1127 }
Chris@1332 1128 }
Chris@1332 1129
Chris@1335 1130 void
Chris@1335 1131 WaveformLayer::paintChannelScaleGuides(LayerGeometryProvider *v,
Chris@1335 1132 QPainter *paint,
Chris@1335 1133 QRect rect,
Chris@1335 1134 int ch) const
Chris@1335 1135 {
Chris@1335 1136 int x0 = rect.left();
Chris@1335 1137 int x1 = rect.right();
Chris@1335 1138
Chris@1335 1139 int n = 10;
Chris@1335 1140 int py = -1;
Chris@1335 1141
Chris@1335 1142 double gain = m_effectiveGains[ch];
Chris@1335 1143
Chris@1335 1144 if (v->hasLightBackground() &&
Chris@1335 1145 v->getViewManager() &&
Chris@1335 1146 v->getViewManager()->shouldShowScaleGuides()) {
Chris@1335 1147
Chris@1335 1148 paint->setPen(QColor(240, 240, 240));
Chris@1335 1149
Chris@1335 1150 for (int i = 1; i < n; ++i) {
Chris@1335 1151
Chris@1335 1152 double val = 0.0, nval = 0.0;
Chris@1335 1153
Chris@1335 1154 switch (m_scale) {
Chris@1335 1155
Chris@1335 1156 case LinearScale:
Chris@1335 1157 val = (i * gain) / n;
Chris@1335 1158 if (i > 0) nval = -val;
Chris@1335 1159 break;
Chris@1335 1160
Chris@1335 1161 case MeterScale:
Chris@1335 1162 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@1335 1163 break;
Chris@1335 1164
Chris@1335 1165 case dBScale:
Chris@1335 1166 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@1335 1167 break;
Chris@1335 1168 }
Chris@1335 1169
Chris@1335 1170 if (val < -1.0 || val > 1.0) continue;
Chris@1335 1171
Chris@1335 1172 int y = getYForValue(v, val, ch);
Chris@1335 1173
Chris@1335 1174 if (py >= 0 && abs(y - py) < 10) continue;
Chris@1335 1175 else py = y;
Chris@1335 1176
Chris@1335 1177 int ny = y;
Chris@1335 1178 if (nval != 0.0) {
Chris@1335 1179 ny = getYForValue(v, nval, ch);
Chris@1335 1180 }
Chris@1335 1181
Chris@1335 1182 paint->drawLine(x0, y, x1, y);
Chris@1335 1183 if (ny != y) {
Chris@1335 1184 paint->drawLine(x0, ny, x1, ny);
Chris@1335 1185 }
Chris@1335 1186 }
Chris@1335 1187 }
Chris@1335 1188 }
Chris@1335 1189
Chris@25 1190 QString
Chris@918 1191 WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@25 1192 {
Chris@25 1193 int x = pos.x();
Chris@25 1194
Chris@1474 1195 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 1196 if (!model || !model->isOK()) return "";
Chris@25 1197
Chris@1325 1198 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@25 1199
Chris@1325 1200 int desiredBlockSize = 1;
Chris@1325 1201 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1325 1202 desiredBlockSize = zoomLevel.level;
Chris@1325 1203 }
Chris@1325 1204
Chris@1474 1205 int blockSize = model->getSummaryBlockSize(desiredBlockSize);
Chris@365 1206
Chris@908 1207 sv_frame_t f0, f1;
Chris@1325 1208 if (!getSourceFramesForX(v, x, blockSize, f0, f1)) return "";
Chris@25 1209
Chris@25 1210 QString text;
Chris@25 1211
Chris@1474 1212 RealTime rt0 = RealTime::frame2RealTime(f0, model->getSampleRate());
Chris@1474 1213 RealTime rt1 = RealTime::frame2RealTime(f1, model->getSampleRate());
Chris@25 1214
Chris@25 1215 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@1266 1216 text += tr("Time:\t%1 - %2")
Chris@1266 1217 .arg(rt0.toText(true).c_str())
Chris@1266 1218 .arg(rt1.toText(true).c_str());
Chris@25 1219 } else {
Chris@1266 1220 text += tr("Time:\t%1")
Chris@1266 1221 .arg(rt0.toText(true).c_str());
Chris@25 1222 }
Chris@25 1223
Chris@805 1224 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1225 bool mergingChannels = false, mixingChannels = false;
Chris@25 1226
Chris@67 1227 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1228 mergingChannels, mixingChannels);
Chris@25 1229 if (channels == 0) return "";
Chris@25 1230
Chris@805 1231 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 1232
Chris@1266 1233 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@1474 1234 model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 1235
Chris@1266 1236 if (ranges.empty()) continue;
Chris@1266 1237
Chris@1266 1238 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@1266 1239
Chris@1266 1240 QString label = tr("Level:");
Chris@1266 1241 if (minChannel != maxChannel) {
Chris@1266 1242 if (ch == 0) label = tr("Left:");
Chris@1266 1243 else if (ch == 1) label = tr("Right:");
Chris@1266 1244 else label = tr("Channel %1").arg(ch + 1);
Chris@1266 1245 }
Chris@25 1246
Chris@76 1247 bool singleValue = false;
Chris@908 1248 double min, max;
Chris@76 1249
Chris@386 1250 if (fabs(range.min()) < 0.01) {
Chris@386 1251 min = range.min();
Chris@386 1252 max = range.max();
Chris@76 1253 singleValue = (min == max);
Chris@76 1254 } else {
Chris@908 1255 int imin = int(lrint(range.min() * 10000));
Chris@908 1256 int imax = int(lrint(range.max() * 10000));
Chris@76 1257 singleValue = (imin == imax);
Chris@908 1258 min = double(imin)/10000;
Chris@908 1259 max = double(imax)/10000;
Chris@76 1260 }
Chris@76 1261
Chris@1266 1262 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
Chris@1266 1263 fabsf(range.max())))
Chris@1266 1264 * 100);
Chris@25 1265
Chris@1266 1266 if (!singleValue) {
Chris@1266 1267 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@1266 1268 .arg(label).arg(min).arg(max).arg(double(db)/100);
Chris@1266 1269 } else {
Chris@1266 1270 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@1266 1271 .arg(label).arg(min).arg(double(db)/100);
Chris@1266 1272 }
Chris@25 1273 }
Chris@25 1274
Chris@25 1275 return text;
Chris@25 1276 }
Chris@25 1277
Chris@0 1278 int
Chris@918 1279 WaveformLayer::getYForValue(const LayerGeometryProvider *v, double value, int channel) const
Chris@68 1280 {
Chris@805 1281 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1282 bool mergingChannels = false, mixingChannels = false;
Chris@274 1283
Chris@274 1284 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1285 mergingChannels, mixingChannels);
Chris@853 1286 if (channels == 0) return 0;
Chris@68 1287 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 1288
Chris@918 1289 int h = v->getPaintHeight();
Chris@68 1290 int m = (h / channels) / 2;
Chris@1266 1291
Chris@68 1292 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 1293 m_channelMode != MergeChannels) {
Chris@68 1294 m = (h / channels);
Chris@68 1295 }
Chris@68 1296
Chris@274 1297 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 1298
Chris@68 1299 int vy = 0;
Chris@68 1300
Chris@274 1301 switch (m_scale) {
Chris@68 1302
Chris@68 1303 case LinearScale:
Chris@68 1304 vy = int(m * value);
Chris@68 1305 break;
Chris@68 1306
Chris@68 1307 case MeterScale:
Chris@68 1308 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 1309 break;
Chris@68 1310
Chris@68 1311 case dBScale:
Chris@1367 1312 vy = int(dBscale(value, m));
Chris@68 1313 break;
Chris@68 1314 }
Chris@68 1315
Chris@1338 1316 // SVCERR << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << endl;
Chris@324 1317
Chris@68 1318 return my - vy;
Chris@68 1319 }
Chris@68 1320
Chris@908 1321 double
Chris@918 1322 WaveformLayer::getValueForY(const LayerGeometryProvider *v, int y, int &channel) const
Chris@261 1323 {
Chris@805 1324 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1325 bool mergingChannels = false, mixingChannels = false;
Chris@274 1326
Chris@274 1327 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1328 mergingChannels, mixingChannels);
Chris@853 1329 if (channels == 0) return 0;
Chris@261 1330 if (maxChannel < minChannel) return 0;
Chris@261 1331
Chris@918 1332 int h = v->getPaintHeight();
Chris@261 1333 int m = (h / channels) / 2;
Chris@261 1334
Chris@261 1335 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1336 m_channelMode != MergeChannels) {
Chris@261 1337 m = (h / channels);
Chris@261 1338 }
Chris@274 1339
Chris@274 1340 channel = (y * channels) / h + minChannel;
Chris@261 1341
Chris@261 1342 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1343
Chris@262 1344 int vy = my - y;
Chris@908 1345 double value = 0;
Chris@908 1346 double thresh = -50.f;
Chris@261 1347
Chris@274 1348 switch (m_scale) {
Chris@261 1349
Chris@261 1350 case LinearScale:
Chris@908 1351 value = double(vy) / m;
Chris@261 1352 break;
Chris@261 1353
Chris@261 1354 case MeterScale:
Chris@261 1355 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1356 break;
Chris@261 1357
Chris@261 1358 case dBScale:
Chris@908 1359 value = (-thresh * double(vy)) / m + thresh;
Chris@274 1360 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1361 break;
Chris@261 1362 }
Chris@261 1363
Chris@274 1364 return value / m_gain;
Chris@261 1365 }
Chris@261 1366
Chris@261 1367 bool
Chris@918 1368 WaveformLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
Chris@908 1369 double &value, QString &unit) const
Chris@261 1370 {
Chris@805 1371 int channel;
Chris@261 1372
Chris@274 1373 value = getValueForY(v, y, channel);
Chris@261 1374
Chris@274 1375 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1376
Chris@908 1377 double thresh = -50.f;
Chris@274 1378
Chris@908 1379 if (value > 0.0) {
Chris@908 1380 value = 10.0 * log10(value);
Chris@274 1381 if (value < thresh) value = thresh;
Chris@274 1382 } else value = thresh;
Chris@274 1383
Chris@274 1384 unit = "dBV";
Chris@274 1385
Chris@274 1386 } else {
Chris@274 1387 unit = "V";
Chris@274 1388 }
Chris@274 1389
Chris@274 1390 return true;
Chris@274 1391 }
Chris@274 1392
Chris@274 1393 bool
Chris@918 1394 WaveformLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
Chris@908 1395 double &diff, QString &unit) const
Chris@274 1396 {
Chris@805 1397 int c0, c1;
Chris@908 1398 double v0 = getValueForY(v, y0, c0);
Chris@908 1399 double v1 = getValueForY(v, y1, c1);
Chris@274 1400
Chris@274 1401 if (c0 != c1) {
Chris@274 1402 // different channels, not comparable
Chris@908 1403 diff = 0.0;
Chris@274 1404 unit = "";
Chris@274 1405 return false;
Chris@274 1406 }
Chris@274 1407
Chris@274 1408 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1409
Chris@908 1410 double thresh = -50.0;
Chris@274 1411
Chris@274 1412 if (v1 == v0) diff = thresh;
Chris@274 1413 else {
Chris@274 1414 if (v1 > v0) diff = v0 / v1;
Chris@274 1415 else diff = v1 / v0;
Chris@274 1416
Chris@908 1417 diff = 10.0 * log10(diff);
Chris@274 1418 if (diff < thresh) diff = thresh;
Chris@274 1419 }
Chris@274 1420
Chris@274 1421 unit = "dBV";
Chris@274 1422
Chris@274 1423 } else {
Chris@908 1424 diff = fabs(v1 - v0);
Chris@274 1425 unit = "V";
Chris@274 1426 }
Chris@274 1427
Chris@261 1428 return true;
Chris@261 1429 }
Chris@261 1430
Chris@68 1431 int
Chris@918 1432 WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@0 1433 {
Chris@1474 1434 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1474 1435 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1474 1436 // which is too new for us
Chris@1474 1437 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1474 1438
Chris@0 1439 if (m_scale == LinearScale) {
Chris@1266 1440 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1441 } else {
Chris@1266 1442 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@1266 1443 paint.fontMetrics().width(Strings::minus_infinity)) + 13;
Chris@0 1444 }
Chris@0 1445 }
Chris@0 1446
Chris@0 1447 void
Chris@918 1448 WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@0 1449 {
Chris@1474 1450 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 1451 if (!model || !model->isOK()) {
Chris@1266 1452 return;
Chris@0 1453 }
Chris@0 1454
Chris@805 1455 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1456 bool mergingChannels = false, mixingChannels = false;
Chris@0 1457
Chris@67 1458 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1459 mergingChannels, mixingChannels);
Chris@0 1460 if (channels == 0) return;
Chris@0 1461
Chris@0 1462 int h = rect.height(), w = rect.width();
Chris@0 1463 int textHeight = paint.fontMetrics().height();
Chris@0 1464 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1465
Chris@908 1466 double gain = m_gain;
Chris@67 1467
Chris@805 1468 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1469
Chris@1266 1470 int lastLabelledY = -1;
Chris@0 1471
Chris@805 1472 if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1473
Chris@68 1474 int n = 10;
Chris@0 1475
Chris@1266 1476 for (int i = 0; i <= n; ++i) {
Chris@68 1477
Chris@908 1478 double val = 0.0, nval = 0.0;
Chris@1266 1479 QString text = "";
Chris@0 1480
Chris@68 1481 switch (m_scale) {
Chris@68 1482
Chris@68 1483 case LinearScale:
Chris@68 1484 val = (i * gain) / n;
Chris@1266 1485 text = QString("%1").arg(double(i) / n);
Chris@1266 1486 if (i == 0) text = "0.0";
Chris@68 1487 else {
Chris@68 1488 nval = -val;
Chris@68 1489 if (i == n) text = "1.0";
Chris@68 1490 }
Chris@68 1491 break;
Chris@0 1492
Chris@68 1493 case MeterScale:
Chris@68 1494 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@1266 1495 text = QString("%1").arg(meterdbs[i]);
Chris@1266 1496 if (i == n) text = tr("0dB");
Chris@1266 1497 if (i == 0) {
Chris@1147 1498 text = Strings::minus_infinity;
Chris@68 1499 val = 0.0;
Chris@1266 1500 }
Chris@68 1501 break;
Chris@0 1502
Chris@68 1503 case dBScale:
Chris@68 1504 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@1266 1505 text = QString("%1").arg(-(10*n) + i * 10);
Chris@1266 1506 if (i == n) text = tr("0dB");
Chris@1266 1507 if (i == 0) {
Chris@1147 1508 text = Strings::minus_infinity;
Chris@68 1509 val = 0.0;
Chris@1266 1510 }
Chris@68 1511 break;
Chris@68 1512 }
Chris@0 1513
Chris@68 1514 if (val < -1.0 || val > 1.0) continue;
Chris@0 1515
Chris@274 1516 int y = getYForValue(v, val, ch);
Chris@0 1517
Chris@68 1518 int ny = y;
Chris@68 1519 if (nval != 0.0) {
Chris@274 1520 ny = getYForValue(v, nval, ch);
Chris@68 1521 }
Chris@0 1522
Chris@68 1523 bool spaceForLabel = (i == 0 ||
Chris@68 1524 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1525
Chris@68 1526 if (spaceForLabel) {
Chris@0 1527
Chris@68 1528 int tx = 3;
Chris@68 1529 if (m_scale != LinearScale) {
Chris@68 1530 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1531 }
Chris@68 1532
Chris@68 1533 int ty = y;
Chris@68 1534 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1535 ty = paint.fontMetrics().ascent();
Chris@68 1536 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1537 ty = h - paint.fontMetrics().descent();
Chris@68 1538 } else {
Chris@68 1539 ty += toff;
Chris@68 1540 }
Chris@68 1541 paint.drawText(tx, ty, text);
Chris@0 1542
Chris@68 1543 lastLabelledY = ty - toff;
Chris@67 1544
Chris@68 1545 if (ny != y) {
Chris@68 1546 ty = ny;
Chris@68 1547 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1548 ty = paint.fontMetrics().ascent();
Chris@68 1549 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1550 ty = h - paint.fontMetrics().descent();
Chris@68 1551 } else {
Chris@68 1552 ty += toff;
Chris@68 1553 }
Chris@68 1554 paint.drawText(tx, ty, text);
Chris@68 1555 }
Chris@68 1556
Chris@68 1557 paint.drawLine(w - 7, y, w, y);
Chris@68 1558 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1559
Chris@68 1560 } else {
Chris@68 1561
Chris@68 1562 paint.drawLine(w - 4, y, w, y);
Chris@68 1563 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1564 }
Chris@1266 1565 }
Chris@0 1566 }
Chris@0 1567 }
Chris@0 1568
Chris@316 1569 void
Chris@316 1570 WaveformLayer::toXml(QTextStream &stream,
Chris@316 1571 QString indent, QString extraAttributes) const
Chris@6 1572 {
Chris@6 1573 QString s;
Chris@6 1574
Chris@285 1575 QString colourName, colourSpec, darkbg;
Chris@285 1576 ColourDatabase::getInstance()->getStringValues
Chris@285 1577 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1578
Chris@6 1579 s += QString("gain=\"%1\" "
Chris@1266 1580 "showMeans=\"%2\" "
Chris@1266 1581 "greyscale=\"%3\" "
Chris@1266 1582 "channelMode=\"%4\" "
Chris@1266 1583 "channel=\"%5\" "
Chris@287 1584 "scale=\"%6\" "
Chris@709 1585 "middleLineHeight=\"%7\" "
Chris@1266 1586 "aggressive=\"%8\" "
Chris@709 1587 "autoNormalize=\"%9\"")
Chris@1266 1588 .arg(m_gain)
Chris@1266 1589 .arg(m_showMeans)
Chris@1367 1590 .arg(true) // Option removed, but effectively always on, so
Chris@1367 1591 // retained in the session file for compatibility
Chris@1266 1592 .arg(m_channelMode)
Chris@1266 1593 .arg(m_channel)
Chris@1266 1594 .arg(m_scale)
Chris@709 1595 .arg(m_middleLineHeight)
Chris@1266 1596 .arg(m_aggressive)
Chris@67 1597 .arg(m_autoNormalize);
Chris@6 1598
Chris@316 1599 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 1600 }
Chris@6 1601
Chris@11 1602 void
Chris@11 1603 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1604 {
Chris@11 1605 bool ok = false;
Chris@11 1606
Chris@287 1607 SingleColourLayer::setProperties(attributes);
Chris@287 1608
Chris@11 1609 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1610 if (ok) setGain(gain);
Chris@11 1611
Chris@11 1612 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@1266 1613 attributes.value("showMeans") == "true");
Chris@11 1614 setShowMeans(showMeans);
Chris@11 1615
Chris@11 1616 ChannelMode channelMode = (ChannelMode)
Chris@1266 1617 attributes.value("channelMode").toInt(&ok);
Chris@11 1618 if (ok) setChannelMode(channelMode);
Chris@11 1619
Chris@11 1620 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1621 if (ok) setChannel(channel);
Chris@11 1622
Chris@709 1623 Scale scale = (Scale)attributes.value("scale").toInt(&ok);
Chris@11 1624 if (ok) setScale(scale);
Chris@11 1625
Chris@709 1626 float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
Chris@709 1627 if (ok) setMiddleLineHeight(middleLineHeight);
Chris@709 1628
Chris@11 1629 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@1266 1630 attributes.value("aggressive") == "true");
Chris@1367 1631 setAggressiveCacheing(aggressive);
Chris@67 1632
Chris@67 1633 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1634 attributes.value("autoNormalize") == "true");
Chris@67 1635 setAutoNormalize(autoNormalize);
Chris@11 1636 }
Chris@11 1637
Chris@133 1638 int
Chris@133 1639 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1640 {
Chris@133 1641 defaultStep = 50;
Chris@133 1642 return 100;
Chris@133 1643 }
Chris@0 1644
Chris@133 1645 int
Chris@133 1646 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1647 {
Chris@908 1648 int val = int(lrint(log10(m_gain) * 20.0) + 50);
Chris@133 1649 if (val < 0) val = 0;
Chris@133 1650 if (val > 100) val = 100;
Chris@133 1651 return val;
Chris@133 1652 }
Chris@133 1653
Chris@133 1654 void
Chris@133 1655 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1656 {
Chris@908 1657 setGain(powf(10, float(step - 50) / 20.f));
Chris@133 1658 }
Chris@133 1659