annotate layer/WaveformLayer.cpp @ 1024:3bce4c45b681 spectrogram-minor-refactor

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