annotate layer/WaveformLayer.cpp @ 640:c6d705bf1672

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