| 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@59 | 7     This file copyright 2006 Chris Cannam. | 
| 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@0 | 19 #include "base/View.h" | 
| Chris@0 | 20 #include "base/Profiler.h" | 
| Chris@0 | 21 | 
| Chris@0 | 22 #include <QPainter> | 
| Chris@0 | 23 #include <QPixmap> | 
| Chris@0 | 24 | 
| Chris@0 | 25 #include <iostream> | 
| Chris@0 | 26 #include <cmath> | 
| Chris@0 | 27 | 
| Chris@4 | 28 //#define DEBUG_WAVEFORM_PAINT 1 | 
| Chris@4 | 29 | 
| Chris@0 | 30 using std::cerr; | 
| Chris@0 | 31 using std::endl; | 
| Chris@0 | 32 | 
| Chris@44 | 33 WaveformLayer::WaveformLayer() : | 
| Chris@44 | 34     Layer(), | 
| Chris@0 | 35     m_model(0), | 
| Chris@0 | 36     m_gain(1.0f), | 
| Chris@67 | 37     m_autoNormalize(false), | 
| Chris@0 | 38     m_colour(Qt::black), | 
| Chris@0 | 39     m_showMeans(true), | 
| Chris@0 | 40     m_greyscale(true), | 
| Chris@0 | 41     m_channelMode(SeparateChannels), | 
| Chris@0 | 42     m_channel(-1), | 
| Chris@0 | 43     m_scale(LinearScale), | 
| Chris@0 | 44     m_aggressive(false), | 
| Chris@0 | 45     m_cache(0), | 
| Chris@0 | 46     m_cacheValid(false) | 
| Chris@0 | 47 { | 
| Chris@44 | 48 | 
| Chris@0 | 49 } | 
| Chris@0 | 50 | 
| Chris@0 | 51 WaveformLayer::~WaveformLayer() | 
| Chris@0 | 52 { | 
| Chris@0 | 53     delete m_cache; | 
| Chris@0 | 54 } | 
| Chris@0 | 55 | 
| Chris@0 | 56 void | 
| Chris@0 | 57 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model) | 
| Chris@0 | 58 { | 
| Chris@69 | 59     bool channelsChanged = false; | 
| Chris@69 | 60     if (m_channel == -1) { | 
| Chris@69 | 61         if (!m_model) { | 
| Chris@69 | 62             if (model) { | 
| Chris@69 | 63                 channelsChanged = true; | 
| Chris@69 | 64             } | 
| Chris@69 | 65         } else { | 
| Chris@69 | 66             if (model && | 
| Chris@69 | 67                 m_model->getChannelCount() != model->getChannelCount()) { | 
| Chris@69 | 68                 channelsChanged = true; | 
| Chris@69 | 69             } | 
| Chris@69 | 70         } | 
| Chris@69 | 71     } | 
| Chris@69 | 72 | 
| Chris@0 | 73     m_model = model; | 
| Chris@0 | 74     m_cacheValid = false; | 
| Chris@0 | 75     if (!m_model || !m_model->isOK()) return; | 
| Chris@0 | 76 | 
| Chris@0 | 77     connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | 
| Chris@0 | 78     connect(m_model, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@0 | 79 	    this, SIGNAL(modelChanged(size_t, size_t))); | 
| Chris@0 | 80 | 
| Chris@0 | 81     connect(m_model, SIGNAL(completionChanged()), | 
| Chris@0 | 82 	    this, SIGNAL(modelCompletionChanged())); | 
| Chris@0 | 83 | 
| Chris@0 | 84     emit modelReplaced(); | 
| Chris@69 | 85 | 
| Chris@69 | 86     if (channelsChanged) emit layerParametersChanged(); | 
| Chris@0 | 87 } | 
| Chris@0 | 88 | 
| Chris@0 | 89 Layer::PropertyList | 
| Chris@0 | 90 WaveformLayer::getProperties() const | 
| Chris@0 | 91 { | 
| Chris@0 | 92     PropertyList list; | 
| Chris@0 | 93     list.push_back(tr("Colour")); | 
| Chris@0 | 94     list.push_back(tr("Scale")); | 
| Chris@0 | 95     list.push_back(tr("Gain")); | 
| Chris@67 | 96     list.push_back(tr("Normalize Visible Area")); | 
| Chris@68 | 97 | 
| Chris@68 | 98     if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) { | 
| Chris@68 | 99         list.push_back(tr("Channels")); | 
| Chris@68 | 100     } | 
| Chris@68 | 101 | 
| Chris@0 | 102     return list; | 
| Chris@0 | 103 } | 
| Chris@0 | 104 | 
| Chris@0 | 105 Layer::PropertyType | 
| Chris@0 | 106 WaveformLayer::getPropertyType(const PropertyName &name) const | 
| Chris@0 | 107 { | 
| Chris@0 | 108     if (name == tr("Gain")) return RangeProperty; | 
| Chris@67 | 109     if (name == tr("Normalize Visible Area")) return ToggleProperty; | 
| Chris@0 | 110     if (name == tr("Colour")) return ValueProperty; | 
| Chris@67 | 111     if (name == tr("Channels")) return ValueProperty; | 
| Chris@0 | 112     if (name == tr("Scale")) return ValueProperty; | 
| Chris@0 | 113     return InvalidProperty; | 
| Chris@0 | 114 } | 
| Chris@0 | 115 | 
| Chris@0 | 116 QString | 
| Chris@0 | 117 WaveformLayer::getPropertyGroupName(const PropertyName &name) const | 
| Chris@0 | 118 { | 
| Chris@0 | 119     if (name == tr("Gain") || | 
| Chris@67 | 120         name == tr("Normalize Visible Area") || | 
| Chris@0 | 121 	name == tr("Scale")) return tr("Scale"); | 
| Chris@0 | 122     return QString(); | 
| Chris@0 | 123 } | 
| Chris@0 | 124 | 
| Chris@0 | 125 int | 
| Chris@0 | 126 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name, | 
| Chris@67 | 127                                         int *min, int *max) const | 
| Chris@0 | 128 { | 
| Chris@0 | 129     int deft = 0; | 
| Chris@0 | 130 | 
| Chris@56 | 131     int garbage0, garbage1; | 
| Chris@56 | 132     if (!min) min = &garbage0; | 
| Chris@56 | 133     if (!max) max = &garbage1; | 
| Chris@10 | 134 | 
| Chris@0 | 135     if (name == tr("Gain")) { | 
| Chris@0 | 136 | 
| Chris@0 | 137 	*min = -50; | 
| Chris@0 | 138 	*max = 50; | 
| Chris@0 | 139 | 
| Chris@34 | 140 	deft = lrint(log10(m_gain) * 20.0); | 
| Chris@0 | 141 	if (deft < *min) deft = *min; | 
| Chris@0 | 142 	if (deft > *max) deft = *max; | 
| Chris@0 | 143 | 
| Chris@67 | 144     } else if (name == tr("Normalize Visible Area")) { | 
| Chris@67 | 145 | 
| Chris@67 | 146         deft = (m_autoNormalize ? 1 : 0); | 
| Chris@67 | 147 | 
| Chris@0 | 148     } else if (name == tr("Colour")) { | 
| Chris@0 | 149 | 
| Chris@0 | 150 	*min = 0; | 
| Chris@0 | 151 	*max = 5; | 
| Chris@0 | 152 | 
| Chris@0 | 153 	if (m_colour == Qt::black) deft = 0; | 
| Chris@0 | 154 	else if (m_colour == Qt::darkRed) deft = 1; | 
| Chris@0 | 155 	else if (m_colour == Qt::darkBlue) deft = 2; | 
| Chris@0 | 156 	else if (m_colour == Qt::darkGreen) deft = 3; | 
| Chris@0 | 157 	else if (m_colour == QColor(200, 50, 255)) deft = 4; | 
| Chris@0 | 158 	else if (m_colour == QColor(255, 150, 50)) deft = 5; | 
| Chris@0 | 159 | 
| Chris@67 | 160     } else if (name == tr("Channels")) { | 
| Chris@0 | 161 | 
| Chris@67 | 162         *min = 0; | 
| Chris@67 | 163         *max = 2; | 
| Chris@67 | 164         if (m_channelMode == MixChannels) deft = 1; | 
| Chris@67 | 165         else if (m_channelMode == MergeChannels) deft = 2; | 
| Chris@67 | 166         else deft = 0; | 
| Chris@0 | 167 | 
| Chris@0 | 168     } else if (name == tr("Scale")) { | 
| Chris@0 | 169 | 
| Chris@0 | 170 	*min = 0; | 
| Chris@0 | 171 	*max = 2; | 
| Chris@0 | 172 | 
| Chris@0 | 173 	deft = (int)m_scale; | 
| Chris@0 | 174 | 
| Chris@0 | 175     } else { | 
| Chris@0 | 176 	deft = Layer::getPropertyRangeAndValue(name, min, max); | 
| Chris@0 | 177     } | 
| Chris@0 | 178 | 
| Chris@0 | 179     return deft; | 
| Chris@0 | 180 } | 
| Chris@0 | 181 | 
| Chris@0 | 182 QString | 
| Chris@0 | 183 WaveformLayer::getPropertyValueLabel(const PropertyName &name, | 
| Chris@0 | 184 				    int value) const | 
| Chris@0 | 185 { | 
| Chris@0 | 186     if (name == tr("Colour")) { | 
| Chris@0 | 187 	switch (value) { | 
| Chris@0 | 188 	default: | 
| Chris@0 | 189 	case 0: return tr("Black"); | 
| Chris@0 | 190 	case 1: return tr("Red"); | 
| Chris@0 | 191 	case 2: return tr("Blue"); | 
| Chris@0 | 192 	case 3: return tr("Green"); | 
| Chris@0 | 193 	case 4: return tr("Purple"); | 
| Chris@0 | 194 	case 5: return tr("Orange"); | 
| Chris@0 | 195 	} | 
| Chris@0 | 196     } | 
| Chris@0 | 197     if (name == tr("Scale")) { | 
| Chris@0 | 198 	switch (value) { | 
| Chris@0 | 199 	default: | 
| Chris@0 | 200 	case 0: return tr("Linear"); | 
| Chris@0 | 201 	case 1: return tr("Meter"); | 
| Chris@0 | 202 	case 2: return tr("dB"); | 
| Chris@0 | 203 	} | 
| Chris@0 | 204     } | 
| Chris@67 | 205     if (name == tr("Channels")) { | 
| Chris@67 | 206         switch (value) { | 
| Chris@67 | 207         default: | 
| Chris@67 | 208         case 0: return tr("Separate"); | 
| Chris@67 | 209         case 1: return tr("Mean"); | 
| Chris@67 | 210         case 2: return tr("Butterfly"); | 
| Chris@67 | 211         } | 
| Chris@67 | 212     } | 
| Chris@0 | 213     return tr("<unknown>"); | 
| Chris@0 | 214 } | 
| Chris@0 | 215 | 
| Chris@0 | 216 void | 
| Chris@0 | 217 WaveformLayer::setProperty(const PropertyName &name, int value) | 
| Chris@0 | 218 { | 
| Chris@0 | 219     if (name == tr("Gain")) { | 
| Chris@0 | 220 	setGain(pow(10, float(value)/20.0)); | 
| Chris@67 | 221     } else if (name == tr("Normalize Visible Area")) { | 
| Chris@67 | 222         setAutoNormalize(value ? true : false); | 
| Chris@0 | 223     } else if (name == tr("Colour")) { | 
| Chris@0 | 224 	switch (value) { | 
| Chris@0 | 225 	default: | 
| Chris@0 | 226 	case 0:	setBaseColour(Qt::black); break; | 
| Chris@0 | 227 	case 1: setBaseColour(Qt::darkRed); break; | 
| Chris@0 | 228 	case 2: setBaseColour(Qt::darkBlue); break; | 
| Chris@0 | 229 	case 3: setBaseColour(Qt::darkGreen); break; | 
| Chris@0 | 230 	case 4: setBaseColour(QColor(200, 50, 255)); break; | 
| Chris@0 | 231 	case 5: setBaseColour(QColor(255, 150, 50)); break; | 
| Chris@0 | 232 	} | 
| Chris@67 | 233     } else if (name == tr("Channels")) { | 
| Chris@67 | 234         if (value == 1) setChannelMode(MixChannels); | 
| Chris@67 | 235         else if (value == 2) setChannelMode(MergeChannels); | 
| Chris@67 | 236         else setChannelMode(SeparateChannels); | 
| Chris@0 | 237     } else if (name == tr("Scale")) { | 
| Chris@0 | 238 	switch (value) { | 
| Chris@0 | 239 	default: | 
| Chris@0 | 240 	case 0: setScale(LinearScale); break; | 
| Chris@0 | 241 	case 1: setScale(MeterScale); break; | 
| Chris@0 | 242 	case 2: setScale(dBScale); break; | 
| Chris@0 | 243 	} | 
| Chris@0 | 244     } | 
| Chris@0 | 245 } | 
| Chris@0 | 246 | 
| Chris@0 | 247 void | 
| Chris@67 | 248 WaveformLayer::setGain(float gain) | 
| Chris@0 | 249 { | 
| Chris@0 | 250     if (m_gain == gain) return; | 
| Chris@0 | 251     m_gain = gain; | 
| Chris@0 | 252     m_cacheValid = false; | 
| Chris@0 | 253     emit layerParametersChanged(); | 
| Chris@0 | 254 } | 
| Chris@0 | 255 | 
| Chris@0 | 256 void | 
| Chris@67 | 257 WaveformLayer::setAutoNormalize(bool autoNormalize) | 
| Chris@67 | 258 { | 
| Chris@67 | 259     if (m_autoNormalize == autoNormalize) return; | 
| Chris@67 | 260     m_autoNormalize = autoNormalize; | 
| Chris@67 | 261     m_cacheValid = false; | 
| Chris@67 | 262     emit layerParametersChanged(); | 
| Chris@67 | 263 } | 
| Chris@67 | 264 | 
| Chris@67 | 265 void | 
| Chris@0 | 266 WaveformLayer::setBaseColour(QColor colour) | 
| Chris@0 | 267 { | 
| Chris@0 | 268     if (m_colour == colour) return; | 
| Chris@0 | 269     m_colour = colour; | 
| Chris@0 | 270     m_cacheValid = false; | 
| Chris@0 | 271     emit layerParametersChanged(); | 
| Chris@0 | 272 } | 
| Chris@0 | 273 | 
| Chris@0 | 274 void | 
| Chris@0 | 275 WaveformLayer::setShowMeans(bool showMeans) | 
| Chris@0 | 276 { | 
| Chris@0 | 277     if (m_showMeans == showMeans) return; | 
| Chris@0 | 278     m_showMeans = showMeans; | 
| Chris@0 | 279     m_cacheValid = false; | 
| Chris@0 | 280     emit layerParametersChanged(); | 
| Chris@0 | 281 } | 
| Chris@0 | 282 | 
| Chris@0 | 283 void | 
| Chris@0 | 284 WaveformLayer::setUseGreyscale(bool useGreyscale) | 
| Chris@0 | 285 { | 
| Chris@0 | 286     if (m_greyscale == useGreyscale) return; | 
| Chris@0 | 287     m_greyscale = useGreyscale; | 
| Chris@0 | 288     m_cacheValid = false; | 
| Chris@0 | 289     emit layerParametersChanged(); | 
| Chris@0 | 290 } | 
| Chris@0 | 291 | 
| Chris@0 | 292 void | 
| Chris@0 | 293 WaveformLayer::setChannelMode(ChannelMode channelMode) | 
| Chris@0 | 294 { | 
| Chris@0 | 295     if (m_channelMode == channelMode) return; | 
| Chris@0 | 296     m_channelMode = channelMode; | 
| Chris@0 | 297     m_cacheValid = false; | 
| Chris@0 | 298     emit layerParametersChanged(); | 
| Chris@0 | 299 } | 
| Chris@0 | 300 | 
| Chris@0 | 301 void | 
| Chris@0 | 302 WaveformLayer::setChannel(int channel) | 
| Chris@0 | 303 { | 
| Chris@0 | 304     std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl; | 
| Chris@0 | 305 | 
| Chris@0 | 306     if (m_channel == channel) return; | 
| Chris@0 | 307     m_channel = channel; | 
| Chris@0 | 308     m_cacheValid = false; | 
| Chris@0 | 309     emit layerParametersChanged(); | 
| Chris@0 | 310 } | 
| Chris@0 | 311 | 
| Chris@0 | 312 void | 
| Chris@0 | 313 WaveformLayer::setScale(Scale scale) | 
| Chris@0 | 314 { | 
| Chris@0 | 315     if (m_scale == scale) return; | 
| Chris@0 | 316     m_scale = scale; | 
| Chris@0 | 317     m_cacheValid = false; | 
| Chris@0 | 318     emit layerParametersChanged(); | 
| Chris@0 | 319 } | 
| Chris@0 | 320 | 
| Chris@0 | 321 void | 
| Chris@0 | 322 WaveformLayer::setAggressiveCacheing(bool aggressive) | 
| Chris@0 | 323 { | 
| Chris@0 | 324     if (m_aggressive == aggressive) return; | 
| Chris@0 | 325     m_aggressive = aggressive; | 
| Chris@0 | 326     m_cacheValid = false; | 
| Chris@0 | 327     emit layerParametersChanged(); | 
| Chris@0 | 328 } | 
| Chris@0 | 329 | 
| Chris@0 | 330 int | 
| Chris@0 | 331 WaveformLayer::getCompletion() const | 
| Chris@0 | 332 { | 
| Chris@0 | 333     int completion = 100; | 
| Chris@0 | 334     if (!m_model || !m_model->isOK()) return completion; | 
| Chris@0 | 335     if (m_model->isReady(&completion)) return 100; | 
| Chris@0 | 336     return completion; | 
| Chris@0 | 337 } | 
| Chris@0 | 338 | 
| Chris@0 | 339 int | 
| Chris@0 | 340 WaveformLayer::dBscale(float sample, int m) const | 
| Chris@0 | 341 { | 
| Chris@67 | 342 //!!!    if (sample < 0.0) return -dBscale(-sample, m); | 
| Chris@67 | 343     if (sample < 0.0) return dBscale(-sample, m); | 
| Chris@0 | 344     float dB = AudioLevel::multiplier_to_dB(sample); | 
| Chris@0 | 345     if (dB < -50.0) return 0; | 
| Chris@0 | 346     if (dB > 0.0) return m; | 
| Chris@0 | 347     return int(((dB + 50.0) * m) / 50.0 + 0.1); | 
| Chris@0 | 348 } | 
| Chris@0 | 349 | 
| Chris@0 | 350 size_t | 
| Chris@67 | 351 WaveformLayer::getChannelArrangement(size_t &min, size_t &max, | 
| Chris@67 | 352                                      bool &merging, bool &mixing) | 
| Chris@0 | 353     const | 
| Chris@0 | 354 { | 
| Chris@0 | 355     if (!m_model || !m_model->isOK()) return 0; | 
| Chris@0 | 356 | 
| Chris@0 | 357     size_t channels = m_model->getChannelCount(); | 
| Chris@0 | 358     if (channels == 0) return 0; | 
| Chris@0 | 359 | 
| Chris@0 | 360     size_t rawChannels = channels; | 
| Chris@0 | 361 | 
| Chris@0 | 362     if (m_channel == -1) { | 
| Chris@0 | 363 	min = 0; | 
| Chris@67 | 364 	if (m_channelMode == MergeChannels || | 
| Chris@67 | 365             m_channelMode == MixChannels) { | 
| Chris@0 | 366 	    max = 0; | 
| Chris@0 | 367 	    channels = 1; | 
| Chris@0 | 368 	} else { | 
| Chris@0 | 369 	    max = channels - 1; | 
| Chris@0 | 370 	} | 
| Chris@0 | 371     } else { | 
| Chris@0 | 372 	min = m_channel; | 
| Chris@0 | 373 	max = m_channel; | 
| Chris@0 | 374 	rawChannels = 1; | 
| Chris@0 | 375 	channels = 1; | 
| Chris@0 | 376     } | 
| Chris@0 | 377 | 
| Chris@0 | 378     merging = (m_channelMode == MergeChannels && rawChannels > 1); | 
| Chris@67 | 379     mixing = (m_channelMode == MixChannels && rawChannels > 1); | 
| Chris@0 | 380 | 
| Chris@0 | 381 //    std::cerr << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << std::endl; | 
| Chris@0 | 382 | 
| Chris@0 | 383     return channels; | 
| Chris@0 | 384 } | 
| Chris@0 | 385 | 
| Chris@67 | 386 bool | 
| Chris@67 | 387 WaveformLayer::isLayerScrollable(const View *) const | 
| Chris@67 | 388 { | 
| Chris@67 | 389     return !m_autoNormalize; | 
| Chris@67 | 390 } | 
| Chris@67 | 391 | 
| Chris@68 | 392 static float meterdbs[] = { -40, -30, -20, -15, -10, | 
| Chris@68 | 393                             -5, -3, -2, -1, -0.5, 0 }; | 
| Chris@68 | 394 | 
| Chris@0 | 395 void | 
| Chris@44 | 396 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const | 
| Chris@0 | 397 { | 
| Chris@0 | 398     if (!m_model || !m_model->isOK()) { | 
| Chris@0 | 399 	return; | 
| Chris@0 | 400     } | 
| Chris@0 | 401 | 
| Chris@44 | 402     long startFrame = v->getStartFrame(); | 
| Chris@44 | 403     int zoomLevel = v->getZoomLevel(); | 
| Chris@0 | 404 | 
| Chris@2 | 405 #ifdef DEBUG_WAVEFORM_PAINT | 
| Chris@0 | 406     Profiler profiler("WaveformLayer::paint", true); | 
| Chris@0 | 407     std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y() | 
| Chris@0 | 408 	      << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << ", start " << startFrame << std::endl; | 
| Chris@2 | 409 #endif | 
| Chris@0 | 410 | 
| Chris@0 | 411     size_t channels = 0, minChannel = 0, maxChannel = 0; | 
| Chris@67 | 412     bool mergingChannels = false, mixingChannels = false; | 
| Chris@0 | 413 | 
| Chris@67 | 414     channels = getChannelArrangement(minChannel, maxChannel, | 
| Chris@67 | 415                                      mergingChannels, mixingChannels); | 
| Chris@0 | 416     if (channels == 0) return; | 
| Chris@0 | 417 | 
| Chris@44 | 418     int w = v->width(); | 
| Chris@44 | 419     int h = v->height(); | 
| Chris@0 | 420 | 
| Chris@0 | 421     bool ready = m_model->isReady(); | 
| Chris@0 | 422     QPainter *paint; | 
| Chris@0 | 423 | 
| Chris@0 | 424     if (m_aggressive) { | 
| Chris@0 | 425 | 
| Chris@0 | 426 	if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) { | 
| Chris@0 | 427 	    m_cacheValid = false; | 
| Chris@0 | 428 	} | 
| Chris@0 | 429 | 
| Chris@0 | 430 	if (m_cacheValid) { | 
| Chris@0 | 431 	    viewPainter.drawPixmap(rect, *m_cache, rect); | 
| Chris@0 | 432 	    return; | 
| Chris@0 | 433 	} | 
| Chris@0 | 434 | 
| Chris@0 | 435 	if (!m_cache || m_cache->width() != w || m_cache->height() != h) { | 
| Chris@0 | 436 	    delete m_cache; | 
| Chris@0 | 437 	    m_cache = new QPixmap(w, h); | 
| Chris@0 | 438 	} | 
| Chris@0 | 439 | 
| Chris@0 | 440 	paint = new QPainter(m_cache); | 
| Chris@0 | 441 | 
| Chris@0 | 442 	paint->setPen(Qt::NoPen); | 
| Chris@44 | 443 	paint->setBrush(v->palette().background()); | 
| Chris@0 | 444 	paint->drawRect(rect); | 
| Chris@0 | 445 | 
| Chris@0 | 446 	paint->setPen(Qt::black); | 
| Chris@0 | 447 	paint->setBrush(Qt::NoBrush); | 
| Chris@0 | 448 | 
| Chris@0 | 449     } else { | 
| Chris@0 | 450 	paint = &viewPainter; | 
| Chris@0 | 451     } | 
| Chris@0 | 452 | 
| Chris@28 | 453     paint->setRenderHint(QPainter::Antialiasing, false); | 
| Chris@28 | 454 | 
| Chris@0 | 455     int x0 = 0, x1 = w - 1; | 
| Chris@0 | 456     int y0 = 0, y1 = h - 1; | 
| Chris@0 | 457 | 
| Chris@0 | 458     x0 = rect.left(); | 
| Chris@0 | 459     x1 = rect.right(); | 
| Chris@0 | 460     y0 = rect.top(); | 
| Chris@0 | 461     y1 = rect.bottom(); | 
| Chris@0 | 462 | 
| Chris@28 | 463     if (x0 > 0) --x0; | 
| Chris@44 | 464     if (x1 < v->width()) ++x1; | 
| Chris@28 | 465 | 
| Chris@44 | 466     long frame0 = v->getFrameForX(x0); | 
| Chris@44 | 467     long frame1 = v->getFrameForX(x1 + 1); | 
| Chris@0 | 468 | 
| Chris@4 | 469 #ifdef DEBUG_WAVEFORM_PAINT | 
| Chris@4 | 470     std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" <<  std::endl; | 
| Chris@4 | 471 #endif | 
| Chris@0 | 472 | 
| Chris@0 | 473     RangeSummarisableTimeValueModel::RangeBlock ranges; | 
| Chris@0 | 474     RangeSummarisableTimeValueModel::RangeBlock otherChannelRanges; | 
| Chris@0 | 475     RangeSummarisableTimeValueModel::Range range; | 
| Chris@0 | 476 | 
| Chris@0 | 477     QColor greys[3]; | 
| Chris@0 | 478     if (m_colour == Qt::black) { | 
| Chris@27 | 479 	for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest | 
| Chris@0 | 480 	    int level = 192 - 64 * i; | 
| Chris@0 | 481 	    greys[i] = QColor(level, level, level); | 
| Chris@0 | 482 	} | 
| Chris@0 | 483     } else { | 
| Chris@44 | 484 	int hue, sat, val; | 
| Chris@44 | 485 	m_colour.getHsv(&hue, &sat, &val); | 
| Chris@27 | 486 	for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest | 
| Chris@44 | 487 	    if (v->hasLightBackground()) { | 
| Chris@44 | 488 		greys[i] = QColor::fromHsv(hue, sat * (i + 1) / 4, val); | 
| Chris@27 | 489 	    } else { | 
| Chris@44 | 490 		greys[i] = QColor::fromHsv(hue, sat * (3 - i) / 4, val); | 
| Chris@27 | 491 	    } | 
| Chris@27 | 492 	} | 
| Chris@0 | 493     } | 
| Chris@0 | 494 | 
| Chris@0 | 495     QColor midColour = m_colour; | 
| Chris@0 | 496     if (midColour == Qt::black) { | 
| Chris@0 | 497 	midColour = Qt::gray; | 
| Chris@44 | 498     } else if (v->hasLightBackground()) { | 
| Chris@0 | 499 	midColour = midColour.light(150); | 
| Chris@0 | 500     } else { | 
| Chris@0 | 501 	midColour = midColour.light(50); | 
| Chris@0 | 502     } | 
| Chris@0 | 503 | 
| Chris@67 | 504     while (m_effectiveGains.size() <= maxChannel) { | 
| Chris@67 | 505         m_effectiveGains.push_back(m_gain); | 
| Chris@67 | 506     } | 
| Chris@67 | 507 | 
| Chris@0 | 508     for (size_t ch = minChannel; ch <= maxChannel; ++ch) { | 
| Chris@0 | 509 | 
| Chris@0 | 510 	int prevRangeBottom = -1, prevRangeTop = -1; | 
| Chris@28 | 511 	QColor prevRangeBottomColour = m_colour, prevRangeTopColour = m_colour; | 
| Chris@0 | 512 | 
| Chris@67 | 513         m_effectiveGains[ch] = m_gain; | 
| Chris@67 | 514 | 
| Chris@67 | 515         if (m_autoNormalize) { | 
| Chris@67 | 516             RangeSummarisableTimeValueModel::Range range = | 
| Chris@67 | 517                 m_model->getRange(ch, startFrame < 0 ? 0 : startFrame, | 
| Chris@67 | 518                                   v->getEndFrame()); | 
| Chris@67 | 519             if (mergingChannels || mixingChannels) { | 
| Chris@67 | 520                 RangeSummarisableTimeValueModel::Range otherRange = | 
| Chris@67 | 521                     m_model->getRange(1, startFrame < 0 ? 0 : startFrame, | 
| Chris@67 | 522                                       v->getEndFrame()); | 
| Chris@67 | 523                 range.max = std::max(range.max, otherRange.max); | 
| Chris@67 | 524                 range.min = std::min(range.min, otherRange.min); | 
| Chris@67 | 525                 range.absmean = std::min(range.absmean, otherRange.absmean); | 
| Chris@67 | 526             } | 
| Chris@67 | 527             m_effectiveGains[ch] = 1.0 / std::max(fabsf(range.max), | 
| Chris@67 | 528                                                   fabsf(range.min)); | 
| Chris@67 | 529         } | 
| Chris@67 | 530 | 
| Chris@67 | 531         float gain = m_effectiveGains[ch]; | 
| Chris@67 | 532 | 
| Chris@68 | 533 	int m = (h / channels) / 2; | 
| Chris@68 | 534 	int my = m + (((ch - minChannel) * h) / channels); | 
| Chris@68 | 535 | 
| Chris@68 | 536 //	std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl; | 
| Chris@68 | 537 | 
| Chris@68 | 538 	if (my - m > y1 || my + m < y0) continue; | 
| Chris@68 | 539 | 
| Chris@68 | 540         if ((m_scale == dBScale || m_scale == MeterScale) && | 
| Chris@68 | 541             m_channelMode != MergeChannels) { | 
| Chris@68 | 542             m = (h / channels); | 
| Chris@68 | 543             my = m + (((ch - minChannel) * h) / channels); | 
| Chris@68 | 544         } | 
| Chris@68 | 545 | 
| Chris@68 | 546 	paint->setPen(greys[0]); | 
| Chris@68 | 547 	paint->drawLine(x0, my, x1, my); | 
| Chris@68 | 548 | 
| Chris@68 | 549         int n = 10; | 
| Chris@68 | 550         int py = -1; | 
| Chris@68 | 551 | 
| Chris@68 | 552         if (v->hasLightBackground()) { | 
| Chris@68 | 553 | 
| Chris@68 | 554             paint->setPen(QColor(240, 240, 240)); | 
| Chris@68 | 555 | 
| Chris@68 | 556             for (int i = 1; i < n; ++i) { | 
| Chris@68 | 557 | 
| Chris@68 | 558                 float val = 0.0, nval = 0.0; | 
| Chris@68 | 559 | 
| Chris@68 | 560                 switch (m_scale) { | 
| Chris@68 | 561 | 
| Chris@68 | 562                 case LinearScale: | 
| Chris@68 | 563                     val = (i * gain) / n; | 
| Chris@68 | 564                     if (i > 0) nval = -val; | 
| Chris@68 | 565                     break; | 
| Chris@68 | 566 | 
| Chris@68 | 567                 case MeterScale: | 
| Chris@68 | 568                     val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain; | 
| Chris@68 | 569                     break; | 
| Chris@68 | 570 | 
| Chris@68 | 571                 case dBScale: | 
| Chris@68 | 572                     val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain; | 
| Chris@68 | 573                     break; | 
| Chris@68 | 574                 } | 
| Chris@68 | 575 | 
| Chris@68 | 576                 if (val < -1.0 || val > 1.0) continue; | 
| Chris@68 | 577 | 
| Chris@68 | 578                 int y = getYForValue(v, m_scale, val, ch, minChannel, maxChannel); | 
| Chris@68 | 579 | 
| Chris@68 | 580                 if (py >= 0 && abs(y - py) < 10) continue; | 
| Chris@68 | 581                 else py = y; | 
| Chris@68 | 582 | 
| Chris@68 | 583                 int ny = y; | 
| Chris@68 | 584                 if (nval != 0.0) { | 
| Chris@68 | 585                     ny = getYForValue(v, m_scale, nval, ch, minChannel, maxChannel); | 
| Chris@68 | 586                 } | 
| Chris@68 | 587 | 
| Chris@68 | 588                 paint->drawLine(x0, y, x1, y); | 
| Chris@68 | 589                 if (ny != y) { | 
| Chris@68 | 590                     paint->drawLine(x0, ny, x1, ny); | 
| Chris@68 | 591                 } | 
| Chris@68 | 592             } | 
| Chris@68 | 593         } | 
| Chris@68 | 594 | 
| Chris@68 | 595 	if (frame1 <= 0) continue; | 
| Chris@68 | 596 | 
| Chris@68 | 597 	size_t modelZoomLevel = zoomLevel; | 
| Chris@68 | 598 | 
| Chris@68 | 599 	ranges = m_model->getRanges | 
| Chris@68 | 600 	    (ch, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel); | 
| Chris@68 | 601 | 
| Chris@68 | 602 	if (mergingChannels || mixingChannels) { | 
| Chris@68 | 603 	    otherChannelRanges = m_model->getRanges | 
| Chris@68 | 604 		(1, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel); | 
| Chris@68 | 605 	} | 
| Chris@68 | 606 | 
| Chris@0 | 607 	for (int x = x0; x <= x1; ++x) { | 
| Chris@0 | 608 | 
| Chris@0 | 609 	    range = RangeSummarisableTimeValueModel::Range(); | 
| Chris@0 | 610 	    size_t index = x - x0; | 
| Chris@0 | 611 	    size_t maxIndex = index; | 
| Chris@0 | 612 | 
| Chris@0 | 613 	    if (frame0 < 0) { | 
| Chris@0 | 614 		if (index < size_t(-frame0 / zoomLevel)) { | 
| Chris@0 | 615 		    continue; | 
| Chris@0 | 616 		} else { | 
| Chris@0 | 617 		    index -= -frame0 / zoomLevel; | 
| Chris@0 | 618 		    maxIndex = index; | 
| Chris@0 | 619 		} | 
| Chris@0 | 620 	    } | 
| Chris@0 | 621 | 
| Chris@0 | 622 	    if (int(modelZoomLevel) != zoomLevel) { | 
| Chris@0 | 623 | 
| Chris@0 | 624 		index = size_t((double(index) * zoomLevel) / modelZoomLevel); | 
| Chris@0 | 625 | 
| Chris@0 | 626 		if (int(modelZoomLevel) < zoomLevel) { | 
| Chris@0 | 627 		    // Peaks may be missed!  The model should avoid | 
| Chris@0 | 628 		    // this by rounding zoom levels up rather than | 
| Chris@0 | 629 		    // down, but we'd better cope in case it doesn't | 
| Chris@0 | 630 		    maxIndex = index; | 
| Chris@0 | 631 		} else { | 
| Chris@0 | 632 		    maxIndex = size_t((double(index + 1) * zoomLevel) | 
| Chris@0 | 633 				      / modelZoomLevel) - 1; | 
| Chris@0 | 634 		} | 
| Chris@0 | 635 	    } | 
| Chris@0 | 636 | 
| Chris@0 | 637 	    if (index < ranges.size()) { | 
| Chris@0 | 638 | 
| Chris@0 | 639 		range = ranges[index]; | 
| Chris@0 | 640 | 
| Chris@0 | 641 		if (maxIndex > index && maxIndex < ranges.size()) { | 
| Chris@0 | 642 		    range.max = std::max(range.max, ranges[maxIndex].max); | 
| Chris@0 | 643 		    range.min = std::min(range.min, ranges[maxIndex].min); | 
| Chris@0 | 644 		    range.absmean = (range.absmean + | 
| Chris@0 | 645 				     ranges[maxIndex].absmean) / 2; | 
| Chris@0 | 646 		} | 
| Chris@0 | 647 | 
| Chris@0 | 648 	    } else { | 
| Chris@0 | 649 		continue; | 
| Chris@0 | 650 	    } | 
| Chris@0 | 651 | 
| Chris@0 | 652 	    int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0; | 
| Chris@0 | 653 | 
| Chris@0 | 654 	    if (mergingChannels) { | 
| Chris@0 | 655 | 
| Chris@0 | 656 		if (index < otherChannelRanges.size()) { | 
| Chris@0 | 657 | 
| Chris@0 | 658 		    range.max = fabsf(range.max); | 
| Chris@0 | 659 		    range.min = -fabsf(otherChannelRanges[index].max); | 
| Chris@0 | 660 		    range.absmean = (range.absmean + | 
| Chris@0 | 661 				     otherChannelRanges[index].absmean) / 2; | 
| Chris@0 | 662 | 
| Chris@0 | 663 		    if (maxIndex > index && maxIndex < ranges.size()) { | 
| Chris@0 | 664 			// let's not concern ourselves about the mean | 
| Chris@0 | 665 			range.min = std::min | 
| Chris@0 | 666 			    (range.min, | 
| Chris@0 | 667 			     -fabsf(otherChannelRanges[maxIndex].max)); | 
| Chris@0 | 668 		    } | 
| Chris@0 | 669 		} | 
| Chris@67 | 670 | 
| Chris@67 | 671 	    } else if (mixingChannels) { | 
| Chris@67 | 672 | 
| Chris@67 | 673 		if (index < otherChannelRanges.size()) { | 
| Chris@67 | 674 | 
| Chris@67 | 675                     range.max = (range.max + otherChannelRanges[index].max) / 2; | 
| Chris@67 | 676                     range.min = (range.min + otherChannelRanges[index].min) / 2; | 
| Chris@67 | 677                     range.absmean = (range.absmean + otherChannelRanges[index].absmean) / 2; | 
| Chris@67 | 678                 } | 
| Chris@67 | 679             } | 
| Chris@0 | 680 | 
| Chris@0 | 681 	    int greyLevels = 1; | 
| Chris@0 | 682 	    if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4; | 
| Chris@0 | 683 | 
| Chris@0 | 684 	    switch (m_scale) { | 
| Chris@0 | 685 | 
| Chris@0 | 686 	    case LinearScale: | 
| Chris@67 | 687 		rangeBottom = int( m * greyLevels * range.min * gain); | 
| Chris@67 | 688 		rangeTop    = int( m * greyLevels * range.max * gain); | 
| Chris@67 | 689 		meanBottom  = int(-m * range.absmean * gain); | 
| Chris@67 | 690 		meanTop     = int( m * range.absmean * gain); | 
| Chris@0 | 691 		break; | 
| Chris@0 | 692 | 
| Chris@0 | 693 	    case dBScale: | 
| Chris@67 | 694                 if (!mergingChannels) { | 
| Chris@67 | 695                     int db0 = dBscale(range.min * gain, m); | 
| Chris@67 | 696                     int db1 = dBscale(range.max * gain, m); | 
| Chris@67 | 697                     rangeTop    = std::max(db0, db1); | 
| Chris@67 | 698                     meanTop     = std::min(db0, db1); | 
| Chris@67 | 699                     if (mixingChannels) rangeBottom = meanTop; | 
| Chris@67 | 700                     else rangeBottom = dBscale(range.absmean * gain, m); | 
| Chris@67 | 701                     meanBottom  = rangeBottom; | 
| Chris@67 | 702                 } else { | 
| Chris@67 | 703                     rangeBottom = -dBscale(range.min * gain, m * greyLevels); | 
| Chris@67 | 704                     rangeTop    =  dBscale(range.max * gain, m * greyLevels); | 
| Chris@67 | 705                     meanBottom  = -dBscale(range.absmean * gain, m); | 
| Chris@67 | 706                     meanTop     =  dBscale(range.absmean * gain, m); | 
| Chris@67 | 707                 } | 
| Chris@0 | 708 		break; | 
| Chris@0 | 709 | 
| Chris@0 | 710 	    case MeterScale: | 
| Chris@67 | 711                 if (!mergingChannels) { | 
| Chris@67 | 712                     int r0 = abs(AudioLevel::multiplier_to_preview(range.min * gain, m)); | 
| Chris@67 | 713                     int r1 = abs(AudioLevel::multiplier_to_preview(range.max * gain, m)); | 
| Chris@67 | 714                     rangeTop    = std::max(r0, r1); | 
| Chris@67 | 715                     meanTop     = std::min(r0, r1); | 
| Chris@67 | 716                     if (mixingChannels) rangeBottom = meanTop; | 
| Chris@67 | 717                     else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean * gain, m); | 
| Chris@67 | 718                     meanBottom  = rangeBottom; | 
| Chris@67 | 719                 } else { | 
| Chris@67 | 720                     rangeBottom =  AudioLevel::multiplier_to_preview(range.min * gain, m * greyLevels); | 
| Chris@67 | 721                     rangeTop    =  AudioLevel::multiplier_to_preview(range.max * gain, m * greyLevels); | 
| Chris@67 | 722                     meanBottom  = -AudioLevel::multiplier_to_preview(range.absmean * gain, m); | 
| Chris@67 | 723                     meanTop     =  AudioLevel::multiplier_to_preview(range.absmean * gain, m); | 
| Chris@67 | 724                 } | 
| Chris@67 | 725                 break; | 
| Chris@0 | 726 	    } | 
| Chris@0 | 727 | 
| Chris@27 | 728 	    rangeBottom = my * greyLevels - rangeBottom; | 
| Chris@27 | 729 	    rangeTop    = my * greyLevels - rangeTop; | 
| Chris@27 | 730 	    meanBottom  = my - meanBottom; | 
| Chris@27 | 731 	    meanTop     = my - meanTop; | 
| Chris@27 | 732 | 
| Chris@27 | 733 	    int topFill = (rangeTop % greyLevels); | 
| Chris@27 | 734 	    if (topFill > 0) topFill = greyLevels - topFill; | 
| Chris@27 | 735 | 
| Chris@27 | 736 	    int bottomFill = (rangeBottom % greyLevels); | 
| Chris@27 | 737 | 
| Chris@0 | 738 	    rangeTop = rangeTop / greyLevels; | 
| Chris@0 | 739 	    rangeBottom = rangeBottom / greyLevels; | 
| Chris@0 | 740 | 
| Chris@0 | 741 	    bool clipped = false; | 
| Chris@27 | 742 | 
| Chris@27 | 743 	    if (rangeTop < my - m) { rangeTop = my - m; } | 
| Chris@27 | 744 	    if (rangeTop > my + m) { rangeTop = my + m; } | 
| Chris@27 | 745 	    if (rangeBottom < my - m) { rangeBottom = my - m; } | 
| Chris@27 | 746 	    if (rangeBottom > my + m) { rangeBottom = my + m; } | 
| Chris@27 | 747 | 
| Chris@67 | 748 	    if (range.max <= -1.0 || | 
| Chris@67 | 749 		range.max >= 1.0) clipped = true; | 
| Chris@0 | 750 | 
| Chris@0 | 751 	    if (meanBottom > rangeBottom) meanBottom = rangeBottom; | 
| Chris@0 | 752 	    if (meanTop < rangeTop) meanTop = rangeTop; | 
| Chris@0 | 753 | 
| Chris@0 | 754 	    bool drawMean = m_showMeans; | 
| Chris@0 | 755 	    if (meanTop == rangeTop) { | 
| Chris@0 | 756 		if (meanTop < meanBottom) ++meanTop; | 
| Chris@0 | 757 		else drawMean = false; | 
| Chris@0 | 758 	    } | 
| Chris@67 | 759 	    if (meanBottom == rangeBottom && m_scale == LinearScale) { | 
| Chris@0 | 760 		if (meanBottom > meanTop) --meanBottom; | 
| Chris@0 | 761 		else drawMean = false; | 
| Chris@0 | 762 	    } | 
| Chris@0 | 763 | 
| Chris@0 | 764 	    if (x != x0 && prevRangeBottom != -1) { | 
| Chris@0 | 765 		if (prevRangeBottom > rangeBottom && | 
| Chris@0 | 766 		    prevRangeTop    > rangeBottom) { | 
| Chris@28 | 767 //		    paint->setPen(midColour); | 
| Chris@28 | 768 		    paint->setPen(m_colour); | 
| Chris@0 | 769 		    paint->drawLine(x-1, prevRangeTop, x, rangeBottom); | 
| Chris@28 | 770 		    paint->setPen(prevRangeTopColour); | 
| Chris@0 | 771 		    paint->drawPoint(x-1, prevRangeTop); | 
| Chris@0 | 772 		} else if (prevRangeBottom < rangeTop && | 
| Chris@0 | 773 			   prevRangeTop    < rangeTop) { | 
| Chris@28 | 774 //		    paint->setPen(midColour); | 
| Chris@28 | 775 		    paint->setPen(m_colour); | 
| Chris@0 | 776 		    paint->drawLine(x-1, prevRangeBottom, x, rangeTop); | 
| Chris@28 | 777 		    paint->setPen(prevRangeBottomColour); | 
| Chris@0 | 778 		    paint->drawPoint(x-1, prevRangeBottom); | 
| Chris@0 | 779 		} | 
| Chris@0 | 780 	    } | 
| Chris@0 | 781 | 
| Chris@0 | 782 	    if (ready) { | 
| Chris@67 | 783 		if (clipped /*!!! || | 
| Chris@67 | 784 		    range.min * gain <= -1.0 || | 
| Chris@67 | 785 		    range.max * gain >=  1.0 */) { | 
| Chris@0 | 786 		    paint->setPen(Qt::red); | 
| Chris@0 | 787 		} else { | 
| Chris@0 | 788 		    paint->setPen(m_colour); | 
| Chris@0 | 789 		} | 
| Chris@0 | 790 	    } else { | 
| Chris@0 | 791 		paint->setPen(midColour); | 
| Chris@0 | 792 	    } | 
| Chris@0 | 793 | 
| Chris@0 | 794 	    paint->drawLine(x, rangeBottom, x, rangeTop); | 
| Chris@0 | 795 | 
| Chris@28 | 796 	    prevRangeTopColour = m_colour; | 
| Chris@28 | 797 	    prevRangeBottomColour = m_colour; | 
| Chris@28 | 798 | 
| Chris@0 | 799 	    if (m_greyscale && (m_scale == LinearScale) && ready) { | 
| Chris@0 | 800 		if (!clipped) { | 
| Chris@0 | 801 		    if (rangeTop < rangeBottom) { | 
| Chris@0 | 802 			if (topFill > 0 && | 
| Chris@0 | 803 			    (!drawMean || (rangeTop < meanTop - 1))) { | 
| Chris@0 | 804 			    paint->setPen(greys[topFill - 1]); | 
| Chris@27 | 805 			    paint->drawPoint(x, rangeTop); | 
| Chris@28 | 806 			    prevRangeTopColour = greys[topFill - 1]; | 
| Chris@0 | 807 			} | 
| Chris@0 | 808 			if (bottomFill > 0 && | 
| Chris@0 | 809 			    (!drawMean || (rangeBottom > meanBottom + 1))) { | 
| Chris@0 | 810 			    paint->setPen(greys[bottomFill - 1]); | 
| Chris@27 | 811 			    paint->drawPoint(x, rangeBottom); | 
| Chris@28 | 812 			    prevRangeBottomColour = greys[bottomFill - 1]; | 
| Chris@0 | 813 			} | 
| Chris@0 | 814 		    } | 
| Chris@0 | 815 		} | 
| Chris@0 | 816 	    } | 
| Chris@0 | 817 | 
| Chris@0 | 818 	    if (drawMean) { | 
| Chris@0 | 819 		paint->setPen(midColour); | 
| Chris@0 | 820 		paint->drawLine(x, meanBottom, x, meanTop); | 
| Chris@0 | 821 	    } | 
| Chris@0 | 822 | 
| Chris@0 | 823 	    prevRangeBottom = rangeBottom; | 
| Chris@0 | 824 	    prevRangeTop = rangeTop; | 
| Chris@0 | 825 	} | 
| Chris@0 | 826     } | 
| Chris@0 | 827 | 
| Chris@0 | 828     if (m_aggressive) { | 
| Chris@41 | 829 | 
| Chris@44 | 830 	if (ready && rect == v->rect()) { | 
| Chris@0 | 831 	    m_cacheValid = true; | 
| Chris@0 | 832 	    m_cacheZoomLevel = zoomLevel; | 
| Chris@0 | 833 	} | 
| Chris@0 | 834 	paint->end(); | 
| Chris@0 | 835 	delete paint; | 
| Chris@0 | 836 	viewPainter.drawPixmap(rect, *m_cache, rect); | 
| Chris@0 | 837     } | 
| Chris@0 | 838 } | 
| Chris@0 | 839 | 
| Chris@25 | 840 QString | 
| Chris@44 | 841 WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const | 
| Chris@25 | 842 { | 
| Chris@25 | 843     int x = pos.x(); | 
| Chris@25 | 844 | 
| Chris@25 | 845     if (!m_model || !m_model->isOK()) return ""; | 
| Chris@25 | 846 | 
| Chris@44 | 847     long f0 = v->getFrameForX(x); | 
| Chris@44 | 848     long f1 = v->getFrameForX(x + 1); | 
| Chris@25 | 849 | 
| Chris@25 | 850     if (f0 < 0) f0 = 0; | 
| Chris@25 | 851     if (f1 <= f0) return ""; | 
| Chris@25 | 852 | 
| Chris@25 | 853     QString text; | 
| Chris@25 | 854 | 
| Chris@25 | 855     RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate()); | 
| Chris@25 | 856     RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate()); | 
| Chris@25 | 857 | 
| Chris@25 | 858     if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) { | 
| Chris@25 | 859 	text += tr("Time:\t%1 - %2") | 
| Chris@25 | 860 	    .arg(rt0.toText(true).c_str()) | 
| Chris@25 | 861 	    .arg(rt1.toText(true).c_str()); | 
| Chris@25 | 862     } else { | 
| Chris@25 | 863 	text += tr("Time:\t%1") | 
| Chris@25 | 864 	    .arg(rt0.toText(true).c_str()); | 
| Chris@25 | 865     } | 
| Chris@25 | 866 | 
| Chris@25 | 867     size_t channels = 0, minChannel = 0, maxChannel = 0; | 
| Chris@67 | 868     bool mergingChannels = false, mixingChannels = false; | 
| Chris@25 | 869 | 
| Chris@67 | 870     channels = getChannelArrangement(minChannel, maxChannel, | 
| Chris@67 | 871                                      mergingChannels, mixingChannels); | 
| Chris@25 | 872     if (channels == 0) return ""; | 
| Chris@25 | 873 | 
| Chris@25 | 874     for (size_t ch = minChannel; ch <= maxChannel; ++ch) { | 
| Chris@25 | 875 | 
| Chris@44 | 876 	size_t blockSize = v->getZoomLevel(); | 
| Chris@25 | 877 	RangeSummarisableTimeValueModel::RangeBlock ranges = | 
| Chris@25 | 878 	    m_model->getRanges(ch, f0, f1, blockSize); | 
| Chris@25 | 879 | 
| Chris@25 | 880 	if (ranges.empty()) continue; | 
| Chris@25 | 881 | 
| Chris@25 | 882 	RangeSummarisableTimeValueModel::Range range = ranges[0]; | 
| Chris@25 | 883 | 
| Chris@25 | 884 	QString label = tr("Level:"); | 
| Chris@25 | 885 	if (minChannel != maxChannel) { | 
| Chris@25 | 886 	    if (ch == 0) label = tr("Left:"); | 
| Chris@25 | 887 	    else if (ch == 1) label = tr("Right:"); | 
| Chris@25 | 888 	    else label = tr("Channel %1").arg(ch + 1); | 
| Chris@25 | 889 	} | 
| Chris@25 | 890 | 
| Chris@25 | 891 	int min = int(range.min * 1000); | 
| Chris@25 | 892 	int max = int(range.max * 1000); | 
| Chris@25 | 893 	int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min), | 
| Chris@25 | 894 							   fabsf(range.max))) | 
| Chris@25 | 895 		     * 100); | 
| Chris@25 | 896 | 
| Chris@25 | 897 	if (min != max) { | 
| Chris@25 | 898 	    text += tr("\n%1\t%2 - %3 (%4 dB peak)") | 
| Chris@25 | 899 		.arg(label).arg(float(min)/1000).arg(float(max)/1000).arg(float(db)/100); | 
| Chris@25 | 900 	} else { | 
| Chris@25 | 901 	    text += tr("\n%1\t%2 (%3 dB peak)") | 
| Chris@25 | 902 		.arg(label).arg(float(min)/1000).arg(float(db)/100); | 
| Chris@25 | 903 	} | 
| Chris@25 | 904     } | 
| Chris@25 | 905 | 
| Chris@25 | 906     return text; | 
| Chris@25 | 907 } | 
| Chris@25 | 908 | 
| Chris@0 | 909 int | 
| Chris@68 | 910 WaveformLayer::getYForValue(View *v, Scale scale, float value, size_t channel, | 
| Chris@68 | 911                             size_t minChannel, size_t maxChannel) const | 
| Chris@68 | 912 { | 
| Chris@68 | 913     if (maxChannel < minChannel || channel < minChannel) return 0; | 
| Chris@68 | 914 | 
| Chris@68 | 915     int w = v->width(); | 
| Chris@68 | 916     int h = v->height(); | 
| Chris@68 | 917 | 
| Chris@68 | 918     int channels = maxChannel - minChannel + 1; | 
| Chris@68 | 919     int m = (h / channels) / 2; | 
| Chris@68 | 920     int my = m + (((channel - minChannel) * h) / channels); | 
| Chris@68 | 921 | 
| Chris@68 | 922     if ((m_scale == dBScale || m_scale == MeterScale) && | 
| Chris@68 | 923         m_channelMode != MergeChannels) { | 
| Chris@68 | 924         m = (h / channels); | 
| Chris@68 | 925         my = m + (((channel - minChannel) * h) / channels); | 
| Chris@68 | 926     } | 
| Chris@68 | 927 | 
| Chris@68 | 928     int vy = 0; | 
| Chris@68 | 929 | 
| Chris@68 | 930     switch (scale) { | 
| Chris@68 | 931 | 
| Chris@68 | 932     case LinearScale: | 
| Chris@68 | 933         vy = int(m * value); | 
| Chris@68 | 934         break; | 
| Chris@68 | 935 | 
| Chris@68 | 936     case MeterScale: | 
| Chris@68 | 937         vy = AudioLevel::multiplier_to_preview(value, m); | 
| Chris@68 | 938         break; | 
| Chris@68 | 939 | 
| Chris@68 | 940     case dBScale: | 
| Chris@68 | 941         vy = dBscale(value, m); | 
| Chris@68 | 942         break; | 
| Chris@68 | 943     } | 
| Chris@68 | 944 | 
| Chris@68 | 945     return my - vy; | 
| Chris@68 | 946 } | 
| Chris@68 | 947 | 
| Chris@68 | 948 int | 
| Chris@44 | 949 WaveformLayer::getVerticalScaleWidth(View *v, QPainter &paint) const | 
| Chris@0 | 950 { | 
| Chris@0 | 951     if (m_scale == LinearScale) { | 
| Chris@0 | 952 	return paint.fontMetrics().width("0.0") + 13; | 
| Chris@0 | 953     } else { | 
| Chris@0 | 954 	return std::max(paint.fontMetrics().width(tr("0dB")), | 
| Chris@0 | 955 			paint.fontMetrics().width(tr("-Inf"))) + 13; | 
| Chris@0 | 956     } | 
| Chris@0 | 957 } | 
| Chris@0 | 958 | 
| Chris@0 | 959 void | 
| Chris@44 | 960 WaveformLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const | 
| Chris@0 | 961 { | 
| Chris@0 | 962     if (!m_model || !m_model->isOK()) { | 
| Chris@0 | 963 	return; | 
| Chris@0 | 964     } | 
| Chris@0 | 965 | 
| Chris@0 | 966     size_t channels = 0, minChannel = 0, maxChannel = 0; | 
| Chris@67 | 967     bool mergingChannels = false, mixingChannels = false; | 
| Chris@0 | 968 | 
| Chris@67 | 969     channels = getChannelArrangement(minChannel, maxChannel, | 
| Chris@67 | 970                                      mergingChannels, mixingChannels); | 
| Chris@0 | 971     if (channels == 0) return; | 
| Chris@0 | 972 | 
| Chris@0 | 973     int h = rect.height(), w = rect.width(); | 
| Chris@0 | 974     int textHeight = paint.fontMetrics().height(); | 
| Chris@0 | 975     int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1; | 
| Chris@0 | 976 | 
| Chris@67 | 977     float gain = m_gain; | 
| Chris@67 | 978 | 
| Chris@0 | 979     for (size_t ch = minChannel; ch <= maxChannel; ++ch) { | 
| Chris@0 | 980 | 
| Chris@68 | 981 	int lastLabelledY = -1; | 
| Chris@0 | 982 | 
| Chris@67 | 983         if (ch < m_effectiveGains.size()) gain = m_effectiveGains[ch]; | 
| Chris@67 | 984 | 
| Chris@68 | 985         int n = 10; | 
| Chris@0 | 986 | 
| Chris@68 | 987 	for (int i = 0; i <= n; ++i) { | 
| Chris@68 | 988 | 
| Chris@68 | 989             float val = 0.0, nval = 0.0; | 
| Chris@0 | 990 	    QString text = ""; | 
| Chris@0 | 991 | 
| Chris@68 | 992             switch (m_scale) { | 
| Chris@68 | 993 | 
| Chris@68 | 994             case LinearScale: | 
| Chris@68 | 995                 val = (i * gain) / n; | 
| Chris@68 | 996 		text = QString("%1").arg(float(i) / n); | 
| Chris@68 | 997 		if (i == 0) text = "0.0"; | 
| Chris@68 | 998                 else { | 
| Chris@68 | 999                     nval = -val; | 
| Chris@68 | 1000                     if (i == n) text = "1.0"; | 
| Chris@68 | 1001                 } | 
| Chris@68 | 1002                 break; | 
| Chris@0 | 1003 | 
| Chris@68 | 1004             case MeterScale: | 
| Chris@68 | 1005                 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain; | 
| Chris@68 | 1006 		text = QString("%1").arg(meterdbs[i]); | 
| Chris@68 | 1007 		if (i == n) text = tr("0dB"); | 
| Chris@68 | 1008 		if (i == 0) { | 
| Chris@68 | 1009                     text = tr("-Inf"); | 
| Chris@68 | 1010                     val = 0.0; | 
| Chris@68 | 1011 		} | 
| Chris@68 | 1012                 break; | 
| Chris@0 | 1013 | 
| Chris@68 | 1014             case dBScale: | 
| Chris@68 | 1015                 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain; | 
| Chris@68 | 1016 		text = QString("%1").arg(-(10*n) + i * 10); | 
| Chris@68 | 1017 		if (i == n) text = tr("0dB"); | 
| Chris@68 | 1018 		if (i == 0) { | 
| Chris@68 | 1019                     text = tr("-Inf"); | 
| Chris@68 | 1020                     val = 0.0; | 
| Chris@68 | 1021 		} | 
| Chris@68 | 1022                 break; | 
| Chris@68 | 1023             } | 
| Chris@0 | 1024 | 
| Chris@68 | 1025             if (val < -1.0 || val > 1.0) continue; | 
| Chris@0 | 1026 | 
| Chris@68 | 1027             int y = getYForValue(v, m_scale, val, ch, minChannel, maxChannel); | 
| Chris@0 | 1028 | 
| Chris@68 | 1029             int ny = y; | 
| Chris@68 | 1030             if (nval != 0.0) { | 
| Chris@68 | 1031                 ny = getYForValue(v, m_scale, nval, ch, minChannel, maxChannel); | 
| Chris@68 | 1032             } | 
| Chris@0 | 1033 | 
| Chris@68 | 1034             bool spaceForLabel = (i == 0 || | 
| Chris@68 | 1035                                   abs(y - lastLabelledY) >= textHeight - 1); | 
| Chris@0 | 1036 | 
| Chris@68 | 1037             if (spaceForLabel) { | 
| Chris@0 | 1038 | 
| Chris@68 | 1039                 int tx = 3; | 
| Chris@68 | 1040                 if (m_scale != LinearScale) { | 
| Chris@68 | 1041                     tx = w - 10 - paint.fontMetrics().width(text); | 
| Chris@68 | 1042                 } | 
| Chris@68 | 1043 | 
| Chris@68 | 1044                 int ty = y; | 
| Chris@68 | 1045                 if (ty < paint.fontMetrics().ascent()) { | 
| Chris@68 | 1046                     ty = paint.fontMetrics().ascent(); | 
| Chris@68 | 1047                 } else if (ty > h - paint.fontMetrics().descent()) { | 
| Chris@68 | 1048                     ty = h - paint.fontMetrics().descent(); | 
| Chris@68 | 1049                 } else { | 
| Chris@68 | 1050                     ty += toff; | 
| Chris@68 | 1051                 } | 
| Chris@68 | 1052                 paint.drawText(tx, ty, text); | 
| Chris@0 | 1053 | 
| Chris@68 | 1054                 lastLabelledY = ty - toff; | 
| Chris@67 | 1055 | 
| Chris@68 | 1056                 if (ny != y) { | 
| Chris@68 | 1057                     ty = ny; | 
| Chris@68 | 1058                     if (ty < paint.fontMetrics().ascent()) { | 
| Chris@68 | 1059                         ty = paint.fontMetrics().ascent(); | 
| Chris@68 | 1060                     } else if (ty > h - paint.fontMetrics().descent()) { | 
| Chris@68 | 1061                         ty = h - paint.fontMetrics().descent(); | 
| Chris@68 | 1062                     } else { | 
| Chris@68 | 1063                         ty += toff; | 
| Chris@68 | 1064                     } | 
| Chris@68 | 1065                     paint.drawText(tx, ty, text); | 
| Chris@68 | 1066                 } | 
| Chris@68 | 1067 | 
| Chris@68 | 1068                 paint.drawLine(w - 7, y, w, y); | 
| Chris@68 | 1069                 if (ny != y) paint.drawLine(w - 7, ny, w, ny); | 
| Chris@68 | 1070 | 
| Chris@68 | 1071             } else { | 
| Chris@68 | 1072 | 
| Chris@68 | 1073                 paint.drawLine(w - 4, y, w, y); | 
| Chris@68 | 1074                 if (ny != y) paint.drawLine(w - 4, ny, w, ny); | 
| Chris@68 | 1075             } | 
| Chris@0 | 1076 	} | 
| Chris@0 | 1077     } | 
| Chris@0 | 1078 } | 
| Chris@0 | 1079 | 
| Chris@6 | 1080 QString | 
| Chris@6 | 1081 WaveformLayer::toXmlString(QString indent, QString extraAttributes) const | 
| Chris@6 | 1082 { | 
| Chris@6 | 1083     QString s; | 
| Chris@6 | 1084 | 
| Chris@6 | 1085     s += QString("gain=\"%1\" " | 
| Chris@6 | 1086 		 "colour=\"%2\" " | 
| Chris@6 | 1087 		 "showMeans=\"%3\" " | 
| Chris@6 | 1088 		 "greyscale=\"%4\" " | 
| Chris@6 | 1089 		 "channelMode=\"%5\" " | 
| Chris@6 | 1090 		 "channel=\"%6\" " | 
| Chris@6 | 1091 		 "scale=\"%7\" " | 
| Chris@67 | 1092 		 "aggressive=\"%8\" " | 
| Chris@67 | 1093                  "autoNormalize=\"%9\"") | 
| Chris@6 | 1094 	.arg(m_gain) | 
| Chris@6 | 1095 	.arg(encodeColour(m_colour)) | 
| Chris@6 | 1096 	.arg(m_showMeans) | 
| Chris@6 | 1097 	.arg(m_greyscale) | 
| Chris@6 | 1098 	.arg(m_channelMode) | 
| Chris@6 | 1099 	.arg(m_channel) | 
| Chris@6 | 1100 	.arg(m_scale) | 
| Chris@67 | 1101 	.arg(m_aggressive) | 
| Chris@67 | 1102         .arg(m_autoNormalize); | 
| Chris@6 | 1103 | 
| Chris@6 | 1104     return Layer::toXmlString(indent, extraAttributes + " " + s); | 
| Chris@6 | 1105 } | 
| Chris@6 | 1106 | 
| Chris@11 | 1107 void | 
| Chris@11 | 1108 WaveformLayer::setProperties(const QXmlAttributes &attributes) | 
| Chris@11 | 1109 { | 
| Chris@11 | 1110     bool ok = false; | 
| Chris@11 | 1111 | 
| Chris@11 | 1112     float gain = attributes.value("gain").toFloat(&ok); | 
| Chris@11 | 1113     if (ok) setGain(gain); | 
| Chris@11 | 1114 | 
| Chris@11 | 1115     QString colourSpec = attributes.value("colour"); | 
| Chris@11 | 1116     if (colourSpec != "") { | 
| Chris@11 | 1117 	QColor colour(colourSpec); | 
| Chris@11 | 1118 	if (colour.isValid()) { | 
| Chris@11 | 1119 	    setBaseColour(QColor(colourSpec)); | 
| Chris@11 | 1120 	} | 
| Chris@11 | 1121     } | 
| Chris@11 | 1122 | 
| Chris@11 | 1123     bool showMeans = (attributes.value("showMeans") == "1" || | 
| Chris@11 | 1124 		      attributes.value("showMeans") == "true"); | 
| Chris@11 | 1125     setShowMeans(showMeans); | 
| Chris@11 | 1126 | 
| Chris@11 | 1127     bool greyscale = (attributes.value("greyscale") == "1" || | 
| Chris@11 | 1128 		      attributes.value("greyscale") == "true"); | 
| Chris@11 | 1129     setUseGreyscale(greyscale); | 
| Chris@11 | 1130 | 
| Chris@11 | 1131     ChannelMode channelMode = (ChannelMode) | 
| Chris@11 | 1132 	attributes.value("channelMode").toInt(&ok); | 
| Chris@11 | 1133     if (ok) setChannelMode(channelMode); | 
| Chris@11 | 1134 | 
| Chris@11 | 1135     int channel = attributes.value("channel").toInt(&ok); | 
| Chris@11 | 1136     if (ok) setChannel(channel); | 
| Chris@11 | 1137 | 
| Chris@11 | 1138     Scale scale = (Scale) | 
| Chris@11 | 1139 	attributes.value("scale").toInt(&ok); | 
| Chris@11 | 1140     if (ok) setScale(scale); | 
| Chris@11 | 1141 | 
| Chris@11 | 1142     bool aggressive = (attributes.value("aggressive") == "1" || | 
| Chris@11 | 1143 		       attributes.value("aggressive") == "true"); | 
| Chris@11 | 1144     setUseGreyscale(aggressive); | 
| Chris@67 | 1145 | 
| Chris@67 | 1146     bool autoNormalize = (attributes.value("autoNormalize") == "1" || | 
| Chris@67 | 1147                           attributes.value("autoNormalize") == "true"); | 
| Chris@67 | 1148     setAutoNormalize(autoNormalize); | 
| Chris@11 | 1149 } | 
| Chris@11 | 1150 | 
| Chris@0 | 1151 #ifdef INCLUDE_MOCFILES | 
| Chris@0 | 1152 #include "WaveformLayer.moc.cpp" | 
| Chris@0 | 1153 #endif | 
| Chris@0 | 1154 |