annotate layer/WaveformLayer.cpp @ 312:6de6f78b13a1

* Make it possible to drop audio files, layer files, session files and images onto SV panes. Need to do a bit more work on where we expect the dropped file to go, particularly in the case of audio files -- at the moment they're always opened in new panes, but it may be better to by default replace whatever is in the target pane.
author Chris Cannam
date Wed, 10 Oct 2007 15:18:02 +0000
parents e9549ea3f825
children c0b9eec70639
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@285 22 #include "base/ColourDatabase.h"
Chris@0 23
Chris@0 24 #include <QPainter>
Chris@0 25 #include <QPixmap>
Chris@0 26
Chris@0 27 #include <iostream>
Chris@0 28 #include <cmath>
Chris@0 29
Chris@4 30 //#define DEBUG_WAVEFORM_PAINT 1
Chris@4 31
Chris@0 32 using std::cerr;
Chris@0 33 using std::endl;
Chris@0 34
Chris@44 35 WaveformLayer::WaveformLayer() :
Chris@287 36 SingleColourLayer(),
Chris@0 37 m_model(0),
Chris@0 38 m_gain(1.0f),
Chris@67 39 m_autoNormalize(false),
Chris@0 40 m_showMeans(true),
Chris@0 41 m_greyscale(true),
Chris@0 42 m_channelMode(SeparateChannels),
Chris@0 43 m_channel(-1),
Chris@0 44 m_scale(LinearScale),
Chris@0 45 m_aggressive(false),
Chris@0 46 m_cache(0),
Chris@0 47 m_cacheValid(false)
Chris@0 48 {
Chris@44 49
Chris@0 50 }
Chris@0 51
Chris@0 52 WaveformLayer::~WaveformLayer()
Chris@0 53 {
Chris@0 54 delete m_cache;
Chris@0 55 }
Chris@0 56
Chris@0 57 void
Chris@0 58 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model)
Chris@0 59 {
Chris@69 60 bool channelsChanged = false;
Chris@69 61 if (m_channel == -1) {
Chris@69 62 if (!m_model) {
Chris@69 63 if (model) {
Chris@69 64 channelsChanged = true;
Chris@69 65 }
Chris@69 66 } else {
Chris@69 67 if (model &&
Chris@69 68 m_model->getChannelCount() != model->getChannelCount()) {
Chris@69 69 channelsChanged = true;
Chris@69 70 }
Chris@69 71 }
Chris@69 72 }
Chris@69 73
Chris@0 74 m_model = model;
Chris@0 75 m_cacheValid = false;
Chris@0 76 if (!m_model || !m_model->isOK()) return;
Chris@0 77
Chris@0 78 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 79 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 80 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 81
Chris@0 82 connect(m_model, SIGNAL(completionChanged()),
Chris@0 83 this, SIGNAL(modelCompletionChanged()));
Chris@0 84
Chris@301 85 connect(model, SIGNAL(alignmentCompletionChanged()),
Chris@301 86 this, SIGNAL(modelCompletionChanged()));
Chris@301 87
Chris@0 88 emit modelReplaced();
Chris@69 89
Chris@69 90 if (channelsChanged) emit layerParametersChanged();
Chris@0 91 }
Chris@0 92
Chris@0 93 Layer::PropertyList
Chris@0 94 WaveformLayer::getProperties() const
Chris@0 95 {
Chris@287 96 PropertyList list = SingleColourLayer::getProperties();
Chris@87 97 list.push_back("Scale");
Chris@87 98 list.push_back("Gain");
Chris@87 99 list.push_back("Normalize Visible Area");
Chris@68 100
Chris@68 101 if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) {
Chris@87 102 list.push_back("Channels");
Chris@68 103 }
Chris@68 104
Chris@0 105 return list;
Chris@0 106 }
Chris@0 107
Chris@87 108 QString
Chris@87 109 WaveformLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 110 {
Chris@87 111 if (name == "Scale") return tr("Scale");
Chris@87 112 if (name == "Gain") return tr("Gain");
Chris@87 113 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 114 if (name == "Channels") return tr("Channels");
Chris@287 115 return SingleColourLayer::getPropertyLabel(name);
Chris@87 116 }
Chris@87 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@101 291 // std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::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@0 385 // std::cerr << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << std::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@0 399 void
Chris@44 400 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const
Chris@0 401 {
Chris@0 402 if (!m_model || !m_model->isOK()) {
Chris@0 403 return;
Chris@0 404 }
Chris@0 405
Chris@44 406 long startFrame = v->getStartFrame();
Chris@302 407 long endFrame = v->getEndFrame();
Chris@44 408 int zoomLevel = v->getZoomLevel();
Chris@0 409
Chris@2 410 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 411 Profiler profiler("WaveformLayer::paint", true);
Chris@0 412 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 413 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << ", start " << startFrame << std::endl;
Chris@2 414 #endif
Chris@0 415
Chris@0 416 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 417 bool mergingChannels = false, mixingChannels = false;
Chris@0 418
Chris@67 419 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 420 mergingChannels, mixingChannels);
Chris@0 421 if (channels == 0) return;
Chris@0 422
Chris@44 423 int w = v->width();
Chris@44 424 int h = v->height();
Chris@0 425
Chris@0 426 bool ready = m_model->isReady();
Chris@0 427 QPainter *paint;
Chris@0 428
Chris@0 429 if (m_aggressive) {
Chris@0 430
Chris@214 431 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 432 std::cerr << "WaveformLayer::paint: aggressive is true" << std::endl;
Chris@214 433 #endif
Chris@214 434
Chris@0 435 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@0 436 m_cacheValid = false;
Chris@0 437 }
Chris@0 438
Chris@214 439 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@214 440 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 441 if (m_cache) {
Chris@214 442 std::cerr << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << std::endl;
Chris@214 443 }
Chris@214 444 #endif
Chris@214 445 delete m_cache;
Chris@214 446 m_cache = new QPixmap(w, h);
Chris@214 447 m_cacheValid = false;
Chris@214 448 }
Chris@214 449
Chris@0 450 if (m_cacheValid) {
Chris@0 451 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 452 return;
Chris@0 453 }
Chris@0 454
Chris@0 455 paint = new QPainter(m_cache);
Chris@0 456
Chris@0 457 paint->setPen(Qt::NoPen);
Chris@287 458 paint->setBrush(getBackgroundQColor(v));
Chris@0 459 paint->drawRect(rect);
Chris@0 460
Chris@287 461 paint->setPen(getForegroundQColor(v));
Chris@0 462 paint->setBrush(Qt::NoBrush);
Chris@0 463
Chris@0 464 } else {
Chris@0 465 paint = &viewPainter;
Chris@0 466 }
Chris@0 467
Chris@28 468 paint->setRenderHint(QPainter::Antialiasing, false);
Chris@28 469
Chris@0 470 int x0 = 0, x1 = w - 1;
Chris@0 471 int y0 = 0, y1 = h - 1;
Chris@0 472
Chris@0 473 x0 = rect.left();
Chris@0 474 x1 = rect.right();
Chris@0 475 y0 = rect.top();
Chris@0 476 y1 = rect.bottom();
Chris@0 477
Chris@28 478 if (x0 > 0) --x0;
Chris@44 479 if (x1 < v->width()) ++x1;
Chris@28 480
Chris@44 481 long frame0 = v->getFrameForX(x0);
Chris@44 482 long frame1 = v->getFrameForX(x1 + 1);
Chris@302 483
Chris@4 484 #ifdef DEBUG_WAVEFORM_PAINT
Chris@4 485 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl;
Chris@4 486 #endif
Chris@0 487
Chris@200 488 RangeSummarisableTimeValueModel::RangeBlock *ranges =
Chris@200 489 new RangeSummarisableTimeValueModel::RangeBlock;
Chris@200 490
Chris@200 491 RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges = 0;
Chris@0 492 RangeSummarisableTimeValueModel::Range range;
Chris@287 493
Chris@287 494 QColor baseColour = getBaseQColor();
Chris@287 495 std::vector<QColor> greys = getPartialShades(v);
Chris@0 496
Chris@285 497 QColor midColour = baseColour;
Chris@0 498 if (midColour == Qt::black) {
Chris@0 499 midColour = Qt::gray;
Chris@44 500 } else if (v->hasLightBackground()) {
Chris@0 501 midColour = midColour.light(150);
Chris@0 502 } else {
Chris@0 503 midColour = midColour.light(50);
Chris@0 504 }
Chris@0 505
Chris@67 506 while (m_effectiveGains.size() <= maxChannel) {
Chris@67 507 m_effectiveGains.push_back(m_gain);
Chris@67 508 }
Chris@67 509
Chris@302 510 // Although a long for purposes of comparison against the view
Chris@302 511 // start and end frames, these are known to be non-negative
Chris@302 512 long modelStart = long(m_model->getStartFrame());
Chris@302 513 long modelEnd = long(m_model->getEndFrame());
Chris@302 514
Chris@302 515 #ifdef DEBUG_WAVEFORM_PAINT
Chris@302 516 std::cerr << "Model start = " << modelStart << ", end = " << modelEnd << std::endl;
Chris@302 517 #endif
Chris@302 518
Chris@0 519 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 520
Chris@0 521 int prevRangeBottom = -1, prevRangeTop = -1;
Chris@285 522 QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour;
Chris@302 523 size_t rangeStart, rangeEnd;
Chris@0 524
Chris@67 525 m_effectiveGains[ch] = m_gain;
Chris@67 526
Chris@67 527 if (m_autoNormalize) {
Chris@302 528
Chris@302 529 if (startFrame < modelStart) rangeStart = modelStart;
Chris@302 530 else rangeStart = startFrame;
Chris@302 531
Chris@302 532 if (endFrame < 0) rangeEnd = 0;
Chris@302 533 else if (endFrame > modelEnd) rangeEnd = modelEnd;
Chris@302 534 else rangeEnd = endFrame;
Chris@302 535
Chris@302 536 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@302 537
Chris@67 538 RangeSummarisableTimeValueModel::Range range =
Chris@302 539 m_model->getSummary(ch, rangeStart, rangeEnd - rangeStart);
Chris@67 540 if (mergingChannels || mixingChannels) {
Chris@67 541 RangeSummarisableTimeValueModel::Range otherRange =
Chris@302 542 m_model->getSummary(1, rangeStart, rangeEnd - rangeStart);
Chris@67 543 range.max = std::max(range.max, otherRange.max);
Chris@67 544 range.min = std::min(range.min, otherRange.min);
Chris@67 545 range.absmean = std::min(range.absmean, otherRange.absmean);
Chris@67 546 }
Chris@67 547 m_effectiveGains[ch] = 1.0 / std::max(fabsf(range.max),
Chris@67 548 fabsf(range.min));
Chris@67 549 }
Chris@67 550
Chris@67 551 float gain = m_effectiveGains[ch];
Chris@67 552
Chris@68 553 int m = (h / channels) / 2;
Chris@68 554 int my = m + (((ch - minChannel) * h) / channels);
Chris@68 555
Chris@68 556 // std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl;
Chris@68 557
Chris@68 558 if (my - m > y1 || my + m < y0) continue;
Chris@68 559
Chris@68 560 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 561 m_channelMode != MergeChannels) {
Chris@68 562 m = (h / channels);
Chris@68 563 my = m + (((ch - minChannel) * h) / channels);
Chris@68 564 }
Chris@68 565
Chris@295 566 paint->setPen(greys[1]);
Chris@68 567 paint->drawLine(x0, my, x1, my);
Chris@68 568
Chris@68 569 int n = 10;
Chris@68 570 int py = -1;
Chris@68 571
Chris@195 572 if (v->hasLightBackground() &&
Chris@195 573 v->getViewManager() &&
Chris@195 574 v->getViewManager()->shouldShowScaleGuides()) {
Chris@68 575
Chris@68 576 paint->setPen(QColor(240, 240, 240));
Chris@68 577
Chris@68 578 for (int i = 1; i < n; ++i) {
Chris@68 579
Chris@68 580 float val = 0.0, nval = 0.0;
Chris@68 581
Chris@68 582 switch (m_scale) {
Chris@68 583
Chris@68 584 case LinearScale:
Chris@68 585 val = (i * gain) / n;
Chris@68 586 if (i > 0) nval = -val;
Chris@68 587 break;
Chris@68 588
Chris@68 589 case MeterScale:
Chris@68 590 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 591 break;
Chris@68 592
Chris@68 593 case dBScale:
Chris@68 594 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 595 break;
Chris@68 596 }
Chris@68 597
Chris@68 598 if (val < -1.0 || val > 1.0) continue;
Chris@68 599
Chris@274 600 int y = getYForValue(v, val, ch);
Chris@68 601
Chris@68 602 if (py >= 0 && abs(y - py) < 10) continue;
Chris@68 603 else py = y;
Chris@68 604
Chris@68 605 int ny = y;
Chris@68 606 if (nval != 0.0) {
Chris@274 607 ny = getYForValue(v, nval, ch);
Chris@68 608 }
Chris@68 609
Chris@68 610 paint->drawLine(x0, y, x1, y);
Chris@68 611 if (ny != y) {
Chris@68 612 paint->drawLine(x0, ny, x1, ny);
Chris@68 613 }
Chris@68 614 }
Chris@68 615 }
Chris@68 616
Chris@302 617 if (frame1 < modelStart) continue;
Chris@68 618
Chris@68 619 size_t modelZoomLevel = zoomLevel;
Chris@68 620
Chris@302 621 if (frame0 < modelStart) rangeStart = modelStart;
Chris@302 622 else rangeStart = frame0;
Chris@302 623
Chris@302 624 if (frame1 < 0) rangeEnd = 0;
Chris@302 625 else if (frame1 > modelEnd) rangeEnd = modelEnd;
Chris@302 626 else rangeEnd = frame1;
Chris@302 627
Chris@302 628 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@302 629
Chris@302 630 m_model->getSummaries
Chris@302 631 (ch, rangeStart, rangeEnd - rangeStart, *ranges, modelZoomLevel);
Chris@302 632
Chris@302 633 // std::cerr << ranges->size() << " ranges" << std::endl;
Chris@68 634
Chris@68 635 if (mergingChannels || mixingChannels) {
Chris@71 636 if (m_model->getChannelCount() > 1) {
Chris@200 637 if (!otherChannelRanges) {
Chris@200 638 otherChannelRanges =
Chris@200 639 new RangeSummarisableTimeValueModel::RangeBlock;
Chris@200 640 }
Chris@302 641 m_model->getSummaries
Chris@302 642 (1, rangeStart, rangeEnd - rangeStart, *otherChannelRanges,
Chris@200 643 modelZoomLevel);
Chris@71 644 } else {
Chris@200 645 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@71 646 otherChannelRanges = ranges;
Chris@71 647 }
Chris@68 648 }
Chris@68 649
Chris@0 650 for (int x = x0; x <= x1; ++x) {
Chris@0 651
Chris@0 652 range = RangeSummarisableTimeValueModel::Range();
Chris@0 653 size_t index = x - x0;
Chris@0 654 size_t maxIndex = index;
Chris@0 655
Chris@302 656 if (frame0 < modelStart) {
Chris@302 657 if (index < size_t((modelStart - frame0) / zoomLevel)) {
Chris@302 658 continue;
Chris@302 659 } else {
Chris@302 660 index -= ((modelStart - frame0) / zoomLevel);
Chris@302 661 maxIndex = index;
Chris@302 662 }
Chris@302 663 }
Chris@0 664
Chris@0 665 if (int(modelZoomLevel) != zoomLevel) {
Chris@0 666
Chris@0 667 index = size_t((double(index) * zoomLevel) / modelZoomLevel);
Chris@0 668
Chris@0 669 if (int(modelZoomLevel) < zoomLevel) {
Chris@0 670 // Peaks may be missed! The model should avoid
Chris@0 671 // this by rounding zoom levels up rather than
Chris@0 672 // down, but we'd better cope in case it doesn't
Chris@0 673 maxIndex = index;
Chris@0 674 } else {
Chris@0 675 maxIndex = size_t((double(index + 1) * zoomLevel)
Chris@0 676 / modelZoomLevel) - 1;
Chris@0 677 }
Chris@0 678 }
Chris@0 679
Chris@200 680 if (ranges && index < ranges->size()) {
Chris@0 681
Chris@200 682 range = (*ranges)[index];
Chris@0 683
Chris@200 684 if (maxIndex > index && maxIndex < ranges->size()) {
Chris@200 685 range.max = std::max(range.max, (*ranges)[maxIndex].max);
Chris@200 686 range.min = std::min(range.min, (*ranges)[maxIndex].min);
Chris@0 687 range.absmean = (range.absmean +
Chris@200 688 (*ranges)[maxIndex].absmean) / 2;
Chris@0 689 }
Chris@0 690
Chris@0 691 } else {
Chris@0 692 continue;
Chris@0 693 }
Chris@0 694
Chris@0 695 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 696
Chris@0 697 if (mergingChannels) {
Chris@0 698
Chris@200 699 if (otherChannelRanges && index < otherChannelRanges->size()) {
Chris@0 700
Chris@0 701 range.max = fabsf(range.max);
Chris@200 702 range.min = -fabsf((*otherChannelRanges)[index].max);
Chris@0 703 range.absmean = (range.absmean +
Chris@200 704 (*otherChannelRanges)[index].absmean) / 2;
Chris@0 705
Chris@200 706 if (maxIndex > index && maxIndex < otherChannelRanges->size()) {
Chris@0 707 // let's not concern ourselves about the mean
Chris@0 708 range.min = std::min
Chris@0 709 (range.min,
Chris@200 710 -fabsf((*otherChannelRanges)[maxIndex].max));
Chris@0 711 }
Chris@0 712 }
Chris@67 713
Chris@67 714 } else if (mixingChannels) {
Chris@67 715
Chris@200 716 if (otherChannelRanges && index < otherChannelRanges->size()) {
Chris@67 717
Chris@200 718 range.max = (range.max + (*otherChannelRanges)[index].max) / 2;
Chris@200 719 range.min = (range.min + (*otherChannelRanges)[index].min) / 2;
Chris@200 720 range.absmean = (range.absmean + (*otherChannelRanges)[index].absmean) / 2;
Chris@67 721 }
Chris@67 722 }
Chris@0 723
Chris@0 724 int greyLevels = 1;
Chris@0 725 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 726
Chris@0 727 switch (m_scale) {
Chris@0 728
Chris@0 729 case LinearScale:
Chris@67 730 rangeBottom = int( m * greyLevels * range.min * gain);
Chris@67 731 rangeTop = int( m * greyLevels * range.max * gain);
Chris@67 732 meanBottom = int(-m * range.absmean * gain);
Chris@67 733 meanTop = int( m * range.absmean * gain);
Chris@0 734 break;
Chris@0 735
Chris@0 736 case dBScale:
Chris@67 737 if (!mergingChannels) {
Chris@67 738 int db0 = dBscale(range.min * gain, m);
Chris@67 739 int db1 = dBscale(range.max * gain, m);
Chris@67 740 rangeTop = std::max(db0, db1);
Chris@67 741 meanTop = std::min(db0, db1);
Chris@67 742 if (mixingChannels) rangeBottom = meanTop;
Chris@67 743 else rangeBottom = dBscale(range.absmean * gain, m);
Chris@67 744 meanBottom = rangeBottom;
Chris@67 745 } else {
Chris@67 746 rangeBottom = -dBscale(range.min * gain, m * greyLevels);
Chris@67 747 rangeTop = dBscale(range.max * gain, m * greyLevels);
Chris@67 748 meanBottom = -dBscale(range.absmean * gain, m);
Chris@67 749 meanTop = dBscale(range.absmean * gain, m);
Chris@67 750 }
Chris@0 751 break;
Chris@0 752
Chris@0 753 case MeterScale:
Chris@67 754 if (!mergingChannels) {
Chris@67 755 int r0 = abs(AudioLevel::multiplier_to_preview(range.min * gain, m));
Chris@67 756 int r1 = abs(AudioLevel::multiplier_to_preview(range.max * gain, m));
Chris@67 757 rangeTop = std::max(r0, r1);
Chris@67 758 meanTop = std::min(r0, r1);
Chris@67 759 if (mixingChannels) rangeBottom = meanTop;
Chris@67 760 else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 761 meanBottom = rangeBottom;
Chris@67 762 } else {
Chris@67 763 rangeBottom = AudioLevel::multiplier_to_preview(range.min * gain, m * greyLevels);
Chris@67 764 rangeTop = AudioLevel::multiplier_to_preview(range.max * gain, m * greyLevels);
Chris@67 765 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 766 meanTop = AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 767 }
Chris@67 768 break;
Chris@0 769 }
Chris@0 770
Chris@27 771 rangeBottom = my * greyLevels - rangeBottom;
Chris@27 772 rangeTop = my * greyLevels - rangeTop;
Chris@27 773 meanBottom = my - meanBottom;
Chris@27 774 meanTop = my - meanTop;
Chris@27 775
Chris@27 776 int topFill = (rangeTop % greyLevels);
Chris@27 777 if (topFill > 0) topFill = greyLevels - topFill;
Chris@27 778
Chris@27 779 int bottomFill = (rangeBottom % greyLevels);
Chris@27 780
Chris@0 781 rangeTop = rangeTop / greyLevels;
Chris@0 782 rangeBottom = rangeBottom / greyLevels;
Chris@0 783
Chris@0 784 bool clipped = false;
Chris@27 785
Chris@27 786 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@27 787 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@27 788 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@27 789 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@27 790
Chris@67 791 if (range.max <= -1.0 ||
Chris@67 792 range.max >= 1.0) clipped = true;
Chris@0 793
Chris@0 794 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 795 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 796
Chris@0 797 bool drawMean = m_showMeans;
Chris@0 798 if (meanTop == rangeTop) {
Chris@0 799 if (meanTop < meanBottom) ++meanTop;
Chris@0 800 else drawMean = false;
Chris@0 801 }
Chris@67 802 if (meanBottom == rangeBottom && m_scale == LinearScale) {
Chris@0 803 if (meanBottom > meanTop) --meanBottom;
Chris@0 804 else drawMean = false;
Chris@0 805 }
Chris@0 806
Chris@0 807 if (x != x0 && prevRangeBottom != -1) {
Chris@0 808 if (prevRangeBottom > rangeBottom &&
Chris@0 809 prevRangeTop > rangeBottom) {
Chris@28 810 // paint->setPen(midColour);
Chris@285 811 paint->setPen(baseColour);
Chris@0 812 paint->drawLine(x-1, prevRangeTop, x, rangeBottom);
Chris@28 813 paint->setPen(prevRangeTopColour);
Chris@0 814 paint->drawPoint(x-1, prevRangeTop);
Chris@0 815 } else if (prevRangeBottom < rangeTop &&
Chris@0 816 prevRangeTop < rangeTop) {
Chris@28 817 // paint->setPen(midColour);
Chris@285 818 paint->setPen(baseColour);
Chris@0 819 paint->drawLine(x-1, prevRangeBottom, x, rangeTop);
Chris@28 820 paint->setPen(prevRangeBottomColour);
Chris@0 821 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 822 }
Chris@0 823 }
Chris@0 824
Chris@0 825 if (ready) {
Chris@67 826 if (clipped /*!!! ||
Chris@67 827 range.min * gain <= -1.0 ||
Chris@67 828 range.max * gain >= 1.0 */) {
Chris@285 829 paint->setPen(Qt::red); //!!! getContrastingColour
Chris@0 830 } else {
Chris@285 831 paint->setPen(baseColour);
Chris@0 832 }
Chris@0 833 } else {
Chris@0 834 paint->setPen(midColour);
Chris@0 835 }
Chris@0 836
Chris@0 837 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@0 838
Chris@285 839 prevRangeTopColour = baseColour;
Chris@285 840 prevRangeBottomColour = baseColour;
Chris@28 841
Chris@0 842 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 843 if (!clipped) {
Chris@0 844 if (rangeTop < rangeBottom) {
Chris@0 845 if (topFill > 0 &&
Chris@0 846 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 847 paint->setPen(greys[topFill - 1]);
Chris@27 848 paint->drawPoint(x, rangeTop);
Chris@28 849 prevRangeTopColour = greys[topFill - 1];
Chris@0 850 }
Chris@0 851 if (bottomFill > 0 &&
Chris@0 852 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 853 paint->setPen(greys[bottomFill - 1]);
Chris@27 854 paint->drawPoint(x, rangeBottom);
Chris@28 855 prevRangeBottomColour = greys[bottomFill - 1];
Chris@0 856 }
Chris@0 857 }
Chris@0 858 }
Chris@0 859 }
Chris@0 860
Chris@0 861 if (drawMean) {
Chris@0 862 paint->setPen(midColour);
Chris@0 863 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 864 }
Chris@0 865
Chris@0 866 prevRangeBottom = rangeBottom;
Chris@0 867 prevRangeTop = rangeTop;
Chris@0 868 }
Chris@0 869 }
Chris@0 870
Chris@0 871 if (m_aggressive) {
Chris@41 872
Chris@44 873 if (ready && rect == v->rect()) {
Chris@0 874 m_cacheValid = true;
Chris@0 875 m_cacheZoomLevel = zoomLevel;
Chris@0 876 }
Chris@0 877 paint->end();
Chris@0 878 delete paint;
Chris@0 879 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 880 }
Chris@200 881
Chris@200 882 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@200 883 delete ranges;
Chris@0 884 }
Chris@0 885
Chris@25 886 QString
Chris@44 887 WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 888 {
Chris@25 889 int x = pos.x();
Chris@25 890
Chris@25 891 if (!m_model || !m_model->isOK()) return "";
Chris@25 892
Chris@44 893 long f0 = v->getFrameForX(x);
Chris@44 894 long f1 = v->getFrameForX(x + 1);
Chris@25 895
Chris@25 896 if (f0 < 0) f0 = 0;
Chris@25 897 if (f1 <= f0) return "";
Chris@25 898
Chris@25 899 QString text;
Chris@25 900
Chris@25 901 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 902 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 903
Chris@25 904 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@25 905 text += tr("Time:\t%1 - %2")
Chris@25 906 .arg(rt0.toText(true).c_str())
Chris@25 907 .arg(rt1.toText(true).c_str());
Chris@25 908 } else {
Chris@25 909 text += tr("Time:\t%1")
Chris@25 910 .arg(rt0.toText(true).c_str());
Chris@25 911 }
Chris@25 912
Chris@25 913 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 914 bool mergingChannels = false, mixingChannels = false;
Chris@25 915
Chris@67 916 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 917 mergingChannels, mixingChannels);
Chris@25 918 if (channels == 0) return "";
Chris@25 919
Chris@25 920 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 921
Chris@44 922 size_t blockSize = v->getZoomLevel();
Chris@200 923 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@302 924 m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 925
Chris@25 926 if (ranges.empty()) continue;
Chris@25 927
Chris@25 928 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@25 929
Chris@25 930 QString label = tr("Level:");
Chris@25 931 if (minChannel != maxChannel) {
Chris@25 932 if (ch == 0) label = tr("Left:");
Chris@25 933 else if (ch == 1) label = tr("Right:");
Chris@25 934 else label = tr("Channel %1").arg(ch + 1);
Chris@25 935 }
Chris@25 936
Chris@76 937 bool singleValue = false;
Chris@76 938 float min, max;
Chris@76 939
Chris@76 940 if (fabs(range.min) < 0.01) {
Chris@76 941 min = range.min;
Chris@76 942 max = range.max;
Chris@76 943 singleValue = (min == max);
Chris@76 944 } else {
Chris@76 945 int imin = int(range.min * 1000);
Chris@76 946 int imax = int(range.max * 1000);
Chris@76 947 singleValue = (imin == imax);
Chris@76 948 min = float(imin)/1000;
Chris@76 949 max = float(imax)/1000;
Chris@76 950 }
Chris@76 951
Chris@25 952 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min),
Chris@25 953 fabsf(range.max)))
Chris@25 954 * 100);
Chris@25 955
Chris@76 956 if (!singleValue) {
Chris@25 957 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@76 958 .arg(label).arg(min).arg(max).arg(float(db)/100);
Chris@25 959 } else {
Chris@25 960 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@76 961 .arg(label).arg(min).arg(float(db)/100);
Chris@25 962 }
Chris@25 963 }
Chris@25 964
Chris@25 965 return text;
Chris@25 966 }
Chris@25 967
Chris@0 968 int
Chris@274 969 WaveformLayer::getYForValue(const View *v, float value, size_t channel) const
Chris@68 970 {
Chris@274 971 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 972 bool mergingChannels = false, mixingChannels = false;
Chris@274 973
Chris@274 974 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 975 mergingChannels, mixingChannels);
Chris@274 976
Chris@68 977 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 978
Chris@68 979 int h = v->height();
Chris@68 980 int m = (h / channels) / 2;
Chris@68 981
Chris@68 982 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 983 m_channelMode != MergeChannels) {
Chris@68 984 m = (h / channels);
Chris@68 985 }
Chris@68 986
Chris@274 987 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 988
Chris@68 989 int vy = 0;
Chris@68 990
Chris@274 991 switch (m_scale) {
Chris@68 992
Chris@68 993 case LinearScale:
Chris@68 994 vy = int(m * value);
Chris@68 995 break;
Chris@68 996
Chris@68 997 case MeterScale:
Chris@68 998 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 999 break;
Chris@68 1000
Chris@68 1001 case dBScale:
Chris@68 1002 vy = dBscale(value, m);
Chris@68 1003 break;
Chris@68 1004 }
Chris@68 1005
Chris@68 1006 return my - vy;
Chris@68 1007 }
Chris@68 1008
Chris@261 1009 float
Chris@274 1010 WaveformLayer::getValueForY(const View *v, int y, size_t &channel) const
Chris@261 1011 {
Chris@274 1012 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1013 bool mergingChannels = false, mixingChannels = false;
Chris@274 1014
Chris@274 1015 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1016 mergingChannels, mixingChannels);
Chris@274 1017
Chris@261 1018 if (maxChannel < minChannel) return 0;
Chris@261 1019
Chris@261 1020 int h = v->height();
Chris@261 1021 int m = (h / channels) / 2;
Chris@261 1022
Chris@261 1023 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1024 m_channelMode != MergeChannels) {
Chris@261 1025 m = (h / channels);
Chris@261 1026 }
Chris@274 1027
Chris@274 1028 channel = (y * channels) / h + minChannel;
Chris@261 1029
Chris@261 1030 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1031
Chris@262 1032 int vy = my - y;
Chris@261 1033 float value = 0;
Chris@274 1034 float thresh = -50.f;
Chris@261 1035
Chris@274 1036 switch (m_scale) {
Chris@261 1037
Chris@261 1038 case LinearScale:
Chris@261 1039 value = float(vy) / m;
Chris@261 1040 break;
Chris@261 1041
Chris@261 1042 case MeterScale:
Chris@261 1043 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1044 break;
Chris@261 1045
Chris@261 1046 case dBScale:
Chris@274 1047 value = (-thresh * float(vy)) / m + thresh;
Chris@274 1048 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1049 break;
Chris@261 1050 }
Chris@261 1051
Chris@274 1052 return value / m_gain;
Chris@261 1053 }
Chris@261 1054
Chris@261 1055 bool
Chris@267 1056 WaveformLayer::getYScaleValue(const View *v, int y,
Chris@261 1057 float &value, QString &unit) const
Chris@261 1058 {
Chris@274 1059 size_t channel;
Chris@261 1060
Chris@274 1061 value = getValueForY(v, y, channel);
Chris@261 1062
Chris@274 1063 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1064
Chris@274 1065 float thresh = -50.f;
Chris@274 1066
Chris@274 1067 if (value > 0.f) {
Chris@274 1068 value = 10.f * log10f(value);
Chris@274 1069 if (value < thresh) value = thresh;
Chris@274 1070 } else value = thresh;
Chris@274 1071
Chris@274 1072 unit = "dBV";
Chris@274 1073
Chris@274 1074 } else {
Chris@274 1075 unit = "V";
Chris@274 1076 }
Chris@274 1077
Chris@274 1078 return true;
Chris@274 1079 }
Chris@274 1080
Chris@274 1081 bool
Chris@274 1082 WaveformLayer::getYScaleDifference(const View *v, int y0, int y1,
Chris@274 1083 float &diff, QString &unit) const
Chris@274 1084 {
Chris@274 1085 size_t c0, c1;
Chris@274 1086 float v0 = getValueForY(v, y0, c0);
Chris@274 1087 float v1 = getValueForY(v, y1, c1);
Chris@274 1088
Chris@274 1089 if (c0 != c1) {
Chris@274 1090 // different channels, not comparable
Chris@274 1091 diff = 0.f;
Chris@274 1092 unit = "";
Chris@274 1093 return false;
Chris@274 1094 }
Chris@274 1095
Chris@274 1096 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1097
Chris@274 1098 float thresh = -50.f;
Chris@274 1099
Chris@274 1100 if (v1 == v0) diff = thresh;
Chris@274 1101 else {
Chris@274 1102 if (v1 > v0) diff = v0 / v1;
Chris@274 1103 else diff = v1 / v0;
Chris@274 1104
Chris@274 1105 diff = 10.f * log10f(diff);
Chris@274 1106 if (diff < thresh) diff = thresh;
Chris@274 1107 }
Chris@274 1108
Chris@274 1109 unit = "dBV";
Chris@274 1110
Chris@274 1111 } else {
Chris@274 1112 diff = fabsf(v1 - v0);
Chris@274 1113 unit = "V";
Chris@274 1114 }
Chris@274 1115
Chris@261 1116 return true;
Chris@261 1117 }
Chris@261 1118
Chris@68 1119 int
Chris@248 1120 WaveformLayer::getVerticalScaleWidth(View *, QPainter &paint) const
Chris@0 1121 {
Chris@0 1122 if (m_scale == LinearScale) {
Chris@0 1123 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1124 } else {
Chris@0 1125 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 1126 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 1127 }
Chris@0 1128 }
Chris@0 1129
Chris@0 1130 void
Chris@44 1131 WaveformLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 1132 {
Chris@0 1133 if (!m_model || !m_model->isOK()) {
Chris@0 1134 return;
Chris@0 1135 }
Chris@0 1136
Chris@0 1137 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1138 bool mergingChannels = false, mixingChannels = false;
Chris@0 1139
Chris@67 1140 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1141 mergingChannels, mixingChannels);
Chris@0 1142 if (channels == 0) return;
Chris@0 1143
Chris@0 1144 int h = rect.height(), w = rect.width();
Chris@0 1145 int textHeight = paint.fontMetrics().height();
Chris@0 1146 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1147
Chris@67 1148 float gain = m_gain;
Chris@67 1149
Chris@0 1150 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1151
Chris@68 1152 int lastLabelledY = -1;
Chris@0 1153
Chris@67 1154 if (ch < m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1155
Chris@68 1156 int n = 10;
Chris@0 1157
Chris@68 1158 for (int i = 0; i <= n; ++i) {
Chris@68 1159
Chris@68 1160 float val = 0.0, nval = 0.0;
Chris@0 1161 QString text = "";
Chris@0 1162
Chris@68 1163 switch (m_scale) {
Chris@68 1164
Chris@68 1165 case LinearScale:
Chris@68 1166 val = (i * gain) / n;
Chris@68 1167 text = QString("%1").arg(float(i) / n);
Chris@68 1168 if (i == 0) text = "0.0";
Chris@68 1169 else {
Chris@68 1170 nval = -val;
Chris@68 1171 if (i == n) text = "1.0";
Chris@68 1172 }
Chris@68 1173 break;
Chris@0 1174
Chris@68 1175 case MeterScale:
Chris@68 1176 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 1177 text = QString("%1").arg(meterdbs[i]);
Chris@68 1178 if (i == n) text = tr("0dB");
Chris@68 1179 if (i == 0) {
Chris@68 1180 text = tr("-Inf");
Chris@68 1181 val = 0.0;
Chris@68 1182 }
Chris@68 1183 break;
Chris@0 1184
Chris@68 1185 case dBScale:
Chris@68 1186 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 1187 text = QString("%1").arg(-(10*n) + i * 10);
Chris@68 1188 if (i == n) text = tr("0dB");
Chris@68 1189 if (i == 0) {
Chris@68 1190 text = tr("-Inf");
Chris@68 1191 val = 0.0;
Chris@68 1192 }
Chris@68 1193 break;
Chris@68 1194 }
Chris@0 1195
Chris@68 1196 if (val < -1.0 || val > 1.0) continue;
Chris@0 1197
Chris@274 1198 int y = getYForValue(v, val, ch);
Chris@0 1199
Chris@68 1200 int ny = y;
Chris@68 1201 if (nval != 0.0) {
Chris@274 1202 ny = getYForValue(v, nval, ch);
Chris@68 1203 }
Chris@0 1204
Chris@68 1205 bool spaceForLabel = (i == 0 ||
Chris@68 1206 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1207
Chris@68 1208 if (spaceForLabel) {
Chris@0 1209
Chris@68 1210 int tx = 3;
Chris@68 1211 if (m_scale != LinearScale) {
Chris@68 1212 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1213 }
Chris@68 1214
Chris@68 1215 int ty = y;
Chris@68 1216 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1217 ty = paint.fontMetrics().ascent();
Chris@68 1218 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1219 ty = h - paint.fontMetrics().descent();
Chris@68 1220 } else {
Chris@68 1221 ty += toff;
Chris@68 1222 }
Chris@68 1223 paint.drawText(tx, ty, text);
Chris@0 1224
Chris@68 1225 lastLabelledY = ty - toff;
Chris@67 1226
Chris@68 1227 if (ny != y) {
Chris@68 1228 ty = ny;
Chris@68 1229 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1230 ty = paint.fontMetrics().ascent();
Chris@68 1231 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1232 ty = h - paint.fontMetrics().descent();
Chris@68 1233 } else {
Chris@68 1234 ty += toff;
Chris@68 1235 }
Chris@68 1236 paint.drawText(tx, ty, text);
Chris@68 1237 }
Chris@68 1238
Chris@68 1239 paint.drawLine(w - 7, y, w, y);
Chris@68 1240 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1241
Chris@68 1242 } else {
Chris@68 1243
Chris@68 1244 paint.drawLine(w - 4, y, w, y);
Chris@68 1245 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1246 }
Chris@0 1247 }
Chris@0 1248 }
Chris@0 1249 }
Chris@0 1250
Chris@6 1251 QString
Chris@6 1252 WaveformLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 1253 {
Chris@6 1254 QString s;
Chris@6 1255
Chris@285 1256 QString colourName, colourSpec, darkbg;
Chris@285 1257 ColourDatabase::getInstance()->getStringValues
Chris@285 1258 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1259
Chris@6 1260 s += QString("gain=\"%1\" "
Chris@287 1261 "showMeans=\"%2\" "
Chris@287 1262 "greyscale=\"%3\" "
Chris@287 1263 "channelMode=\"%4\" "
Chris@287 1264 "channel=\"%5\" "
Chris@287 1265 "scale=\"%6\" "
Chris@287 1266 "aggressive=\"%7\" "
Chris@287 1267 "autoNormalize=\"%8\"")
Chris@6 1268 .arg(m_gain)
Chris@6 1269 .arg(m_showMeans)
Chris@6 1270 .arg(m_greyscale)
Chris@6 1271 .arg(m_channelMode)
Chris@287 1272 .arg(m_channel)
Chris@6 1273 .arg(m_scale)
Chris@67 1274 .arg(m_aggressive)
Chris@67 1275 .arg(m_autoNormalize);
Chris@6 1276
Chris@287 1277 return SingleColourLayer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 1278 }
Chris@6 1279
Chris@11 1280 void
Chris@11 1281 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1282 {
Chris@11 1283 bool ok = false;
Chris@11 1284
Chris@287 1285 SingleColourLayer::setProperties(attributes);
Chris@287 1286
Chris@11 1287 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1288 if (ok) setGain(gain);
Chris@11 1289
Chris@11 1290 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 1291 attributes.value("showMeans") == "true");
Chris@11 1292 setShowMeans(showMeans);
Chris@11 1293
Chris@11 1294 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 1295 attributes.value("greyscale") == "true");
Chris@11 1296 setUseGreyscale(greyscale);
Chris@11 1297
Chris@11 1298 ChannelMode channelMode = (ChannelMode)
Chris@11 1299 attributes.value("channelMode").toInt(&ok);
Chris@11 1300 if (ok) setChannelMode(channelMode);
Chris@11 1301
Chris@11 1302 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1303 if (ok) setChannel(channel);
Chris@11 1304
Chris@11 1305 Scale scale = (Scale)
Chris@11 1306 attributes.value("scale").toInt(&ok);
Chris@11 1307 if (ok) setScale(scale);
Chris@11 1308
Chris@11 1309 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 1310 attributes.value("aggressive") == "true");
Chris@11 1311 setUseGreyscale(aggressive);
Chris@67 1312
Chris@67 1313 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1314 attributes.value("autoNormalize") == "true");
Chris@67 1315 setAutoNormalize(autoNormalize);
Chris@11 1316 }
Chris@11 1317
Chris@133 1318 int
Chris@133 1319 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1320 {
Chris@133 1321 defaultStep = 50;
Chris@133 1322 return 100;
Chris@133 1323 }
Chris@0 1324
Chris@133 1325 int
Chris@133 1326 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1327 {
Chris@133 1328 int val = lrint(log10(m_gain) * 20.0) + 50;
Chris@133 1329 if (val < 0) val = 0;
Chris@133 1330 if (val > 100) val = 100;
Chris@133 1331 return val;
Chris@133 1332 }
Chris@133 1333
Chris@133 1334 void
Chris@133 1335 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1336 {
Chris@133 1337 setGain(pow(10, float(step - 50) / 20.0));
Chris@133 1338 }
Chris@133 1339