annotate layer/WaveformLayer.cpp @ 789:9fd1bdf214dd tonioni

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