| 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@484 | 7     This file copyright 2006-2009 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 "SpectrogramLayer.h" | 
| Chris@0 | 17 | 
| Chris@128 | 18 #include "view/View.h" | 
| Chris@0 | 19 #include "base/Profiler.h" | 
| Chris@0 | 20 #include "base/AudioLevel.h" | 
| Chris@0 | 21 #include "base/Window.h" | 
| Chris@24 | 22 #include "base/Pitch.h" | 
| Chris@118 | 23 #include "base/Preferences.h" | 
| Chris@167 | 24 #include "base/RangeMapper.h" | 
| Chris@253 | 25 #include "base/LogRange.h" | 
| Chris@376 | 26 #include "widgets/CommandHistory.h" | 
| Chris@376 | 27 #include "ColourMapper.h" | 
| Chris@283 | 28 #include "ImageRegionFinder.h" | 
| Chris@484 | 29 #include "data/model/Dense3DModelPeakCache.h" | 
| Chris@690 | 30 #include "PianoScale.h" | 
| Chris@0 | 31 | 
| Chris@0 | 32 #include <QPainter> | 
| Chris@0 | 33 #include <QImage> | 
| Chris@0 | 34 #include <QPixmap> | 
| Chris@0 | 35 #include <QRect> | 
| Chris@0 | 36 #include <QTimer> | 
| Chris@92 | 37 #include <QApplication> | 
| Chris@178 | 38 #include <QMessageBox> | 
| Chris@283 | 39 #include <QMouseEvent> | 
| Chris@316 | 40 #include <QTextStream> | 
| Chris@0 | 41 | 
| Chris@0 | 42 #include <iostream> | 
| Chris@0 | 43 | 
| Chris@0 | 44 #include <cassert> | 
| Chris@0 | 45 #include <cmath> | 
| Chris@0 | 46 | 
| Chris@545 | 47 #ifndef __GNUC__ | 
| Chris@545 | 48 #include <alloca.h> | 
| Chris@545 | 49 #endif | 
| Chris@545 | 50 | 
| Chris@490 | 51 //#define DEBUG_SPECTROGRAM_REPAINT 1 | 
| Chris@0 | 52 | 
| Chris@907 | 53 using std::vector; | 
| Chris@907 | 54 | 
| Chris@44 | 55 SpectrogramLayer::SpectrogramLayer(Configuration config) : | 
| Chris@0 | 56     m_model(0), | 
| Chris@0 | 57     m_channel(0), | 
| Chris@0 | 58     m_windowSize(1024), | 
| Chris@0 | 59     m_windowType(HanningWindow), | 
| Chris@97 | 60     m_windowHopLevel(2), | 
| Chris@109 | 61     m_zeroPadLevel(0), | 
| Chris@107 | 62     m_fftSize(1024), | 
| Chris@0 | 63     m_gain(1.0), | 
| Chris@215 | 64     m_initialGain(1.0), | 
| Chris@37 | 65     m_threshold(0.0), | 
| Chris@215 | 66     m_initialThreshold(0.0), | 
| Chris@9 | 67     m_colourRotation(0), | 
| Chris@215 | 68     m_initialRotation(0), | 
| Chris@119 | 69     m_minFrequency(10), | 
| Chris@0 | 70     m_maxFrequency(8000), | 
| Chris@135 | 71     m_initialMaxFrequency(8000), | 
| Chris@0 | 72     m_colourScale(dBColourScale), | 
| Chris@197 | 73     m_colourMap(0), | 
| Chris@0 | 74     m_frequencyScale(LinearFrequencyScale), | 
| Chris@37 | 75     m_binDisplay(AllBins), | 
| Chris@862 | 76     m_normalization(NoNormalization), | 
| Chris@133 | 77     m_lastEmittedZoomStep(-1), | 
| Chris@390 | 78     m_synchronous(false), | 
| Chris@608 | 79     m_haveDetailedScale(false), | 
| Chris@215 | 80     m_lastPaintBlockWidth(0), | 
| Chris@0 | 81     m_updateTimer(0), | 
| Chris@44 | 82     m_candidateFillStartFrame(0), | 
| Chris@193 | 83     m_exiting(false), | 
| Chris@193 | 84     m_sliceableModel(0) | 
| Chris@0 | 85 { | 
| Chris@215 | 86     if (config == FullRangeDb) { | 
| Chris@215 | 87         m_initialMaxFrequency = 0; | 
| Chris@215 | 88         setMaxFrequency(0); | 
| Chris@215 | 89     } else if (config == MelodicRange) { | 
| Chris@0 | 90 	setWindowSize(8192); | 
| Chris@97 | 91 	setWindowHopLevel(4); | 
| Chris@215 | 92         m_initialMaxFrequency = 1500; | 
| Chris@215 | 93 	setMaxFrequency(1500); | 
| Chris@215 | 94         setMinFrequency(40); | 
| Chris@0 | 95 	setColourScale(LinearColourScale); | 
| Chris@215 | 96         setColourMap(ColourMapper::Sunset); | 
| Chris@215 | 97         setFrequencyScale(LogFrequencyScale); | 
| Chris@224 | 98 //        setGain(20); | 
| Chris@37 | 99     } else if (config == MelodicPeaks) { | 
| Chris@37 | 100 	setWindowSize(4096); | 
| Chris@97 | 101 	setWindowHopLevel(5); | 
| Chris@135 | 102         m_initialMaxFrequency = 2000; | 
| Chris@40 | 103 	setMaxFrequency(2000); | 
| Chris@37 | 104 	setMinFrequency(40); | 
| Chris@37 | 105 	setFrequencyScale(LogFrequencyScale); | 
| Chris@215 | 106 	setColourScale(LinearColourScale); | 
| Chris@37 | 107 	setBinDisplay(PeakFrequencies); | 
| Chris@862 | 108         setNormalization(NormalizeColumns); | 
| Chris@0 | 109     } | 
| Chris@110 | 110 | 
| Chris@122 | 111     Preferences *prefs = Preferences::getInstance(); | 
| Chris@122 | 112     connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@122 | 113             this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 
| Chris@122 | 114     setWindowType(prefs->getWindowType()); | 
| Chris@122 | 115 | 
| Chris@197 | 116     initialisePalette(); | 
| Chris@0 | 117 } | 
| Chris@0 | 118 | 
| Chris@0 | 119 SpectrogramLayer::~SpectrogramLayer() | 
| Chris@0 | 120 { | 
| Chris@0 | 121     delete m_updateTimer; | 
| Chris@0 | 122     m_updateTimer = 0; | 
| Chris@0 | 123 | 
| Chris@130 | 124     invalidateFFTModels(); | 
| Chris@0 | 125 } | 
| Chris@0 | 126 | 
| Chris@0 | 127 void | 
| Chris@0 | 128 SpectrogramLayer::setModel(const DenseTimeValueModel *model) | 
| Chris@0 | 129 { | 
| Chris@682 | 130 //    cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl; | 
| Chris@34 | 131 | 
| Chris@110 | 132     if (model == m_model) return; | 
| Chris@110 | 133 | 
| Chris@0 | 134     m_model = model; | 
| Chris@130 | 135     invalidateFFTModels(); | 
| Chris@0 | 136 | 
| Chris@0 | 137     if (!m_model || !m_model->isOK()) return; | 
| Chris@0 | 138 | 
| Chris@320 | 139     connectSignals(m_model); | 
| Chris@0 | 140 | 
| Chris@0 | 141     connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); | 
| Chris@906 | 142     connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), | 
| Chris@906 | 143 	    this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t))); | 
| Chris@0 | 144 | 
| Chris@0 | 145     emit modelReplaced(); | 
| Chris@110 | 146 } | 
| Chris@115 | 147 | 
| Chris@0 | 148 Layer::PropertyList | 
| Chris@0 | 149 SpectrogramLayer::getProperties() const | 
| Chris@0 | 150 { | 
| Chris@0 | 151     PropertyList list; | 
| Chris@87 | 152     list.push_back("Colour"); | 
| Chris@87 | 153     list.push_back("Colour Scale"); | 
| Chris@87 | 154     list.push_back("Window Size"); | 
| Chris@97 | 155     list.push_back("Window Increment"); | 
| Chris@862 | 156     list.push_back("Normalization"); | 
| Chris@87 | 157     list.push_back("Bin Display"); | 
| Chris@87 | 158     list.push_back("Threshold"); | 
| Chris@87 | 159     list.push_back("Gain"); | 
| Chris@87 | 160     list.push_back("Colour Rotation"); | 
| Chris@153 | 161 //    list.push_back("Min Frequency"); | 
| Chris@153 | 162 //    list.push_back("Max Frequency"); | 
| Chris@87 | 163     list.push_back("Frequency Scale"); | 
| Chris@153 | 164 ////    list.push_back("Zero Padding"); | 
| Chris@0 | 165     return list; | 
| Chris@0 | 166 } | 
| Chris@0 | 167 | 
| Chris@87 | 168 QString | 
| Chris@87 | 169 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const | 
| Chris@87 | 170 { | 
| Chris@87 | 171     if (name == "Colour") return tr("Colour"); | 
| Chris@87 | 172     if (name == "Colour Scale") return tr("Colour Scale"); | 
| Chris@87 | 173     if (name == "Window Size") return tr("Window Size"); | 
| Chris@112 | 174     if (name == "Window Increment") return tr("Window Overlap"); | 
| Chris@862 | 175     if (name == "Normalization") return tr("Normalization"); | 
| Chris@87 | 176     if (name == "Bin Display") return tr("Bin Display"); | 
| Chris@87 | 177     if (name == "Threshold") return tr("Threshold"); | 
| Chris@87 | 178     if (name == "Gain") return tr("Gain"); | 
| Chris@87 | 179     if (name == "Colour Rotation") return tr("Colour Rotation"); | 
| Chris@87 | 180     if (name == "Min Frequency") return tr("Min Frequency"); | 
| Chris@87 | 181     if (name == "Max Frequency") return tr("Max Frequency"); | 
| Chris@87 | 182     if (name == "Frequency Scale") return tr("Frequency Scale"); | 
| Chris@109 | 183     if (name == "Zero Padding") return tr("Smoothing"); | 
| Chris@87 | 184     return ""; | 
| Chris@87 | 185 } | 
| Chris@87 | 186 | 
| Chris@335 | 187 QString | 
| Chris@862 | 188 SpectrogramLayer::getPropertyIconName(const PropertyName &) const | 
| Chris@335 | 189 { | 
| Chris@335 | 190     return ""; | 
| Chris@335 | 191 } | 
| Chris@335 | 192 | 
| Chris@0 | 193 Layer::PropertyType | 
| Chris@0 | 194 SpectrogramLayer::getPropertyType(const PropertyName &name) const | 
| Chris@0 | 195 { | 
| Chris@87 | 196     if (name == "Gain") return RangeProperty; | 
| Chris@87 | 197     if (name == "Colour Rotation") return RangeProperty; | 
| Chris@87 | 198     if (name == "Threshold") return RangeProperty; | 
| Chris@109 | 199     if (name == "Zero Padding") return ToggleProperty; | 
| Chris@0 | 200     return ValueProperty; | 
| Chris@0 | 201 } | 
| Chris@0 | 202 | 
| Chris@0 | 203 QString | 
| Chris@0 | 204 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const | 
| Chris@0 | 205 { | 
| Chris@153 | 206     if (name == "Bin Display" || | 
| Chris@153 | 207         name == "Frequency Scale") return tr("Bins"); | 
| Chris@87 | 208     if (name == "Window Size" || | 
| Chris@109 | 209 	name == "Window Increment" || | 
| Chris@109 | 210         name == "Zero Padding") return tr("Window"); | 
| Chris@87 | 211     if (name == "Colour" || | 
| Chris@87 | 212 	name == "Threshold" || | 
| Chris@87 | 213 	name == "Colour Rotation") return tr("Colour"); | 
| Chris@862 | 214     if (name == "Normalization" || | 
| Chris@153 | 215         name == "Gain" || | 
| Chris@87 | 216 	name == "Colour Scale") return tr("Scale"); | 
| Chris@0 | 217     return QString(); | 
| Chris@0 | 218 } | 
| Chris@0 | 219 | 
| Chris@0 | 220 int | 
| Chris@0 | 221 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, | 
| Chris@216 | 222 					   int *min, int *max, int *deflt) const | 
| Chris@0 | 223 { | 
| Chris@216 | 224     int val = 0; | 
| Chris@216 | 225 | 
| Chris@216 | 226     int garbage0, garbage1, garbage2; | 
| Chris@55 | 227     if (!min) min = &garbage0; | 
| Chris@55 | 228     if (!max) max = &garbage1; | 
| Chris@216 | 229     if (!deflt) deflt = &garbage2; | 
| Chris@10 | 230 | 
| Chris@87 | 231     if (name == "Gain") { | 
| Chris@0 | 232 | 
| Chris@0 | 233 	*min = -50; | 
| Chris@0 | 234 	*max = 50; | 
| Chris@0 | 235 | 
| Chris@906 | 236         *deflt = int(lrint(log10(m_initialGain) * 20.0)); | 
| Chris@216 | 237 	if (*deflt < *min) *deflt = *min; | 
| Chris@216 | 238 	if (*deflt > *max) *deflt = *max; | 
| Chris@216 | 239 | 
| Chris@906 | 240 	val = int(lrint(log10(m_gain) * 20.0)); | 
| Chris@216 | 241 	if (val < *min) val = *min; | 
| Chris@216 | 242 	if (val > *max) val = *max; | 
| Chris@0 | 243 | 
| Chris@87 | 244     } else if (name == "Threshold") { | 
| Chris@37 | 245 | 
| Chris@37 | 246 	*min = -50; | 
| Chris@37 | 247 	*max = 0; | 
| Chris@37 | 248 | 
| Chris@906 | 249         *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); | 
| Chris@216 | 250 	if (*deflt < *min) *deflt = *min; | 
| Chris@216 | 251 	if (*deflt > *max) *deflt = *max; | 
| Chris@216 | 252 | 
| Chris@906 | 253 	val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); | 
| Chris@216 | 254 	if (val < *min) val = *min; | 
| Chris@216 | 255 	if (val > *max) val = *max; | 
| Chris@37 | 256 | 
| Chris@87 | 257     } else if (name == "Colour Rotation") { | 
| Chris@9 | 258 | 
| Chris@9 | 259 	*min = 0; | 
| Chris@9 | 260 	*max = 256; | 
| Chris@216 | 261         *deflt = m_initialRotation; | 
| Chris@216 | 262 | 
| Chris@216 | 263 	val = m_colourRotation; | 
| Chris@9 | 264 | 
| Chris@87 | 265     } else if (name == "Colour Scale") { | 
| Chris@0 | 266 | 
| Chris@0 | 267 	*min = 0; | 
| Chris@176 | 268 	*max = 4; | 
| Chris@216 | 269         *deflt = int(dBColourScale); | 
| Chris@216 | 270 | 
| Chris@216 | 271 	val = (int)m_colourScale; | 
| Chris@0 | 272 | 
| Chris@87 | 273     } else if (name == "Colour") { | 
| Chris@0 | 274 | 
| Chris@0 | 275 	*min = 0; | 
| Chris@196 | 276 	*max = ColourMapper::getColourMapCount() - 1; | 
| Chris@216 | 277         *deflt = 0; | 
| Chris@216 | 278 | 
| Chris@216 | 279 	val = m_colourMap; | 
| Chris@0 | 280 | 
| Chris@87 | 281     } else if (name == "Window Size") { | 
| Chris@0 | 282 | 
| Chris@0 | 283 	*min = 0; | 
| Chris@0 | 284 	*max = 10; | 
| Chris@216 | 285         *deflt = 5; | 
| Chris@0 | 286 | 
| Chris@216 | 287 	val = 0; | 
| Chris@0 | 288 	int ws = m_windowSize; | 
| Chris@216 | 289 	while (ws > 32) { ws >>= 1; val ++; } | 
| Chris@0 | 290 | 
| Chris@97 | 291     } else if (name == "Window Increment") { | 
| Chris@0 | 292 | 
| Chris@0 | 293 	*min = 0; | 
| Chris@97 | 294 	*max = 5; | 
| Chris@216 | 295         *deflt = 2; | 
| Chris@216 | 296 | 
| Chris@216 | 297         val = m_windowHopLevel; | 
| Chris@0 | 298 | 
| Chris@109 | 299     } else if (name == "Zero Padding") { | 
| Chris@109 | 300 | 
| Chris@109 | 301 	*min = 0; | 
| Chris@109 | 302 	*max = 1; | 
| Chris@216 | 303         *deflt = 0; | 
| Chris@109 | 304 | 
| Chris@216 | 305         val = m_zeroPadLevel > 0 ? 1 : 0; | 
| Chris@109 | 306 | 
| Chris@87 | 307     } else if (name == "Min Frequency") { | 
| Chris@37 | 308 | 
| Chris@37 | 309 	*min = 0; | 
| Chris@37 | 310 	*max = 9; | 
| Chris@216 | 311         *deflt = 1; | 
| Chris@37 | 312 | 
| Chris@37 | 313 	switch (m_minFrequency) { | 
| Chris@216 | 314 	case 0: default: val = 0; break; | 
| Chris@216 | 315 	case 10: val = 1; break; | 
| Chris@216 | 316 	case 20: val = 2; break; | 
| Chris@216 | 317 	case 40: val = 3; break; | 
| Chris@216 | 318 	case 100: val = 4; break; | 
| Chris@216 | 319 	case 250: val = 5; break; | 
| Chris@216 | 320 	case 500: val = 6; break; | 
| Chris@216 | 321 	case 1000: val = 7; break; | 
| Chris@216 | 322 	case 4000: val = 8; break; | 
| Chris@216 | 323 	case 10000: val = 9; break; | 
| Chris@37 | 324 	} | 
| Chris@37 | 325 | 
| Chris@87 | 326     } else if (name == "Max Frequency") { | 
| Chris@0 | 327 | 
| Chris@0 | 328 	*min = 0; | 
| Chris@0 | 329 	*max = 9; | 
| Chris@216 | 330         *deflt = 6; | 
| Chris@0 | 331 | 
| Chris@0 | 332 	switch (m_maxFrequency) { | 
| Chris@216 | 333 	case 500: val = 0; break; | 
| Chris@216 | 334 	case 1000: val = 1; break; | 
| Chris@216 | 335 	case 1500: val = 2; break; | 
| Chris@216 | 336 	case 2000: val = 3; break; | 
| Chris@216 | 337 	case 4000: val = 4; break; | 
| Chris@216 | 338 	case 6000: val = 5; break; | 
| Chris@216 | 339 	case 8000: val = 6; break; | 
| Chris@216 | 340 	case 12000: val = 7; break; | 
| Chris@216 | 341 	case 16000: val = 8; break; | 
| Chris@216 | 342 	default: val = 9; break; | 
| Chris@0 | 343 	} | 
| Chris@0 | 344 | 
| Chris@87 | 345     } else if (name == "Frequency Scale") { | 
| Chris@0 | 346 | 
| Chris@0 | 347 	*min = 0; | 
| Chris@0 | 348 	*max = 1; | 
| Chris@216 | 349         *deflt = int(LinearFrequencyScale); | 
| Chris@216 | 350 	val = (int)m_frequencyScale; | 
| Chris@0 | 351 | 
| Chris@87 | 352     } else if (name == "Bin Display") { | 
| Chris@35 | 353 | 
| Chris@35 | 354 	*min = 0; | 
| Chris@35 | 355 	*max = 2; | 
| Chris@216 | 356         *deflt = int(AllBins); | 
| Chris@216 | 357 	val = (int)m_binDisplay; | 
| Chris@35 | 358 | 
| Chris@862 | 359     } else if (name == "Normalization") { | 
| Chris@36 | 360 | 
| Chris@862 | 361         *min = 0; | 
| Chris@862 | 362         *max = 3; | 
| Chris@862 | 363         *deflt = int(NoNormalization); | 
| Chris@862 | 364         val = (int)m_normalization; | 
| Chris@120 | 365 | 
| Chris@0 | 366     } else { | 
| Chris@216 | 367 	val = Layer::getPropertyRangeAndValue(name, min, max, deflt); | 
| Chris@0 | 368     } | 
| Chris@0 | 369 | 
| Chris@216 | 370     return val; | 
| Chris@0 | 371 } | 
| Chris@0 | 372 | 
| Chris@0 | 373 QString | 
| Chris@0 | 374 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, | 
| Chris@9 | 375 					int value) const | 
| Chris@0 | 376 { | 
| Chris@87 | 377     if (name == "Colour") { | 
| Chris@196 | 378         return ColourMapper::getColourMapName(value); | 
| Chris@0 | 379     } | 
| Chris@87 | 380     if (name == "Colour Scale") { | 
| Chris@0 | 381 	switch (value) { | 
| Chris@0 | 382 	default: | 
| Chris@37 | 383 	case 0: return tr("Linear"); | 
| Chris@37 | 384 	case 1: return tr("Meter"); | 
| Chris@215 | 385 	case 2: return tr("dBV^2"); | 
| Chris@215 | 386 	case 3: return tr("dBV"); | 
| Chris@119 | 387 	case 4: return tr("Phase"); | 
| Chris@0 | 388 	} | 
| Chris@0 | 389     } | 
| Chris@862 | 390     if (name == "Normalization") { | 
| Chris@862 | 391         return ""; // icon only | 
| Chris@862 | 392     } | 
| Chris@87 | 393     if (name == "Window Size") { | 
| Chris@0 | 394 	return QString("%1").arg(32 << value); | 
| Chris@0 | 395     } | 
| Chris@97 | 396     if (name == "Window Increment") { | 
| Chris@0 | 397 	switch (value) { | 
| Chris@0 | 398 	default: | 
| Chris@112 | 399 	case 0: return tr("None"); | 
| Chris@112 | 400 	case 1: return tr("25 %"); | 
| Chris@112 | 401 	case 2: return tr("50 %"); | 
| Chris@112 | 402 	case 3: return tr("75 %"); | 
| Chris@112 | 403 	case 4: return tr("87.5 %"); | 
| Chris@112 | 404 	case 5: return tr("93.75 %"); | 
| Chris@0 | 405 	} | 
| Chris@0 | 406     } | 
| Chris@109 | 407     if (name == "Zero Padding") { | 
| Chris@109 | 408         if (value == 0) return tr("None"); | 
| Chris@109 | 409         return QString("%1x").arg(value + 1); | 
| Chris@109 | 410     } | 
| Chris@87 | 411     if (name == "Min Frequency") { | 
| Chris@37 | 412 	switch (value) { | 
| Chris@37 | 413 	default: | 
| Chris@38 | 414 	case 0: return tr("No min"); | 
| Chris@37 | 415 	case 1: return tr("10 Hz"); | 
| Chris@37 | 416 	case 2: return tr("20 Hz"); | 
| Chris@37 | 417 	case 3: return tr("40 Hz"); | 
| Chris@37 | 418 	case 4: return tr("100 Hz"); | 
| Chris@37 | 419 	case 5: return tr("250 Hz"); | 
| Chris@37 | 420 	case 6: return tr("500 Hz"); | 
| Chris@37 | 421 	case 7: return tr("1 KHz"); | 
| Chris@37 | 422 	case 8: return tr("4 KHz"); | 
| Chris@37 | 423 	case 9: return tr("10 KHz"); | 
| Chris@37 | 424 	} | 
| Chris@37 | 425     } | 
| Chris@87 | 426     if (name == "Max Frequency") { | 
| Chris@0 | 427 	switch (value) { | 
| Chris@0 | 428 	default: | 
| Chris@0 | 429 	case 0: return tr("500 Hz"); | 
| Chris@0 | 430 	case 1: return tr("1 KHz"); | 
| Chris@0 | 431 	case 2: return tr("1.5 KHz"); | 
| Chris@0 | 432 	case 3: return tr("2 KHz"); | 
| Chris@0 | 433 	case 4: return tr("4 KHz"); | 
| Chris@0 | 434 	case 5: return tr("6 KHz"); | 
| Chris@0 | 435 	case 6: return tr("8 KHz"); | 
| Chris@0 | 436 	case 7: return tr("12 KHz"); | 
| Chris@0 | 437 	case 8: return tr("16 KHz"); | 
| Chris@38 | 438 	case 9: return tr("No max"); | 
| Chris@0 | 439 	} | 
| Chris@0 | 440     } | 
| Chris@87 | 441     if (name == "Frequency Scale") { | 
| Chris@0 | 442 	switch (value) { | 
| Chris@0 | 443 	default: | 
| Chris@0 | 444 	case 0: return tr("Linear"); | 
| Chris@0 | 445 	case 1: return tr("Log"); | 
| Chris@0 | 446 	} | 
| Chris@0 | 447     } | 
| Chris@87 | 448     if (name == "Bin Display") { | 
| Chris@35 | 449 	switch (value) { | 
| Chris@35 | 450 	default: | 
| Chris@37 | 451 	case 0: return tr("All Bins"); | 
| Chris@37 | 452 	case 1: return tr("Peak Bins"); | 
| Chris@37 | 453 	case 2: return tr("Frequencies"); | 
| Chris@35 | 454 	} | 
| Chris@35 | 455     } | 
| Chris@0 | 456     return tr("<unknown>"); | 
| Chris@0 | 457 } | 
| Chris@0 | 458 | 
| Chris@862 | 459 QString | 
| Chris@862 | 460 SpectrogramLayer::getPropertyValueIconName(const PropertyName &name, | 
| Chris@862 | 461                                            int value) const | 
| Chris@862 | 462 { | 
| Chris@862 | 463     if (name == "Normalization") { | 
| Chris@862 | 464         switch(value) { | 
| Chris@862 | 465         default: | 
| Chris@862 | 466         case 0: return "normalise-none"; | 
| Chris@862 | 467         case 1: return "normalise-columns"; | 
| Chris@862 | 468         case 2: return "normalise"; | 
| Chris@862 | 469         case 3: return "normalise-hybrid"; | 
| Chris@862 | 470         } | 
| Chris@862 | 471     } | 
| Chris@862 | 472     return ""; | 
| Chris@862 | 473 } | 
| Chris@862 | 474 | 
| Chris@167 | 475 RangeMapper * | 
| Chris@167 | 476 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const | 
| Chris@167 | 477 { | 
| Chris@167 | 478     if (name == "Gain") { | 
| Chris@167 | 479         return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); | 
| Chris@167 | 480     } | 
| Chris@167 | 481     if (name == "Threshold") { | 
| Chris@167 | 482         return new LinearRangeMapper(-50, 0, -50, 0, tr("dB")); | 
| Chris@167 | 483     } | 
| Chris@167 | 484     return 0; | 
| Chris@167 | 485 } | 
| Chris@167 | 486 | 
| Chris@0 | 487 void | 
| Chris@0 | 488 SpectrogramLayer::setProperty(const PropertyName &name, int value) | 
| Chris@0 | 489 { | 
| Chris@87 | 490     if (name == "Gain") { | 
| Chris@906 | 491 	setGain(float(pow(10, float(value)/20.0))); | 
| Chris@87 | 492     } else if (name == "Threshold") { | 
| Chris@37 | 493 	if (value == -50) setThreshold(0.0); | 
| Chris@906 | 494 	else setThreshold(float(AudioLevel::dB_to_multiplier(value))); | 
| Chris@87 | 495     } else if (name == "Colour Rotation") { | 
| Chris@9 | 496 	setColourRotation(value); | 
| Chris@87 | 497     } else if (name == "Colour") { | 
| Chris@197 | 498         setColourMap(value); | 
| Chris@87 | 499     } else if (name == "Window Size") { | 
| Chris@0 | 500 	setWindowSize(32 << value); | 
| Chris@97 | 501     } else if (name == "Window Increment") { | 
| Chris@97 | 502         setWindowHopLevel(value); | 
| Chris@109 | 503     } else if (name == "Zero Padding") { | 
| Chris@109 | 504         setZeroPadLevel(value > 0.1 ? 3 : 0); | 
| Chris@87 | 505     } else if (name == "Min Frequency") { | 
| Chris@37 | 506 	switch (value) { | 
| Chris@37 | 507 	default: | 
| Chris@37 | 508 	case 0: setMinFrequency(0); break; | 
| Chris@37 | 509 	case 1: setMinFrequency(10); break; | 
| Chris@37 | 510 	case 2: setMinFrequency(20); break; | 
| Chris@37 | 511 	case 3: setMinFrequency(40); break; | 
| Chris@37 | 512 	case 4: setMinFrequency(100); break; | 
| Chris@37 | 513 	case 5: setMinFrequency(250); break; | 
| Chris@37 | 514 	case 6: setMinFrequency(500); break; | 
| Chris@37 | 515 	case 7: setMinFrequency(1000); break; | 
| Chris@37 | 516 	case 8: setMinFrequency(4000); break; | 
| Chris@37 | 517 	case 9: setMinFrequency(10000); break; | 
| Chris@37 | 518 	} | 
| Chris@133 | 519         int vs = getCurrentVerticalZoomStep(); | 
| Chris@133 | 520         if (vs != m_lastEmittedZoomStep) { | 
| Chris@133 | 521             emit verticalZoomChanged(); | 
| Chris@133 | 522             m_lastEmittedZoomStep = vs; | 
| Chris@133 | 523         } | 
| Chris@87 | 524     } else if (name == "Max Frequency") { | 
| Chris@0 | 525 	switch (value) { | 
| Chris@0 | 526 	case 0: setMaxFrequency(500); break; | 
| Chris@0 | 527 	case 1: setMaxFrequency(1000); break; | 
| Chris@0 | 528 	case 2: setMaxFrequency(1500); break; | 
| Chris@0 | 529 	case 3: setMaxFrequency(2000); break; | 
| Chris@0 | 530 	case 4: setMaxFrequency(4000); break; | 
| Chris@0 | 531 	case 5: setMaxFrequency(6000); break; | 
| Chris@0 | 532 	case 6: setMaxFrequency(8000); break; | 
| Chris@0 | 533 	case 7: setMaxFrequency(12000); break; | 
| Chris@0 | 534 	case 8: setMaxFrequency(16000); break; | 
| Chris@0 | 535 	default: | 
| Chris@0 | 536 	case 9: setMaxFrequency(0); break; | 
| Chris@0 | 537 	} | 
| Chris@133 | 538         int vs = getCurrentVerticalZoomStep(); | 
| Chris@133 | 539         if (vs != m_lastEmittedZoomStep) { | 
| Chris@133 | 540             emit verticalZoomChanged(); | 
| Chris@133 | 541             m_lastEmittedZoomStep = vs; | 
| Chris@133 | 542         } | 
| Chris@87 | 543     } else if (name == "Colour Scale") { | 
| Chris@0 | 544 	switch (value) { | 
| Chris@0 | 545 	default: | 
| Chris@0 | 546 	case 0: setColourScale(LinearColourScale); break; | 
| Chris@0 | 547 	case 1: setColourScale(MeterColourScale); break; | 
| Chris@215 | 548 	case 2: setColourScale(dBSquaredColourScale); break; | 
| Chris@215 | 549 	case 3: setColourScale(dBColourScale); break; | 
| Chris@119 | 550 	case 4: setColourScale(PhaseColourScale); break; | 
| Chris@0 | 551 	} | 
| Chris@87 | 552     } else if (name == "Frequency Scale") { | 
| Chris@0 | 553 	switch (value) { | 
| Chris@0 | 554 	default: | 
| Chris@0 | 555 	case 0: setFrequencyScale(LinearFrequencyScale); break; | 
| Chris@0 | 556 	case 1: setFrequencyScale(LogFrequencyScale); break; | 
| Chris@0 | 557 	} | 
| Chris@87 | 558     } else if (name == "Bin Display") { | 
| Chris@35 | 559 	switch (value) { | 
| Chris@35 | 560 	default: | 
| Chris@37 | 561 	case 0: setBinDisplay(AllBins); break; | 
| Chris@37 | 562 	case 1: setBinDisplay(PeakBins); break; | 
| Chris@37 | 563 	case 2: setBinDisplay(PeakFrequencies); break; | 
| Chris@35 | 564 	} | 
| Chris@862 | 565     } else if (name == "Normalization") { | 
| Chris@862 | 566         switch (value) { | 
| Chris@862 | 567         default: | 
| Chris@862 | 568         case 0: setNormalization(NoNormalization); break; | 
| Chris@862 | 569         case 1: setNormalization(NormalizeColumns); break; | 
| Chris@862 | 570         case 2: setNormalization(NormalizeVisibleArea); break; | 
| Chris@862 | 571         case 3: setNormalization(NormalizeHybrid); break; | 
| Chris@862 | 572         } | 
| Chris@0 | 573     } | 
| Chris@0 | 574 } | 
| Chris@0 | 575 | 
| Chris@0 | 576 void | 
| Chris@478 | 577 SpectrogramLayer::invalidateImageCaches() | 
| Chris@95 | 578 { | 
| Chris@478 | 579     for (ViewImageCache::iterator i = m_imageCaches.begin(); | 
| Chris@478 | 580          i != m_imageCaches.end(); ++i) { | 
| Chris@95 | 581         i->second.validArea = QRect(); | 
| Chris@95 | 582     } | 
| Chris@95 | 583 } | 
| Chris@95 | 584 | 
| Chris@95 | 585 void | 
| Chris@906 | 586 SpectrogramLayer::invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame) | 
| Chris@95 | 587 { | 
| Chris@478 | 588     for (ViewImageCache::iterator i = m_imageCaches.begin(); | 
| Chris@478 | 589          i != m_imageCaches.end(); ++i) { | 
| Chris@131 | 590 | 
| Chris@95 | 591         //!!! when are views removed from the map? on setLayerDormant? | 
| Chris@918 | 592         const LayerGeometryProvider *v = i->first; | 
| Chris@95 | 593 | 
| Chris@391 | 594 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 595         SVDEBUG << "SpectrogramLayer::invalidateImageCaches(" | 
| Chris@391 | 596                   << startFrame << ", " << endFrame << "): view range is " | 
| Chris@391 | 597                   << v->getStartFrame() << ", " << v->getEndFrame() | 
| Chris@585 | 598                   << endl; | 
| Chris@391 | 599 | 
| Chris@682 | 600         cerr << "Valid area was: " << i->second.validArea.x() << ", " | 
| Chris@391 | 601                   << i->second.validArea.y() << " " | 
| Chris@391 | 602                   << i->second.validArea.width() << "x" | 
| Chris@682 | 603                   << i->second.validArea.height() << endl; | 
| Chris@391 | 604 #endif | 
| Chris@391 | 605 | 
| Chris@806 | 606         if (int(startFrame) > v->getStartFrame()) { | 
| Chris@391 | 607             if (startFrame >= v->getEndFrame()) { | 
| Chris@391 | 608 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 609                 cerr << "Modified start frame is off right of view" << endl; | 
| Chris@391 | 610 #endif | 
| Chris@391 | 611                 return; | 
| Chris@391 | 612             } | 
| Chris@391 | 613             int x = v->getXForFrame(startFrame); | 
| Chris@407 | 614 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 615             SVDEBUG << "clipping from 0 to " << x-1 << endl; | 
| Chris@407 | 616 #endif | 
| Chris@391 | 617             if (x > 1) { | 
| Chris@391 | 618                 i->second.validArea &= | 
| Chris@918 | 619                     QRect(0, 0, x-1, v->getPaintHeight()); | 
| Chris@391 | 620             } else { | 
| Chris@391 | 621                 i->second.validArea = QRect(); | 
| Chris@391 | 622             } | 
| Chris@391 | 623         } else { | 
| Chris@806 | 624             if (int(endFrame) < v->getStartFrame()) { | 
| Chris@391 | 625 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 626                 cerr << "Modified end frame is off left of view" << endl; | 
| Chris@391 | 627 #endif | 
| Chris@391 | 628                 return; | 
| Chris@391 | 629             } | 
| Chris@391 | 630             int x = v->getXForFrame(endFrame); | 
| Chris@391 | 631 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@918 | 632             SVDEBUG << "clipping from " << x+1 << " to " << v->getPaintWidth() | 
| Chris@585 | 633                       << endl; | 
| Chris@391 | 634 #endif | 
| Chris@918 | 635             if (x < v->getPaintWidth()) { | 
| Chris@391 | 636                 i->second.validArea &= | 
| Chris@918 | 637                     QRect(x+1, 0, v->getPaintWidth()-(x+1), v->getPaintHeight()); | 
| Chris@391 | 638             } else { | 
| Chris@391 | 639                 i->second.validArea = QRect(); | 
| Chris@391 | 640             } | 
| Chris@95 | 641         } | 
| Chris@391 | 642 | 
| Chris@391 | 643 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 644         cerr << "Valid area is now: " << i->second.validArea.x() << ", " | 
| Chris@391 | 645                   << i->second.validArea.y() << " " | 
| Chris@391 | 646                   << i->second.validArea.width() << "x" | 
| Chris@682 | 647                   << i->second.validArea.height() << endl; | 
| Chris@391 | 648 #endif | 
| Chris@95 | 649     } | 
| Chris@95 | 650 } | 
| Chris@95 | 651 | 
| Chris@95 | 652 void | 
| Chris@122 | 653 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) | 
| Chris@122 | 654 { | 
| Chris@587 | 655     SVDEBUG << "SpectrogramLayer::preferenceChanged(" << name << ")" << endl; | 
| Chris@122 | 656 | 
| Chris@122 | 657     if (name == "Window Type") { | 
| Chris@122 | 658         setWindowType(Preferences::getInstance()->getWindowType()); | 
| Chris@122 | 659         return; | 
| Chris@122 | 660     } | 
| Chris@490 | 661     if (name == "Spectrogram Y Smoothing") { | 
| Chris@490 | 662         invalidateImageCaches(); | 
| Chris@490 | 663         invalidateMagnitudes(); | 
| Chris@490 | 664         emit layerParametersChanged(); | 
| Chris@490 | 665     } | 
| Chris@490 | 666     if (name == "Spectrogram X Smoothing") { | 
| Chris@478 | 667         invalidateImageCaches(); | 
| Chris@122 | 668         invalidateMagnitudes(); | 
| Chris@122 | 669         emit layerParametersChanged(); | 
| Chris@122 | 670     } | 
| Chris@122 | 671     if (name == "Tuning Frequency") { | 
| Chris@122 | 672         emit layerParametersChanged(); | 
| Chris@122 | 673     } | 
| Chris@122 | 674 } | 
| Chris@122 | 675 | 
| Chris@122 | 676 void | 
| Chris@0 | 677 SpectrogramLayer::setChannel(int ch) | 
| Chris@0 | 678 { | 
| Chris@0 | 679     if (m_channel == ch) return; | 
| Chris@0 | 680 | 
| Chris@478 | 681     invalidateImageCaches(); | 
| Chris@0 | 682     m_channel = ch; | 
| Chris@130 | 683     invalidateFFTModels(); | 
| Chris@9 | 684 | 
| Chris@0 | 685     emit layerParametersChanged(); | 
| Chris@0 | 686 } | 
| Chris@0 | 687 | 
| Chris@0 | 688 int | 
| Chris@0 | 689 SpectrogramLayer::getChannel() const | 
| Chris@0 | 690 { | 
| Chris@0 | 691     return m_channel; | 
| Chris@0 | 692 } | 
| Chris@0 | 693 | 
| Chris@0 | 694 void | 
| Chris@805 | 695 SpectrogramLayer::setWindowSize(int ws) | 
| Chris@0 | 696 { | 
| Chris@0 | 697     if (m_windowSize == ws) return; | 
| Chris@0 | 698 | 
| Chris@478 | 699     invalidateImageCaches(); | 
| Chris@0 | 700 | 
| Chris@0 | 701     m_windowSize = ws; | 
| Chris@109 | 702     m_fftSize = ws * (m_zeroPadLevel + 1); | 
| Chris@0 | 703 | 
| Chris@130 | 704     invalidateFFTModels(); | 
| Chris@9 | 705 | 
| Chris@9 | 706     emit layerParametersChanged(); | 
| Chris@0 | 707 } | 
| Chris@0 | 708 | 
| Chris@805 | 709 int | 
| Chris@0 | 710 SpectrogramLayer::getWindowSize() const | 
| Chris@0 | 711 { | 
| Chris@0 | 712     return m_windowSize; | 
| Chris@0 | 713 } | 
| Chris@0 | 714 | 
| Chris@0 | 715 void | 
| Chris@805 | 716 SpectrogramLayer::setWindowHopLevel(int v) | 
| Chris@0 | 717 { | 
| Chris@97 | 718     if (m_windowHopLevel == v) return; | 
| Chris@0 | 719 | 
| Chris@478 | 720     invalidateImageCaches(); | 
| Chris@0 | 721 | 
| Chris@97 | 722     m_windowHopLevel = v; | 
| Chris@0 | 723 | 
| Chris@130 | 724     invalidateFFTModels(); | 
| Chris@9 | 725 | 
| Chris@9 | 726     emit layerParametersChanged(); | 
| Chris@9 | 727 | 
| Chris@110 | 728 //    fillCache(); | 
| Chris@0 | 729 } | 
| Chris@0 | 730 | 
| Chris@805 | 731 int | 
| Chris@97 | 732 SpectrogramLayer::getWindowHopLevel() const | 
| Chris@0 | 733 { | 
| Chris@97 | 734     return m_windowHopLevel; | 
| Chris@0 | 735 } | 
| Chris@0 | 736 | 
| Chris@0 | 737 void | 
| Chris@805 | 738 SpectrogramLayer::setZeroPadLevel(int v) | 
| Chris@109 | 739 { | 
| Chris@109 | 740     if (m_zeroPadLevel == v) return; | 
| Chris@109 | 741 | 
| Chris@478 | 742     invalidateImageCaches(); | 
| Chris@109 | 743 | 
| Chris@109 | 744     m_zeroPadLevel = v; | 
| Chris@109 | 745     m_fftSize = m_windowSize * (v + 1); | 
| Chris@110 | 746 | 
| Chris@130 | 747     invalidateFFTModels(); | 
| Chris@109 | 748 | 
| Chris@109 | 749     emit layerParametersChanged(); | 
| Chris@109 | 750 } | 
| Chris@109 | 751 | 
| Chris@805 | 752 int | 
| Chris@109 | 753 SpectrogramLayer::getZeroPadLevel() const | 
| Chris@109 | 754 { | 
| Chris@109 | 755     return m_zeroPadLevel; | 
| Chris@109 | 756 } | 
| Chris@109 | 757 | 
| Chris@109 | 758 void | 
| Chris@0 | 759 SpectrogramLayer::setWindowType(WindowType w) | 
| Chris@0 | 760 { | 
| Chris@0 | 761     if (m_windowType == w) return; | 
| Chris@0 | 762 | 
| Chris@478 | 763     invalidateImageCaches(); | 
| Chris@0 | 764 | 
| Chris@0 | 765     m_windowType = w; | 
| Chris@110 | 766 | 
| Chris@130 | 767     invalidateFFTModels(); | 
| Chris@9 | 768 | 
| Chris@9 | 769     emit layerParametersChanged(); | 
| Chris@0 | 770 } | 
| Chris@0 | 771 | 
| Chris@0 | 772 WindowType | 
| Chris@0 | 773 SpectrogramLayer::getWindowType() const | 
| Chris@0 | 774 { | 
| Chris@0 | 775     return m_windowType; | 
| Chris@0 | 776 } | 
| Chris@0 | 777 | 
| Chris@0 | 778 void | 
| Chris@0 | 779 SpectrogramLayer::setGain(float gain) | 
| Chris@0 | 780 { | 
| Chris@587 | 781 //    SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " | 
| Chris@585 | 782 //	      << m_gain << ")" << endl; | 
| Chris@55 | 783 | 
| Chris@40 | 784     if (m_gain == gain) return; | 
| Chris@0 | 785 | 
| Chris@478 | 786     invalidateImageCaches(); | 
| Chris@0 | 787 | 
| Chris@0 | 788     m_gain = gain; | 
| Chris@0 | 789 | 
| Chris@9 | 790     emit layerParametersChanged(); | 
| Chris@0 | 791 } | 
| Chris@0 | 792 | 
| Chris@0 | 793 float | 
| Chris@0 | 794 SpectrogramLayer::getGain() const | 
| Chris@0 | 795 { | 
| Chris@0 | 796     return m_gain; | 
| Chris@0 | 797 } | 
| Chris@0 | 798 | 
| Chris@0 | 799 void | 
| Chris@37 | 800 SpectrogramLayer::setThreshold(float threshold) | 
| Chris@37 | 801 { | 
| Chris@40 | 802     if (m_threshold == threshold) return; | 
| Chris@37 | 803 | 
| Chris@478 | 804     invalidateImageCaches(); | 
| Chris@37 | 805 | 
| Chris@37 | 806     m_threshold = threshold; | 
| Chris@37 | 807 | 
| Chris@37 | 808     emit layerParametersChanged(); | 
| Chris@37 | 809 } | 
| Chris@37 | 810 | 
| Chris@37 | 811 float | 
| Chris@37 | 812 SpectrogramLayer::getThreshold() const | 
| Chris@37 | 813 { | 
| Chris@37 | 814     return m_threshold; | 
| Chris@37 | 815 } | 
| Chris@37 | 816 | 
| Chris@37 | 817 void | 
| Chris@805 | 818 SpectrogramLayer::setMinFrequency(int mf) | 
| Chris@37 | 819 { | 
| Chris@37 | 820     if (m_minFrequency == mf) return; | 
| Chris@37 | 821 | 
| Chris@587 | 822 //    SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl; | 
| Chris@187 | 823 | 
| Chris@478 | 824     invalidateImageCaches(); | 
| Chris@119 | 825     invalidateMagnitudes(); | 
| Chris@37 | 826 | 
| Chris@37 | 827     m_minFrequency = mf; | 
| Chris@37 | 828 | 
| Chris@37 | 829     emit layerParametersChanged(); | 
| Chris@37 | 830 } | 
| Chris@37 | 831 | 
| Chris@805 | 832 int | 
| Chris@37 | 833 SpectrogramLayer::getMinFrequency() const | 
| Chris@37 | 834 { | 
| Chris@37 | 835     return m_minFrequency; | 
| Chris@37 | 836 } | 
| Chris@37 | 837 | 
| Chris@37 | 838 void | 
| Chris@805 | 839 SpectrogramLayer::setMaxFrequency(int mf) | 
| Chris@0 | 840 { | 
| Chris@0 | 841     if (m_maxFrequency == mf) return; | 
| Chris@0 | 842 | 
| Chris@587 | 843 //    SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl; | 
| Chris@187 | 844 | 
| Chris@478 | 845     invalidateImageCaches(); | 
| Chris@119 | 846     invalidateMagnitudes(); | 
| Chris@0 | 847 | 
| Chris@0 | 848     m_maxFrequency = mf; | 
| Chris@0 | 849 | 
| Chris@9 | 850     emit layerParametersChanged(); | 
| Chris@0 | 851 } | 
| Chris@0 | 852 | 
| Chris@805 | 853 int | 
| Chris@0 | 854 SpectrogramLayer::getMaxFrequency() const | 
| Chris@0 | 855 { | 
| Chris@0 | 856     return m_maxFrequency; | 
| Chris@0 | 857 } | 
| Chris@0 | 858 | 
| Chris@0 | 859 void | 
| Chris@9 | 860 SpectrogramLayer::setColourRotation(int r) | 
| Chris@9 | 861 { | 
| Chris@478 | 862     invalidateImageCaches(); | 
| Chris@9 | 863 | 
| Chris@9 | 864     if (r < 0) r = 0; | 
| Chris@9 | 865     if (r > 256) r = 256; | 
| Chris@9 | 866     int distance = r - m_colourRotation; | 
| Chris@9 | 867 | 
| Chris@9 | 868     if (distance != 0) { | 
| Chris@197 | 869 	rotatePalette(-distance); | 
| Chris@9 | 870 	m_colourRotation = r; | 
| Chris@9 | 871     } | 
| Chris@9 | 872 | 
| Chris@9 | 873     emit layerParametersChanged(); | 
| Chris@9 | 874 } | 
| Chris@9 | 875 | 
| Chris@9 | 876 void | 
| Chris@0 | 877 SpectrogramLayer::setColourScale(ColourScale colourScale) | 
| Chris@0 | 878 { | 
| Chris@0 | 879     if (m_colourScale == colourScale) return; | 
| Chris@0 | 880 | 
| Chris@478 | 881     invalidateImageCaches(); | 
| Chris@0 | 882 | 
| Chris@0 | 883     m_colourScale = colourScale; | 
| Chris@0 | 884 | 
| Chris@9 | 885     emit layerParametersChanged(); | 
| Chris@0 | 886 } | 
| Chris@0 | 887 | 
| Chris@0 | 888 SpectrogramLayer::ColourScale | 
| Chris@0 | 889 SpectrogramLayer::getColourScale() const | 
| Chris@0 | 890 { | 
| Chris@0 | 891     return m_colourScale; | 
| Chris@0 | 892 } | 
| Chris@0 | 893 | 
| Chris@0 | 894 void | 
| Chris@197 | 895 SpectrogramLayer::setColourMap(int map) | 
| Chris@0 | 896 { | 
| Chris@197 | 897     if (m_colourMap == map) return; | 
| Chris@0 | 898 | 
| Chris@478 | 899     invalidateImageCaches(); | 
| Chris@0 | 900 | 
| Chris@197 | 901     m_colourMap = map; | 
| Chris@197 | 902     initialisePalette(); | 
| Chris@9 | 903 | 
| Chris@0 | 904     emit layerParametersChanged(); | 
| Chris@0 | 905 } | 
| Chris@0 | 906 | 
| Chris@196 | 907 int | 
| Chris@197 | 908 SpectrogramLayer::getColourMap() const | 
| Chris@0 | 909 { | 
| Chris@197 | 910     return m_colourMap; | 
| Chris@0 | 911 } | 
| Chris@0 | 912 | 
| Chris@0 | 913 void | 
| Chris@0 | 914 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) | 
| Chris@0 | 915 { | 
| Chris@0 | 916     if (m_frequencyScale == frequencyScale) return; | 
| Chris@0 | 917 | 
| Chris@478 | 918     invalidateImageCaches(); | 
| Chris@0 | 919     m_frequencyScale = frequencyScale; | 
| Chris@9 | 920 | 
| Chris@9 | 921     emit layerParametersChanged(); | 
| Chris@0 | 922 } | 
| Chris@0 | 923 | 
| Chris@0 | 924 SpectrogramLayer::FrequencyScale | 
| Chris@0 | 925 SpectrogramLayer::getFrequencyScale() const | 
| Chris@0 | 926 { | 
| Chris@0 | 927     return m_frequencyScale; | 
| Chris@0 | 928 } | 
| Chris@0 | 929 | 
| Chris@0 | 930 void | 
| Chris@37 | 931 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay) | 
| Chris@35 | 932 { | 
| Chris@37 | 933     if (m_binDisplay == binDisplay) return; | 
| Chris@35 | 934 | 
| Chris@478 | 935     invalidateImageCaches(); | 
| Chris@37 | 936     m_binDisplay = binDisplay; | 
| Chris@35 | 937 | 
| Chris@35 | 938     emit layerParametersChanged(); | 
| Chris@35 | 939 } | 
| Chris@35 | 940 | 
| Chris@37 | 941 SpectrogramLayer::BinDisplay | 
| Chris@37 | 942 SpectrogramLayer::getBinDisplay() const | 
| Chris@35 | 943 { | 
| Chris@37 | 944     return m_binDisplay; | 
| Chris@35 | 945 } | 
| Chris@35 | 946 | 
| Chris@35 | 947 void | 
| Chris@862 | 948 SpectrogramLayer::setNormalization(Normalization n) | 
| Chris@36 | 949 { | 
| Chris@862 | 950     if (m_normalization == n) return; | 
| Chris@36 | 951 | 
| Chris@478 | 952     invalidateImageCaches(); | 
| Chris@119 | 953     invalidateMagnitudes(); | 
| Chris@862 | 954     m_normalization = n; | 
| Chris@36 | 955 | 
| Chris@36 | 956     emit layerParametersChanged(); | 
| Chris@36 | 957 } | 
| Chris@36 | 958 | 
| Chris@862 | 959 SpectrogramLayer::Normalization | 
| Chris@862 | 960 SpectrogramLayer::getNormalization() const | 
| Chris@36 | 961 { | 
| Chris@862 | 962     return m_normalization; | 
| Chris@120 | 963 } | 
| Chris@120 | 964 | 
| Chris@120 | 965 void | 
| Chris@918 | 966 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) | 
| Chris@29 | 967 { | 
| Chris@33 | 968     if (dormant) { | 
| Chris@33 | 969 | 
| Chris@331 | 970 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 971         SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")" | 
| Chris@585 | 972                   << endl; | 
| Chris@331 | 973 #endif | 
| Chris@331 | 974 | 
| Chris@131 | 975         if (isLayerDormant(v)) { | 
| Chris@131 | 976             return; | 
| Chris@131 | 977         } | 
| Chris@131 | 978 | 
| Chris@131 | 979         Layer::setLayerDormant(v, true); | 
| Chris@33 | 980 | 
| Chris@920 | 981         const View *view = v->getView(); | 
| Chris@920 | 982 | 
| Chris@478 | 983 	invalidateImageCaches(); | 
| Chris@920 | 984         m_imageCaches.erase(view); | 
| Chris@920 | 985 | 
| Chris@920 | 986         if (m_fftModels.find(view) != m_fftModels.end()) { | 
| Chris@920 | 987 | 
| Chris@920 | 988             if (m_sliceableModel == m_fftModels[view].first) { | 
| Chris@193 | 989                 bool replaced = false; | 
| Chris@193 | 990                 for (ViewFFTMap::iterator i = m_fftModels.begin(); | 
| Chris@193 | 991                      i != m_fftModels.end(); ++i) { | 
| Chris@193 | 992                     if (i->second.first != m_sliceableModel) { | 
| Chris@193 | 993                         emit sliceableModelReplaced(m_sliceableModel, i->second.first); | 
| Chris@193 | 994                         replaced = true; | 
| Chris@193 | 995                         break; | 
| Chris@193 | 996                     } | 
| Chris@193 | 997                 } | 
| Chris@193 | 998                 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); | 
| Chris@193 | 999             } | 
| Chris@193 | 1000 | 
| Chris@920 | 1001             delete m_fftModels[view].first; | 
| Chris@920 | 1002             m_fftModels.erase(view); | 
| Chris@920 | 1003 | 
| Chris@920 | 1004             delete m_peakCaches[view]; | 
| Chris@920 | 1005             m_peakCaches.erase(view); | 
| Chris@114 | 1006         } | 
| Chris@33 | 1007 | 
| Chris@33 | 1008     } else { | 
| Chris@33 | 1009 | 
| Chris@131 | 1010         Layer::setLayerDormant(v, false); | 
| Chris@33 | 1011     } | 
| Chris@29 | 1012 } | 
| Chris@29 | 1013 | 
| Chris@29 | 1014 void | 
| Chris@0 | 1015 SpectrogramLayer::cacheInvalid() | 
| Chris@0 | 1016 { | 
| Chris@391 | 1017 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1018     SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl; | 
| Chris@391 | 1019 #endif | 
| Chris@391 | 1020 | 
| Chris@478 | 1021     invalidateImageCaches(); | 
| Chris@119 | 1022     invalidateMagnitudes(); | 
| Chris@0 | 1023 } | 
| Chris@0 | 1024 | 
| Chris@0 | 1025 void | 
| Chris@906 | 1026 SpectrogramLayer::cacheInvalid(sv_frame_t from, sv_frame_t to) | 
| Chris@0 | 1027 { | 
| Chris@391 | 1028 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1029     SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; | 
| Chris@391 | 1030 #endif | 
| Chris@391 | 1031 | 
| Chris@478 | 1032     invalidateImageCaches(from, to); | 
| Chris@391 | 1033     invalidateMagnitudes(); | 
| Chris@0 | 1034 } | 
| Chris@0 | 1035 | 
| Chris@0 | 1036 void | 
| Chris@0 | 1037 SpectrogramLayer::fillTimerTimedOut() | 
| Chris@0 | 1038 { | 
| Chris@115 | 1039     if (!m_model) return; | 
| Chris@115 | 1040 | 
| Chris@115 | 1041     bool allDone = true; | 
| Chris@115 | 1042 | 
| Chris@184 | 1043 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1044     SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl; | 
| Chris@184 | 1045 #endif | 
| Chris@184 | 1046 | 
| Chris@130 | 1047     for (ViewFFTMap::iterator i = m_fftModels.begin(); | 
| Chris@130 | 1048          i != m_fftModels.end(); ++i) { | 
| Chris@115 | 1049 | 
| Chris@130 | 1050         const FFTModel *model = i->second.first; | 
| Chris@906 | 1051         sv_frame_t lastFill = i->second.second; | 
| Chris@115 | 1052 | 
| Chris@130 | 1053         if (model) { | 
| Chris@130 | 1054 | 
| Chris@906 | 1055             sv_frame_t fill = model->getFillExtent(); | 
| Chris@115 | 1056 | 
| Chris@0 | 1057 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1058             SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl; | 
| Chris@0 | 1059 #endif | 
| Chris@115 | 1060 | 
| Chris@115 | 1061             if (fill >= lastFill) { | 
| Chris@115 | 1062                 if (fill >= m_model->getEndFrame() && lastFill > 0) { | 
| Chris@0 | 1063 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1064                     cerr << "complete!" << endl; | 
| Chris@0 | 1065 #endif | 
| Chris@478 | 1066                     invalidateImageCaches(); | 
| Chris@184 | 1067                     i->second.second = -1; | 
| Chris@115 | 1068                     emit modelChanged(); | 
| Chris@115 | 1069 | 
| Chris@115 | 1070                 } else if (fill > lastFill) { | 
| Chris@0 | 1071 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1072                     cerr << "SpectrogramLayer: emitting modelChanged(" | 
| Chris@682 | 1073                               << lastFill << "," << fill << ")" << endl; | 
| Chris@0 | 1074 #endif | 
| Chris@478 | 1075                     invalidateImageCaches(lastFill, fill); | 
| Chris@184 | 1076                     i->second.second = fill; | 
| Chris@806 | 1077                     emit modelChangedWithin(lastFill, fill); | 
| Chris@115 | 1078                 } | 
| Chris@115 | 1079             } else { | 
| Chris@0 | 1080 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1081                 cerr << "SpectrogramLayer: going backwards, emitting modelChanged(" | 
| Chris@682 | 1082                           << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl; | 
| Chris@0 | 1083 #endif | 
| Chris@478 | 1084                 invalidateImageCaches(); | 
| Chris@184 | 1085                 i->second.second = fill; | 
| Chris@806 | 1086                 emit modelChangedWithin(m_model->getStartFrame(), m_model->getEndFrame()); | 
| Chris@115 | 1087             } | 
| Chris@115 | 1088 | 
| Chris@115 | 1089             if (i->second.second >= 0) { | 
| Chris@115 | 1090                 allDone = false; | 
| Chris@115 | 1091             } | 
| Chris@115 | 1092         } | 
| Chris@0 | 1093     } | 
| Chris@115 | 1094 | 
| Chris@115 | 1095     if (allDone) { | 
| Chris@115 | 1096 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1097         cerr << "SpectrogramLayer: all complete!" << endl; | 
| Chris@115 | 1098 #endif | 
| Chris@115 | 1099         delete m_updateTimer; | 
| Chris@115 | 1100         m_updateTimer = 0; | 
| Chris@115 | 1101     } | 
| Chris@0 | 1102 } | 
| Chris@0 | 1103 | 
| Chris@224 | 1104 bool | 
| Chris@224 | 1105 SpectrogramLayer::hasLightBackground() const | 
| Chris@224 | 1106 { | 
| Chris@287 | 1107     return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground(); | 
| Chris@224 | 1108 } | 
| Chris@224 | 1109 | 
| Chris@0 | 1110 void | 
| Chris@197 | 1111 SpectrogramLayer::initialisePalette() | 
| Chris@0 | 1112 { | 
| Chris@10 | 1113     int formerRotation = m_colourRotation; | 
| Chris@10 | 1114 | 
| Chris@197 | 1115     if (m_colourMap == (int)ColourMapper::BlackOnWhite) { | 
| Chris@197 | 1116 	m_palette.setColour(NO_VALUE, Qt::white); | 
| Chris@38 | 1117     } else { | 
| Chris@197 | 1118 	m_palette.setColour(NO_VALUE, Qt::black); | 
| Chris@38 | 1119     } | 
| Chris@0 | 1120 | 
| Chris@197 | 1121     ColourMapper mapper(m_colourMap, 1.f, 255.f); | 
| Chris@196 | 1122 | 
| Chris@0 | 1123     for (int pixel = 1; pixel < 256; ++pixel) { | 
| Chris@907 | 1124         m_palette.setColour((unsigned char)pixel, mapper.map(pixel)); | 
| Chris@0 | 1125     } | 
| Chris@9 | 1126 | 
| Chris@196 | 1127     m_crosshairColour = mapper.getContrastingColour(); | 
| Chris@196 | 1128 | 
| Chris@9 | 1129     m_colourRotation = 0; | 
| Chris@197 | 1130     rotatePalette(m_colourRotation - formerRotation); | 
| Chris@10 | 1131     m_colourRotation = formerRotation; | 
| Chris@478 | 1132 | 
| Chris@478 | 1133     m_drawBuffer = QImage(); | 
| Chris@9 | 1134 } | 
| Chris@9 | 1135 | 
| Chris@9 | 1136 void | 
| Chris@197 | 1137 SpectrogramLayer::rotatePalette(int distance) | 
| Chris@9 | 1138 { | 
| Chris@31 | 1139     QColor newPixels[256]; | 
| Chris@9 | 1140 | 
| Chris@197 | 1141     newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE); | 
| Chris@9 | 1142 | 
| Chris@9 | 1143     for (int pixel = 1; pixel < 256; ++pixel) { | 
| Chris@9 | 1144 	int target = pixel + distance; | 
| Chris@9 | 1145 	while (target < 1) target += 255; | 
| Chris@9 | 1146 	while (target > 255) target -= 255; | 
| Chris@907 | 1147 	newPixels[target] = m_palette.getColour((unsigned char)pixel); | 
| Chris@9 | 1148     } | 
| Chris@9 | 1149 | 
| Chris@9 | 1150     for (int pixel = 0; pixel < 256; ++pixel) { | 
| Chris@907 | 1151 	m_palette.setColour((unsigned char)pixel, newPixels[pixel]); | 
| Chris@9 | 1152     } | 
| Chris@478 | 1153 | 
| Chris@478 | 1154     m_drawBuffer = QImage(); | 
| Chris@0 | 1155 } | 
| Chris@0 | 1156 | 
| Chris@38 | 1157 unsigned char | 
| Chris@918 | 1158 SpectrogramLayer::getDisplayValue(LayerGeometryProvider *v, double input) const | 
| Chris@38 | 1159 { | 
| Chris@38 | 1160     int value; | 
| Chris@37 | 1161 | 
| Chris@907 | 1162     double min = 0.0; | 
| Chris@907 | 1163     double max = 1.0; | 
| Chris@120 | 1164 | 
| Chris@862 | 1165     if (m_normalization == NormalizeVisibleArea) { | 
| Chris@120 | 1166         min = m_viewMags[v].getMin(); | 
| Chris@120 | 1167         max = m_viewMags[v].getMax(); | 
| Chris@862 | 1168     } else if (m_normalization != NormalizeColumns) { | 
| Chris@224 | 1169         if (m_colourScale == LinearColourScale //|| | 
| Chris@224 | 1170 //            m_colourScale == MeterColourScale) { | 
| Chris@224 | 1171             ) { | 
| Chris@907 | 1172             max = 0.1; | 
| Chris@120 | 1173         } | 
| Chris@120 | 1174     } | 
| Chris@120 | 1175 | 
| Chris@907 | 1176     double thresh = -80.0; | 
| Chris@907 | 1177 | 
| Chris@907 | 1178     if (max == 0.0) max = 1.0; | 
| Chris@907 | 1179     if (max == min) min = max - 0.0001; | 
| Chris@119 | 1180 | 
| Chris@40 | 1181     switch (m_colourScale) { | 
| Chris@40 | 1182 | 
| Chris@40 | 1183     default: | 
| Chris@40 | 1184     case LinearColourScale: | 
| Chris@907 | 1185         value = int(((input - min) / (max - min)) * 255.0) + 1; | 
| Chris@40 | 1186 	break; | 
| Chris@40 | 1187 | 
| Chris@40 | 1188     case MeterColourScale: | 
| Chris@210 | 1189         value = AudioLevel::multiplier_to_preview | 
| Chris@210 | 1190             ((input - min) / (max - min), 254) + 1; | 
| Chris@40 | 1191 	break; | 
| Chris@119 | 1192 | 
| Chris@210 | 1193     case dBSquaredColourScale: | 
| Chris@215 | 1194         input = ((input - min) * (input - min)) / ((max - min) * (max - min)); | 
| Chris@907 | 1195         if (input > 0.0) { | 
| Chris@907 | 1196             input = 10.0 * log10(input); | 
| Chris@133 | 1197         } else { | 
| Chris@133 | 1198             input = thresh; | 
| Chris@133 | 1199         } | 
| Chris@907 | 1200         if (min > 0.0) { | 
| Chris@907 | 1201             thresh = 10.0 * log10(min * min); | 
| Chris@907 | 1202             if (thresh < -80.0) thresh = -80.0; | 
| Chris@119 | 1203         } | 
| Chris@119 | 1204 	input = (input - thresh) / (-thresh); | 
| Chris@907 | 1205 	if (input < 0.0) input = 0.0; | 
| Chris@907 | 1206 	if (input > 1.0) input = 1.0; | 
| Chris@907 | 1207 	value = int(input * 255.0) + 1; | 
| Chris@119 | 1208 	break; | 
| Chris@40 | 1209 | 
| Chris@215 | 1210     case dBColourScale: | 
| Chris@215 | 1211         //!!! experiment with normalizing the visible area this way. | 
| Chris@215 | 1212         //In any case, we need to have some indication of what the dB | 
| Chris@215 | 1213         //scale is relative to. | 
| Chris@215 | 1214         input = (input - min) / (max - min); | 
| Chris@907 | 1215         if (input > 0.0) { | 
| Chris@907 | 1216             input = 10.0 * log10(input); | 
| Chris@215 | 1217         } else { | 
| Chris@215 | 1218             input = thresh; | 
| Chris@215 | 1219         } | 
| Chris@907 | 1220         if (min > 0.0) { | 
| Chris@907 | 1221             thresh = 10.0 * log10(min); | 
| Chris@907 | 1222             if (thresh < -80.0) thresh = -80.0; | 
| Chris@215 | 1223         } | 
| Chris@215 | 1224 	input = (input - thresh) / (-thresh); | 
| Chris@907 | 1225 	if (input < 0.0) input = 0.0; | 
| Chris@907 | 1226 	if (input > 1.0) input = 1.0; | 
| Chris@907 | 1227 	value = int(input * 255.0) + 1; | 
| Chris@215 | 1228 	break; | 
| Chris@215 | 1229 | 
| Chris@40 | 1230     case PhaseColourScale: | 
| Chris@40 | 1231 	value = int((input * 127.0 / M_PI) + 128); | 
| Chris@40 | 1232 	break; | 
| Chris@0 | 1233     } | 
| Chris@210 | 1234 | 
| Chris@38 | 1235     if (value > UCHAR_MAX) value = UCHAR_MAX; | 
| Chris@38 | 1236     if (value < 0) value = 0; | 
| Chris@907 | 1237     return (unsigned char)value; | 
| Chris@0 | 1238 } | 
| Chris@0 | 1239 | 
| Chris@905 | 1240 double | 
| Chris@40 | 1241 SpectrogramLayer::getEffectiveMinFrequency() const | 
| Chris@40 | 1242 { | 
| Chris@907 | 1243     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@905 | 1244     double minf = double(sr) / m_fftSize; | 
| Chris@40 | 1245 | 
| Chris@40 | 1246     if (m_minFrequency > 0.0) { | 
| Chris@805 | 1247 	int minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.01); | 
| Chris@40 | 1248 	if (minbin < 1) minbin = 1; | 
| Chris@107 | 1249 	minf = minbin * sr / m_fftSize; | 
| Chris@40 | 1250     } | 
| Chris@40 | 1251 | 
| Chris@40 | 1252     return minf; | 
| Chris@40 | 1253 } | 
| Chris@40 | 1254 | 
| Chris@905 | 1255 double | 
| Chris@40 | 1256 SpectrogramLayer::getEffectiveMaxFrequency() const | 
| Chris@40 | 1257 { | 
| Chris@907 | 1258     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@905 | 1259     double maxf = double(sr) / 2; | 
| Chris@40 | 1260 | 
| Chris@40 | 1261     if (m_maxFrequency > 0.0) { | 
| Chris@805 | 1262 	int maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); | 
| Chris@107 | 1263 	if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; | 
| Chris@107 | 1264 	maxf = maxbin * sr / m_fftSize; | 
| Chris@40 | 1265     } | 
| Chris@40 | 1266 | 
| Chris@40 | 1267     return maxf; | 
| Chris@40 | 1268 } | 
| Chris@40 | 1269 | 
| Chris@0 | 1270 bool | 
| Chris@918 | 1271 SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const | 
| Chris@0 | 1272 { | 
| Chris@382 | 1273     Profiler profiler("SpectrogramLayer::getYBinRange"); | 
| Chris@382 | 1274 | 
| Chris@918 | 1275     int h = v->getPaintHeight(); | 
| Chris@0 | 1276     if (y < 0 || y >= h) return false; | 
| Chris@0 | 1277 | 
| Chris@907 | 1278     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@905 | 1279     double minf = getEffectiveMinFrequency(); | 
| Chris@905 | 1280     double maxf = getEffectiveMaxFrequency(); | 
| Chris@0 | 1281 | 
| Chris@38 | 1282     bool logarithmic = (m_frequencyScale == LogFrequencyScale); | 
| Chris@38 | 1283 | 
| Chris@44 | 1284     q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); | 
| Chris@44 | 1285     q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); | 
| Chris@38 | 1286 | 
| Chris@490 | 1287     // Now map these on to ("proportions of") actual bins, using raw | 
| Chris@490 | 1288     // FFT size (unsmoothed) | 
| Chris@490 | 1289 | 
| Chris@490 | 1290     q0 = (q0 * m_fftSize) / sr; | 
| Chris@490 | 1291     q1 = (q1 * m_fftSize) / sr; | 
| Chris@0 | 1292 | 
| Chris@0 | 1293     return true; | 
| Chris@0 | 1294 } | 
| Chris@486 | 1295 | 
| Chris@486 | 1296 bool | 
| Chris@918 | 1297 SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const | 
| Chris@486 | 1298 { | 
| Chris@486 | 1299     Profiler profiler("SpectrogramLayer::getSmoothedYBinRange"); | 
| Chris@486 | 1300 | 
| Chris@918 | 1301     int h = v->getPaintHeight(); | 
| Chris@486 | 1302     if (y < 0 || y >= h) return false; | 
| Chris@486 | 1303 | 
| Chris@907 | 1304     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@905 | 1305     double minf = getEffectiveMinFrequency(); | 
| Chris@905 | 1306     double maxf = getEffectiveMaxFrequency(); | 
| Chris@486 | 1307 | 
| Chris@486 | 1308     bool logarithmic = (m_frequencyScale == LogFrequencyScale); | 
| Chris@486 | 1309 | 
| Chris@486 | 1310     q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); | 
| Chris@486 | 1311     q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); | 
| Chris@486 | 1312 | 
| Chris@490 | 1313     // Now map these on to ("proportions of") actual bins, using raw | 
| Chris@490 | 1314     // FFT size (unsmoothed) | 
| Chris@490 | 1315 | 
| Chris@490 | 1316     q0 = (q0 * getFFTSize(v)) / sr; | 
| Chris@490 | 1317     q1 = (q1 * getFFTSize(v)) / sr; | 
| Chris@486 | 1318 | 
| Chris@486 | 1319     return true; | 
| Chris@486 | 1320 } | 
| Chris@38 | 1321 | 
| Chris@0 | 1322 bool | 
| Chris@918 | 1323 SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const | 
| Chris@0 | 1324 { | 
| Chris@907 | 1325     sv_frame_t modelStart = m_model->getStartFrame(); | 
| Chris@907 | 1326     sv_frame_t modelEnd = m_model->getEndFrame(); | 
| Chris@0 | 1327 | 
| Chris@0 | 1328     // Each pixel column covers an exact range of sample frames: | 
| Chris@907 | 1329     sv_frame_t f0 = v->getFrameForX(x) - modelStart; | 
| Chris@907 | 1330     sv_frame_t f1 = v->getFrameForX(x + 1) - modelStart - 1; | 
| Chris@20 | 1331 | 
| Chris@41 | 1332     if (f1 < int(modelStart) || f0 > int(modelEnd)) { | 
| Chris@41 | 1333 	return false; | 
| Chris@41 | 1334     } | 
| Chris@20 | 1335 | 
| Chris@0 | 1336     // And that range may be drawn from a possibly non-integral | 
| Chris@0 | 1337     // range of spectrogram windows: | 
| Chris@0 | 1338 | 
| Chris@805 | 1339     int windowIncrement = getWindowIncrement(); | 
| Chris@905 | 1340     s0 = double(f0) / windowIncrement; | 
| Chris@905 | 1341     s1 = double(f1) / windowIncrement; | 
| Chris@0 | 1342 | 
| Chris@0 | 1343     return true; | 
| Chris@0 | 1344 } | 
| Chris@0 | 1345 | 
| Chris@0 | 1346 bool | 
| Chris@918 | 1347 SpectrogramLayer::getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &min, RealTime &max) const | 
| Chris@0 | 1348 { | 
| Chris@905 | 1349     double s0 = 0, s1 = 0; | 
| Chris@44 | 1350     if (!getXBinRange(v, x, s0, s1)) return false; | 
| Chris@0 | 1351 | 
| Chris@0 | 1352     int s0i = int(s0 + 0.001); | 
| Chris@0 | 1353     int s1i = int(s1); | 
| Chris@0 | 1354 | 
| Chris@0 | 1355     int windowIncrement = getWindowIncrement(); | 
| Chris@0 | 1356     int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2; | 
| Chris@0 | 1357     int w1 = s1i * windowIncrement + windowIncrement + | 
| Chris@0 | 1358 	(m_windowSize - windowIncrement)/2 - 1; | 
| Chris@0 | 1359 | 
| Chris@0 | 1360     min = RealTime::frame2RealTime(w0, m_model->getSampleRate()); | 
| Chris@0 | 1361     max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); | 
| Chris@0 | 1362     return true; | 
| Chris@0 | 1363 } | 
| Chris@0 | 1364 | 
| Chris@0 | 1365 bool | 
| Chris@918 | 1366 SpectrogramLayer::getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) | 
| Chris@0 | 1367 const | 
| Chris@0 | 1368 { | 
| Chris@905 | 1369     double q0 = 0, q1 = 0; | 
| Chris@44 | 1370     if (!getYBinRange(v, y, q0, q1)) return false; | 
| Chris@0 | 1371 | 
| Chris@0 | 1372     int q0i = int(q0 + 0.001); | 
| Chris@0 | 1373     int q1i = int(q1); | 
| Chris@0 | 1374 | 
| Chris@907 | 1375     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@0 | 1376 | 
| Chris@0 | 1377     for (int q = q0i; q <= q1i; ++q) { | 
| Chris@121 | 1378 	if (q == q0i) freqMin = (sr * q) / m_fftSize; | 
| Chris@121 | 1379 	if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize; | 
| Chris@0 | 1380     } | 
| Chris@0 | 1381     return true; | 
| Chris@0 | 1382 } | 
| Chris@35 | 1383 | 
| Chris@35 | 1384 bool | 
| Chris@918 | 1385 SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, | 
| Chris@905 | 1386 					     double &freqMin, double &freqMax, | 
| Chris@905 | 1387 					     double &adjFreqMin, double &adjFreqMax) | 
| Chris@35 | 1388 const | 
| Chris@35 | 1389 { | 
| Chris@277 | 1390     if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 
| Chris@277 | 1391 	return false; | 
| Chris@277 | 1392     } | 
| Chris@277 | 1393 | 
| Chris@130 | 1394     FFTModel *fft = getFFTModel(v); | 
| Chris@114 | 1395     if (!fft) return false; | 
| Chris@110 | 1396 | 
| Chris@905 | 1397     double s0 = 0, s1 = 0; | 
| Chris@44 | 1398     if (!getXBinRange(v, x, s0, s1)) return false; | 
| Chris@35 | 1399 | 
| Chris@905 | 1400     double q0 = 0, q1 = 0; | 
| Chris@44 | 1401     if (!getYBinRange(v, y, q0, q1)) return false; | 
| Chris@35 | 1402 | 
| Chris@35 | 1403     int s0i = int(s0 + 0.001); | 
| Chris@35 | 1404     int s1i = int(s1); | 
| Chris@35 | 1405 | 
| Chris@35 | 1406     int q0i = int(q0 + 0.001); | 
| Chris@35 | 1407     int q1i = int(q1); | 
| Chris@35 | 1408 | 
| Chris@907 | 1409     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@35 | 1410 | 
| Chris@35 | 1411     bool haveAdj = false; | 
| Chris@35 | 1412 | 
| Chris@37 | 1413     bool peaksOnly = (m_binDisplay == PeakBins || | 
| Chris@37 | 1414 		      m_binDisplay == PeakFrequencies); | 
| Chris@37 | 1415 | 
| Chris@35 | 1416     for (int q = q0i; q <= q1i; ++q) { | 
| Chris@35 | 1417 | 
| Chris@35 | 1418 	for (int s = s0i; s <= s1i; ++s) { | 
| Chris@35 | 1419 | 
| Chris@160 | 1420             if (!fft->isColumnAvailable(s)) continue; | 
| Chris@117 | 1421 | 
| Chris@905 | 1422 	    double binfreq = (double(sr) * q) / m_windowSize; | 
| Chris@35 | 1423 	    if (q == q0i) freqMin = binfreq; | 
| Chris@35 | 1424 	    if (q == q1i) freqMax = binfreq; | 
| Chris@37 | 1425 | 
| Chris@114 | 1426 	    if (peaksOnly && !fft->isLocalPeak(s, q)) continue; | 
| Chris@38 | 1427 | 
| Chris@907 | 1428 	    if (!fft->isOverThreshold(s, q, float(m_threshold * double(m_fftSize)/2.0))) continue; | 
| Chris@907 | 1429 | 
| Chris@907 | 1430             double freq = binfreq; | 
| Chris@40 | 1431 | 
| Chris@114 | 1432 	    if (s < int(fft->getWidth()) - 1) { | 
| Chris@38 | 1433 | 
| Chris@277 | 1434                 fft->estimateStableFrequency(s, q, freq); | 
| Chris@35 | 1435 | 
| Chris@38 | 1436 		if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; | 
| Chris@38 | 1437 		if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; | 
| Chris@35 | 1438 | 
| Chris@35 | 1439 		haveAdj = true; | 
| Chris@35 | 1440 	    } | 
| Chris@35 | 1441 	} | 
| Chris@35 | 1442     } | 
| Chris@35 | 1443 | 
| Chris@35 | 1444     if (!haveAdj) { | 
| Chris@40 | 1445 	adjFreqMin = adjFreqMax = 0.0; | 
| Chris@35 | 1446     } | 
| Chris@35 | 1447 | 
| Chris@35 | 1448     return haveAdj; | 
| Chris@35 | 1449 } | 
| Chris@0 | 1450 | 
| Chris@0 | 1451 bool | 
| Chris@918 | 1452 SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, | 
| Chris@905 | 1453 				      double &min, double &max, | 
| Chris@905 | 1454 				      double &phaseMin, double &phaseMax) const | 
| Chris@0 | 1455 { | 
| Chris@277 | 1456     if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 
| Chris@277 | 1457 	return false; | 
| Chris@277 | 1458     } | 
| Chris@277 | 1459 | 
| Chris@905 | 1460     double q0 = 0, q1 = 0; | 
| Chris@44 | 1461     if (!getYBinRange(v, y, q0, q1)) return false; | 
| Chris@0 | 1462 | 
| Chris@905 | 1463     double s0 = 0, s1 = 0; | 
| Chris@44 | 1464     if (!getXBinRange(v, x, s0, s1)) return false; | 
| Chris@0 | 1465 | 
| Chris@0 | 1466     int q0i = int(q0 + 0.001); | 
| Chris@0 | 1467     int q1i = int(q1); | 
| Chris@0 | 1468 | 
| Chris@0 | 1469     int s0i = int(s0 + 0.001); | 
| Chris@0 | 1470     int s1i = int(s1); | 
| Chris@0 | 1471 | 
| Chris@37 | 1472     bool rv = false; | 
| Chris@37 | 1473 | 
| Chris@805 | 1474     int zp = getZeroPadLevel(v); | 
| Chris@122 | 1475     q0i *= zp + 1; | 
| Chris@122 | 1476     q1i *= zp + 1; | 
| Chris@122 | 1477 | 
| Chris@130 | 1478     FFTModel *fft = getFFTModel(v); | 
| Chris@0 | 1479 | 
| Chris@114 | 1480     if (fft) { | 
| Chris@114 | 1481 | 
| Chris@114 | 1482         int cw = fft->getWidth(); | 
| Chris@114 | 1483         int ch = fft->getHeight(); | 
| Chris@0 | 1484 | 
| Chris@110 | 1485         min = 0.0; | 
| Chris@110 | 1486         max = 0.0; | 
| Chris@110 | 1487         phaseMin = 0.0; | 
| Chris@110 | 1488         phaseMax = 0.0; | 
| Chris@110 | 1489         bool have = false; | 
| Chris@0 | 1490 | 
| Chris@110 | 1491         for (int q = q0i; q <= q1i; ++q) { | 
| Chris@110 | 1492             for (int s = s0i; s <= s1i; ++s) { | 
| Chris@110 | 1493                 if (s >= 0 && q >= 0 && s < cw && q < ch) { | 
| Chris@117 | 1494 | 
| Chris@160 | 1495                     if (!fft->isColumnAvailable(s)) continue; | 
| Chris@110 | 1496 | 
| Chris@905 | 1497                     double value; | 
| Chris@38 | 1498 | 
| Chris@114 | 1499                     value = fft->getPhaseAt(s, q); | 
| Chris@110 | 1500                     if (!have || value < phaseMin) { phaseMin = value; } | 
| Chris@110 | 1501                     if (!have || value > phaseMax) { phaseMax = value; } | 
| Chris@91 | 1502 | 
| Chris@907 | 1503                     value = fft->getMagnitudeAt(s, q) / (m_fftSize/2.0); | 
| Chris@110 | 1504                     if (!have || value < min) { min = value; } | 
| Chris@110 | 1505                     if (!have || value > max) { max = value; } | 
| Chris@110 | 1506 | 
| Chris@110 | 1507                     have = true; | 
| Chris@110 | 1508                 } | 
| Chris@110 | 1509             } | 
| Chris@110 | 1510         } | 
| Chris@110 | 1511 | 
| Chris@110 | 1512         if (have) { | 
| Chris@110 | 1513             rv = true; | 
| Chris@110 | 1514         } | 
| Chris@0 | 1515     } | 
| Chris@0 | 1516 | 
| Chris@37 | 1517     return rv; | 
| Chris@0 | 1518 } | 
| Chris@0 | 1519 | 
| Chris@805 | 1520 int | 
| Chris@918 | 1521 SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const | 
| Chris@114 | 1522 { | 
| Chris@114 | 1523     //!!! tidy all this stuff | 
| Chris@114 | 1524 | 
| Chris@114 | 1525     if (m_binDisplay != AllBins) return 0; | 
| Chris@221 | 1526 | 
| Chris@221 | 1527     Preferences::SpectrogramSmoothing smoothing = | 
| Chris@221 | 1528         Preferences::getInstance()->getSpectrogramSmoothing(); | 
| Chris@221 | 1529 | 
| Chris@221 | 1530     if (smoothing == Preferences::NoSpectrogramSmoothing || | 
| Chris@221 | 1531         smoothing == Preferences::SpectrogramInterpolated) return 0; | 
| Chris@221 | 1532 | 
| Chris@114 | 1533     if (m_frequencyScale == LogFrequencyScale) return 3; | 
| Chris@114 | 1534 | 
| Chris@907 | 1535     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@114 | 1536 | 
| Chris@805 | 1537     int maxbin = m_fftSize / 2; | 
| Chris@114 | 1538     if (m_maxFrequency > 0) { | 
| Chris@184 | 1539 	maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); | 
| Chris@184 | 1540 	if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; | 
| Chris@114 | 1541     } | 
| Chris@114 | 1542 | 
| Chris@805 | 1543     int minbin = 1; | 
| Chris@114 | 1544     if (m_minFrequency > 0) { | 
| Chris@114 | 1545 	minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1); | 
| Chris@114 | 1546 	if (minbin < 1) minbin = 1; | 
| Chris@184 | 1547 	if (minbin >= maxbin) minbin = maxbin - 1; | 
| Chris@114 | 1548     } | 
| Chris@114 | 1549 | 
| Chris@905 | 1550     double perPixel = | 
| Chris@918 | 1551         double(v->getPaintHeight()) / | 
| Chris@905 | 1552         double((maxbin - minbin) / (m_zeroPadLevel + 1)); | 
| Chris@118 | 1553 | 
| Chris@118 | 1554     if (perPixel > 2.8) { | 
| Chris@118 | 1555         return 3; // 4x oversampling | 
| Chris@118 | 1556     } else if (perPixel > 1.5) { | 
| Chris@118 | 1557         return 1; // 2x | 
| Chris@114 | 1558     } else { | 
| Chris@118 | 1559         return 0; // 1x | 
| Chris@114 | 1560     } | 
| Chris@114 | 1561 } | 
| Chris@114 | 1562 | 
| Chris@805 | 1563 int | 
| Chris@918 | 1564 SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const | 
| Chris@114 | 1565 { | 
| Chris@114 | 1566     return m_fftSize * (getZeroPadLevel(v) + 1); | 
| Chris@114 | 1567 } | 
| Chris@114 | 1568 | 
| Chris@130 | 1569 FFTModel * | 
| Chris@918 | 1570 SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const | 
| Chris@114 | 1571 { | 
| Chris@114 | 1572     if (!m_model) return 0; | 
| Chris@114 | 1573 | 
| Chris@805 | 1574     int fftSize = getFFTSize(v); | 
| Chris@114 | 1575 | 
| Chris@920 | 1576     const View *view = v->getView(); | 
| Chris@920 | 1577 | 
| Chris@920 | 1578     if (m_fftModels.find(view) != m_fftModels.end()) { | 
| Chris@920 | 1579         if (m_fftModels[view].first == 0) { | 
| Chris@184 | 1580 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1581             SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; | 
| Chris@184 | 1582 #endif | 
| Chris@184 | 1583             return 0; | 
| Chris@184 | 1584         } | 
| Chris@920 | 1585         if (m_fftModels[view].first->getHeight() != fftSize / 2 + 1) { | 
| Chris@184 | 1586 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@920 | 1587             SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; | 
| Chris@184 | 1588 #endif | 
| Chris@920 | 1589             delete m_fftModels[view].first; | 
| Chris@920 | 1590             m_fftModels.erase(view); | 
| Chris@920 | 1591             delete m_peakCaches[view]; | 
| Chris@920 | 1592             m_peakCaches.erase(view); | 
| Chris@184 | 1593         } else { | 
| Chris@184 | 1594 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@920 | 1595             SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view].first->getHeight() << endl; | 
| Chris@184 | 1596 #endif | 
| Chris@920 | 1597             return m_fftModels[view].first; | 
| Chris@114 | 1598         } | 
| Chris@114 | 1599     } | 
| Chris@114 | 1600 | 
| Chris@920 | 1601     if (m_fftModels.find(view) == m_fftModels.end()) { | 
| Chris@169 | 1602 | 
| Chris@169 | 1603         FFTModel *model = new FFTModel(m_model, | 
| Chris@169 | 1604                                        m_channel, | 
| Chris@169 | 1605                                        m_windowType, | 
| Chris@169 | 1606                                        m_windowSize, | 
| Chris@169 | 1607                                        getWindowIncrement(), | 
| Chris@975 | 1608                                        fftSize); | 
| Chris@169 | 1609 | 
| Chris@178 | 1610         if (!model->isOK()) { | 
| Chris@178 | 1611             QMessageBox::critical | 
| Chris@178 | 1612                 (0, tr("FFT cache failed"), | 
| Chris@178 | 1613                  tr("Failed to create the FFT model for this spectrogram.\n" | 
| Chris@178 | 1614                     "There may be insufficient memory or disc space to continue.")); | 
| Chris@178 | 1615             delete model; | 
| Chris@920 | 1616             m_fftModels[view] = FFTFillPair(0, 0); | 
| Chris@178 | 1617             return 0; | 
| Chris@178 | 1618         } | 
| Chris@178 | 1619 | 
| Chris@193 | 1620         if (!m_sliceableModel) { | 
| Chris@248 | 1621 #ifdef DEBUG_SPECTROGRAM | 
| Chris@682 | 1622             cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << endl; | 
| Chris@248 | 1623 #endif | 
| Chris@193 | 1624             ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); | 
| Chris@193 | 1625             m_sliceableModel = model; | 
| Chris@193 | 1626         } | 
| Chris@193 | 1627 | 
| Chris@920 | 1628         m_fftModels[view] = FFTFillPair(model, 0); | 
| Chris@849 | 1629 | 
| Chris@114 | 1630         delete m_updateTimer; | 
| Chris@114 | 1631         m_updateTimer = new QTimer((SpectrogramLayer *)this); | 
| Chris@114 | 1632         connect(m_updateTimer, SIGNAL(timeout()), | 
| Chris@114 | 1633                 this, SLOT(fillTimerTimedOut())); | 
| Chris@114 | 1634         m_updateTimer->start(200); | 
| Chris@114 | 1635     } | 
| Chris@114 | 1636 | 
| Chris@920 | 1637     return m_fftModels[view].first; | 
| Chris@114 | 1638 } | 
| Chris@114 | 1639 | 
| Chris@484 | 1640 Dense3DModelPeakCache * | 
| Chris@918 | 1641 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const | 
| Chris@484 | 1642 { | 
| Chris@920 | 1643     const View *view = v->getView(); | 
| Chris@920 | 1644     if (!m_peakCaches[view]) { | 
| Chris@484 | 1645         FFTModel *f = getFFTModel(v); | 
| Chris@484 | 1646         if (!f) return 0; | 
| Chris@920 | 1647         m_peakCaches[view] = new Dense3DModelPeakCache(f, 8); | 
| Chris@484 | 1648     } | 
| Chris@920 | 1649     return m_peakCaches[view]; | 
| Chris@484 | 1650 } | 
| Chris@484 | 1651 | 
| Chris@193 | 1652 const Model * | 
| Chris@193 | 1653 SpectrogramLayer::getSliceableModel() const | 
| Chris@193 | 1654 { | 
| Chris@193 | 1655     if (m_sliceableModel) return m_sliceableModel; | 
| Chris@193 | 1656     if (m_fftModels.empty()) return 0; | 
| Chris@193 | 1657     m_sliceableModel = m_fftModels.begin()->second.first; | 
| Chris@193 | 1658     return m_sliceableModel; | 
| Chris@193 | 1659 } | 
| Chris@193 | 1660 | 
| Chris@114 | 1661 void | 
| Chris@130 | 1662 SpectrogramLayer::invalidateFFTModels() | 
| Chris@114 | 1663 { | 
| Chris@130 | 1664     for (ViewFFTMap::iterator i = m_fftModels.begin(); | 
| Chris@130 | 1665          i != m_fftModels.end(); ++i) { | 
| Chris@115 | 1666         delete i->second.first; | 
| Chris@114 | 1667     } | 
| Chris@486 | 1668     for (PeakCacheMap::iterator i = m_peakCaches.begin(); | 
| Chris@486 | 1669          i != m_peakCaches.end(); ++i) { | 
| Chris@486 | 1670         delete i->second; | 
| Chris@486 | 1671     } | 
| Chris@114 | 1672 | 
| Chris@130 | 1673     m_fftModels.clear(); | 
| Chris@486 | 1674     m_peakCaches.clear(); | 
| Chris@193 | 1675 | 
| Chris@193 | 1676     if (m_sliceableModel) { | 
| Chris@682 | 1677         cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << endl; | 
| Chris@193 | 1678         emit sliceableModelReplaced(m_sliceableModel, 0); | 
| Chris@193 | 1679         m_sliceableModel = 0; | 
| Chris@193 | 1680     } | 
| Chris@114 | 1681 } | 
| Chris@114 | 1682 | 
| Chris@0 | 1683 void | 
| Chris@119 | 1684 SpectrogramLayer::invalidateMagnitudes() | 
| Chris@119 | 1685 { | 
| Chris@119 | 1686     m_viewMags.clear(); | 
| Chris@119 | 1687     for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin(); | 
| Chris@119 | 1688          i != m_columnMags.end(); ++i) { | 
| Chris@119 | 1689         *i = MagnitudeRange(); | 
| Chris@119 | 1690     } | 
| Chris@119 | 1691 } | 
| Chris@119 | 1692 | 
| Chris@119 | 1693 bool | 
| Chris@918 | 1694 SpectrogramLayer::updateViewMagnitudes(LayerGeometryProvider *v) const | 
| Chris@119 | 1695 { | 
| Chris@119 | 1696     MagnitudeRange mag; | 
| Chris@119 | 1697 | 
| Chris@918 | 1698     int x0 = 0, x1 = v->getPaintWidth(); | 
| Chris@905 | 1699     double s00 = 0, s01 = 0, s10 = 0, s11 = 0; | 
| Chris@119 | 1700 | 
| Chris@203 | 1701     if (!getXBinRange(v, x0, s00, s01)) { | 
| Chris@907 | 1702         s00 = s01 = double(m_model->getStartFrame()) / getWindowIncrement(); | 
| Chris@203 | 1703     } | 
| Chris@203 | 1704 | 
| Chris@203 | 1705     if (!getXBinRange(v, x1, s10, s11)) { | 
| Chris@907 | 1706         s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); | 
| Chris@203 | 1707     } | 
| Chris@119 | 1708 | 
| Chris@119 | 1709     int s0 = int(std::min(s00, s10) + 0.0001); | 
| Chris@203 | 1710     int s1 = int(std::max(s01, s11) + 0.0001); | 
| Chris@203 | 1711 | 
| Chris@587 | 1712 //    SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; | 
| Chris@119 | 1713 | 
| Chris@248 | 1714     if (int(m_columnMags.size()) <= s1) { | 
| Chris@119 | 1715         m_columnMags.resize(s1 + 1); | 
| Chris@119 | 1716     } | 
| Chris@119 | 1717 | 
| Chris@119 | 1718     for (int s = s0; s <= s1; ++s) { | 
| Chris@119 | 1719         if (m_columnMags[s].isSet()) { | 
| Chris@119 | 1720             mag.sample(m_columnMags[s]); | 
| Chris@119 | 1721         } | 
| Chris@119 | 1722     } | 
| Chris@119 | 1723 | 
| Chris@184 | 1724 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1725     SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols " | 
| Chris@585 | 1726               << s0 << " -> " << s1 << " inclusive" << endl; | 
| Chris@184 | 1727 #endif | 
| Chris@119 | 1728 | 
| Chris@119 | 1729     if (!mag.isSet()) return false; | 
| Chris@119 | 1730     if (mag == m_viewMags[v]) return false; | 
| Chris@119 | 1731     m_viewMags[v] = mag; | 
| Chris@119 | 1732     return true; | 
| Chris@119 | 1733 } | 
| Chris@119 | 1734 | 
| Chris@119 | 1735 void | 
| Chris@389 | 1736 SpectrogramLayer::setSynchronousPainting(bool synchronous) | 
| Chris@389 | 1737 { | 
| Chris@389 | 1738     m_synchronous = synchronous; | 
| Chris@389 | 1739 } | 
| Chris@389 | 1740 | 
| Chris@389 | 1741 void | 
| Chris@916 | 1742 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const | 
| Chris@0 | 1743 { | 
| Chris@253 | 1744     // What a lovely, old-fashioned function this is. | 
| Chris@253 | 1745     // It's practically FORTRAN 77 in its clarity and linearity. | 
| Chris@253 | 1746 | 
| Chris@334 | 1747     Profiler profiler("SpectrogramLayer::paint", false); | 
| Chris@334 | 1748 | 
| Chris@0 | 1749 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1750     SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl; | 
| Chris@95 | 1751 | 
| Chris@682 | 1752     cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; | 
| Chris@0 | 1753 #endif | 
| Chris@95 | 1754 | 
| Chris@907 | 1755     sv_frame_t startFrame = v->getStartFrame(); | 
| Chris@133 | 1756     if (startFrame < 0) m_candidateFillStartFrame = 0; | 
| Chris@133 | 1757     else m_candidateFillStartFrame = startFrame; | 
| Chris@44 | 1758 | 
| Chris@0 | 1759     if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 
| Chris@0 | 1760 	return; | 
| Chris@0 | 1761     } | 
| Chris@0 | 1762 | 
| Chris@47 | 1763     if (isLayerDormant(v)) { | 
| Chris@587 | 1764 	SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; | 
| Chris@29 | 1765     } | 
| Chris@29 | 1766 | 
| Chris@48 | 1767     // Need to do this even if !isLayerDormant, as that could mean v | 
| Chris@48 | 1768     // is not in the dormancy map at all -- we need it to be present | 
| Chris@48 | 1769     // and accountable for when determining whether we need the cache | 
| Chris@48 | 1770     // in the cache-fill thread above. | 
| Chris@806 | 1771     //!!! no inter use cache-fill thread | 
| Chris@131 | 1772     const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); | 
| Chris@48 | 1773 | 
| Chris@805 | 1774     int fftSize = getFFTSize(v); | 
| Chris@484 | 1775 /* | 
| Chris@130 | 1776     FFTModel *fft = getFFTModel(v); | 
| Chris@114 | 1777     if (!fft) { | 
| Chris@682 | 1778 	cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl; | 
| Chris@0 | 1779 	return; | 
| Chris@0 | 1780     } | 
| Chris@484 | 1781 */ | 
| Chris@920 | 1782 | 
| Chris@920 | 1783     const View *view = v->getView(); | 
| Chris@920 | 1784 | 
| Chris@920 | 1785     ImageCache &cache = m_imageCaches[view]; | 
| Chris@95 | 1786 | 
| Chris@95 | 1787 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 1788     SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache. | 
| Chris@585 | 1789 | 
| Chris@585 | 1790 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl; | 
| Chris@95 | 1791 #endif | 
| Chris@95 | 1792 | 
| Chris@248 | 1793 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@0 | 1794     bool stillCacheing = (m_updateTimer != 0); | 
| Chris@587 | 1795     SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl; | 
| Chris@0 | 1796 #endif | 
| Chris@0 | 1797 | 
| Chris@44 | 1798     int zoomLevel = v->getZoomLevel(); | 
| Chris@0 | 1799 | 
| Chris@0 | 1800     int x0 = 0; | 
| Chris@918 | 1801     int x1 = v->getPaintWidth(); | 
| Chris@0 | 1802 | 
| Chris@478 | 1803     bool recreateWholeImageCache = true; | 
| Chris@0 | 1804 | 
| Chris@95 | 1805     x0 = rect.left(); | 
| Chris@95 | 1806     x1 = rect.right() + 1; | 
| Chris@481 | 1807 /* | 
| Chris@905 | 1808     double xPixelRatio = double(fft->getResolution()) / double(zoomLevel); | 
| Chris@682 | 1809     cerr << "xPixelRatio = " << xPixelRatio << endl; | 
| Chris@479 | 1810     if (xPixelRatio < 1.f) xPixelRatio = 1.f; | 
| Chris@481 | 1811 */ | 
| Chris@95 | 1812     if (cache.validArea.width() > 0) { | 
| Chris@95 | 1813 | 
| Chris@482 | 1814         int cw = cache.image.width(); | 
| Chris@482 | 1815         int ch = cache.image.height(); | 
| Chris@482 | 1816 | 
| Chris@95 | 1817 	if (int(cache.zoomLevel) == zoomLevel && | 
| Chris@918 | 1818 	    cw == v->getPaintWidth() && | 
| Chris@918 | 1819 	    ch == v->getPaintHeight()) { | 
| Chris@95 | 1820 | 
| Chris@95 | 1821 	    if (v->getXForFrame(cache.startFrame) == | 
| Chris@95 | 1822 		v->getXForFrame(startFrame) && | 
| Chris@95 | 1823                 cache.validArea.x() <= x0 && | 
| Chris@95 | 1824                 cache.validArea.x() + cache.validArea.width() >= x1) { | 
| Chris@0 | 1825 | 
| Chris@0 | 1826 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1827 		cerr << "SpectrogramLayer: image cache good" << endl; | 
| Chris@0 | 1828 #endif | 
| Chris@0 | 1829 | 
| Chris@478 | 1830 		paint.drawImage(rect, cache.image, rect); | 
| Chris@479 | 1831                 //!!! | 
| Chris@479 | 1832 //                paint.drawImage(v->rect(), cache.image, | 
| Chris@479 | 1833 //                                QRect(QPoint(0, 0), cache.image.size())); | 
| Chris@479 | 1834 | 
| Chris@121 | 1835                 illuminateLocalFeatures(v, paint); | 
| Chris@0 | 1836 		return; | 
| Chris@0 | 1837 | 
| Chris@0 | 1838 	    } else { | 
| Chris@0 | 1839 | 
| Chris@0 | 1840 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1841 		cerr << "SpectrogramLayer: image cache partially OK" << endl; | 
| Chris@0 | 1842 #endif | 
| Chris@0 | 1843 | 
| Chris@478 | 1844 		recreateWholeImageCache = false; | 
| Chris@0 | 1845 | 
| Chris@95 | 1846 		int dx = v->getXForFrame(cache.startFrame) - | 
| Chris@44 | 1847 		         v->getXForFrame(startFrame); | 
| Chris@0 | 1848 | 
| Chris@0 | 1849 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1850 		cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl; | 
| Chris@0 | 1851 #endif | 
| Chris@0 | 1852 | 
| Chris@95 | 1853 		if (dx != 0 && | 
| Chris@482 | 1854                     dx > -cw && | 
| Chris@482 | 1855                     dx <  cw) { | 
| Chris@482 | 1856 | 
| Chris@482 | 1857                     int dxp = dx; | 
| Chris@482 | 1858                     if (dxp < 0) dxp = -dxp; | 
| Chris@907 | 1859                     size_t copy = (cw - dxp) * sizeof(QRgb); | 
| Chris@482 | 1860                     for (int y = 0; y < ch; ++y) { | 
| Chris@482 | 1861                         QRgb *line = (QRgb *)cache.image.scanLine(y); | 
| Chris@482 | 1862                         if (dx < 0) { | 
| Chris@482 | 1863                             memmove(line, line + dxp, copy); | 
| Chris@482 | 1864                         } else { | 
| Chris@482 | 1865                             memmove(line + dxp, line, copy); | 
| Chris@482 | 1866                         } | 
| Chris@331 | 1867                     } | 
| Chris@0 | 1868 | 
| Chris@95 | 1869                     int px = cache.validArea.x(); | 
| Chris@95 | 1870                     int pw = cache.validArea.width(); | 
| Chris@0 | 1871 | 
| Chris@0 | 1872 		    if (dx < 0) { | 
| Chris@482 | 1873 			x0 = cw + dx; | 
| Chris@482 | 1874 			x1 = cw; | 
| Chris@95 | 1875                         px += dx; | 
| Chris@95 | 1876                         if (px < 0) { | 
| Chris@95 | 1877                             pw += px; | 
| Chris@95 | 1878                             px = 0; | 
| Chris@95 | 1879                             if (pw < 0) pw = 0; | 
| Chris@95 | 1880                         } | 
| Chris@0 | 1881 		    } else { | 
| Chris@0 | 1882 			x0 = 0; | 
| Chris@0 | 1883 			x1 = dx; | 
| Chris@95 | 1884                         px += dx; | 
| Chris@482 | 1885                         if (px + pw > cw) { | 
| Chris@482 | 1886                             pw = int(cw) - px; | 
| Chris@95 | 1887                             if (pw < 0) pw = 0; | 
| Chris@95 | 1888                         } | 
| Chris@0 | 1889 		    } | 
| Chris@95 | 1890 | 
| Chris@95 | 1891                     cache.validArea = | 
| Chris@95 | 1892                         QRect(px, cache.validArea.y(), | 
| Chris@95 | 1893                               pw, cache.validArea.height()); | 
| Chris@95 | 1894 | 
| Chris@331 | 1895 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1896                     cerr << "valid area now " | 
| Chris@331 | 1897                               << px << "," << cache.validArea.y() | 
| Chris@331 | 1898                               << " " << pw << "x" << cache.validArea.height() | 
| Chris@682 | 1899                               << endl; | 
| Chris@331 | 1900 #endif | 
| Chris@479 | 1901 /* | 
| Chris@478 | 1902 		    paint.drawImage(rect & cache.validArea, | 
| Chris@478 | 1903                                      cache.image, | 
| Chris@95 | 1904                                      rect & cache.validArea); | 
| Chris@479 | 1905 */ | 
| Chris@331 | 1906                 } else if (dx != 0) { | 
| Chris@331 | 1907 | 
| Chris@331 | 1908                     // we scrolled too far to be of use | 
| Chris@331 | 1909 | 
| Chris@391 | 1910 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1911                     cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl; | 
| Chris@391 | 1912 #endif | 
| Chris@391 | 1913 | 
| Chris@331 | 1914                     cache.validArea = QRect(); | 
| Chris@478 | 1915                     recreateWholeImageCache = true; | 
| Chris@331 | 1916                 } | 
| Chris@0 | 1917 	    } | 
| Chris@0 | 1918 	} else { | 
| Chris@0 | 1919 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1920 	    cerr << "SpectrogramLayer: image cache useless" << endl; | 
| Chris@224 | 1921             if (int(cache.zoomLevel) != zoomLevel) { | 
| Chris@682 | 1922                 cerr << "(cache zoomLevel " << cache.zoomLevel | 
| Chris@682 | 1923                           << " != " << zoomLevel << ")" << endl; | 
| Chris@224 | 1924             } | 
| Chris@918 | 1925             if (cw != v->getPaintWidth()) { | 
| Chris@682 | 1926                 cerr << "(cache width " << cw | 
| Chris@918 | 1927                           << " != " << v->getPaintWidth(); | 
| Chris@224 | 1928             } | 
| Chris@918 | 1929             if (ch != v->getPaintHeight()) { | 
| Chris@682 | 1930                 cerr << "(cache height " << ch | 
| Chris@918 | 1931                           << " != " << v->getPaintHeight(); | 
| Chris@224 | 1932             } | 
| Chris@0 | 1933 #endif | 
| Chris@95 | 1934             cache.validArea = QRect(); | 
| Chris@478 | 1935 //            recreateWholeImageCache = true; | 
| Chris@0 | 1936 	} | 
| Chris@0 | 1937     } | 
| Chris@95 | 1938 | 
| Chris@133 | 1939     if (updateViewMagnitudes(v)) { | 
| Chris@184 | 1940 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1941         cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | 
| Chris@184 | 1942 #endif | 
| Chris@862 | 1943         if (m_normalization == NormalizeVisibleArea) { | 
| Chris@331 | 1944             cache.validArea = QRect(); | 
| Chris@478 | 1945             recreateWholeImageCache = true; | 
| Chris@331 | 1946         } | 
| Chris@133 | 1947     } else { | 
| Chris@184 | 1948 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1949         cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | 
| Chris@184 | 1950 #endif | 
| Chris@133 | 1951     } | 
| Chris@133 | 1952 | 
| Chris@478 | 1953     if (recreateWholeImageCache) { | 
| Chris@95 | 1954         x0 = 0; | 
| Chris@918 | 1955         x1 = v->getPaintWidth(); | 
| Chris@95 | 1956     } | 
| Chris@95 | 1957 | 
| Chris@215 | 1958     struct timeval tv; | 
| Chris@215 | 1959     (void)gettimeofday(&tv, 0); | 
| Chris@215 | 1960     RealTime mainPaintStart = RealTime::fromTimeval(tv); | 
| Chris@215 | 1961 | 
| Chris@215 | 1962     int paintBlockWidth = m_lastPaintBlockWidth; | 
| Chris@215 | 1963 | 
| Chris@389 | 1964     if (m_synchronous) { | 
| Chris@389 | 1965         if (paintBlockWidth < x1 - x0) { | 
| Chris@389 | 1966             // always paint full width | 
| Chris@389 | 1967             paintBlockWidth = x1 - x0; | 
| Chris@389 | 1968         } | 
| Chris@215 | 1969     } else { | 
| Chris@389 | 1970         if (paintBlockWidth == 0) { | 
| Chris@389 | 1971             paintBlockWidth = (300000 / zoomLevel); | 
| Chris@389 | 1972         } else { | 
| Chris@389 | 1973             RealTime lastTime = m_lastPaintTime; | 
| Chris@389 | 1974             while (lastTime > RealTime::fromMilliseconds(200) && | 
| Chris@389 | 1975                    paintBlockWidth > 50) { | 
| Chris@389 | 1976                 paintBlockWidth /= 2; | 
| Chris@389 | 1977                 lastTime = lastTime / 2; | 
| Chris@389 | 1978             } | 
| Chris@389 | 1979             while (lastTime < RealTime::fromMilliseconds(90) && | 
| Chris@389 | 1980                    paintBlockWidth < 1500) { | 
| Chris@389 | 1981                 paintBlockWidth *= 2; | 
| Chris@389 | 1982                 lastTime = lastTime * 2; | 
| Chris@389 | 1983             } | 
| Chris@215 | 1984         } | 
| Chris@389 | 1985 | 
| Chris@389 | 1986         if (paintBlockWidth < 20) paintBlockWidth = 20; | 
| Chris@215 | 1987     } | 
| Chris@215 | 1988 | 
| Chris@525 | 1989 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 1990     cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl; | 
| Chris@525 | 1991 #endif | 
| Chris@224 | 1992 | 
| Chris@224 | 1993     // We always paint the full height when refreshing the cache. | 
| Chris@224 | 1994     // Smaller heights can be used when painting direct from cache | 
| Chris@224 | 1995     // (further up in this function), but we want to ensure the cache | 
| Chris@224 | 1996     // is coherent without having to worry about vertical matching of | 
| Chris@224 | 1997     // required and valid areas as well as horizontal. | 
| Chris@224 | 1998 | 
| Chris@918 | 1999     int h = v->getPaintHeight(); | 
| Chris@215 | 2000 | 
| Chris@96 | 2001     if (cache.validArea.width() > 0) { | 
| Chris@96 | 2002 | 
| Chris@331 | 2003         // If part of the cache is known to be valid, select a strip | 
| Chris@331 | 2004         // immediately to left or right of the valid part | 
| Chris@331 | 2005 | 
| Chris@481 | 2006         //!!! this really needs to be coordinated with the selection | 
| Chris@481 | 2007         //!!! of m_drawBuffer boundaries in the bufferBinResolution | 
| Chris@481 | 2008         //!!! case below | 
| Chris@481 | 2009 | 
| Chris@96 | 2010         int vx0 = 0, vx1 = 0; | 
| Chris@96 | 2011         vx0 = cache.validArea.x(); | 
| Chris@96 | 2012         vx1 = cache.validArea.x() + cache.validArea.width(); | 
| Chris@96 | 2013 | 
| Chris@96 | 2014 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2015         cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl; | 
| Chris@331 | 2016 #endif | 
| Chris@96 | 2017         if (x0 < vx0) { | 
| Chris@96 | 2018             if (x0 + paintBlockWidth < vx0) { | 
| Chris@96 | 2019                 x0 = vx0 - paintBlockWidth; | 
| Chris@331 | 2020             } | 
| Chris@331 | 2021             x1 = vx0; | 
| Chris@331 | 2022         } else if (x0 >= vx1) { | 
| Chris@331 | 2023             x0 = vx1; | 
| Chris@331 | 2024             if (x1 > x0 + paintBlockWidth) { | 
| Chris@331 | 2025                 x1 = x0 + paintBlockWidth; | 
| Chris@331 | 2026             } | 
| Chris@331 | 2027         } else { | 
| Chris@331 | 2028             // x0 is within the valid area | 
| Chris@331 | 2029             if (x1 > vx1) { | 
| Chris@331 | 2030                 x0 = vx1; | 
| Chris@331 | 2031                 if (x0 + paintBlockWidth < x1) { | 
| Chris@331 | 2032                     x1 = x0 + paintBlockWidth; | 
| Chris@331 | 2033                 } | 
| Chris@96 | 2034             } else { | 
| Chris@331 | 2035                 x1 = x0; // it's all valid, paint nothing | 
| Chris@95 | 2036             } | 
| Chris@96 | 2037         } | 
| Chris@331 | 2038 | 
| Chris@96 | 2039         cache.validArea = QRect | 
| Chris@96 | 2040             (std::min(vx0, x0), cache.validArea.y(), | 
| Chris@96 | 2041              std::max(vx1 - std::min(vx0, x0), | 
| Chris@337 | 2042                        x1 - std::min(vx0, x0)), | 
| Chris@96 | 2043              cache.validArea.height()); | 
| Chris@337 | 2044 | 
| Chris@337 | 2045 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2046         cerr << "Valid area becomes " << cache.validArea.x() | 
| Chris@337 | 2047                   << ", " << cache.validArea.y() << ", " | 
| Chris@337 | 2048                   << cache.validArea.width() << "x" | 
| Chris@682 | 2049                   << cache.validArea.height() << endl; | 
| Chris@337 | 2050 #endif | 
| Chris@95 | 2051 | 
| Chris@96 | 2052     } else { | 
| Chris@96 | 2053         if (x1 > x0 + paintBlockWidth) { | 
| Chris@133 | 2054             int sfx = x1; | 
| Chris@133 | 2055             if (startFrame < 0) sfx = v->getXForFrame(0); | 
| Chris@133 | 2056             if (sfx >= x0 && sfx + paintBlockWidth <= x1) { | 
| Chris@133 | 2057                 x0 = sfx; | 
| Chris@133 | 2058                 x1 = x0 + paintBlockWidth; | 
| Chris@133 | 2059             } else { | 
| Chris@133 | 2060                 int mid = (x1 + x0) / 2; | 
| Chris@133 | 2061                 x0 = mid - paintBlockWidth/2; | 
| Chris@133 | 2062                 x1 = x0 + paintBlockWidth; | 
| Chris@133 | 2063             } | 
| Chris@95 | 2064         } | 
| Chris@337 | 2065 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2066         cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0) | 
| Chris@682 | 2067                   << "x" << h << endl; | 
| Chris@337 | 2068 #endif | 
| Chris@224 | 2069         cache.validArea = QRect(x0, 0, x1 - x0, h); | 
| Chris@95 | 2070     } | 
| Chris@95 | 2071 | 
| Chris@481 | 2072 /* | 
| Chris@480 | 2073     if (xPixelRatio != 1.f) { | 
| Chris@480 | 2074         x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001); | 
| Chris@480 | 2075         x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001); | 
| Chris@480 | 2076     } | 
| Chris@481 | 2077 */ | 
| Chris@0 | 2078     int w = x1 - x0; | 
| Chris@0 | 2079 | 
| Chris@95 | 2080 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2081     cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl; | 
| Chris@95 | 2082 #endif | 
| Chris@95 | 2083 | 
| Chris@907 | 2084     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@122 | 2085 | 
| Chris@122 | 2086     // Set minFreq and maxFreq to the frequency extents of the possibly | 
| Chris@122 | 2087     // zero-padded visible bin range, and displayMinFreq and displayMaxFreq | 
| Chris@122 | 2088     // to the actual scale frequency extents (presumably not zero padded). | 
| Chris@253 | 2089 | 
| Chris@253 | 2090     // If we are zero padding, we want to use the zero-padded | 
| Chris@253 | 2091     // equivalents of the bins that we would be using if not zero | 
| Chris@253 | 2092     // padded, to avoid spaces at the top and bottom of the display. | 
| Chris@253 | 2093 | 
| Chris@253 | 2094     // Note fftSize is the actual zero-padded fft size, m_fftSize the | 
| Chris@253 | 2095     // nominal fft size. | 
| Chris@35 | 2096 | 
| Chris@805 | 2097     int maxbin = m_fftSize / 2; | 
| Chris@35 | 2098     if (m_maxFrequency > 0) { | 
| Chris@253 | 2099 	maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001); | 
| Chris@253 | 2100 	if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; | 
| Chris@35 | 2101     } | 
| Chris@111 | 2102 | 
| Chris@805 | 2103     int minbin = 1; | 
| Chris@37 | 2104     if (m_minFrequency > 0) { | 
| Chris@253 | 2105 	minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001); | 
| Chris@682 | 2106 //        cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl; | 
| Chris@40 | 2107 	if (minbin < 1) minbin = 1; | 
| Chris@184 | 2108 	if (minbin >= maxbin) minbin = maxbin - 1; | 
| Chris@37 | 2109     } | 
| Chris@37 | 2110 | 
| Chris@253 | 2111     int zpl = getZeroPadLevel(v) + 1; | 
| Chris@253 | 2112     minbin = minbin * zpl; | 
| Chris@253 | 2113     maxbin = (maxbin + 1) * zpl - 1; | 
| Chris@253 | 2114 | 
| Chris@905 | 2115     double minFreq = (double(minbin) * sr) / fftSize; | 
| Chris@905 | 2116     double maxFreq = (double(maxbin) * sr) / fftSize; | 
| Chris@905 | 2117 | 
| Chris@905 | 2118     double displayMinFreq = minFreq; | 
| Chris@905 | 2119     double displayMaxFreq = maxFreq; | 
| Chris@122 | 2120 | 
| Chris@122 | 2121     if (fftSize != m_fftSize) { | 
| Chris@122 | 2122         displayMinFreq = getEffectiveMinFrequency(); | 
| Chris@122 | 2123         displayMaxFreq = getEffectiveMaxFrequency(); | 
| Chris@122 | 2124     } | 
| Chris@122 | 2125 | 
| Chris@682 | 2126 //    cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl; | 
| Chris@253 | 2127 | 
| Chris@518 | 2128     int increment = getWindowIncrement(); | 
| Chris@40 | 2129 | 
| Chris@40 | 2130     bool logarithmic = (m_frequencyScale == LogFrequencyScale); | 
| Chris@488 | 2131 /* | 
| Chris@905 | 2132     double yforbin[maxbin - minbin + 1]; | 
| Chris@481 | 2133 | 
| Chris@805 | 2134     for (int q = minbin; q <= maxbin; ++q) { | 
| Chris@905 | 2135         double f0 = (double(q) * sr) / fftSize; | 
| Chris@477 | 2136         yforbin[q - minbin] = | 
| Chris@382 | 2137             v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, | 
| Chris@382 | 2138                                 logarithmic); | 
| Chris@92 | 2139     } | 
| Chris@488 | 2140 */ | 
| Chris@119 | 2141     MagnitudeRange overallMag = m_viewMags[v]; | 
| Chris@119 | 2142     bool overallMagChanged = false; | 
| Chris@119 | 2143 | 
| Chris@137 | 2144 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@905 | 2145     cerr << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; | 
| Chris@137 | 2146 #endif | 
| Chris@137 | 2147 | 
| Chris@331 | 2148     if (w == 0) { | 
| Chris@587 | 2149         SVDEBUG << "*** NOTE: w == 0" << endl; | 
| Chris@331 | 2150     } | 
| Chris@331 | 2151 | 
| Chris@331 | 2152 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@805 | 2153     int pixels = 0; | 
| Chris@331 | 2154 #endif | 
| Chris@331 | 2155 | 
| Chris@382 | 2156     Profiler outerprof("SpectrogramLayer::paint: all cols"); | 
| Chris@382 | 2157 | 
| Chris@481 | 2158     // The draw buffer contains a fragment at either our pixel | 
| Chris@481 | 2159     // resolution (if there is more than one time-bin per pixel) or | 
| Chris@481 | 2160     // time-bin resolution (if a time-bin spans more than one pixel). | 
| Chris@481 | 2161     // We need to ensure that it starts and ends at points where a | 
| Chris@481 | 2162     // time-bin boundary occurs at an exact pixel boundary, and with a | 
| Chris@481 | 2163     // certain amount of overlap across existing pixels so that we can | 
| Chris@481 | 2164     // scale and draw from it without smoothing errors at the edges. | 
| Chris@481 | 2165 | 
| Chris@481 | 2166     // If (getFrameForX(x) / increment) * increment == | 
| Chris@481 | 2167     // getFrameForX(x), then x is a time-bin boundary.  We want two | 
| Chris@481 | 2168     // such boundaries at either side of the draw buffer -- one which | 
| Chris@481 | 2169     // we draw up to, and one which we subsequently crop at. | 
| Chris@481 | 2170 | 
| Chris@481 | 2171     bool bufferBinResolution = false; | 
| Chris@481 | 2172     if (increment > zoomLevel) bufferBinResolution = true; | 
| Chris@481 | 2173 | 
| Chris@907 | 2174     sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1; | 
| Chris@907 | 2175     sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1; | 
| Chris@481 | 2176 | 
| Chris@481 | 2177     int bufwid; | 
| Chris@481 | 2178 | 
| Chris@481 | 2179     if (bufferBinResolution) { | 
| Chris@481 | 2180 | 
| Chris@482 | 2181         for (int x = x0; ; --x) { | 
| Chris@907 | 2182             sv_frame_t f = v->getFrameForX(x); | 
| Chris@481 | 2183             if ((f / increment) * increment == f) { | 
| Chris@481 | 2184                 if (leftCropFrame == -1) leftCropFrame = f; | 
| Chris@482 | 2185                 else if (x < x0 - 2) { leftBoundaryFrame = f; break; } | 
| Chris@481 | 2186             } | 
| Chris@481 | 2187         } | 
| Chris@482 | 2188         for (int x = x0 + w; ; ++x) { | 
| Chris@907 | 2189             sv_frame_t f = v->getFrameForX(x); | 
| Chris@481 | 2190             if ((f / increment) * increment == f) { | 
| Chris@481 | 2191                 if (rightCropFrame == -1) rightCropFrame = f; | 
| Chris@482 | 2192                 else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; } | 
| Chris@481 | 2193             } | 
| Chris@481 | 2194         } | 
| Chris@485 | 2195 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@481 | 2196         cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl; | 
| Chris@481 | 2197         cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl; | 
| Chris@485 | 2198 #endif | 
| Chris@481 | 2199 | 
| Chris@907 | 2200         bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment); | 
| Chris@481 | 2201 | 
| Chris@481 | 2202     } else { | 
| Chris@481 | 2203 | 
| Chris@481 | 2204         bufwid = w; | 
| Chris@481 | 2205     } | 
| Chris@481 | 2206 | 
| Chris@907 | 2207     vector<int> binforx(bufwid); | 
| Chris@907 | 2208     vector<double> binfory(h); | 
| Chris@907 | 2209 | 
| Chris@484 | 2210     bool usePeaksCache = false; | 
| Chris@484 | 2211 | 
| Chris@481 | 2212     if (bufferBinResolution) { | 
| Chris@481 | 2213         for (int x = 0; x < bufwid; ++x) { | 
| Chris@907 | 2214             binforx[x] = int(leftBoundaryFrame / increment) + x; | 
| Chris@482 | 2215 //            cerr << "binforx[" << x << "] = " << binforx[x] << endl; | 
| Chris@481 | 2216         } | 
| Chris@481 | 2217         m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); | 
| Chris@481 | 2218     } else { | 
| Chris@481 | 2219         for (int x = 0; x < bufwid; ++x) { | 
| Chris@905 | 2220             double s0 = 0, s1 = 0; | 
| Chris@481 | 2221             if (getXBinRange(v, x + x0, s0, s1)) { | 
| Chris@481 | 2222                 binforx[x] = int(s0 + 0.0001); | 
| Chris@481 | 2223             } else { | 
| Chris@487 | 2224                 binforx[x] = -1; //??? | 
| Chris@481 | 2225             } | 
| Chris@481 | 2226         } | 
| Chris@481 | 2227         if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) { | 
| Chris@481 | 2228             m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); | 
| Chris@480 | 2229         } | 
| Chris@484 | 2230         usePeaksCache = (increment * 8) < zoomLevel; | 
| Chris@487 | 2231         if (m_colourScale == PhaseColourScale) usePeaksCache = false; | 
| Chris@480 | 2232     } | 
| Chris@481 | 2233 | 
| Chris@616 | 2234 // No longer exists in Qt5:    m_drawBuffer.setNumColors(256); | 
| Chris@481 | 2235     for (int pixel = 0; pixel < 256; ++pixel) { | 
| Chris@907 | 2236         m_drawBuffer.setColor((unsigned char)pixel, | 
| Chris@907 | 2237                               m_palette.getColour((unsigned char)pixel).rgb()); | 
| Chris@481 | 2238     } | 
| Chris@481 | 2239 | 
| Chris@481 | 2240     m_drawBuffer.fill(0); | 
| Chris@480 | 2241 | 
| Chris@488 | 2242     if (m_binDisplay != PeakFrequencies) { | 
| Chris@488 | 2243 | 
| Chris@488 | 2244         for (int y = 0; y < h; ++y) { | 
| Chris@905 | 2245             double q0 = 0, q1 = 0; | 
| Chris@488 | 2246             if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { | 
| Chris@488 | 2247                 binfory[y] = -1; | 
| Chris@488 | 2248             } else { | 
| Chris@490 | 2249                 binfory[y] = q0; | 
| Chris@488 | 2250 //                cerr << "binfory[" << y << "] = " << binfory[y] << endl; | 
| Chris@488 | 2251             } | 
| Chris@480 | 2252         } | 
| Chris@488 | 2253 | 
| Chris@491 | 2254         paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache, | 
| Chris@491 | 2255                         overallMag, overallMagChanged); | 
| Chris@488 | 2256 | 
| Chris@488 | 2257     } else { | 
| Chris@488 | 2258 | 
| Chris@488 | 2259         paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, | 
| Chris@488 | 2260                                        minbin, maxbin, | 
| Chris@488 | 2261                                        displayMinFreq, displayMaxFreq, | 
| Chris@491 | 2262                                        logarithmic, | 
| Chris@491 | 2263                                        overallMag, overallMagChanged); | 
| Chris@480 | 2264     } | 
| Chris@481 | 2265 | 
| Chris@480 | 2266 /* | 
| Chris@479 | 2267     for (int x = 0; x < w / xPixelRatio; ++x) { | 
| Chris@35 | 2268 | 
| Chris@382 | 2269         Profiler innerprof("SpectrogramLayer::paint: 1 pixel column"); | 
| Chris@382 | 2270 | 
| Chris@478 | 2271         runOutOfData = !paintColumnValues(v, fft, x0, x, | 
| Chris@478 | 2272                                           minbin, maxbin, | 
| Chris@478 | 2273                                           displayMinFreq, displayMaxFreq, | 
| Chris@479 | 2274                                           xPixelRatio, | 
| Chris@478 | 2275                                           h, yforbin); | 
| Chris@477 | 2276 | 
| Chris@331 | 2277         if (runOutOfData) { | 
| Chris@331 | 2278 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2279             cerr << "Run out of data -- dropping out of loop" << endl; | 
| Chris@331 | 2280 #endif | 
| Chris@331 | 2281             break; | 
| Chris@331 | 2282         } | 
| Chris@35 | 2283     } | 
| Chris@480 | 2284 */ | 
| Chris@331 | 2285 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2286 //    cerr << pixels << " pixels drawn" << endl; | 
| Chris@331 | 2287 #endif | 
| Chris@331 | 2288 | 
| Chris@119 | 2289     if (overallMagChanged) { | 
| Chris@119 | 2290         m_viewMags[v] = overallMag; | 
| Chris@209 | 2291 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2292         cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl; | 
| Chris@209 | 2293 #endif | 
| Chris@119 | 2294     } else { | 
| Chris@209 | 2295 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2296         cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | 
| Chris@209 | 2297 #endif | 
| Chris@119 | 2298     } | 
| Chris@119 | 2299 | 
| Chris@382 | 2300     outerprof.end(); | 
| Chris@382 | 2301 | 
| Chris@382 | 2302     Profiler profiler2("SpectrogramLayer::paint: draw image"); | 
| Chris@137 | 2303 | 
| Chris@478 | 2304     if (recreateWholeImageCache) { | 
| Chris@407 | 2305 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@918 | 2306         SVDEBUG << "Recreating image cache: width = " << v->getPaintWidth() | 
| Chris@585 | 2307                   << ", height = " << h << endl; | 
| Chris@407 | 2308 #endif | 
| Chris@918 | 2309 	cache.image = QImage(v->getPaintWidth(), h, QImage::Format_ARGB32_Premultiplied); | 
| Chris@0 | 2310     } | 
| Chris@0 | 2311 | 
| Chris@331 | 2312     if (w > 0) { | 
| Chris@224 | 2313 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2314         SVDEBUG << "Painting " << w << "x" << h | 
| Chris@331 | 2315                   << " from draw buffer at " << 0 << "," << 0 | 
| Chris@480 | 2316                   << " to " << w << "x" << h << " on cache at " | 
| Chris@585 | 2317                   << x0 << "," << 0 << endl; | 
| Chris@224 | 2318 #endif | 
| Chris@224 | 2319 | 
| Chris@478 | 2320         QPainter cachePainter(&cache.image); | 
| Chris@481 | 2321 | 
| Chris@481 | 2322         if (bufferBinResolution) { | 
| Chris@481 | 2323             int scaledLeft = v->getXForFrame(leftBoundaryFrame); | 
| Chris@481 | 2324             int scaledRight = v->getXForFrame(rightBoundaryFrame); | 
| Chris@485 | 2325 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2326             SVDEBUG << "Rescaling image from " << bufwid | 
| Chris@481 | 2327                  << "x" << h << " to " | 
| Chris@481 | 2328                  << scaledRight-scaledLeft << "x" << h << endl; | 
| Chris@485 | 2329 #endif | 
| Chris@490 | 2330             Preferences::SpectrogramXSmoothing xsmoothing = | 
| Chris@490 | 2331                 Preferences::getInstance()->getSpectrogramXSmoothing(); | 
| Chris@587 | 2332 //            SVDEBUG << "xsmoothing == " << xsmoothing << endl; | 
| Chris@481 | 2333             QImage scaled = m_drawBuffer.scaled | 
| Chris@481 | 2334                 (scaledRight - scaledLeft, h, | 
| Chris@490 | 2335                  Qt::IgnoreAspectRatio, | 
| Chris@490 | 2336                  ((xsmoothing == Preferences::SpectrogramXInterpolated) ? | 
| Chris@490 | 2337                   Qt::SmoothTransformation : Qt::FastTransformation)); | 
| Chris@481 | 2338             int scaledLeftCrop = v->getXForFrame(leftCropFrame); | 
| Chris@481 | 2339             int scaledRightCrop = v->getXForFrame(rightCropFrame); | 
| Chris@485 | 2340 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2341             SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " | 
| Chris@481 | 2342                  << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; | 
| Chris@485 | 2343 #endif | 
| Chris@481 | 2344             cachePainter.drawImage | 
| Chris@481 | 2345                 (QRect(scaledLeftCrop, 0, | 
| Chris@481 | 2346                        scaledRightCrop - scaledLeftCrop, h), | 
| Chris@481 | 2347                  scaled, | 
| Chris@481 | 2348                  QRect(scaledLeftCrop - scaledLeft, 0, | 
| Chris@481 | 2349                        scaledRightCrop - scaledLeftCrop, h)); | 
| Chris@481 | 2350         } else { | 
| Chris@481 | 2351             cachePainter.drawImage(QRect(x0, 0, w, h), | 
| Chris@481 | 2352                                    m_drawBuffer, | 
| Chris@481 | 2353                                    QRect(0, 0, w, h)); | 
| Chris@481 | 2354         } | 
| Chris@481 | 2355 | 
| Chris@331 | 2356         cachePainter.end(); | 
| Chris@331 | 2357     } | 
| Chris@331 | 2358 | 
| Chris@337 | 2359     QRect pr = rect & cache.validArea; | 
| Chris@337 | 2360 | 
| Chris@337 | 2361 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2362     SVDEBUG << "Painting " << pr.width() << "x" << pr.height() | 
| Chris@337 | 2363               << " from cache at " << pr.x() << "," << pr.y() | 
| Chris@585 | 2364               << " to window" << endl; | 
| Chris@337 | 2365 #endif | 
| Chris@337 | 2366 | 
| Chris@478 | 2367     paint.drawImage(pr.x(), pr.y(), cache.image, | 
| Chris@479 | 2368                     pr.x(), pr.y(), pr.width(), pr.height()); | 
| Chris@479 | 2369     //!!! | 
| Chris@479 | 2370 //    paint.drawImage(v->rect(), cache.image, | 
| Chris@479 | 2371 //                    QRect(QPoint(0, 0), cache.image.size())); | 
| Chris@337 | 2372 | 
| Chris@331 | 2373     cache.startFrame = startFrame; | 
| Chris@331 | 2374     cache.zoomLevel = zoomLevel; | 
| Chris@119 | 2375 | 
| Chris@389 | 2376     if (!m_synchronous) { | 
| Chris@389 | 2377 | 
| Chris@862 | 2378         if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) { | 
| Chris@0 | 2379 | 
| Chris@389 | 2380             if (cache.validArea.x() > 0) { | 
| Chris@95 | 2381 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2382                 SVDEBUG << "SpectrogramLayer::paint() updating left (0, " | 
| Chris@585 | 2383                           << cache.validArea.x() << ")" << endl; | 
| Chris@95 | 2384 #endif | 
| Chris@918 | 2385                 v->getView()->update(0, 0, cache.validArea.x(), h); | 
| Chris@389 | 2386             } | 
| Chris@389 | 2387 | 
| Chris@389 | 2388             if (cache.validArea.x() + cache.validArea.width() < | 
| Chris@478 | 2389                 cache.image.width()) { | 
| Chris@389 | 2390 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2391                 SVDEBUG << "SpectrogramLayer::paint() updating right (" | 
| Chris@389 | 2392                           << cache.validArea.x() + cache.validArea.width() | 
| Chris@389 | 2393                           << ", " | 
| Chris@478 | 2394                           << cache.image.width() - (cache.validArea.x() + | 
| Chris@389 | 2395                                                      cache.validArea.width()) | 
| Chris@585 | 2396                           << ")" << endl; | 
| Chris@389 | 2397 #endif | 
| Chris@918 | 2398                 v->getView()->update(cache.validArea.x() + cache.validArea.width(), | 
| Chris@389 | 2399                           0, | 
| Chris@478 | 2400                           cache.image.width() - (cache.validArea.x() + | 
| Chris@389 | 2401                                                   cache.validArea.width()), | 
| Chris@389 | 2402                           h); | 
| Chris@389 | 2403             } | 
| Chris@389 | 2404         } else { | 
| Chris@389 | 2405             // overallMagChanged | 
| Chris@682 | 2406             cerr << "\noverallMagChanged - updating all\n" << endl; | 
| Chris@389 | 2407             cache.validArea = QRect(); | 
| Chris@918 | 2408             v->getView()->update(); | 
| Chris@119 | 2409         } | 
| Chris@95 | 2410     } | 
| Chris@0 | 2411 | 
| Chris@121 | 2412     illuminateLocalFeatures(v, paint); | 
| Chris@120 | 2413 | 
| Chris@0 | 2414 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2415     SVDEBUG << "SpectrogramLayer::paint() returning" << endl; | 
| Chris@0 | 2416 #endif | 
| Chris@131 | 2417 | 
| Chris@389 | 2418     if (!m_synchronous) { | 
| Chris@389 | 2419         m_lastPaintBlockWidth = paintBlockWidth; | 
| Chris@389 | 2420         (void)gettimeofday(&tv, 0); | 
| Chris@389 | 2421         m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; | 
| Chris@389 | 2422     } | 
| Chris@0 | 2423 } | 
| Chris@0 | 2424 | 
| Chris@480 | 2425 bool | 
| Chris@918 | 2426 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, | 
| Chris@488 | 2427                                                  int w, | 
| Chris@488 | 2428                                                  int h, | 
| Chris@907 | 2429                                                  const vector<int> &binforx, | 
| Chris@488 | 2430                                                  int minbin, | 
| Chris@488 | 2431                                                  int maxbin, | 
| Chris@905 | 2432                                                  double displayMinFreq, | 
| Chris@905 | 2433                                                  double displayMaxFreq, | 
| Chris@491 | 2434                                                  bool logarithmic, | 
| Chris@491 | 2435                                                  MagnitudeRange &overallMag, | 
| Chris@491 | 2436                                                  bool &overallMagChanged) const | 
| Chris@488 | 2437 { | 
| Chris@488 | 2438     Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies"); | 
| Chris@488 | 2439 | 
| Chris@488 | 2440 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@488 | 2441     cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; | 
| Chris@488 | 2442 #endif | 
| Chris@488 | 2443     if (minbin < 0) minbin = 0; | 
| Chris@488 | 2444     if (maxbin < 0) maxbin = minbin+1; | 
| Chris@488 | 2445 | 
| Chris@488 | 2446     FFTModel *fft = getFFTModel(v); | 
| Chris@488 | 2447     if (!fft) return false; | 
| Chris@488 | 2448 | 
| Chris@488 | 2449     FFTModel::PeakSet peakfreqs; | 
| Chris@488 | 2450 | 
| Chris@848 | 2451     int psx = -1; | 
| Chris@545 | 2452 | 
| Chris@545 | 2453 #ifdef __GNUC__ | 
| Chris@488 | 2454     float values[maxbin - minbin + 1]; | 
| Chris@545 | 2455 #else | 
| Chris@545 | 2456     float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); | 
| Chris@545 | 2457 #endif | 
| Chris@488 | 2458 | 
| Chris@488 | 2459     for (int x = 0; x < w; ++x) { | 
| Chris@488 | 2460 | 
| Chris@488 | 2461         if (binforx[x] < 0) continue; | 
| Chris@488 | 2462 | 
| Chris@488 | 2463         int sx0 = binforx[x]; | 
| Chris@488 | 2464         int sx1 = sx0; | 
| Chris@488 | 2465         if (x+1 < w) sx1 = binforx[x+1]; | 
| Chris@488 | 2466         if (sx0 < 0) sx0 = sx1 - 1; | 
| Chris@488 | 2467         if (sx0 < 0) continue; | 
| Chris@488 | 2468         if (sx1 <= sx0) sx1 = sx0 + 1; | 
| Chris@488 | 2469 | 
| Chris@488 | 2470         for (int sx = sx0; sx < sx1; ++sx) { | 
| Chris@488 | 2471 | 
| Chris@488 | 2472             if (sx < 0 || sx >= int(fft->getWidth())) continue; | 
| Chris@488 | 2473 | 
| Chris@488 | 2474             if (!m_synchronous) { | 
| Chris@488 | 2475                 if (!fft->isColumnAvailable(sx)) { | 
| Chris@488 | 2476 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2477                     cerr << "Met unavailable column at col " << sx << endl; | 
| Chris@488 | 2478 #endif | 
| Chris@488 | 2479                     return false; | 
| Chris@488 | 2480                 } | 
| Chris@488 | 2481             } | 
| Chris@488 | 2482 | 
| Chris@488 | 2483             MagnitudeRange mag; | 
| Chris@488 | 2484 | 
| Chris@488 | 2485             if (sx != psx) { | 
| Chris@488 | 2486                 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx, | 
| Chris@488 | 2487                                                     minbin, maxbin - 1); | 
| Chris@488 | 2488                 if (m_colourScale == PhaseColourScale) { | 
| Chris@488 | 2489                     fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); | 
| Chris@862 | 2490                 } else if (m_normalization == NormalizeColumns) { | 
| Chris@488 | 2491                     fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); | 
| Chris@862 | 2492                 } else if (m_normalization == NormalizeHybrid) { | 
| Chris@719 | 2493                     fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); | 
| Chris@905 | 2494                     double max = fft->getMaximumMagnitudeAt(sx); | 
| Chris@719 | 2495                     if (max > 0.f) { | 
| Chris@719 | 2496                         for (int i = minbin; i <= maxbin; ++i) { | 
| Chris@907 | 2497                             values[i - minbin] = float(values[i - minbin] * log10(max)); | 
| Chris@719 | 2498                         } | 
| Chris@719 | 2499                     } | 
| Chris@488 | 2500                 } else { | 
| Chris@488 | 2501                     fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); | 
| Chris@488 | 2502                 } | 
| Chris@488 | 2503                 psx = sx; | 
| Chris@488 | 2504             } | 
| Chris@488 | 2505 | 
| Chris@488 | 2506             for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin(); | 
| Chris@488 | 2507                  pi != peakfreqs.end(); ++pi) { | 
| Chris@488 | 2508 | 
| Chris@488 | 2509                 int bin = pi->first; | 
| Chris@907 | 2510                 double freq = pi->second; | 
| Chris@488 | 2511 | 
| Chris@488 | 2512                 if (bin < minbin) continue; | 
| Chris@488 | 2513                 if (bin > maxbin) break; | 
| Chris@488 | 2514 | 
| Chris@907 | 2515                 double value = values[bin - minbin]; | 
| Chris@488 | 2516 | 
| Chris@488 | 2517                 if (m_colourScale != PhaseColourScale) { | 
| Chris@862 | 2518                     if (m_normalization != NormalizeColumns) { | 
| Chris@907 | 2519                         value /= (m_fftSize/2.0); | 
| Chris@488 | 2520                     } | 
| Chris@907 | 2521                     mag.sample(float(value)); | 
| Chris@488 | 2522                     value *= m_gain; | 
| Chris@488 | 2523                 } | 
| Chris@488 | 2524 | 
| Chris@905 | 2525                 double y = v->getYForFrequency | 
| Chris@488 | 2526                     (freq, displayMinFreq, displayMaxFreq, logarithmic); | 
| Chris@488 | 2527 | 
| Chris@558 | 2528                 int iy = int(y + 0.5); | 
| Chris@558 | 2529                 if (iy < 0 || iy >= h) continue; | 
| Chris@558 | 2530 | 
| Chris@558 | 2531                 m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value)); | 
| Chris@488 | 2532             } | 
| Chris@488 | 2533 | 
| Chris@488 | 2534             if (mag.isSet()) { | 
| Chris@488 | 2535                 if (sx >= int(m_columnMags.size())) { | 
| Chris@540 | 2536 #ifdef DEBUG_SPECTROGRAM | 
| Chris@682 | 2537                     cerr << "INTERNAL ERROR: " << sx << " >= " | 
| Chris@488 | 2538                               << m_columnMags.size() | 
| Chris@488 | 2539                               << " at SpectrogramLayer.cpp::paintDrawBuffer" | 
| Chris@682 | 2540                               << endl; | 
| Chris@540 | 2541 #endif | 
| Chris@490 | 2542                 } else { | 
| Chris@490 | 2543                     m_columnMags[sx].sample(mag); | 
| Chris@491 | 2544                     if (overallMag.sample(mag)) overallMagChanged = true; | 
| Chris@488 | 2545                 } | 
| Chris@488 | 2546             } | 
| Chris@488 | 2547         } | 
| Chris@488 | 2548     } | 
| Chris@488 | 2549 | 
| Chris@488 | 2550     return true; | 
| Chris@488 | 2551 } | 
| Chris@488 | 2552 | 
| Chris@488 | 2553 bool | 
| Chris@918 | 2554 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v, | 
| Chris@481 | 2555                                   int w, | 
| Chris@481 | 2556                                   int h, | 
| Chris@907 | 2557                                   const vector<int> &binforx, | 
| Chris@907 | 2558                                   const vector<double> &binfory, | 
| Chris@491 | 2559                                   bool usePeaksCache, | 
| Chris@491 | 2560                                   MagnitudeRange &overallMag, | 
| Chris@491 | 2561                                   bool &overallMagChanged) const | 
| Chris@480 | 2562 { | 
| Chris@481 | 2563     Profiler profiler("SpectrogramLayer::paintDrawBuffer"); | 
| Chris@480 | 2564 | 
| Chris@490 | 2565     int minbin = int(binfory[0] + 0.0001); | 
| Chris@907 | 2566     int maxbin = int(binfory[h-1]); | 
| Chris@480 | 2567 | 
| Chris@485 | 2568 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@481 | 2569     cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; | 
| Chris@485 | 2570 #endif | 
| Chris@480 | 2571     if (minbin < 0) minbin = 0; | 
| Chris@480 | 2572     if (maxbin < 0) maxbin = minbin+1; | 
| Chris@480 | 2573 | 
| Chris@484 | 2574     DenseThreeDimensionalModel *sourceModel = 0; | 
| Chris@484 | 2575     FFTModel *fft = 0; | 
| Chris@484 | 2576     int divisor = 1; | 
| Chris@485 | 2577 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@484 | 2578     cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; | 
| Chris@485 | 2579 #endif | 
| Chris@484 | 2580     if (usePeaksCache) { //!!! | 
| Chris@484 | 2581         sourceModel = getPeakCache(v); | 
| Chris@484 | 2582         divisor = 8;//!!! | 
| Chris@484 | 2583         minbin = 0; | 
| Chris@484 | 2584         maxbin = sourceModel->getHeight(); | 
| Chris@484 | 2585     } else { | 
| Chris@484 | 2586         sourceModel = fft = getFFTModel(v); | 
| Chris@484 | 2587     } | 
| Chris@484 | 2588 | 
| Chris@484 | 2589     if (!sourceModel) return false; | 
| Chris@484 | 2590 | 
| Chris@490 | 2591     bool interpolate = false; | 
| Chris@490 | 2592     Preferences::SpectrogramSmoothing smoothing = | 
| Chris@490 | 2593         Preferences::getInstance()->getSpectrogramSmoothing(); | 
| Chris@490 | 2594     if (smoothing == Preferences::SpectrogramInterpolated || | 
| Chris@490 | 2595         smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { | 
| Chris@490 | 2596         if (m_binDisplay != PeakBins && | 
| Chris@490 | 2597             m_binDisplay != PeakFrequencies) { | 
| Chris@490 | 2598             interpolate = true; | 
| Chris@490 | 2599         } | 
| Chris@490 | 2600     } | 
| Chris@490 | 2601 | 
| Chris@480 | 2602     int psx = -1; | 
| Chris@545 | 2603 | 
| Chris@545 | 2604 #ifdef __GNUC__ | 
| Chris@490 | 2605     float autoarray[maxbin - minbin + 1]; | 
| Chris@545 | 2606     float peaks[h]; | 
| Chris@545 | 2607 #else | 
| Chris@545 | 2608     float *autoarray = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); | 
| Chris@545 | 2609     float *peaks = (float *)alloca(h * sizeof(float)); | 
| Chris@545 | 2610 #endif | 
| Chris@545 | 2611 | 
| Chris@490 | 2612     const float *values = autoarray; | 
| Chris@484 | 2613     DenseThreeDimensionalModel::Column c; | 
| Chris@480 | 2614 | 
| Chris@480 | 2615     for (int x = 0; x < w; ++x) { | 
| Chris@480 | 2616 | 
| Chris@482 | 2617         if (binforx[x] < 0) continue; | 
| Chris@482 | 2618 | 
| Chris@488 | 2619 //        float columnGain = m_gain; | 
| Chris@487 | 2620         float columnMax = 0.f; | 
| Chris@487 | 2621 | 
| Chris@484 | 2622         int sx0 = binforx[x] / divisor; | 
| Chris@483 | 2623         int sx1 = sx0; | 
| Chris@484 | 2624         if (x+1 < w) sx1 = binforx[x+1] / divisor; | 
| Chris@483 | 2625         if (sx0 < 0) sx0 = sx1 - 1; | 
| Chris@483 | 2626         if (sx0 < 0) continue; | 
| Chris@483 | 2627         if (sx1 <= sx0) sx1 = sx0 + 1; | 
| Chris@483 | 2628 | 
| Chris@483 | 2629         for (int y = 0; y < h; ++y) peaks[y] = 0.f; | 
| Chris@480 | 2630 | 
| Chris@483 | 2631         for (int sx = sx0; sx < sx1; ++sx) { | 
| Chris@483 | 2632 | 
| Chris@518 | 2633 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2634 //            cerr << "sx = " << sx << endl; | 
| Chris@518 | 2635 #endif | 
| Chris@518 | 2636 | 
| Chris@484 | 2637             if (sx < 0 || sx >= int(sourceModel->getWidth())) continue; | 
| Chris@483 | 2638 | 
| Chris@483 | 2639             if (!m_synchronous) { | 
| Chris@484 | 2640                 if (!sourceModel->isColumnAvailable(sx)) { | 
| Chris@480 | 2641 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@682 | 2642                     cerr << "Met unavailable column at col " << sx << endl; | 
| Chris@480 | 2643 #endif | 
| Chris@483 | 2644                     return false; | 
| Chris@480 | 2645                 } | 
| Chris@483 | 2646             } | 
| Chris@483 | 2647 | 
| Chris@488 | 2648             MagnitudeRange mag; | 
| Chris@488 | 2649 | 
| Chris@483 | 2650             if (sx != psx) { | 
| Chris@484 | 2651                 if (fft) { | 
| Chris@485 | 2652 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2653                     SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl; | 
| Chris@485 | 2654 #endif | 
| Chris@487 | 2655                     if (m_colourScale == PhaseColourScale) { | 
| Chris@490 | 2656                         fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 
| Chris@862 | 2657                     } else if (m_normalization == NormalizeColumns) { | 
| Chris@490 | 2658                         fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 
| Chris@862 | 2659                     } else if (m_normalization == NormalizeHybrid) { | 
| Chris@719 | 2660                         fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 
| Chris@905 | 2661                         double max = fft->getMaximumMagnitudeAt(sx); | 
| Chris@862 | 2662                         float scale = log10(max + 1.f); | 
| Chris@862 | 2663                         cout << "sx = " << sx << ", max = " << max << ", log10(max) = " << log10(max) << ", scale = " << scale << endl; | 
| Chris@719 | 2664                         for (int i = minbin; i <= maxbin; ++i) { | 
| Chris@862 | 2665                             autoarray[i - minbin] *= scale; | 
| Chris@719 | 2666                         } | 
| Chris@487 | 2667                     } else { | 
| Chris@490 | 2668                         fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 
| Chris@487 | 2669                     } | 
| Chris@484 | 2670                 } else { | 
| Chris@485 | 2671 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2672                     SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl; | 
| Chris@485 | 2673 #endif | 
| Chris@484 | 2674                     c = sourceModel->getColumn(sx); | 
| Chris@862 | 2675                     if (m_normalization == NormalizeColumns || | 
| Chris@862 | 2676                         m_normalization == NormalizeHybrid) { | 
| Chris@487 | 2677                         for (int y = 0; y < h; ++y) { | 
| Chris@487 | 2678                             if (c[y] > columnMax) columnMax = c[y]; | 
| Chris@487 | 2679                         } | 
| Chris@487 | 2680                     } | 
| Chris@490 | 2681                     values = c.constData() + minbin; | 
| Chris@484 | 2682                 } | 
| Chris@483 | 2683                 psx = sx; | 
| Chris@483 | 2684             } | 
| Chris@483 | 2685 | 
| Chris@483 | 2686             for (int y = 0; y < h; ++y) { | 
| Chris@480 | 2687 | 
| Chris@905 | 2688                 double sy0 = binfory[y]; | 
| Chris@905 | 2689                 double sy1 = sy0 + 1; | 
| Chris@481 | 2690                 if (y+1 < h) sy1 = binfory[y+1]; | 
| Chris@490 | 2691 | 
| Chris@907 | 2692                 double value = 0.0; | 
| Chris@907 | 2693 | 
| Chris@907 | 2694                 if (interpolate && fabs(sy1 - sy0) < 1.0) { | 
| Chris@849 | 2695 | 
| Chris@905 | 2696                     double centre = (sy0 + sy1) / 2; | 
| Chris@907 | 2697                     double dist = (centre - 0.5) - rint(centre - 0.5); | 
| Chris@490 | 2698                     int bin = int(centre); | 
| Chris@490 | 2699                     int other = (dist < 0 ? (bin-1) : (bin+1)); | 
| Chris@490 | 2700                     if (bin < minbin) bin = minbin; | 
| Chris@490 | 2701                     if (bin > maxbin) bin = maxbin; | 
| Chris@490 | 2702                     if (other < minbin || other > maxbin) other = bin; | 
| Chris@907 | 2703                     double prop = 1.0 - fabs(dist); | 
| Chris@905 | 2704 | 
| Chris@905 | 2705                     double v0 = values[bin - minbin]; | 
| Chris@905 | 2706                     double v1 = values[other - minbin]; | 
| Chris@490 | 2707                     if (m_binDisplay == PeakBins) { | 
| Chris@490 | 2708                         if (bin == minbin || bin == maxbin || | 
| Chris@490 | 2709                             v0 < values[bin-minbin-1] || | 
| Chris@907 | 2710                             v0 < values[bin-minbin+1]) v0 = 0.0; | 
| Chris@490 | 2711                         if (other == minbin || other == maxbin || | 
| Chris@490 | 2712                             v1 < values[other-minbin-1] || | 
| Chris@907 | 2713                             v1 < values[other-minbin+1]) v1 = 0.0; | 
| Chris@489 | 2714                     } | 
| Chris@907 | 2715                     if (v0 == 0.0 && v1 == 0.0) continue; | 
| Chris@907 | 2716                     value = prop * v0 + (1.0 - prop) * v1; | 
| Chris@484 | 2717 | 
| Chris@488 | 2718                     if (m_colourScale != PhaseColourScale) { | 
| Chris@862 | 2719                         if (m_normalization != NormalizeColumns && | 
| Chris@862 | 2720                             m_normalization != NormalizeHybrid) { | 
| Chris@907 | 2721                             value /= (m_fftSize/2.0); | 
| Chris@488 | 2722                         } | 
| Chris@907 | 2723                         mag.sample(float(value)); | 
| Chris@488 | 2724                         value *= m_gain; | 
| Chris@488 | 2725                     } | 
| Chris@488 | 2726 | 
| Chris@907 | 2727                     peaks[y] = float(value); | 
| Chris@490 | 2728 | 
| Chris@490 | 2729                 } else { | 
| Chris@490 | 2730 | 
| Chris@490 | 2731                     int by0 = int(sy0 + 0.0001); | 
| Chris@490 | 2732                     int by1 = int(sy1 + 0.0001); | 
| Chris@490 | 2733                     if (by1 < by0 + 1) by1 = by0 + 1; | 
| Chris@490 | 2734 | 
| Chris@490 | 2735                     for (int bin = by0; bin < by1; ++bin) { | 
| Chris@490 | 2736 | 
| Chris@490 | 2737                         value = values[bin - minbin]; | 
| Chris@490 | 2738                         if (m_binDisplay == PeakBins) { | 
| Chris@490 | 2739                             if (bin == minbin || bin == maxbin || | 
| Chris@490 | 2740                                 value < values[bin-minbin-1] || | 
| Chris@490 | 2741                                 value < values[bin-minbin+1]) continue; | 
| Chris@480 | 2742                         } | 
| Chris@490 | 2743 | 
| Chris@490 | 2744                         if (m_colourScale != PhaseColourScale) { | 
| Chris@862 | 2745                             if (m_normalization != NormalizeColumns && | 
| Chris@862 | 2746                                 m_normalization != NormalizeHybrid) { | 
| Chris@907 | 2747                                 value /= (m_fftSize/2.0); | 
| Chris@490 | 2748                             } | 
| Chris@907 | 2749                             mag.sample(float(value)); | 
| Chris@490 | 2750                             value *= m_gain; | 
| Chris@490 | 2751                         } | 
| Chris@490 | 2752 | 
| Chris@907 | 2753                         if (value > peaks[y]) { | 
| Chris@907 | 2754                             peaks[y] = float(value); //!!! not right for phase! | 
| Chris@907 | 2755                         } | 
| Chris@480 | 2756                     } | 
| Chris@480 | 2757                 } | 
| Chris@483 | 2758             } | 
| Chris@488 | 2759 | 
| Chris@488 | 2760             if (mag.isSet()) { | 
| Chris@488 | 2761                 if (sx >= int(m_columnMags.size())) { | 
| Chris@540 | 2762 #ifdef DEBUG_SPECTROGRAM | 
| Chris@682 | 2763                     cerr << "INTERNAL ERROR: " << sx << " >= " | 
| Chris@488 | 2764                               << m_columnMags.size() | 
| Chris@488 | 2765                               << " at SpectrogramLayer.cpp::paintDrawBuffer" | 
| Chris@682 | 2766                               << endl; | 
| Chris@540 | 2767 #endif | 
| Chris@490 | 2768                 } else { | 
| Chris@490 | 2769                     m_columnMags[sx].sample(mag); | 
| Chris@491 | 2770                     if (overallMag.sample(mag)) overallMagChanged = true; | 
| Chris@488 | 2771                 } | 
| Chris@488 | 2772             } | 
| Chris@483 | 2773         } | 
| Chris@483 | 2774 | 
| Chris@483 | 2775         for (int y = 0; y < h; ++y) { | 
| Chris@483 | 2776 | 
| Chris@905 | 2777             double peak = peaks[y]; | 
| Chris@483 | 2778 | 
| Chris@488 | 2779             if (m_colourScale != PhaseColourScale && | 
| Chris@862 | 2780                 (m_normalization == NormalizeColumns || | 
| Chris@862 | 2781                  m_normalization == NormalizeHybrid) && | 
| Chris@488 | 2782                 columnMax > 0.f) { | 
| Chris@488 | 2783                 peak /= columnMax; | 
| Chris@862 | 2784                 if (m_normalization == NormalizeHybrid) { | 
| Chris@862 | 2785                     peak *= log10(columnMax + 1.f); | 
| Chris@719 | 2786                 } | 
| Chris@480 | 2787             } | 
| Chris@483 | 2788 | 
| Chris@483 | 2789             unsigned char peakpix = getDisplayValue(v, peak); | 
| Chris@480 | 2790 | 
| Chris@480 | 2791             m_drawBuffer.setPixel(x, h-y-1, peakpix); | 
| Chris@480 | 2792         } | 
| Chris@480 | 2793     } | 
| Chris@480 | 2794 | 
| Chris@480 | 2795     return true; | 
| Chris@480 | 2796 } | 
| Chris@477 | 2797 | 
| Chris@121 | 2798 void | 
| Chris@918 | 2799 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const | 
| Chris@121 | 2800 { | 
| Chris@382 | 2801     Profiler profiler("SpectrogramLayer::illuminateLocalFeatures"); | 
| Chris@382 | 2802 | 
| Chris@121 | 2803     QPoint localPos; | 
| Chris@121 | 2804     if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { | 
| Chris@121 | 2805         return; | 
| Chris@121 | 2806     } | 
| Chris@121 | 2807 | 
| Chris@682 | 2808 //    cerr << "SpectrogramLayer: illuminateLocalFeatures(" | 
| Chris@682 | 2809 //              << localPos.x() << "," << localPos.y() << ")" << endl; | 
| Chris@121 | 2810 | 
| Chris@905 | 2811     double s0, s1; | 
| Chris@905 | 2812     double f0, f1; | 
| Chris@121 | 2813 | 
| Chris@121 | 2814     if (getXBinRange(v, localPos.x(), s0, s1) && | 
| Chris@121 | 2815         getYBinSourceRange(v, localPos.y(), f0, f1)) { | 
| Chris@121 | 2816 | 
| Chris@121 | 2817         int s0i = int(s0 + 0.001); | 
| Chris@121 | 2818         int s1i = int(s1); | 
| Chris@121 | 2819 | 
| Chris@121 | 2820         int x0 = v->getXForFrame(s0i * getWindowIncrement()); | 
| Chris@121 | 2821         int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement()); | 
| Chris@121 | 2822 | 
| Chris@248 | 2823         int y1 = int(getYForFrequency(v, f1)); | 
| Chris@248 | 2824         int y0 = int(getYForFrequency(v, f0)); | 
| Chris@121 | 2825 | 
| Chris@682 | 2826 //        cerr << "SpectrogramLayer: illuminate " | 
| Chris@682 | 2827 //                  << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl; | 
| Chris@121 | 2828 | 
| Chris@287 | 2829         paint.setPen(v->getForeground()); | 
| Chris@133 | 2830 | 
| Chris@133 | 2831         //!!! should we be using paintCrosshairs for this? | 
| Chris@133 | 2832 | 
| Chris@121 | 2833         paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); | 
| Chris@121 | 2834     } | 
| Chris@121 | 2835 } | 
| Chris@121 | 2836 | 
| Chris@905 | 2837 double | 
| Chris@918 | 2838 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const | 
| Chris@42 | 2839 { | 
| Chris@44 | 2840     return v->getYForFrequency(frequency, | 
| Chris@44 | 2841 			       getEffectiveMinFrequency(), | 
| Chris@44 | 2842 			       getEffectiveMaxFrequency(), | 
| Chris@44 | 2843 			       m_frequencyScale == LogFrequencyScale); | 
| Chris@42 | 2844 } | 
| Chris@42 | 2845 | 
| Chris@905 | 2846 double | 
| Chris@918 | 2847 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const | 
| Chris@42 | 2848 { | 
| Chris@44 | 2849     return v->getFrequencyForY(y, | 
| Chris@44 | 2850 			       getEffectiveMinFrequency(), | 
| Chris@44 | 2851 			       getEffectiveMaxFrequency(), | 
| Chris@44 | 2852 			       m_frequencyScale == LogFrequencyScale); | 
| Chris@42 | 2853 } | 
| Chris@42 | 2854 | 
| Chris@0 | 2855 int | 
| Chris@918 | 2856 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const | 
| Chris@0 | 2857 { | 
| Chris@115 | 2858     if (m_updateTimer == 0) return 100; | 
| Chris@920 | 2859 | 
| Chris@920 | 2860     const View *view = v->getView(); | 
| Chris@920 | 2861 | 
| Chris@920 | 2862     if (m_fftModels.find(view) == m_fftModels.end()) return 100; | 
| Chris@920 | 2863 | 
| Chris@920 | 2864     int completion = m_fftModels[view].first->getCompletion(); | 
| Chris@224 | 2865 #ifdef DEBUG_SPECTROGRAM_REPAINT | 
| Chris@587 | 2866     SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl; | 
| Chris@224 | 2867 #endif | 
| Chris@0 | 2868     return completion; | 
| Chris@0 | 2869 } | 
| Chris@0 | 2870 | 
| Chris@583 | 2871 QString | 
| Chris@918 | 2872 SpectrogramLayer::getError(LayerGeometryProvider *v) const | 
| Chris@583 | 2873 { | 
| Chris@920 | 2874     const View *view = v->getView(); | 
| Chris@920 | 2875     if (m_fftModels.find(view) == m_fftModels.end()) return ""; | 
| Chris@920 | 2876     return m_fftModels[view].first->getError(); | 
| Chris@583 | 2877 } | 
| Chris@583 | 2878 | 
| Chris@28 | 2879 bool | 
| Chris@905 | 2880 SpectrogramLayer::getValueExtents(double &min, double &max, | 
| Chris@101 | 2881                                   bool &logarithmic, QString &unit) const | 
| Chris@79 | 2882 { | 
| Chris@133 | 2883     if (!m_model) return false; | 
| Chris@133 | 2884 | 
| Chris@907 | 2885     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@905 | 2886     min = double(sr) / m_fftSize; | 
| Chris@905 | 2887     max = double(sr) / 2; | 
| Chris@133 | 2888 | 
| Chris@101 | 2889     logarithmic = (m_frequencyScale == LogFrequencyScale); | 
| Chris@79 | 2890     unit = "Hz"; | 
| Chris@79 | 2891     return true; | 
| Chris@79 | 2892 } | 
| Chris@79 | 2893 | 
| Chris@79 | 2894 bool | 
| Chris@905 | 2895 SpectrogramLayer::getDisplayExtents(double &min, double &max) const | 
| Chris@101 | 2896 { | 
| Chris@101 | 2897     min = getEffectiveMinFrequency(); | 
| Chris@101 | 2898     max = getEffectiveMaxFrequency(); | 
| Chris@253 | 2899 | 
| Chris@587 | 2900 //    SVDEBUG << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << endl; | 
| Chris@101 | 2901     return true; | 
| Chris@101 | 2902 } | 
| Chris@101 | 2903 | 
| Chris@101 | 2904 bool | 
| Chris@905 | 2905 SpectrogramLayer::setDisplayExtents(double min, double max) | 
| Chris@120 | 2906 { | 
| Chris@120 | 2907     if (!m_model) return false; | 
| Chris@187 | 2908 | 
| Chris@587 | 2909 //    SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl; | 
| Chris@187 | 2910 | 
| Chris@120 | 2911     if (min < 0) min = 0; | 
| Chris@907 | 2912     if (max > m_model->getSampleRate()/2.0) max = m_model->getSampleRate()/2.0; | 
| Chris@120 | 2913 | 
| Chris@907 | 2914     int minf = int(lrint(min)); | 
| Chris@907 | 2915     int maxf = int(lrint(max)); | 
| Chris@120 | 2916 | 
| Chris@120 | 2917     if (m_minFrequency == minf && m_maxFrequency == maxf) return true; | 
| Chris@120 | 2918 | 
| Chris@478 | 2919     invalidateImageCaches(); | 
| Chris@120 | 2920     invalidateMagnitudes(); | 
| Chris@120 | 2921 | 
| Chris@120 | 2922     m_minFrequency = minf; | 
| Chris@120 | 2923     m_maxFrequency = maxf; | 
| Chris@120 | 2924 | 
| Chris@120 | 2925     emit layerParametersChanged(); | 
| Chris@120 | 2926 | 
| Chris@133 | 2927     int vs = getCurrentVerticalZoomStep(); | 
| Chris@133 | 2928     if (vs != m_lastEmittedZoomStep) { | 
| Chris@133 | 2929         emit verticalZoomChanged(); | 
| Chris@133 | 2930         m_lastEmittedZoomStep = vs; | 
| Chris@133 | 2931     } | 
| Chris@133 | 2932 | 
| Chris@120 | 2933     return true; | 
| Chris@120 | 2934 } | 
| Chris@120 | 2935 | 
| Chris@120 | 2936 bool | 
| Chris@918 | 2937 SpectrogramLayer::getYScaleValue(const LayerGeometryProvider *v, int y, | 
| Chris@905 | 2938                                  double &value, QString &unit) const | 
| Chris@261 | 2939 { | 
| Chris@261 | 2940     value = getFrequencyForY(v, y); | 
| Chris@261 | 2941     unit = "Hz"; | 
| Chris@261 | 2942     return true; | 
| Chris@261 | 2943 } | 
| Chris@261 | 2944 | 
| Chris@261 | 2945 bool | 
| Chris@918 | 2946 SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *, | 
| Chris@907 | 2947                                      sv_frame_t &frame, | 
| Chris@805 | 2948 				     int &resolution, | 
| Chris@28 | 2949 				     SnapType snap) const | 
| Chris@13 | 2950 { | 
| Chris@13 | 2951     resolution = getWindowIncrement(); | 
| Chris@907 | 2952     sv_frame_t left = (frame / resolution) * resolution; | 
| Chris@907 | 2953     sv_frame_t right = left + resolution; | 
| Chris@28 | 2954 | 
| Chris@28 | 2955     switch (snap) { | 
| Chris@28 | 2956     case SnapLeft:  frame = left;  break; | 
| Chris@28 | 2957     case SnapRight: frame = right; break; | 
| Chris@28 | 2958     case SnapNearest: | 
| Chris@28 | 2959     case SnapNeighbouring: | 
| Chris@28 | 2960 	if (frame - left > right - frame) frame = right; | 
| Chris@28 | 2961 	else frame = left; | 
| Chris@28 | 2962 	break; | 
| Chris@28 | 2963     } | 
| Chris@28 | 2964 | 
| Chris@28 | 2965     return true; | 
| Chris@28 | 2966 } | 
| Chris@13 | 2967 | 
| Chris@283 | 2968 void | 
| Chris@918 | 2969 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) | 
| Chris@283 | 2970 { | 
| Chris@920 | 2971     const View *view = v->getView(); | 
| Chris@920 | 2972     ImageCache &cache = m_imageCaches[view]; | 
| Chris@478 | 2973 | 
| Chris@682 | 2974     cerr << "cache width: " << cache.image.width() << ", height: " | 
| Chris@920 | 2975          << cache.image.height() << endl; | 
| Chris@478 | 2976 | 
| Chris@478 | 2977     QImage image = cache.image; | 
| Chris@283 | 2978 | 
| Chris@283 | 2979     ImageRegionFinder finder; | 
| Chris@283 | 2980     QRect rect = finder.findRegionExtents(&image, e->pos()); | 
| Chris@283 | 2981     if (rect.isValid()) { | 
| Chris@283 | 2982         MeasureRect mr; | 
| Chris@283 | 2983         setMeasureRectFromPixrect(v, mr, rect); | 
| Chris@283 | 2984         CommandHistory::getInstance()->addCommand | 
| Chris@283 | 2985             (new AddMeasurementRectCommand(this, mr)); | 
| Chris@283 | 2986     } | 
| Chris@283 | 2987 } | 
| Chris@283 | 2988 | 
| Chris@77 | 2989 bool | 
| Chris@918 | 2990 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, | 
| Chris@77 | 2991                                       QPoint cursorPos, | 
| Chris@77 | 2992                                       std::vector<QRect> &extents) const | 
| Chris@77 | 2993 { | 
| Chris@918 | 2994     QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight()); | 
| Chris@77 | 2995     extents.push_back(vertical); | 
| Chris@77 | 2996 | 
| Chris@77 | 2997     QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); | 
| Chris@77 | 2998     extents.push_back(horizontal); | 
| Chris@77 | 2999 | 
| Chris@608 | 3000     int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); | 
| Chris@264 | 3001 | 
| Chris@280 | 3002     QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2, | 
| Chris@280 | 3003                paint.fontMetrics().width("123456 Hz") + 2, | 
| Chris@280 | 3004                paint.fontMetrics().height()); | 
| Chris@280 | 3005     extents.push_back(freq); | 
| Chris@264 | 3006 | 
| Chris@279 | 3007     QRect pitch(sw, cursorPos.y() + 2, | 
| Chris@279 | 3008                 paint.fontMetrics().width("C#10+50c") + 2, | 
| Chris@279 | 3009                 paint.fontMetrics().height()); | 
| Chris@279 | 3010     extents.push_back(pitch); | 
| Chris@279 | 3011 | 
| Chris@280 | 3012     QRect rt(cursorPos.x(), | 
| Chris@918 | 3013              v->getPaintHeight() - paint.fontMetrics().height() - 2, | 
| Chris@280 | 3014              paint.fontMetrics().width("1234.567 s"), | 
| Chris@280 | 3015              paint.fontMetrics().height()); | 
| Chris@280 | 3016     extents.push_back(rt); | 
| Chris@280 | 3017 | 
| Chris@280 | 3018     int w(paint.fontMetrics().width("1234567890") + 2); | 
| Chris@280 | 3019     QRect frame(cursorPos.x() - w - 2, | 
| Chris@918 | 3020                 v->getPaintHeight() - paint.fontMetrics().height() - 2, | 
| Chris@280 | 3021                 w, | 
| Chris@280 | 3022                 paint.fontMetrics().height()); | 
| Chris@280 | 3023     extents.push_back(frame); | 
| Chris@280 | 3024 | 
| Chris@77 | 3025     return true; | 
| Chris@77 | 3026 } | 
| Chris@77 | 3027 | 
| Chris@77 | 3028 void | 
| Chris@918 | 3029 SpectrogramLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint, | 
| Chris@77 | 3030                                   QPoint cursorPos) const | 
| Chris@77 | 3031 { | 
| Chris@77 | 3032     paint.save(); | 
| Chris@283 | 3033 | 
| Chris@608 | 3034     int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); | 
| Chris@283 | 3035 | 
| Chris@282 | 3036     QFont fn = paint.font(); | 
| Chris@282 | 3037     if (fn.pointSize() > 8) { | 
| Chris@282 | 3038         fn.setPointSize(fn.pointSize() - 1); | 
| Chris@282 | 3039         paint.setFont(fn); | 
| Chris@282 | 3040     } | 
| Chris@77 | 3041     paint.setPen(m_crosshairColour); | 
| Chris@77 | 3042 | 
| Chris@77 | 3043     paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); | 
| Chris@918 | 3044     paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight()); | 
| Chris@77 | 3045 | 
| Chris@905 | 3046     double fundamental = getFrequencyForY(v, cursorPos.y()); | 
| Chris@77 | 3047 | 
| Chris@278 | 3048     v->drawVisibleText(paint, | 
| Chris@278 | 3049                        sw + 2, | 
| Chris@278 | 3050                        cursorPos.y() - 2, | 
| Chris@278 | 3051                        QString("%1 Hz").arg(fundamental), | 
| Chris@278 | 3052                        View::OutlinedText); | 
| Chris@278 | 3053 | 
| Chris@279 | 3054     if (Pitch::isFrequencyInMidiRange(fundamental)) { | 
| Chris@279 | 3055         QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); | 
| Chris@279 | 3056         v->drawVisibleText(paint, | 
| Chris@279 | 3057                            sw + 2, | 
| Chris@279 | 3058                            cursorPos.y() + paint.fontMetrics().ascent() + 2, | 
| Chris@279 | 3059                            pitchLabel, | 
| Chris@279 | 3060                            View::OutlinedText); | 
| Chris@279 | 3061     } | 
| Chris@279 | 3062 | 
| Chris@907 | 3063     sv_frame_t frame = v->getFrameForX(cursorPos.x()); | 
| Chris@279 | 3064     RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); | 
| Chris@280 | 3065     QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); | 
| Chris@280 | 3066     QString frameLabel = QString("%1").arg(frame); | 
| Chris@280 | 3067     v->drawVisibleText(paint, | 
| Chris@280 | 3068                        cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, | 
| Chris@918 | 3069                        v->getPaintHeight() - 2, | 
| Chris@280 | 3070                        frameLabel, | 
| Chris@280 | 3071                        View::OutlinedText); | 
| Chris@280 | 3072     v->drawVisibleText(paint, | 
| Chris@280 | 3073                        cursorPos.x() + 2, | 
| Chris@918 | 3074                        v->getPaintHeight() - 2, | 
| Chris@280 | 3075                        rtLabel, | 
| Chris@280 | 3076                        View::OutlinedText); | 
| Chris@264 | 3077 | 
| Chris@77 | 3078     int harmonic = 2; | 
| Chris@77 | 3079 | 
| Chris@77 | 3080     while (harmonic < 100) { | 
| Chris@77 | 3081 | 
| Chris@907 | 3082         int hy = int(lrint(getYForFrequency(v, fundamental * harmonic))); | 
| Chris@918 | 3083         if (hy < 0 || hy > v->getPaintHeight()) break; | 
| Chris@77 | 3084 | 
| Chris@77 | 3085         int len = 7; | 
| Chris@77 | 3086 | 
| Chris@77 | 3087         if (harmonic % 2 == 0) { | 
| Chris@77 | 3088             if (harmonic % 4 == 0) { | 
| Chris@77 | 3089                 len = 12; | 
| Chris@77 | 3090             } else { | 
| Chris@77 | 3091                 len = 10; | 
| Chris@77 | 3092             } | 
| Chris@77 | 3093         } | 
| Chris@77 | 3094 | 
| Chris@77 | 3095         paint.drawLine(cursorPos.x() - len, | 
| Chris@907 | 3096                        hy, | 
| Chris@77 | 3097                        cursorPos.x(), | 
| Chris@907 | 3098                        hy); | 
| Chris@77 | 3099 | 
| Chris@77 | 3100         ++harmonic; | 
| Chris@77 | 3101     } | 
| Chris@77 | 3102 | 
| Chris@77 | 3103     paint.restore(); | 
| Chris@77 | 3104 } | 
| Chris@77 | 3105 | 
| Chris@25 | 3106 QString | 
| Chris@918 | 3107 SpectrogramLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const | 
| Chris@25 | 3108 { | 
| Chris@25 | 3109     int x = pos.x(); | 
| Chris@25 | 3110     int y = pos.y(); | 
| Chris@0 | 3111 | 
| Chris@25 | 3112     if (!m_model || !m_model->isOK()) return ""; | 
| Chris@0 | 3113 | 
| Chris@905 | 3114     double magMin = 0, magMax = 0; | 
| Chris@905 | 3115     double phaseMin = 0, phaseMax = 0; | 
| Chris@905 | 3116     double freqMin = 0, freqMax = 0; | 
| Chris@905 | 3117     double adjFreqMin = 0, adjFreqMax = 0; | 
| Chris@25 | 3118     QString pitchMin, pitchMax; | 
| Chris@0 | 3119     RealTime rtMin, rtMax; | 
| Chris@0 | 3120 | 
| Chris@38 | 3121     bool haveValues = false; | 
| Chris@0 | 3122 | 
| Chris@44 | 3123     if (!getXBinSourceRange(v, x, rtMin, rtMax)) { | 
| Chris@38 | 3124 	return ""; | 
| Chris@38 | 3125     } | 
| Chris@44 | 3126     if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) { | 
| Chris@38 | 3127 	haveValues = true; | 
| Chris@38 | 3128     } | 
| Chris@0 | 3129 | 
| Chris@35 | 3130     QString adjFreqText = "", adjPitchText = ""; | 
| Chris@35 | 3131 | 
| Chris@38 | 3132     if (m_binDisplay == PeakFrequencies) { | 
| Chris@35 | 3133 | 
| Chris@44 | 3134 	if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, | 
| Chris@38 | 3135 					adjFreqMin, adjFreqMax)) { | 
| Chris@38 | 3136 	    return ""; | 
| Chris@38 | 3137 	} | 
| Chris@35 | 3138 | 
| Chris@35 | 3139 	if (adjFreqMin != adjFreqMax) { | 
| Chris@65 | 3140 	    adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") | 
| Chris@35 | 3141 		.arg(adjFreqMin).arg(adjFreqMax); | 
| Chris@35 | 3142 	} else { | 
| Chris@65 | 3143 	    adjFreqText = tr("Peak Frequency:\t%1 Hz\n") | 
| Chris@35 | 3144 		.arg(adjFreqMin); | 
| Chris@38 | 3145 	} | 
| Chris@38 | 3146 | 
| Chris@38 | 3147 	QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); | 
| Chris@38 | 3148 	QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); | 
| Chris@38 | 3149 | 
| Chris@38 | 3150 	if (pmin != pmax) { | 
| Chris@65 | 3151 	    adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); | 
| Chris@38 | 3152 	} else { | 
| Chris@65 | 3153 	    adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); | 
| Chris@35 | 3154 	} | 
| Chris@35 | 3155 | 
| Chris@35 | 3156     } else { | 
| Chris@35 | 3157 | 
| Chris@44 | 3158 	if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; | 
| Chris@35 | 3159     } | 
| Chris@35 | 3160 | 
| Chris@25 | 3161     QString text; | 
| Chris@25 | 3162 | 
| Chris@25 | 3163     if (rtMin != rtMax) { | 
| Chris@25 | 3164 	text += tr("Time:\t%1 - %2\n") | 
| Chris@25 | 3165 	    .arg(rtMin.toText(true).c_str()) | 
| Chris@25 | 3166 	    .arg(rtMax.toText(true).c_str()); | 
| Chris@25 | 3167     } else { | 
| Chris@25 | 3168 	text += tr("Time:\t%1\n") | 
| Chris@25 | 3169 	    .arg(rtMin.toText(true).c_str()); | 
| Chris@0 | 3170     } | 
| Chris@0 | 3171 | 
| Chris@25 | 3172     if (freqMin != freqMax) { | 
| Chris@65 | 3173 	text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") | 
| Chris@65 | 3174 	    .arg(adjFreqText) | 
| Chris@25 | 3175 	    .arg(freqMin) | 
| Chris@25 | 3176 	    .arg(freqMax) | 
| Chris@65 | 3177 	    .arg(adjPitchText) | 
| Chris@65 | 3178 	    .arg(Pitch::getPitchLabelForFrequency(freqMin)) | 
| Chris@65 | 3179 	    .arg(Pitch::getPitchLabelForFrequency(freqMax)); | 
| Chris@65 | 3180     } else { | 
| Chris@65 | 3181 	text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") | 
| Chris@35 | 3182 	    .arg(adjFreqText) | 
| Chris@25 | 3183 	    .arg(freqMin) | 
| Chris@65 | 3184 	    .arg(adjPitchText) | 
| Chris@65 | 3185 	    .arg(Pitch::getPitchLabelForFrequency(freqMin)); | 
| Chris@25 | 3186     } | 
| Chris@25 | 3187 | 
| Chris@38 | 3188     if (haveValues) { | 
| Chris@905 | 3189 	double dbMin = AudioLevel::multiplier_to_dB(magMin); | 
| Chris@905 | 3190 	double dbMax = AudioLevel::multiplier_to_dB(magMax); | 
| Chris@43 | 3191 	QString dbMinString; | 
| Chris@43 | 3192 	QString dbMaxString; | 
| Chris@43 | 3193 	if (dbMin == AudioLevel::DB_FLOOR) { | 
| Chris@43 | 3194 	    dbMinString = tr("-Inf"); | 
| Chris@43 | 3195 	} else { | 
| Chris@907 | 3196 	    dbMinString = QString("%1").arg(lrint(dbMin)); | 
| Chris@43 | 3197 	} | 
| Chris@43 | 3198 	if (dbMax == AudioLevel::DB_FLOOR) { | 
| Chris@43 | 3199 	    dbMaxString = tr("-Inf"); | 
| Chris@43 | 3200 	} else { | 
| Chris@907 | 3201 	    dbMaxString = QString("%1").arg(lrint(dbMax)); | 
| Chris@43 | 3202 	} | 
| Chris@907 | 3203 	if (lrint(dbMin) != lrint(dbMax)) { | 
| Chris@199 | 3204 	    text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); | 
| Chris@25 | 3205 	} else { | 
| Chris@199 | 3206 	    text += tr("dB:\t%1").arg(dbMinString); | 
| Chris@25 | 3207 	} | 
| Chris@38 | 3208 	if (phaseMin != phaseMax) { | 
| Chris@38 | 3209 	    text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); | 
| Chris@38 | 3210 	} else { | 
| Chris@38 | 3211 	    text += tr("\nPhase:\t%1").arg(phaseMin); | 
| Chris@38 | 3212 	} | 
| Chris@25 | 3213     } | 
| Chris@25 | 3214 | 
| Chris@25 | 3215     return text; | 
| Chris@0 | 3216 } | 
| Chris@25 | 3217 | 
| Chris@0 | 3218 int | 
| Chris@40 | 3219 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const | 
| Chris@40 | 3220 { | 
| Chris@40 | 3221     int cw; | 
| Chris@40 | 3222 | 
| Chris@119 | 3223     cw = paint.fontMetrics().width("-80dB"); | 
| Chris@119 | 3224 | 
| Chris@40 | 3225     return cw; | 
| Chris@40 | 3226 } | 
| Chris@40 | 3227 | 
| Chris@40 | 3228 int | 
| Chris@918 | 3229 SpectrogramLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &paint) const | 
| Chris@0 | 3230 { | 
| Chris@0 | 3231     if (!m_model || !m_model->isOK()) return 0; | 
| Chris@0 | 3232 | 
| Chris@607 | 3233     int cw = 0; | 
| Chris@607 | 3234     if (detailed) cw = getColourScaleWidth(paint); | 
| Chris@40 | 3235 | 
| Chris@0 | 3236     int tw = paint.fontMetrics().width(QString("%1") | 
| Chris@0 | 3237 				     .arg(m_maxFrequency > 0 ? | 
| Chris@0 | 3238 					  m_maxFrequency - 1 : | 
| Chris@0 | 3239 					  m_model->getSampleRate() / 2)); | 
| Chris@0 | 3240 | 
| Chris@234 | 3241     int fw = paint.fontMetrics().width(tr("43Hz")); | 
| Chris@0 | 3242     if (tw < fw) tw = fw; | 
| Chris@40 | 3243 | 
| Chris@40 | 3244     int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); | 
| Chris@0 | 3245 | 
| Chris@40 | 3246     return cw + tickw + tw + 13; | 
| Chris@0 | 3247 } | 
| Chris@0 | 3248 | 
| Chris@0 | 3249 void | 
| Chris@918 | 3250 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const | 
| Chris@0 | 3251 { | 
| Chris@0 | 3252     if (!m_model || !m_model->isOK()) { | 
| Chris@0 | 3253 	return; | 
| Chris@0 | 3254     } | 
| Chris@0 | 3255 | 
| Chris@382 | 3256     Profiler profiler("SpectrogramLayer::paintVerticalScale"); | 
| Chris@122 | 3257 | 
| Chris@120 | 3258     //!!! cache this? | 
| Chris@120 | 3259 | 
| Chris@0 | 3260     int h = rect.height(), w = rect.width(); | 
| Chris@0 | 3261 | 
| Chris@40 | 3262     int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); | 
| Chris@40 | 3263     int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0); | 
| Chris@40 | 3264 | 
| Chris@805 | 3265     int bins = m_fftSize / 2; | 
| Chris@907 | 3266     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@0 | 3267 | 
| Chris@0 | 3268     if (m_maxFrequency > 0) { | 
| Chris@107 | 3269 	bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); | 
| Chris@107 | 3270 	if (bins > m_fftSize / 2) bins = m_fftSize / 2; | 
| Chris@0 | 3271     } | 
| Chris@0 | 3272 | 
| Chris@607 | 3273     int cw = 0; | 
| Chris@607 | 3274 | 
| Chris@607 | 3275     if (detailed) cw = getColourScaleWidth(paint); | 
| Chris@119 | 3276     int cbw = paint.fontMetrics().width("dB"); | 
| Chris@40 | 3277 | 
| Chris@0 | 3278     int py = -1; | 
| Chris@0 | 3279     int textHeight = paint.fontMetrics().height(); | 
| Chris@0 | 3280     int toff = -textHeight + paint.fontMetrics().ascent() + 2; | 
| Chris@0 | 3281 | 
| Chris@607 | 3282     if (detailed && (h > textHeight * 3 + 10)) { | 
| Chris@119 | 3283 | 
| Chris@119 | 3284         int topLines = 2; | 
| Chris@119 | 3285         if (m_colourScale == PhaseColourScale) topLines = 1; | 
| Chris@119 | 3286 | 
| Chris@119 | 3287 	int ch = h - textHeight * (topLines + 1) - 8; | 
| Chris@119 | 3288 //	paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); | 
| Chris@119 | 3289 	paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); | 
| Chris@40 | 3290 | 
| Chris@40 | 3291 	QString top, bottom; | 
| Chris@905 | 3292         double min = m_viewMags[v].getMin(); | 
| Chris@905 | 3293         double max = m_viewMags[v].getMax(); | 
| Chris@905 | 3294 | 
| Chris@905 | 3295         double dBmin = AudioLevel::multiplier_to_dB(min); | 
| Chris@905 | 3296         double dBmax = AudioLevel::multiplier_to_dB(max); | 
| Chris@119 | 3297 | 
| Chris@120 | 3298         if (dBmax < -60.f) dBmax = -60.f; | 
| Chris@907 | 3299         else top = QString("%1").arg(lrint(dBmax)); | 
| Chris@120 | 3300 | 
| Chris@120 | 3301         if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; | 
| Chris@907 | 3302         bottom = QString("%1").arg(lrint(dBmin)); | 
| Chris@119 | 3303 | 
| Chris@119 | 3304         //!!! & phase etc | 
| Chris@119 | 3305 | 
| Chris@119 | 3306         if (m_colourScale != PhaseColourScale) { | 
| Chris@119 | 3307             paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2, | 
| Chris@119 | 3308                            2 + textHeight + toff, "dBFS"); | 
| Chris@119 | 3309         } | 
| Chris@119 | 3310 | 
| Chris@119 | 3311 //	paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2, | 
| Chris@119 | 3312 	paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top), | 
| Chris@119 | 3313 		       2 + textHeight * topLines + toff + textHeight/2, top); | 
| Chris@119 | 3314 | 
| Chris@119 | 3315 	paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom), | 
| Chris@119 | 3316 		       h + toff - 3 - textHeight/2, bottom); | 
| Chris@40 | 3317 | 
| Chris@40 | 3318 	paint.save(); | 
| Chris@40 | 3319 	paint.setBrush(Qt::NoBrush); | 
| Chris@119 | 3320 | 
| Chris@119 | 3321         int lasty = 0; | 
| Chris@119 | 3322         int lastdb = 0; | 
| Chris@119 | 3323 | 
| Chris@40 | 3324 	for (int i = 0; i < ch; ++i) { | 
| Chris@119 | 3325 | 
| Chris@905 | 3326             double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1)); | 
| Chris@119 | 3327             int idb = int(dBval); | 
| Chris@119 | 3328 | 
| Chris@905 | 3329             double value = AudioLevel::dB_to_multiplier(dBval); | 
| Chris@119 | 3330             int colour = getDisplayValue(v, value * m_gain); | 
| Chris@210 | 3331 | 
| Chris@907 | 3332 	    paint.setPen(m_palette.getColour((unsigned char)colour)); | 
| Chris@119 | 3333 | 
| Chris@119 | 3334             int y = textHeight * topLines + 4 + ch - i; | 
| Chris@119 | 3335 | 
| Chris@119 | 3336             paint.drawLine(5 + cw - cbw, y, cw + 2, y); | 
| Chris@119 | 3337 | 
| Chris@119 | 3338             if (i == 0) { | 
| Chris@119 | 3339                 lasty = y; | 
| Chris@119 | 3340                 lastdb = idb; | 
| Chris@119 | 3341             } else if (i < ch - paint.fontMetrics().ascent() && | 
| Chris@120 | 3342                        idb != lastdb && | 
| Chris@119 | 3343                        ((abs(y - lasty) > textHeight && | 
| Chris@119 | 3344                          idb % 10 == 0) || | 
| Chris@119 | 3345                         (abs(y - lasty) > paint.fontMetrics().ascent() && | 
| Chris@119 | 3346                          idb % 5 == 0))) { | 
| Chris@287 | 3347                 paint.setPen(v->getBackground()); | 
| Chris@119 | 3348                 QString text = QString("%1").arg(idb); | 
| Chris@119 | 3349                 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text), | 
| Chris@119 | 3350                                y + toff + textHeight/2, text); | 
| Chris@287 | 3351                 paint.setPen(v->getForeground()); | 
| Chris@119 | 3352                 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y); | 
| Chris@119 | 3353                 lasty = y; | 
| Chris@119 | 3354                 lastdb = idb; | 
| Chris@119 | 3355             } | 
| Chris@40 | 3356 	} | 
| Chris@40 | 3357 	paint.restore(); | 
| Chris@40 | 3358     } | 
| Chris@40 | 3359 | 
| Chris@40 | 3360     paint.drawLine(cw + 7, 0, cw + 7, h); | 
| Chris@40 | 3361 | 
| Chris@0 | 3362     int bin = -1; | 
| Chris@0 | 3363 | 
| Chris@918 | 3364     for (int y = 0; y < v->getPaintHeight(); ++y) { | 
| Chris@849 | 3365 | 
| Chris@905 | 3366 	double q0, q1; | 
| Chris@918 | 3367 	if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; | 
| Chris@0 | 3368 | 
| Chris@0 | 3369 	int vy; | 
| Chris@0 | 3370 | 
| Chris@0 | 3371 	if (int(q0) > bin) { | 
| Chris@0 | 3372 	    vy = y; | 
| Chris@0 | 3373 	    bin = int(q0); | 
| Chris@0 | 3374 	} else { | 
| Chris@0 | 3375 	    continue; | 
| Chris@0 | 3376 	} | 
| Chris@0 | 3377 | 
| Chris@907 | 3378 	int freq = int((sr * bin) / m_fftSize); | 
| Chris@0 | 3379 | 
| Chris@0 | 3380 	if (py >= 0 && (vy - py) < textHeight - 1) { | 
| Chris@40 | 3381 	    if (m_frequencyScale == LinearFrequencyScale) { | 
| Chris@40 | 3382 		paint.drawLine(w - tickw, h - vy, w, h - vy); | 
| Chris@40 | 3383 	    } | 
| Chris@0 | 3384 	    continue; | 
| Chris@0 | 3385 	} | 
| Chris@0 | 3386 | 
| Chris@0 | 3387 	QString text = QString("%1").arg(freq); | 
| Chris@234 | 3388 	if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC | 
| Chris@40 | 3389 	paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); | 
| Chris@0 | 3390 | 
| Chris@0 | 3391 	if (h - vy - textHeight >= -2) { | 
| Chris@40 | 3392 	    int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); | 
| Chris@0 | 3393 	    paint.drawText(tx, h - vy + toff, text); | 
| Chris@0 | 3394 	} | 
| Chris@0 | 3395 | 
| Chris@0 | 3396 	py = vy; | 
| Chris@0 | 3397     } | 
| Chris@40 | 3398 | 
| Chris@40 | 3399     if (m_frequencyScale == LogFrequencyScale) { | 
| Chris@40 | 3400 | 
| Chris@277 | 3401         // piano keyboard | 
| Chris@277 | 3402 | 
| Chris@690 | 3403         PianoScale().paintPianoVertical | 
| Chris@690 | 3404             (v, paint, QRect(w - pkw - 1, 0, pkw, h), | 
| Chris@690 | 3405              getEffectiveMinFrequency(), getEffectiveMaxFrequency()); | 
| Chris@40 | 3406     } | 
| Chris@608 | 3407 | 
| Chris@608 | 3408     m_haveDetailedScale = detailed; | 
| Chris@0 | 3409 } | 
| Chris@0 | 3410 | 
| Chris@187 | 3411 class SpectrogramRangeMapper : public RangeMapper | 
| Chris@187 | 3412 { | 
| Chris@187 | 3413 public: | 
| Chris@901 | 3414     SpectrogramRangeMapper(sv_samplerate_t sr, int /* fftsize */) : | 
| Chris@901 | 3415         m_dist(sr / 2), | 
| Chris@901 | 3416         m_s2(sqrt(sqrt(2))) { } | 
| Chris@187 | 3417     ~SpectrogramRangeMapper() { } | 
| Chris@187 | 3418 | 
| Chris@901 | 3419     virtual int getPositionForValue(double value) const { | 
| Chris@901 | 3420 | 
| Chris@901 | 3421         double dist = m_dist; | 
| Chris@187 | 3422 | 
| Chris@187 | 3423         int n = 0; | 
| Chris@187 | 3424 | 
| Chris@901 | 3425         while (dist > (value + 0.00001) && dist > 0.1) { | 
| Chris@187 | 3426             dist /= m_s2; | 
| Chris@187 | 3427             ++n; | 
| Chris@187 | 3428         } | 
| Chris@187 | 3429 | 
| Chris@187 | 3430         return n; | 
| Chris@187 | 3431     } | 
| Chris@724 | 3432 | 
| Chris@901 | 3433     virtual int getPositionForValueUnclamped(double value) const { | 
| Chris@724 | 3434         // We don't really support this | 
| Chris@724 | 3435         return getPositionForValue(value); | 
| Chris@724 | 3436     } | 
| Chris@187 | 3437 | 
| Chris@901 | 3438     virtual double getValueForPosition(int position) const { | 
| Chris@187 | 3439 | 
| Chris@187 | 3440         // Vertical zoom step 0 shows the entire range from DC -> | 
| Chris@187 | 3441         // Nyquist frequency.  Step 1 shows 2^(1/4) of the range of | 
| Chris@187 | 3442         // step 0, and so on until the visible range is smaller than | 
| Chris@187 | 3443         // the frequency step between bins at the current fft size. | 
| Chris@187 | 3444 | 
| Chris@901 | 3445         double dist = m_dist; | 
| Chris@187 | 3446 | 
| Chris@187 | 3447         int n = 0; | 
| Chris@187 | 3448         while (n < position) { | 
| Chris@187 | 3449             dist /= m_s2; | 
| Chris@187 | 3450             ++n; | 
| Chris@187 | 3451         } | 
| Chris@187 | 3452 | 
| Chris@187 | 3453         return dist; | 
| Chris@187 | 3454     } | 
| Chris@187 | 3455 | 
| Chris@901 | 3456     virtual double getValueForPositionUnclamped(int position) const { | 
| Chris@724 | 3457         // We don't really support this | 
| Chris@724 | 3458         return getValueForPosition(position); | 
| Chris@724 | 3459     } | 
| Chris@724 | 3460 | 
| Chris@187 | 3461     virtual QString getUnit() const { return "Hz"; } | 
| Chris@187 | 3462 | 
| Chris@187 | 3463 protected: | 
| Chris@901 | 3464     double m_dist; | 
| Chris@901 | 3465     double m_s2; | 
| Chris@187 | 3466 }; | 
| Chris@187 | 3467 | 
| Chris@133 | 3468 int | 
| Chris@133 | 3469 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const | 
| Chris@133 | 3470 { | 
| Chris@135 | 3471     if (!m_model) return 0; | 
| Chris@187 | 3472 | 
| Chris@907 | 3473     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@187 | 3474 | 
| Chris@187 | 3475     SpectrogramRangeMapper mapper(sr, m_fftSize); | 
| Chris@187 | 3476 | 
| Chris@905 | 3477 //    int maxStep = mapper.getPositionForValue((double(sr) / m_fftSize) + 0.001); | 
| Chris@187 | 3478     int maxStep = mapper.getPositionForValue(0); | 
| Chris@905 | 3479     int minStep = mapper.getPositionForValue(double(sr) / 2); | 
| Chris@250 | 3480 | 
| Chris@805 | 3481     int initialMax = m_initialMaxFrequency; | 
| Chris@907 | 3482     if (initialMax == 0) initialMax = int(sr / 2); | 
| Chris@250 | 3483 | 
| Chris@250 | 3484     defaultStep = mapper.getPositionForValue(initialMax) - minStep; | 
| Chris@250 | 3485 | 
| Chris@587 | 3486 //    SVDEBUG << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << endl; | 
| Chris@187 | 3487 | 
| Chris@187 | 3488     return maxStep - minStep; | 
| Chris@133 | 3489 } | 
| Chris@133 | 3490 | 
| Chris@133 | 3491 int | 
| Chris@133 | 3492 SpectrogramLayer::getCurrentVerticalZoomStep() const | 
| Chris@133 | 3493 { | 
| Chris@133 | 3494     if (!m_model) return 0; | 
| Chris@133 | 3495 | 
| Chris@905 | 3496     double dmin, dmax; | 
| Chris@133 | 3497     getDisplayExtents(dmin, dmax); | 
| Chris@133 | 3498 | 
| Chris@187 | 3499     SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize); | 
| Chris@187 | 3500     int n = mapper.getPositionForValue(dmax - dmin); | 
| Chris@587 | 3501 //    SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl; | 
| Chris@133 | 3502     return n; | 
| Chris@133 | 3503 } | 
| Chris@133 | 3504 | 
| Chris@133 | 3505 void | 
| Chris@133 | 3506 SpectrogramLayer::setVerticalZoomStep(int step) | 
| Chris@133 | 3507 { | 
| Chris@187 | 3508     if (!m_model) return; | 
| Chris@187 | 3509 | 
| Chris@905 | 3510     double dmin = m_minFrequency, dmax = m_maxFrequency; | 
| Chris@253 | 3511 //    getDisplayExtents(dmin, dmax); | 
| Chris@253 | 3512 | 
| Chris@682 | 3513 //    cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl; | 
| Chris@133 | 3514 | 
| Chris@907 | 3515     sv_samplerate_t sr = m_model->getSampleRate(); | 
| Chris@187 | 3516     SpectrogramRangeMapper mapper(sr, m_fftSize); | 
| Chris@905 | 3517     double newdist = mapper.getValueForPosition(step); | 
| Chris@905 | 3518 | 
| Chris@905 | 3519     double newmin, newmax; | 
| Chris@253 | 3520 | 
| Chris@253 | 3521     if (m_frequencyScale == LogFrequencyScale) { | 
| Chris@253 | 3522 | 
| Chris@253 | 3523         // need to pick newmin and newmax such that | 
| Chris@253 | 3524         // | 
| Chris@253 | 3525         // (log(newmin) + log(newmax)) / 2 == logmid | 
| Chris@253 | 3526         // and | 
| Chris@253 | 3527         // newmax - newmin = newdist | 
| Chris@253 | 3528         // | 
| Chris@253 | 3529         // so log(newmax - newdist) + log(newmax) == 2logmid | 
| Chris@253 | 3530         // log(newmax(newmax - newdist)) == 2logmid | 
| Chris@253 | 3531         // newmax.newmax - newmax.newdist == exp(2logmid) | 
| Chris@253 | 3532         // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0 | 
| Chris@253 | 3533         // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known | 
| Chris@253 | 3534         // | 
| Chris@253 | 3535         // positive root | 
| Chris@253 | 3536         // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2 | 
| Chris@253 | 3537         // | 
| Chris@253 | 3538         // but logmid = (log(dmin) + log(dmax)) / 2 | 
| Chris@253 | 3539         // so exp(2logmid) = exp(log(dmin) + log(dmax)) | 
| Chris@253 | 3540         // = exp(log(dmin.dmax)) | 
| Chris@253 | 3541         // = dmin.dmax | 
| Chris@253 | 3542         // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2 | 
| Chris@253 | 3543 | 
| Chris@907 | 3544         newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2; | 
| Chris@253 | 3545         newmin = newmax - newdist; | 
| Chris@253 | 3546 | 
| Chris@682 | 3547 //        cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; | 
| Chris@253 | 3548 | 
| Chris@253 | 3549     } else { | 
| Chris@905 | 3550         double dmid = (dmax + dmin) / 2; | 
| Chris@253 | 3551         newmin = dmid - newdist / 2; | 
| Chris@253 | 3552         newmax = dmid + newdist / 2; | 
| Chris@253 | 3553     } | 
| Chris@187 | 3554 | 
| Chris@905 | 3555     double mmin, mmax; | 
| Chris@187 | 3556     mmin = 0; | 
| Chris@905 | 3557     mmax = double(sr) / 2; | 
| Chris@133 | 3558 | 
| Chris@187 | 3559     if (newmin < mmin) { | 
| Chris@187 | 3560         newmax += (mmin - newmin); | 
| Chris@187 | 3561         newmin = mmin; | 
| Chris@187 | 3562     } | 
| Chris@187 | 3563     if (newmax > mmax) { | 
| Chris@187 | 3564         newmax = mmax; | 
| Chris@187 | 3565     } | 
| Chris@133 | 3566 | 
| Chris@587 | 3567 //    SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; | 
| Chris@253 | 3568 | 
| Chris@907 | 3569     setMinFrequency(int(lrint(newmin))); | 
| Chris@907 | 3570     setMaxFrequency(int(lrint(newmax))); | 
| Chris@187 | 3571 } | 
| Chris@187 | 3572 | 
| Chris@187 | 3573 RangeMapper * | 
| Chris@187 | 3574 SpectrogramLayer::getNewVerticalZoomRangeMapper() const | 
| Chris@187 | 3575 { | 
| Chris@187 | 3576     if (!m_model) return 0; | 
| Chris@187 | 3577     return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); | 
| Chris@133 | 3578 } | 
| Chris@133 | 3579 | 
| Chris@273 | 3580 void | 
| Chris@918 | 3581 SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const | 
| Chris@273 | 3582 { | 
| Chris@273 | 3583     int y0 = 0; | 
| Chris@907 | 3584     if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY)); | 
| Chris@273 | 3585 | 
| Chris@273 | 3586     int y1 = y0; | 
| Chris@907 | 3587     if (r.endY > 0.0) y1 = int(getYForFrequency(v, r.endY)); | 
| Chris@273 | 3588 | 
| Chris@587 | 3589 //    SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl; | 
| Chris@273 | 3590 | 
| Chris@273 | 3591     r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); | 
| Chris@273 | 3592 } | 
| Chris@273 | 3593 | 
| Chris@273 | 3594 void | 
| Chris@918 | 3595 SpectrogramLayer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const | 
| Chris@273 | 3596 { | 
| Chris@273 | 3597     if (start) { | 
| Chris@273 | 3598         r.startY = getFrequencyForY(v, y); | 
| Chris@273 | 3599         r.endY = r.startY; | 
| Chris@273 | 3600     } else { | 
| Chris@273 | 3601         r.endY = getFrequencyForY(v, y); | 
| Chris@273 | 3602     } | 
| Chris@587 | 3603 //    SVDEBUG << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << endl; | 
| Chris@273 | 3604 | 
| Chris@273 | 3605 } | 
| Chris@273 | 3606 | 
| Chris@316 | 3607 void | 
| Chris@316 | 3608 SpectrogramLayer::toXml(QTextStream &stream, | 
| Chris@316 | 3609                         QString indent, QString extraAttributes) const | 
| Chris@6 | 3610 { | 
| Chris@6 | 3611     QString s; | 
| Chris@6 | 3612 | 
| Chris@6 | 3613     s += QString("channel=\"%1\" " | 
| Chris@6 | 3614 		 "windowSize=\"%2\" " | 
| Chris@153 | 3615 		 "windowHopLevel=\"%3\" " | 
| Chris@153 | 3616 		 "gain=\"%4\" " | 
| Chris@153 | 3617 		 "threshold=\"%5\" ") | 
| Chris@6 | 3618 	.arg(m_channel) | 
| Chris@6 | 3619 	.arg(m_windowSize) | 
| Chris@97 | 3620 	.arg(m_windowHopLevel) | 
| Chris@37 | 3621 	.arg(m_gain) | 
| Chris@37 | 3622 	.arg(m_threshold); | 
| Chris@37 | 3623 | 
| Chris@37 | 3624     s += QString("minFrequency=\"%1\" " | 
| Chris@37 | 3625 		 "maxFrequency=\"%2\" " | 
| Chris@37 | 3626 		 "colourScale=\"%3\" " | 
| Chris@37 | 3627 		 "colourScheme=\"%4\" " | 
| Chris@37 | 3628 		 "colourRotation=\"%5\" " | 
| Chris@37 | 3629 		 "frequencyScale=\"%6\" " | 
| Chris@761 | 3630 		 "binDisplay=\"%7\" ") | 
| Chris@37 | 3631 	.arg(m_minFrequency) | 
| Chris@6 | 3632 	.arg(m_maxFrequency) | 
| Chris@6 | 3633 	.arg(m_colourScale) | 
| Chris@197 | 3634 	.arg(m_colourMap) | 
| Chris@37 | 3635 	.arg(m_colourRotation) | 
| Chris@35 | 3636 	.arg(m_frequencyScale) | 
| Chris@761 | 3637 	.arg(m_binDisplay); | 
| Chris@761 | 3638 | 
| Chris@761 | 3639     s += QString("normalizeColumns=\"%1\" " | 
| Chris@761 | 3640                  "normalizeVisibleArea=\"%2\" " | 
| Chris@761 | 3641                  "normalizeHybrid=\"%3\" ") | 
| Chris@862 | 3642 	.arg(m_normalization == NormalizeColumns ? "true" : "false") | 
| Chris@862 | 3643         .arg(m_normalization == NormalizeVisibleArea ? "true" : "false") | 
| Chris@862 | 3644         .arg(m_normalization == NormalizeHybrid ? "true" : "false"); | 
| Chris@6 | 3645 | 
| Chris@316 | 3646     Layer::toXml(stream, indent, extraAttributes + " " + s); | 
| Chris@6 | 3647 } | 
| Chris@6 | 3648 | 
| Chris@11 | 3649 void | 
| Chris@11 | 3650 SpectrogramLayer::setProperties(const QXmlAttributes &attributes) | 
| Chris@11 | 3651 { | 
| Chris@11 | 3652     bool ok = false; | 
| Chris@11 | 3653 | 
| Chris@11 | 3654     int channel = attributes.value("channel").toInt(&ok); | 
| Chris@11 | 3655     if (ok) setChannel(channel); | 
| Chris@11 | 3656 | 
| Chris@805 | 3657     int windowSize = attributes.value("windowSize").toUInt(&ok); | 
| Chris@11 | 3658     if (ok) setWindowSize(windowSize); | 
| Chris@11 | 3659 | 
| Chris@805 | 3660     int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); | 
| Chris@97 | 3661     if (ok) setWindowHopLevel(windowHopLevel); | 
| Chris@97 | 3662     else { | 
| Chris@805 | 3663         int windowOverlap = attributes.value("windowOverlap").toUInt(&ok); | 
| Chris@97 | 3664         // a percentage value | 
| Chris@97 | 3665         if (ok) { | 
| Chris@97 | 3666             if (windowOverlap == 0) setWindowHopLevel(0); | 
| Chris@97 | 3667             else if (windowOverlap == 25) setWindowHopLevel(1); | 
| Chris@97 | 3668             else if (windowOverlap == 50) setWindowHopLevel(2); | 
| Chris@97 | 3669             else if (windowOverlap == 75) setWindowHopLevel(3); | 
| Chris@97 | 3670             else if (windowOverlap == 90) setWindowHopLevel(4); | 
| Chris@97 | 3671         } | 
| Chris@97 | 3672     } | 
| Chris@11 | 3673 | 
| Chris@11 | 3674     float gain = attributes.value("gain").toFloat(&ok); | 
| Chris@11 | 3675     if (ok) setGain(gain); | 
| Chris@11 | 3676 | 
| Chris@37 | 3677     float threshold = attributes.value("threshold").toFloat(&ok); | 
| Chris@37 | 3678     if (ok) setThreshold(threshold); | 
| Chris@37 | 3679 | 
| Chris@805 | 3680     int minFrequency = attributes.value("minFrequency").toUInt(&ok); | 
| Chris@187 | 3681     if (ok) { | 
| Chris@587 | 3682         SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl; | 
| Chris@187 | 3683         setMinFrequency(minFrequency); | 
| Chris@187 | 3684     } | 
| Chris@37 | 3685 | 
| Chris@805 | 3686     int maxFrequency = attributes.value("maxFrequency").toUInt(&ok); | 
| Chris@187 | 3687     if (ok) { | 
| Chris@587 | 3688         SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl; | 
| Chris@187 | 3689         setMaxFrequency(maxFrequency); | 
| Chris@187 | 3690     } | 
| Chris@11 | 3691 | 
| Chris@11 | 3692     ColourScale colourScale = (ColourScale) | 
| Chris@11 | 3693 	attributes.value("colourScale").toInt(&ok); | 
| Chris@11 | 3694     if (ok) setColourScale(colourScale); | 
| Chris@11 | 3695 | 
| Chris@197 | 3696     int colourMap = attributes.value("colourScheme").toInt(&ok); | 
| Chris@197 | 3697     if (ok) setColourMap(colourMap); | 
| Chris@11 | 3698 | 
| Chris@37 | 3699     int colourRotation = attributes.value("colourRotation").toInt(&ok); | 
| Chris@37 | 3700     if (ok) setColourRotation(colourRotation); | 
| Chris@37 | 3701 | 
| Chris@11 | 3702     FrequencyScale frequencyScale = (FrequencyScale) | 
| Chris@11 | 3703 	attributes.value("frequencyScale").toInt(&ok); | 
| Chris@11 | 3704     if (ok) setFrequencyScale(frequencyScale); | 
| Chris@35 | 3705 | 
| Chris@37 | 3706     BinDisplay binDisplay = (BinDisplay) | 
| Chris@37 | 3707 	attributes.value("binDisplay").toInt(&ok); | 
| Chris@37 | 3708     if (ok) setBinDisplay(binDisplay); | 
| Chris@36 | 3709 | 
| Chris@36 | 3710     bool normalizeColumns = | 
| Chris@36 | 3711 	(attributes.value("normalizeColumns").trimmed() == "true"); | 
| Chris@862 | 3712     if (normalizeColumns) { | 
| Chris@862 | 3713         setNormalization(NormalizeColumns); | 
| Chris@862 | 3714     } | 
| Chris@153 | 3715 | 
| Chris@153 | 3716     bool normalizeVisibleArea = | 
| Chris@153 | 3717 	(attributes.value("normalizeVisibleArea").trimmed() == "true"); | 
| Chris@862 | 3718     if (normalizeVisibleArea) { | 
| Chris@862 | 3719         setNormalization(NormalizeVisibleArea); | 
| Chris@862 | 3720     } | 
| Chris@761 | 3721 | 
| Chris@761 | 3722     bool normalizeHybrid = | 
| Chris@761 | 3723 	(attributes.value("normalizeHybrid").trimmed() == "true"); | 
| Chris@862 | 3724     if (normalizeHybrid) { | 
| Chris@862 | 3725         setNormalization(NormalizeHybrid); | 
| Chris@862 | 3726     } | 
| Chris@11 | 3727 } | 
| Chris@11 | 3728 |