annotate layer/WaveformLayer.cpp @ 317:e251c3599ea8

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