annotate layer/WaveformLayer.cpp @ 1615:911330a28a7c

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