annotate layer/WaveformLayer.cpp @ 947:e53a87a5efb2

Allow layers to be loaded without models if their layer class explicitly says it's OK (otherwise default template won't load, as it has an empty waveform layer)
author Chris Cannam
date Mon, 20 Apr 2015 10:10:26 +0100
parents 4a578a360011
children f6d9f28f37cb 3871dffc31bd
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@115 329 WaveformLayer::getCompletion(View *) 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@67 402 WaveformLayer::isLayerScrollable(const View *) 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@805 411 WaveformLayer::getSourceFramesForX(View *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@365 436 WaveformLayer::getNormalizeGain(View *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@44 476 WaveformLayer::paint(View *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@44 497 int w = v->width();
Chris@44 498 int h = v->height();
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@44 562 if (x1 < v->width()) ++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@805 733 if (ranges && i0 < (int)ranges->size()) {
Chris@362 734
Chris@365 735 range = (*ranges)[i0];
Chris@0 736
Chris@805 737 if (i1 > i0 && i1 < (int)ranges->size()) {
Chris@386 738 range.setMax(std::max(range.max(), (*ranges)[i1].max()));
Chris@386 739 range.setMin(std::min(range.min(), (*ranges)[i1].min()));
Chris@386 740 range.setAbsmean((range.absmean() + (*ranges)[i1].absmean()) / 2);
Chris@0 741 }
Chris@0 742
Chris@0 743 } else {
Chris@612 744 #ifdef DEBUG_WAVEFORM_PAINT
Chris@682 745 cerr << "No (or not enough) ranges for i0 = " << i0 << endl;
Chris@612 746 #endif
Chris@0 747 continue;
Chris@0 748 }
Chris@0 749
Chris@0 750 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 751
Chris@0 752 if (mergingChannels) {
Chris@0 753
Chris@805 754 if (otherChannelRanges && i0 < (int)otherChannelRanges->size()) {
Chris@0 755
Chris@386 756 range.setMax(fabsf(range.max()));
Chris@386 757 range.setMin(-fabsf((*otherChannelRanges)[i0].max()));
Chris@386 758 range.setAbsmean
Chris@386 759 ((range.absmean() +
Chris@386 760 (*otherChannelRanges)[i0].absmean()) / 2);
Chris@0 761
Chris@805 762 if (i1 > i0 && i1 < (int)otherChannelRanges->size()) {
Chris@0 763 // let's not concern ourselves about the mean
Chris@386 764 range.setMin
Chris@386 765 (std::min
Chris@386 766 (range.min(),
Chris@386 767 -fabsf((*otherChannelRanges)[i1].max())));
Chris@0 768 }
Chris@0 769 }
Chris@67 770
Chris@67 771 } else if (mixingChannels) {
Chris@67 772
Chris@805 773 if (otherChannelRanges && i0 < (int)otherChannelRanges->size()) {
Chris@67 774
Chris@386 775 range.setMax((range.max() + (*otherChannelRanges)[i0].max()) / 2);
Chris@386 776 range.setMin((range.min() + (*otherChannelRanges)[i0].min()) / 2);
Chris@386 777 range.setAbsmean((range.absmean() + (*otherChannelRanges)[i0].absmean()) / 2);
Chris@67 778 }
Chris@67 779 }
Chris@0 780
Chris@0 781 int greyLevels = 1;
Chris@0 782 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 783
Chris@0 784 switch (m_scale) {
Chris@0 785
Chris@0 786 case LinearScale:
Chris@908 787 rangeBottom = int(double(m * greyLevels) * range.min() * gain);
Chris@908 788 rangeTop = int(double(m * greyLevels) * range.max() * gain);
Chris@908 789 meanBottom = int(double(-m) * range.absmean() * gain);
Chris@908 790 meanTop = int(double(m) * range.absmean() * gain);
Chris@0 791 break;
Chris@0 792
Chris@0 793 case dBScale:
Chris@67 794 if (!mergingChannels) {
Chris@386 795 int db0 = dBscale(range.min() * gain, m);
Chris@386 796 int db1 = dBscale(range.max() * gain, m);
Chris@67 797 rangeTop = std::max(db0, db1);
Chris@67 798 meanTop = std::min(db0, db1);
Chris@67 799 if (mixingChannels) rangeBottom = meanTop;
Chris@386 800 else rangeBottom = dBscale(range.absmean() * gain, m);
Chris@67 801 meanBottom = rangeBottom;
Chris@67 802 } else {
Chris@386 803 rangeBottom = -dBscale(range.min() * gain, m * greyLevels);
Chris@386 804 rangeTop = dBscale(range.max() * gain, m * greyLevels);
Chris@386 805 meanBottom = -dBscale(range.absmean() * gain, m);
Chris@386 806 meanTop = dBscale(range.absmean() * gain, m);
Chris@67 807 }
Chris@0 808 break;
Chris@0 809
Chris@0 810 case MeterScale:
Chris@67 811 if (!mergingChannels) {
Chris@386 812 int r0 = abs(AudioLevel::multiplier_to_preview(range.min() * gain, m));
Chris@386 813 int r1 = abs(AudioLevel::multiplier_to_preview(range.max() * gain, m));
Chris@67 814 rangeTop = std::max(r0, r1);
Chris@67 815 meanTop = std::min(r0, r1);
Chris@67 816 if (mixingChannels) rangeBottom = meanTop;
Chris@386 817 else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
Chris@67 818 meanBottom = rangeBottom;
Chris@67 819 } else {
Chris@386 820 rangeBottom = -AudioLevel::multiplier_to_preview(range.min() * gain, m * greyLevels);
Chris@386 821 rangeTop = AudioLevel::multiplier_to_preview(range.max() * gain, m * greyLevels);
Chris@386 822 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
Chris@386 823 meanTop = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
Chris@67 824 }
Chris@67 825 break;
Chris@0 826 }
Chris@0 827
Chris@27 828 rangeBottom = my * greyLevels - rangeBottom;
Chris@27 829 rangeTop = my * greyLevels - rangeTop;
Chris@27 830 meanBottom = my - meanBottom;
Chris@27 831 meanTop = my - meanTop;
Chris@27 832
Chris@27 833 int topFill = (rangeTop % greyLevels);
Chris@27 834 if (topFill > 0) topFill = greyLevels - topFill;
Chris@27 835
Chris@27 836 int bottomFill = (rangeBottom % greyLevels);
Chris@27 837
Chris@0 838 rangeTop = rangeTop / greyLevels;
Chris@0 839 rangeBottom = rangeBottom / greyLevels;
Chris@0 840
Chris@0 841 bool clipped = false;
Chris@27 842
Chris@27 843 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@27 844 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@27 845 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@27 846 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@27 847
Chris@386 848 if (range.max() <= -1.0 ||
Chris@386 849 range.max() >= 1.0) clipped = true;
Chris@0 850
Chris@0 851 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 852 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 853
Chris@0 854 bool drawMean = m_showMeans;
Chris@0 855 if (meanTop == rangeTop) {
Chris@0 856 if (meanTop < meanBottom) ++meanTop;
Chris@0 857 else drawMean = false;
Chris@0 858 }
Chris@67 859 if (meanBottom == rangeBottom && m_scale == LinearScale) {
Chris@0 860 if (meanBottom > meanTop) --meanBottom;
Chris@0 861 else drawMean = false;
Chris@0 862 }
Chris@0 863
Chris@0 864 if (x != x0 && prevRangeBottom != -1) {
Chris@673 865 if (prevRangeBottom > rangeBottom + 1 &&
Chris@673 866 prevRangeTop > rangeBottom + 1) {
Chris@28 867 // paint->setPen(midColour);
Chris@285 868 paint->setPen(baseColour);
Chris@673 869 paint->drawLine(x-1, prevRangeTop, x, rangeBottom + 1);
Chris@28 870 paint->setPen(prevRangeTopColour);
Chris@0 871 paint->drawPoint(x-1, prevRangeTop);
Chris@673 872 } else if (prevRangeBottom < rangeTop - 1 &&
Chris@673 873 prevRangeTop < rangeTop - 1) {
Chris@28 874 // paint->setPen(midColour);
Chris@285 875 paint->setPen(baseColour);
Chris@673 876 paint->drawLine(x-1, prevRangeBottom, x, rangeTop - 1);
Chris@28 877 paint->setPen(prevRangeBottomColour);
Chris@0 878 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 879 }
Chris@0 880 }
Chris@0 881
Chris@0 882 if (ready) {
Chris@67 883 if (clipped /*!!! ||
Chris@386 884 range.min() * gain <= -1.0 ||
Chris@386 885 range.max() * gain >= 1.0 */) {
Chris@285 886 paint->setPen(Qt::red); //!!! getContrastingColour
Chris@0 887 } else {
Chris@285 888 paint->setPen(baseColour);
Chris@0 889 }
Chris@0 890 } else {
Chris@0 891 paint->setPen(midColour);
Chris@0 892 }
Chris@0 893
Chris@612 894 #ifdef DEBUG_WAVEFORM_PAINT
Chris@682 895 cerr << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
Chris@612 896 #endif
Chris@612 897
Chris@612 898 if (rangeTop == rangeBottom) {
Chris@612 899 paint->drawPoint(x, rangeTop);
Chris@612 900 } else {
Chris@612 901 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@612 902 }
Chris@0 903
Chris@285 904 prevRangeTopColour = baseColour;
Chris@285 905 prevRangeBottomColour = baseColour;
Chris@28 906
Chris@0 907 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 908 if (!clipped) {
Chris@0 909 if (rangeTop < rangeBottom) {
Chris@0 910 if (topFill > 0 &&
Chris@0 911 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 912 paint->setPen(greys[topFill - 1]);
Chris@27 913 paint->drawPoint(x, rangeTop);
Chris@28 914 prevRangeTopColour = greys[topFill - 1];
Chris@0 915 }
Chris@0 916 if (bottomFill > 0 &&
Chris@0 917 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 918 paint->setPen(greys[bottomFill - 1]);
Chris@27 919 paint->drawPoint(x, rangeBottom);
Chris@28 920 prevRangeBottomColour = greys[bottomFill - 1];
Chris@0 921 }
Chris@0 922 }
Chris@0 923 }
Chris@0 924 }
Chris@0 925
Chris@0 926 if (drawMean) {
Chris@0 927 paint->setPen(midColour);
Chris@0 928 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 929 }
Chris@0 930
Chris@0 931 prevRangeBottom = rangeBottom;
Chris@0 932 prevRangeTop = rangeTop;
Chris@0 933 }
Chris@0 934 }
Chris@0 935
Chris@709 936 if (m_middleLineHeight != 0.5) {
Chris@709 937 paint->restore();
Chris@709 938 }
Chris@709 939
Chris@0 940 if (m_aggressive) {
Chris@41 941
Chris@44 942 if (ready && rect == v->rect()) {
Chris@0 943 m_cacheValid = true;
Chris@0 944 m_cacheZoomLevel = zoomLevel;
Chris@0 945 }
Chris@0 946 paint->end();
Chris@0 947 delete paint;
Chris@0 948 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 949 }
Chris@200 950
Chris@200 951 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@200 952 delete ranges;
Chris@0 953 }
Chris@0 954
Chris@25 955 QString
Chris@44 956 WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 957 {
Chris@25 958 int x = pos.x();
Chris@25 959
Chris@25 960 if (!m_model || !m_model->isOK()) return "";
Chris@25 961
Chris@365 962 int zoomLevel = v->getZoomLevel();
Chris@25 963
Chris@805 964 int modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel);
Chris@365 965
Chris@908 966 sv_frame_t f0, f1;
Chris@365 967 if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) return "";
Chris@25 968
Chris@25 969 QString text;
Chris@25 970
Chris@25 971 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 972 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 973
Chris@25 974 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@25 975 text += tr("Time:\t%1 - %2")
Chris@25 976 .arg(rt0.toText(true).c_str())
Chris@25 977 .arg(rt1.toText(true).c_str());
Chris@25 978 } else {
Chris@25 979 text += tr("Time:\t%1")
Chris@25 980 .arg(rt0.toText(true).c_str());
Chris@25 981 }
Chris@25 982
Chris@805 983 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 984 bool mergingChannels = false, mixingChannels = false;
Chris@25 985
Chris@67 986 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 987 mergingChannels, mixingChannels);
Chris@25 988 if (channels == 0) return "";
Chris@25 989
Chris@805 990 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 991
Chris@805 992 int blockSize = v->getZoomLevel();
Chris@200 993 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@302 994 m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 995
Chris@25 996 if (ranges.empty()) continue;
Chris@25 997
Chris@25 998 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@25 999
Chris@25 1000 QString label = tr("Level:");
Chris@25 1001 if (minChannel != maxChannel) {
Chris@25 1002 if (ch == 0) label = tr("Left:");
Chris@25 1003 else if (ch == 1) label = tr("Right:");
Chris@25 1004 else label = tr("Channel %1").arg(ch + 1);
Chris@25 1005 }
Chris@25 1006
Chris@76 1007 bool singleValue = false;
Chris@908 1008 double min, max;
Chris@76 1009
Chris@386 1010 if (fabs(range.min()) < 0.01) {
Chris@386 1011 min = range.min();
Chris@386 1012 max = range.max();
Chris@76 1013 singleValue = (min == max);
Chris@76 1014 } else {
Chris@908 1015 int imin = int(lrint(range.min() * 10000));
Chris@908 1016 int imax = int(lrint(range.max() * 10000));
Chris@76 1017 singleValue = (imin == imax);
Chris@908 1018 min = double(imin)/10000;
Chris@908 1019 max = double(imax)/10000;
Chris@76 1020 }
Chris@76 1021
Chris@386 1022 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
Chris@386 1023 fabsf(range.max())))
Chris@25 1024 * 100);
Chris@25 1025
Chris@76 1026 if (!singleValue) {
Chris@25 1027 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@908 1028 .arg(label).arg(min).arg(max).arg(double(db)/100);
Chris@25 1029 } else {
Chris@25 1030 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@908 1031 .arg(label).arg(min).arg(double(db)/100);
Chris@25 1032 }
Chris@25 1033 }
Chris@25 1034
Chris@25 1035 return text;
Chris@25 1036 }
Chris@25 1037
Chris@0 1038 int
Chris@908 1039 WaveformLayer::getYForValue(const View *v, double value, int channel) const
Chris@68 1040 {
Chris@805 1041 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1042 bool mergingChannels = false, mixingChannels = false;
Chris@274 1043
Chris@274 1044 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1045 mergingChannels, mixingChannels);
Chris@853 1046 if (channels == 0) return 0;
Chris@68 1047 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 1048
Chris@68 1049 int h = v->height();
Chris@68 1050 int m = (h / channels) / 2;
Chris@68 1051
Chris@68 1052 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 1053 m_channelMode != MergeChannels) {
Chris@68 1054 m = (h / channels);
Chris@68 1055 }
Chris@68 1056
Chris@274 1057 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 1058
Chris@68 1059 int vy = 0;
Chris@68 1060
Chris@274 1061 switch (m_scale) {
Chris@68 1062
Chris@68 1063 case LinearScale:
Chris@68 1064 vy = int(m * value);
Chris@68 1065 break;
Chris@68 1066
Chris@68 1067 case MeterScale:
Chris@68 1068 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 1069 break;
Chris@68 1070
Chris@68 1071 case dBScale:
Chris@68 1072 vy = dBscale(value, m);
Chris@68 1073 break;
Chris@68 1074 }
Chris@68 1075
Chris@682 1076 // cerr << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << endl;
Chris@324 1077
Chris@68 1078 return my - vy;
Chris@68 1079 }
Chris@68 1080
Chris@908 1081 double
Chris@805 1082 WaveformLayer::getValueForY(const View *v, int y, int &channel) const
Chris@261 1083 {
Chris@805 1084 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1085 bool mergingChannels = false, mixingChannels = false;
Chris@274 1086
Chris@274 1087 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1088 mergingChannels, mixingChannels);
Chris@853 1089 if (channels == 0) return 0;
Chris@261 1090 if (maxChannel < minChannel) return 0;
Chris@261 1091
Chris@261 1092 int h = v->height();
Chris@261 1093 int m = (h / channels) / 2;
Chris@261 1094
Chris@261 1095 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1096 m_channelMode != MergeChannels) {
Chris@261 1097 m = (h / channels);
Chris@261 1098 }
Chris@274 1099
Chris@274 1100 channel = (y * channels) / h + minChannel;
Chris@261 1101
Chris@261 1102 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1103
Chris@262 1104 int vy = my - y;
Chris@908 1105 double value = 0;
Chris@908 1106 double thresh = -50.f;
Chris@261 1107
Chris@274 1108 switch (m_scale) {
Chris@261 1109
Chris@261 1110 case LinearScale:
Chris@908 1111 value = double(vy) / m;
Chris@261 1112 break;
Chris@261 1113
Chris@261 1114 case MeterScale:
Chris@261 1115 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1116 break;
Chris@261 1117
Chris@261 1118 case dBScale:
Chris@908 1119 value = (-thresh * double(vy)) / m + thresh;
Chris@274 1120 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1121 break;
Chris@261 1122 }
Chris@261 1123
Chris@274 1124 return value / m_gain;
Chris@261 1125 }
Chris@261 1126
Chris@261 1127 bool
Chris@267 1128 WaveformLayer::getYScaleValue(const View *v, int y,
Chris@908 1129 double &value, QString &unit) const
Chris@261 1130 {
Chris@805 1131 int channel;
Chris@261 1132
Chris@274 1133 value = getValueForY(v, y, channel);
Chris@261 1134
Chris@274 1135 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1136
Chris@908 1137 double thresh = -50.f;
Chris@274 1138
Chris@908 1139 if (value > 0.0) {
Chris@908 1140 value = 10.0 * log10(value);
Chris@274 1141 if (value < thresh) value = thresh;
Chris@274 1142 } else value = thresh;
Chris@274 1143
Chris@274 1144 unit = "dBV";
Chris@274 1145
Chris@274 1146 } else {
Chris@274 1147 unit = "V";
Chris@274 1148 }
Chris@274 1149
Chris@274 1150 return true;
Chris@274 1151 }
Chris@274 1152
Chris@274 1153 bool
Chris@274 1154 WaveformLayer::getYScaleDifference(const View *v, int y0, int y1,
Chris@908 1155 double &diff, QString &unit) const
Chris@274 1156 {
Chris@805 1157 int c0, c1;
Chris@908 1158 double v0 = getValueForY(v, y0, c0);
Chris@908 1159 double v1 = getValueForY(v, y1, c1);
Chris@274 1160
Chris@274 1161 if (c0 != c1) {
Chris@274 1162 // different channels, not comparable
Chris@908 1163 diff = 0.0;
Chris@274 1164 unit = "";
Chris@274 1165 return false;
Chris@274 1166 }
Chris@274 1167
Chris@274 1168 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1169
Chris@908 1170 double thresh = -50.0;
Chris@274 1171
Chris@274 1172 if (v1 == v0) diff = thresh;
Chris@274 1173 else {
Chris@274 1174 if (v1 > v0) diff = v0 / v1;
Chris@274 1175 else diff = v1 / v0;
Chris@274 1176
Chris@908 1177 diff = 10.0 * log10(diff);
Chris@274 1178 if (diff < thresh) diff = thresh;
Chris@274 1179 }
Chris@274 1180
Chris@274 1181 unit = "dBV";
Chris@274 1182
Chris@274 1183 } else {
Chris@908 1184 diff = fabs(v1 - v0);
Chris@274 1185 unit = "V";
Chris@274 1186 }
Chris@274 1187
Chris@261 1188 return true;
Chris@261 1189 }
Chris@261 1190
Chris@68 1191 int
Chris@607 1192 WaveformLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
Chris@0 1193 {
Chris@0 1194 if (m_scale == LinearScale) {
Chris@0 1195 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1196 } else {
Chris@0 1197 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 1198 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 1199 }
Chris@0 1200 }
Chris@0 1201
Chris@0 1202 void
Chris@607 1203 WaveformLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
Chris@0 1204 {
Chris@0 1205 if (!m_model || !m_model->isOK()) {
Chris@0 1206 return;
Chris@0 1207 }
Chris@0 1208
Chris@805 1209 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1210 bool mergingChannels = false, mixingChannels = false;
Chris@0 1211
Chris@67 1212 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1213 mergingChannels, mixingChannels);
Chris@0 1214 if (channels == 0) return;
Chris@0 1215
Chris@0 1216 int h = rect.height(), w = rect.width();
Chris@0 1217 int textHeight = paint.fontMetrics().height();
Chris@0 1218 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1219
Chris@908 1220 double gain = m_gain;
Chris@67 1221
Chris@805 1222 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1223
Chris@68 1224 int lastLabelledY = -1;
Chris@0 1225
Chris@805 1226 if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1227
Chris@68 1228 int n = 10;
Chris@0 1229
Chris@68 1230 for (int i = 0; i <= n; ++i) {
Chris@68 1231
Chris@908 1232 double val = 0.0, nval = 0.0;
Chris@0 1233 QString text = "";
Chris@0 1234
Chris@68 1235 switch (m_scale) {
Chris@68 1236
Chris@68 1237 case LinearScale:
Chris@68 1238 val = (i * gain) / n;
Chris@908 1239 text = QString("%1").arg(double(i) / n);
Chris@68 1240 if (i == 0) text = "0.0";
Chris@68 1241 else {
Chris@68 1242 nval = -val;
Chris@68 1243 if (i == n) text = "1.0";
Chris@68 1244 }
Chris@68 1245 break;
Chris@0 1246
Chris@68 1247 case MeterScale:
Chris@68 1248 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 1249 text = QString("%1").arg(meterdbs[i]);
Chris@68 1250 if (i == n) text = tr("0dB");
Chris@68 1251 if (i == 0) {
Chris@68 1252 text = tr("-Inf");
Chris@68 1253 val = 0.0;
Chris@68 1254 }
Chris@68 1255 break;
Chris@0 1256
Chris@68 1257 case dBScale:
Chris@68 1258 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 1259 text = QString("%1").arg(-(10*n) + i * 10);
Chris@68 1260 if (i == n) text = tr("0dB");
Chris@68 1261 if (i == 0) {
Chris@68 1262 text = tr("-Inf");
Chris@68 1263 val = 0.0;
Chris@68 1264 }
Chris@68 1265 break;
Chris@68 1266 }
Chris@0 1267
Chris@68 1268 if (val < -1.0 || val > 1.0) continue;
Chris@0 1269
Chris@274 1270 int y = getYForValue(v, val, ch);
Chris@0 1271
Chris@68 1272 int ny = y;
Chris@68 1273 if (nval != 0.0) {
Chris@274 1274 ny = getYForValue(v, nval, ch);
Chris@68 1275 }
Chris@0 1276
Chris@68 1277 bool spaceForLabel = (i == 0 ||
Chris@68 1278 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1279
Chris@68 1280 if (spaceForLabel) {
Chris@0 1281
Chris@68 1282 int tx = 3;
Chris@68 1283 if (m_scale != LinearScale) {
Chris@68 1284 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1285 }
Chris@68 1286
Chris@68 1287 int ty = y;
Chris@68 1288 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1289 ty = paint.fontMetrics().ascent();
Chris@68 1290 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1291 ty = h - paint.fontMetrics().descent();
Chris@68 1292 } else {
Chris@68 1293 ty += toff;
Chris@68 1294 }
Chris@68 1295 paint.drawText(tx, ty, text);
Chris@0 1296
Chris@68 1297 lastLabelledY = ty - toff;
Chris@67 1298
Chris@68 1299 if (ny != y) {
Chris@68 1300 ty = ny;
Chris@68 1301 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1302 ty = paint.fontMetrics().ascent();
Chris@68 1303 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1304 ty = h - paint.fontMetrics().descent();
Chris@68 1305 } else {
Chris@68 1306 ty += toff;
Chris@68 1307 }
Chris@68 1308 paint.drawText(tx, ty, text);
Chris@68 1309 }
Chris@68 1310
Chris@68 1311 paint.drawLine(w - 7, y, w, y);
Chris@68 1312 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1313
Chris@68 1314 } else {
Chris@68 1315
Chris@68 1316 paint.drawLine(w - 4, y, w, y);
Chris@68 1317 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1318 }
Chris@0 1319 }
Chris@0 1320 }
Chris@0 1321 }
Chris@0 1322
Chris@316 1323 void
Chris@316 1324 WaveformLayer::toXml(QTextStream &stream,
Chris@316 1325 QString indent, QString extraAttributes) const
Chris@6 1326 {
Chris@6 1327 QString s;
Chris@6 1328
Chris@285 1329 QString colourName, colourSpec, darkbg;
Chris@285 1330 ColourDatabase::getInstance()->getStringValues
Chris@285 1331 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1332
Chris@6 1333 s += QString("gain=\"%1\" "
Chris@287 1334 "showMeans=\"%2\" "
Chris@287 1335 "greyscale=\"%3\" "
Chris@287 1336 "channelMode=\"%4\" "
Chris@287 1337 "channel=\"%5\" "
Chris@287 1338 "scale=\"%6\" "
Chris@709 1339 "middleLineHeight=\"%7\" "
Chris@709 1340 "aggressive=\"%8\" "
Chris@709 1341 "autoNormalize=\"%9\"")
Chris@6 1342 .arg(m_gain)
Chris@6 1343 .arg(m_showMeans)
Chris@6 1344 .arg(m_greyscale)
Chris@6 1345 .arg(m_channelMode)
Chris@287 1346 .arg(m_channel)
Chris@6 1347 .arg(m_scale)
Chris@709 1348 .arg(m_middleLineHeight)
Chris@67 1349 .arg(m_aggressive)
Chris@67 1350 .arg(m_autoNormalize);
Chris@6 1351
Chris@316 1352 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 1353 }
Chris@6 1354
Chris@11 1355 void
Chris@11 1356 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1357 {
Chris@11 1358 bool ok = false;
Chris@11 1359
Chris@287 1360 SingleColourLayer::setProperties(attributes);
Chris@287 1361
Chris@11 1362 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1363 if (ok) setGain(gain);
Chris@11 1364
Chris@11 1365 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 1366 attributes.value("showMeans") == "true");
Chris@11 1367 setShowMeans(showMeans);
Chris@11 1368
Chris@11 1369 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 1370 attributes.value("greyscale") == "true");
Chris@11 1371 setUseGreyscale(greyscale);
Chris@11 1372
Chris@11 1373 ChannelMode channelMode = (ChannelMode)
Chris@11 1374 attributes.value("channelMode").toInt(&ok);
Chris@11 1375 if (ok) setChannelMode(channelMode);
Chris@11 1376
Chris@11 1377 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1378 if (ok) setChannel(channel);
Chris@11 1379
Chris@709 1380 Scale scale = (Scale)attributes.value("scale").toInt(&ok);
Chris@11 1381 if (ok) setScale(scale);
Chris@11 1382
Chris@709 1383 float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
Chris@709 1384 if (ok) setMiddleLineHeight(middleLineHeight);
Chris@709 1385
Chris@11 1386 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 1387 attributes.value("aggressive") == "true");
Chris@11 1388 setUseGreyscale(aggressive);
Chris@67 1389
Chris@67 1390 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1391 attributes.value("autoNormalize") == "true");
Chris@67 1392 setAutoNormalize(autoNormalize);
Chris@11 1393 }
Chris@11 1394
Chris@133 1395 int
Chris@133 1396 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1397 {
Chris@133 1398 defaultStep = 50;
Chris@133 1399 return 100;
Chris@133 1400 }
Chris@0 1401
Chris@133 1402 int
Chris@133 1403 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1404 {
Chris@908 1405 int val = int(lrint(log10(m_gain) * 20.0) + 50);
Chris@133 1406 if (val < 0) val = 0;
Chris@133 1407 if (val > 100) val = 100;
Chris@133 1408 return val;
Chris@133 1409 }
Chris@133 1410
Chris@133 1411 void
Chris@133 1412 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1413 {
Chris@908 1414 setGain(powf(10, float(step - 50) / 20.f));
Chris@133 1415 }
Chris@133 1416