svgui
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006-2009 Chris Cannam and QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "SpectrogramLayer.h" 00017 00018 #include "view/View.h" 00019 #include "base/Profiler.h" 00020 #include "base/AudioLevel.h" 00021 #include "base/Window.h" 00022 #include "base/Pitch.h" 00023 #include "base/Preferences.h" 00024 #include "base/RangeMapper.h" 00025 #include "base/LogRange.h" 00026 #include "widgets/CommandHistory.h" 00027 #include "ColourMapper.h" 00028 #include "ImageRegionFinder.h" 00029 #include "data/model/Dense3DModelPeakCache.h" 00030 #include "PianoScale.h" 00031 00032 #include <QPainter> 00033 #include <QImage> 00034 #include <QPixmap> 00035 #include <QRect> 00036 #include <QTimer> 00037 #include <QApplication> 00038 #include <QMessageBox> 00039 #include <QMouseEvent> 00040 #include <QTextStream> 00041 00042 #include <iostream> 00043 00044 00045 00046 #include <cassert> 00047 #include <cmath> 00048 00049 #ifndef __GNUC__ 00050 #include <alloca.h> 00051 #endif 00052 00053 //#define DEBUG_SPECTROGRAM_REPAINT 1 00054 00055 SpectrogramLayer::SpectrogramLayer(Configuration config) : 00056 m_model(0), 00057 m_channel(0), 00058 m_windowSize(1024), 00059 m_windowType(HanningWindow), 00060 m_windowHopLevel(2), 00061 m_zeroPadLevel(0), 00062 m_fftSize(1024), 00063 m_gain(1.0), 00064 m_initialGain(1.0), 00065 m_threshold(0.0), 00066 m_initialThreshold(0.0), 00067 m_colourRotation(0), 00068 m_initialRotation(0), 00069 m_minFrequency(10), 00070 m_maxFrequency(8000), 00071 m_initialMaxFrequency(8000), 00072 m_colourScale(dBColourScale), 00073 m_colourMap(0), 00074 m_frequencyScale(LinearFrequencyScale), 00075 m_binDisplay(AllBins), 00076 m_normalizeColumns(false), 00077 m_normalizeVisibleArea(false), 00078 m_normalizeHybrid(false), 00079 m_lastEmittedZoomStep(-1), 00080 m_synchronous(false), 00081 m_haveDetailedScale(false), 00082 m_lastPaintBlockWidth(0), 00083 m_updateTimer(0), 00084 m_candidateFillStartFrame(0), 00085 m_exiting(false), 00086 m_sliceableModel(0) 00087 { 00088 if (config == FullRangeDb) { 00089 m_initialMaxFrequency = 0; 00090 setMaxFrequency(0); 00091 } else if (config == MelodicRange) { 00092 setWindowSize(8192); 00093 setWindowHopLevel(4); 00094 m_initialMaxFrequency = 1500; 00095 setMaxFrequency(1500); 00096 setMinFrequency(40); 00097 setColourScale(LinearColourScale); 00098 setColourMap(ColourMapper::Sunset); 00099 setFrequencyScale(LogFrequencyScale); 00100 // setGain(20); 00101 } else if (config == MelodicPeaks) { 00102 setWindowSize(4096); 00103 setWindowHopLevel(5); 00104 m_initialMaxFrequency = 2000; 00105 setMaxFrequency(2000); 00106 setMinFrequency(40); 00107 setFrequencyScale(LogFrequencyScale); 00108 setColourScale(LinearColourScale); 00109 setBinDisplay(PeakFrequencies); 00110 setNormalizeColumns(true); 00111 } 00112 00113 Preferences *prefs = Preferences::getInstance(); 00114 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 00115 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); 00116 setWindowType(prefs->getWindowType()); 00117 00118 initialisePalette(); 00119 } 00120 00121 SpectrogramLayer::~SpectrogramLayer() 00122 { 00123 delete m_updateTimer; 00124 m_updateTimer = 0; 00125 00126 invalidateFFTModels(); 00127 } 00128 00129 void 00130 SpectrogramLayer::setModel(const DenseTimeValueModel *model) 00131 { 00132 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl; 00133 00134 if (model == m_model) return; 00135 00136 m_model = model; 00137 invalidateFFTModels(); 00138 00139 if (!m_model || !m_model->isOK()) return; 00140 00141 connectSignals(m_model); 00142 00143 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); 00144 connect(m_model, SIGNAL(modelChangedWithin(int, int)), 00145 this, SLOT(cacheInvalid(int, int))); 00146 00147 emit modelReplaced(); 00148 } 00149 00150 Layer::PropertyList 00151 SpectrogramLayer::getProperties() const 00152 { 00153 PropertyList list; 00154 list.push_back("Colour"); 00155 list.push_back("Colour Scale"); 00156 list.push_back("Window Size"); 00157 list.push_back("Window Increment"); 00158 list.push_back("Normalize Columns"); 00159 list.push_back("Normalize Visible Area"); 00160 list.push_back("Bin Display"); 00161 list.push_back("Threshold"); 00162 list.push_back("Gain"); 00163 list.push_back("Colour Rotation"); 00164 // list.push_back("Min Frequency"); 00165 // list.push_back("Max Frequency"); 00166 list.push_back("Frequency Scale"); 00168 return list; 00169 } 00170 00171 QString 00172 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const 00173 { 00174 if (name == "Colour") return tr("Colour"); 00175 if (name == "Colour Scale") return tr("Colour Scale"); 00176 if (name == "Window Size") return tr("Window Size"); 00177 if (name == "Window Increment") return tr("Window Overlap"); 00178 if (name == "Normalize Columns") return tr("Normalize Columns"); 00179 if (name == "Normalize Visible Area") return tr("Normalize Visible Area"); 00180 if (name == "Bin Display") return tr("Bin Display"); 00181 if (name == "Threshold") return tr("Threshold"); 00182 if (name == "Gain") return tr("Gain"); 00183 if (name == "Colour Rotation") return tr("Colour Rotation"); 00184 if (name == "Min Frequency") return tr("Min Frequency"); 00185 if (name == "Max Frequency") return tr("Max Frequency"); 00186 if (name == "Frequency Scale") return tr("Frequency Scale"); 00187 if (name == "Zero Padding") return tr("Smoothing"); 00188 return ""; 00189 } 00190 00191 QString 00192 SpectrogramLayer::getPropertyIconName(const PropertyName &name) const 00193 { 00194 if (name == "Normalize Columns") return "normalise-columns"; 00195 if (name == "Normalize Visible Area") return "normalise"; 00196 return ""; 00197 } 00198 00199 Layer::PropertyType 00200 SpectrogramLayer::getPropertyType(const PropertyName &name) const 00201 { 00202 if (name == "Gain") return RangeProperty; 00203 if (name == "Colour Rotation") return RangeProperty; 00204 if (name == "Normalize Columns") return ToggleProperty; 00205 if (name == "Normalize Visible Area") return ToggleProperty; 00206 if (name == "Threshold") return RangeProperty; 00207 if (name == "Zero Padding") return ToggleProperty; 00208 return ValueProperty; 00209 } 00210 00211 QString 00212 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const 00213 { 00214 if (name == "Bin Display" || 00215 name == "Frequency Scale") return tr("Bins"); 00216 if (name == "Window Size" || 00217 name == "Window Increment" || 00218 name == "Zero Padding") return tr("Window"); 00219 if (name == "Colour" || 00220 name == "Threshold" || 00221 name == "Colour Rotation") return tr("Colour"); 00222 if (name == "Normalize Columns" || 00223 name == "Normalize Visible Area" || 00224 name == "Gain" || 00225 name == "Colour Scale") return tr("Scale"); 00226 return QString(); 00227 } 00228 00229 int 00230 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, 00231 int *min, int *max, int *deflt) const 00232 { 00233 int val = 0; 00234 00235 int garbage0, garbage1, garbage2; 00236 if (!min) min = &garbage0; 00237 if (!max) max = &garbage1; 00238 if (!deflt) deflt = &garbage2; 00239 00240 if (name == "Gain") { 00241 00242 *min = -50; 00243 *max = 50; 00244 00245 *deflt = lrintf(log10(m_initialGain) * 20.0);; 00246 if (*deflt < *min) *deflt = *min; 00247 if (*deflt > *max) *deflt = *max; 00248 00249 val = lrintf(log10(m_gain) * 20.0); 00250 if (val < *min) val = *min; 00251 if (val > *max) val = *max; 00252 00253 } else if (name == "Threshold") { 00254 00255 *min = -50; 00256 *max = 0; 00257 00258 *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold)); 00259 if (*deflt < *min) *deflt = *min; 00260 if (*deflt > *max) *deflt = *max; 00261 00262 val = lrintf(AudioLevel::multiplier_to_dB(m_threshold)); 00263 if (val < *min) val = *min; 00264 if (val > *max) val = *max; 00265 00266 } else if (name == "Colour Rotation") { 00267 00268 *min = 0; 00269 *max = 256; 00270 *deflt = m_initialRotation; 00271 00272 val = m_colourRotation; 00273 00274 } else if (name == "Colour Scale") { 00275 00276 *min = 0; 00277 *max = 4; 00278 *deflt = int(dBColourScale); 00279 00280 val = (int)m_colourScale; 00281 00282 } else if (name == "Colour") { 00283 00284 *min = 0; 00285 *max = ColourMapper::getColourMapCount() - 1; 00286 *deflt = 0; 00287 00288 val = m_colourMap; 00289 00290 } else if (name == "Window Size") { 00291 00292 *min = 0; 00293 *max = 10; 00294 *deflt = 5; 00295 00296 val = 0; 00297 int ws = m_windowSize; 00298 while (ws > 32) { ws >>= 1; val ++; } 00299 00300 } else if (name == "Window Increment") { 00301 00302 *min = 0; 00303 *max = 5; 00304 *deflt = 2; 00305 00306 val = m_windowHopLevel; 00307 00308 } else if (name == "Zero Padding") { 00309 00310 *min = 0; 00311 *max = 1; 00312 *deflt = 0; 00313 00314 val = m_zeroPadLevel > 0 ? 1 : 0; 00315 00316 } else if (name == "Min Frequency") { 00317 00318 *min = 0; 00319 *max = 9; 00320 *deflt = 1; 00321 00322 switch (m_minFrequency) { 00323 case 0: default: val = 0; break; 00324 case 10: val = 1; break; 00325 case 20: val = 2; break; 00326 case 40: val = 3; break; 00327 case 100: val = 4; break; 00328 case 250: val = 5; break; 00329 case 500: val = 6; break; 00330 case 1000: val = 7; break; 00331 case 4000: val = 8; break; 00332 case 10000: val = 9; break; 00333 } 00334 00335 } else if (name == "Max Frequency") { 00336 00337 *min = 0; 00338 *max = 9; 00339 *deflt = 6; 00340 00341 switch (m_maxFrequency) { 00342 case 500: val = 0; break; 00343 case 1000: val = 1; break; 00344 case 1500: val = 2; break; 00345 case 2000: val = 3; break; 00346 case 4000: val = 4; break; 00347 case 6000: val = 5; break; 00348 case 8000: val = 6; break; 00349 case 12000: val = 7; break; 00350 case 16000: val = 8; break; 00351 default: val = 9; break; 00352 } 00353 00354 } else if (name == "Frequency Scale") { 00355 00356 *min = 0; 00357 *max = 1; 00358 *deflt = int(LinearFrequencyScale); 00359 val = (int)m_frequencyScale; 00360 00361 } else if (name == "Bin Display") { 00362 00363 *min = 0; 00364 *max = 2; 00365 *deflt = int(AllBins); 00366 val = (int)m_binDisplay; 00367 00368 } else if (name == "Normalize Columns") { 00369 00370 *deflt = 0; 00371 val = (m_normalizeColumns ? 1 : 0); 00372 00373 } else if (name == "Normalize Visible Area") { 00374 00375 *deflt = 0; 00376 val = (m_normalizeVisibleArea ? 1 : 0); 00377 00378 } else { 00379 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); 00380 } 00381 00382 return val; 00383 } 00384 00385 QString 00386 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, 00387 int value) const 00388 { 00389 if (name == "Colour") { 00390 return ColourMapper::getColourMapName(value); 00391 } 00392 if (name == "Colour Scale") { 00393 switch (value) { 00394 default: 00395 case 0: return tr("Linear"); 00396 case 1: return tr("Meter"); 00397 case 2: return tr("dBV^2"); 00398 case 3: return tr("dBV"); 00399 case 4: return tr("Phase"); 00400 } 00401 } 00402 if (name == "Window Size") { 00403 return QString("%1").arg(32 << value); 00404 } 00405 if (name == "Window Increment") { 00406 switch (value) { 00407 default: 00408 case 0: return tr("None"); 00409 case 1: return tr("25 %"); 00410 case 2: return tr("50 %"); 00411 case 3: return tr("75 %"); 00412 case 4: return tr("87.5 %"); 00413 case 5: return tr("93.75 %"); 00414 } 00415 } 00416 if (name == "Zero Padding") { 00417 if (value == 0) return tr("None"); 00418 return QString("%1x").arg(value + 1); 00419 } 00420 if (name == "Min Frequency") { 00421 switch (value) { 00422 default: 00423 case 0: return tr("No min"); 00424 case 1: return tr("10 Hz"); 00425 case 2: return tr("20 Hz"); 00426 case 3: return tr("40 Hz"); 00427 case 4: return tr("100 Hz"); 00428 case 5: return tr("250 Hz"); 00429 case 6: return tr("500 Hz"); 00430 case 7: return tr("1 KHz"); 00431 case 8: return tr("4 KHz"); 00432 case 9: return tr("10 KHz"); 00433 } 00434 } 00435 if (name == "Max Frequency") { 00436 switch (value) { 00437 default: 00438 case 0: return tr("500 Hz"); 00439 case 1: return tr("1 KHz"); 00440 case 2: return tr("1.5 KHz"); 00441 case 3: return tr("2 KHz"); 00442 case 4: return tr("4 KHz"); 00443 case 5: return tr("6 KHz"); 00444 case 6: return tr("8 KHz"); 00445 case 7: return tr("12 KHz"); 00446 case 8: return tr("16 KHz"); 00447 case 9: return tr("No max"); 00448 } 00449 } 00450 if (name == "Frequency Scale") { 00451 switch (value) { 00452 default: 00453 case 0: return tr("Linear"); 00454 case 1: return tr("Log"); 00455 } 00456 } 00457 if (name == "Bin Display") { 00458 switch (value) { 00459 default: 00460 case 0: return tr("All Bins"); 00461 case 1: return tr("Peak Bins"); 00462 case 2: return tr("Frequencies"); 00463 } 00464 } 00465 return tr("<unknown>"); 00466 } 00467 00468 RangeMapper * 00469 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const 00470 { 00471 if (name == "Gain") { 00472 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); 00473 } 00474 if (name == "Threshold") { 00475 return new LinearRangeMapper(-50, 0, -50, 0, tr("dB")); 00476 } 00477 return 0; 00478 } 00479 00480 void 00481 SpectrogramLayer::setProperty(const PropertyName &name, int value) 00482 { 00483 if (name == "Gain") { 00484 setGain(pow(10, float(value)/20.0)); 00485 } else if (name == "Threshold") { 00486 if (value == -50) setThreshold(0.0); 00487 else setThreshold(AudioLevel::dB_to_multiplier(value)); 00488 } else if (name == "Colour Rotation") { 00489 setColourRotation(value); 00490 } else if (name == "Colour") { 00491 setColourMap(value); 00492 } else if (name == "Window Size") { 00493 setWindowSize(32 << value); 00494 } else if (name == "Window Increment") { 00495 setWindowHopLevel(value); 00496 } else if (name == "Zero Padding") { 00497 setZeroPadLevel(value > 0.1 ? 3 : 0); 00498 } else if (name == "Min Frequency") { 00499 switch (value) { 00500 default: 00501 case 0: setMinFrequency(0); break; 00502 case 1: setMinFrequency(10); break; 00503 case 2: setMinFrequency(20); break; 00504 case 3: setMinFrequency(40); break; 00505 case 4: setMinFrequency(100); break; 00506 case 5: setMinFrequency(250); break; 00507 case 6: setMinFrequency(500); break; 00508 case 7: setMinFrequency(1000); break; 00509 case 8: setMinFrequency(4000); break; 00510 case 9: setMinFrequency(10000); break; 00511 } 00512 int vs = getCurrentVerticalZoomStep(); 00513 if (vs != m_lastEmittedZoomStep) { 00514 emit verticalZoomChanged(); 00515 m_lastEmittedZoomStep = vs; 00516 } 00517 } else if (name == "Max Frequency") { 00518 switch (value) { 00519 case 0: setMaxFrequency(500); break; 00520 case 1: setMaxFrequency(1000); break; 00521 case 2: setMaxFrequency(1500); break; 00522 case 3: setMaxFrequency(2000); break; 00523 case 4: setMaxFrequency(4000); break; 00524 case 5: setMaxFrequency(6000); break; 00525 case 6: setMaxFrequency(8000); break; 00526 case 7: setMaxFrequency(12000); break; 00527 case 8: setMaxFrequency(16000); break; 00528 default: 00529 case 9: setMaxFrequency(0); break; 00530 } 00531 int vs = getCurrentVerticalZoomStep(); 00532 if (vs != m_lastEmittedZoomStep) { 00533 emit verticalZoomChanged(); 00534 m_lastEmittedZoomStep = vs; 00535 } 00536 } else if (name == "Colour Scale") { 00537 switch (value) { 00538 default: 00539 case 0: setColourScale(LinearColourScale); break; 00540 case 1: setColourScale(MeterColourScale); break; 00541 case 2: setColourScale(dBSquaredColourScale); break; 00542 case 3: setColourScale(dBColourScale); break; 00543 case 4: setColourScale(PhaseColourScale); break; 00544 } 00545 } else if (name == "Frequency Scale") { 00546 switch (value) { 00547 default: 00548 case 0: setFrequencyScale(LinearFrequencyScale); break; 00549 case 1: setFrequencyScale(LogFrequencyScale); break; 00550 } 00551 } else if (name == "Bin Display") { 00552 switch (value) { 00553 default: 00554 case 0: setBinDisplay(AllBins); break; 00555 case 1: setBinDisplay(PeakBins); break; 00556 case 2: setBinDisplay(PeakFrequencies); break; 00557 } 00558 } else if (name == "Normalize Columns") { 00559 setNormalizeColumns(value ? true : false); 00560 } else if (name == "Normalize Visible Area") { 00561 setNormalizeVisibleArea(value ? true : false); 00562 } 00563 } 00564 00565 void 00566 SpectrogramLayer::invalidateImageCaches() 00567 { 00568 for (ViewImageCache::iterator i = m_imageCaches.begin(); 00569 i != m_imageCaches.end(); ++i) { 00570 i->second.validArea = QRect(); 00571 } 00572 } 00573 00574 void 00575 SpectrogramLayer::invalidateImageCaches(int startFrame, int endFrame) 00576 { 00577 for (ViewImageCache::iterator i = m_imageCaches.begin(); 00578 i != m_imageCaches.end(); ++i) { 00579 00581 const View *v = i->first; 00582 00583 #ifdef DEBUG_SPECTROGRAM_REPAINT 00584 SVDEBUG << "SpectrogramLayer::invalidateImageCaches(" 00585 << startFrame << ", " << endFrame << "): view range is " 00586 << v->getStartFrame() << ", " << v->getEndFrame() 00587 << endl; 00588 00589 cerr << "Valid area was: " << i->second.validArea.x() << ", " 00590 << i->second.validArea.y() << " " 00591 << i->second.validArea.width() << "x" 00592 << i->second.validArea.height() << endl; 00593 #endif 00594 00595 if (int(startFrame) > v->getStartFrame()) { 00596 if (startFrame >= v->getEndFrame()) { 00597 #ifdef DEBUG_SPECTROGRAM_REPAINT 00598 cerr << "Modified start frame is off right of view" << endl; 00599 #endif 00600 return; 00601 } 00602 int x = v->getXForFrame(startFrame); 00603 #ifdef DEBUG_SPECTROGRAM_REPAINT 00604 SVDEBUG << "clipping from 0 to " << x-1 << endl; 00605 #endif 00606 if (x > 1) { 00607 i->second.validArea &= 00608 QRect(0, 0, x-1, v->height()); 00609 } else { 00610 i->second.validArea = QRect(); 00611 } 00612 } else { 00613 if (int(endFrame) < v->getStartFrame()) { 00614 #ifdef DEBUG_SPECTROGRAM_REPAINT 00615 cerr << "Modified end frame is off left of view" << endl; 00616 #endif 00617 return; 00618 } 00619 int x = v->getXForFrame(endFrame); 00620 #ifdef DEBUG_SPECTROGRAM_REPAINT 00621 SVDEBUG << "clipping from " << x+1 << " to " << v->width() 00622 << endl; 00623 #endif 00624 if (x < v->width()) { 00625 i->second.validArea &= 00626 QRect(x+1, 0, v->width()-(x+1), v->height()); 00627 } else { 00628 i->second.validArea = QRect(); 00629 } 00630 } 00631 00632 #ifdef DEBUG_SPECTROGRAM_REPAINT 00633 cerr << "Valid area is now: " << i->second.validArea.x() << ", " 00634 << i->second.validArea.y() << " " 00635 << i->second.validArea.width() << "x" 00636 << i->second.validArea.height() << endl; 00637 #endif 00638 } 00639 } 00640 00641 void 00642 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) 00643 { 00644 SVDEBUG << "SpectrogramLayer::preferenceChanged(" << name << ")" << endl; 00645 00646 if (name == "Window Type") { 00647 setWindowType(Preferences::getInstance()->getWindowType()); 00648 return; 00649 } 00650 if (name == "Spectrogram Y Smoothing") { 00651 invalidateImageCaches(); 00652 invalidateMagnitudes(); 00653 emit layerParametersChanged(); 00654 } 00655 if (name == "Spectrogram X Smoothing") { 00656 invalidateImageCaches(); 00657 invalidateMagnitudes(); 00658 emit layerParametersChanged(); 00659 } 00660 if (name == "Tuning Frequency") { 00661 emit layerParametersChanged(); 00662 } 00663 } 00664 00665 void 00666 SpectrogramLayer::setChannel(int ch) 00667 { 00668 if (m_channel == ch) return; 00669 00670 invalidateImageCaches(); 00671 m_channel = ch; 00672 invalidateFFTModels(); 00673 00674 emit layerParametersChanged(); 00675 } 00676 00677 int 00678 SpectrogramLayer::getChannel() const 00679 { 00680 return m_channel; 00681 } 00682 00683 void 00684 SpectrogramLayer::setWindowSize(int ws) 00685 { 00686 if (m_windowSize == ws) return; 00687 00688 invalidateImageCaches(); 00689 00690 m_windowSize = ws; 00691 m_fftSize = ws * (m_zeroPadLevel + 1); 00692 00693 invalidateFFTModels(); 00694 00695 emit layerParametersChanged(); 00696 } 00697 00698 int 00699 SpectrogramLayer::getWindowSize() const 00700 { 00701 return m_windowSize; 00702 } 00703 00704 void 00705 SpectrogramLayer::setWindowHopLevel(int v) 00706 { 00707 if (m_windowHopLevel == v) return; 00708 00709 invalidateImageCaches(); 00710 00711 m_windowHopLevel = v; 00712 00713 invalidateFFTModels(); 00714 00715 emit layerParametersChanged(); 00716 00717 // fillCache(); 00718 } 00719 00720 int 00721 SpectrogramLayer::getWindowHopLevel() const 00722 { 00723 return m_windowHopLevel; 00724 } 00725 00726 void 00727 SpectrogramLayer::setZeroPadLevel(int v) 00728 { 00729 if (m_zeroPadLevel == v) return; 00730 00731 invalidateImageCaches(); 00732 00733 m_zeroPadLevel = v; 00734 m_fftSize = m_windowSize * (v + 1); 00735 00736 invalidateFFTModels(); 00737 00738 emit layerParametersChanged(); 00739 } 00740 00741 int 00742 SpectrogramLayer::getZeroPadLevel() const 00743 { 00744 return m_zeroPadLevel; 00745 } 00746 00747 void 00748 SpectrogramLayer::setWindowType(WindowType w) 00749 { 00750 if (m_windowType == w) return; 00751 00752 invalidateImageCaches(); 00753 00754 m_windowType = w; 00755 00756 invalidateFFTModels(); 00757 00758 emit layerParametersChanged(); 00759 } 00760 00761 WindowType 00762 SpectrogramLayer::getWindowType() const 00763 { 00764 return m_windowType; 00765 } 00766 00767 void 00768 SpectrogramLayer::setGain(float gain) 00769 { 00770 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " 00771 // << m_gain << ")" << endl; 00772 00773 if (m_gain == gain) return; 00774 00775 invalidateImageCaches(); 00776 00777 m_gain = gain; 00778 00779 emit layerParametersChanged(); 00780 } 00781 00782 float 00783 SpectrogramLayer::getGain() const 00784 { 00785 return m_gain; 00786 } 00787 00788 void 00789 SpectrogramLayer::setThreshold(float threshold) 00790 { 00791 if (m_threshold == threshold) return; 00792 00793 invalidateImageCaches(); 00794 00795 m_threshold = threshold; 00796 00797 emit layerParametersChanged(); 00798 } 00799 00800 float 00801 SpectrogramLayer::getThreshold() const 00802 { 00803 return m_threshold; 00804 } 00805 00806 void 00807 SpectrogramLayer::setMinFrequency(int mf) 00808 { 00809 if (m_minFrequency == mf) return; 00810 00811 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl; 00812 00813 invalidateImageCaches(); 00814 invalidateMagnitudes(); 00815 00816 m_minFrequency = mf; 00817 00818 emit layerParametersChanged(); 00819 } 00820 00821 int 00822 SpectrogramLayer::getMinFrequency() const 00823 { 00824 return m_minFrequency; 00825 } 00826 00827 void 00828 SpectrogramLayer::setMaxFrequency(int mf) 00829 { 00830 if (m_maxFrequency == mf) return; 00831 00832 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl; 00833 00834 invalidateImageCaches(); 00835 invalidateMagnitudes(); 00836 00837 m_maxFrequency = mf; 00838 00839 emit layerParametersChanged(); 00840 } 00841 00842 int 00843 SpectrogramLayer::getMaxFrequency() const 00844 { 00845 return m_maxFrequency; 00846 } 00847 00848 void 00849 SpectrogramLayer::setColourRotation(int r) 00850 { 00851 invalidateImageCaches(); 00852 00853 if (r < 0) r = 0; 00854 if (r > 256) r = 256; 00855 int distance = r - m_colourRotation; 00856 00857 if (distance != 0) { 00858 rotatePalette(-distance); 00859 m_colourRotation = r; 00860 } 00861 00862 emit layerParametersChanged(); 00863 } 00864 00865 void 00866 SpectrogramLayer::setColourScale(ColourScale colourScale) 00867 { 00868 if (m_colourScale == colourScale) return; 00869 00870 invalidateImageCaches(); 00871 00872 m_colourScale = colourScale; 00873 00874 emit layerParametersChanged(); 00875 } 00876 00877 SpectrogramLayer::ColourScale 00878 SpectrogramLayer::getColourScale() const 00879 { 00880 return m_colourScale; 00881 } 00882 00883 void 00884 SpectrogramLayer::setColourMap(int map) 00885 { 00886 if (m_colourMap == map) return; 00887 00888 invalidateImageCaches(); 00889 00890 m_colourMap = map; 00891 initialisePalette(); 00892 00893 emit layerParametersChanged(); 00894 } 00895 00896 int 00897 SpectrogramLayer::getColourMap() const 00898 { 00899 return m_colourMap; 00900 } 00901 00902 void 00903 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) 00904 { 00905 if (m_frequencyScale == frequencyScale) return; 00906 00907 invalidateImageCaches(); 00908 m_frequencyScale = frequencyScale; 00909 00910 emit layerParametersChanged(); 00911 } 00912 00913 SpectrogramLayer::FrequencyScale 00914 SpectrogramLayer::getFrequencyScale() const 00915 { 00916 return m_frequencyScale; 00917 } 00918 00919 void 00920 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay) 00921 { 00922 if (m_binDisplay == binDisplay) return; 00923 00924 invalidateImageCaches(); 00925 m_binDisplay = binDisplay; 00926 00927 emit layerParametersChanged(); 00928 } 00929 00930 SpectrogramLayer::BinDisplay 00931 SpectrogramLayer::getBinDisplay() const 00932 { 00933 return m_binDisplay; 00934 } 00935 00936 void 00937 SpectrogramLayer::setNormalizeColumns(bool n) 00938 { 00939 if (m_normalizeColumns == n) return; 00940 00941 invalidateImageCaches(); 00942 invalidateMagnitudes(); 00943 m_normalizeColumns = n; 00944 00945 emit layerParametersChanged(); 00946 } 00947 00948 bool 00949 SpectrogramLayer::getNormalizeColumns() const 00950 { 00951 return m_normalizeColumns; 00952 } 00953 00954 void 00955 SpectrogramLayer::setNormalizeHybrid(bool n) 00956 { 00957 if (m_normalizeHybrid == n) return; 00958 00959 invalidateImageCaches(); 00960 invalidateMagnitudes(); 00961 m_normalizeHybrid = n; 00962 00963 emit layerParametersChanged(); 00964 } 00965 00966 bool 00967 SpectrogramLayer::getNormalizeHybrid() const 00968 { 00969 return m_normalizeHybrid; 00970 } 00971 00972 void 00973 SpectrogramLayer::setNormalizeVisibleArea(bool n) 00974 { 00975 SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n 00976 << ") (from " << m_normalizeVisibleArea << ")" << endl; 00977 00978 if (m_normalizeVisibleArea == n) return; 00979 00980 invalidateImageCaches(); 00981 invalidateMagnitudes(); 00982 m_normalizeVisibleArea = n; 00983 00984 emit layerParametersChanged(); 00985 } 00986 00987 bool 00988 SpectrogramLayer::getNormalizeVisibleArea() const 00989 { 00990 return m_normalizeVisibleArea; 00991 } 00992 00993 void 00994 SpectrogramLayer::setLayerDormant(const View *v, bool dormant) 00995 { 00996 if (dormant) { 00997 00998 #ifdef DEBUG_SPECTROGRAM_REPAINT 00999 SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")" 01000 << endl; 01001 #endif 01002 01003 if (isLayerDormant(v)) { 01004 return; 01005 } 01006 01007 Layer::setLayerDormant(v, true); 01008 01009 invalidateImageCaches(); 01010 m_imageCaches.erase(v); 01011 01012 if (m_fftModels.find(v) != m_fftModels.end()) { 01013 01014 if (m_sliceableModel == m_fftModels[v].first) { 01015 bool replaced = false; 01016 for (ViewFFTMap::iterator i = m_fftModels.begin(); 01017 i != m_fftModels.end(); ++i) { 01018 if (i->second.first != m_sliceableModel) { 01019 emit sliceableModelReplaced(m_sliceableModel, i->second.first); 01020 replaced = true; 01021 break; 01022 } 01023 } 01024 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); 01025 } 01026 01027 delete m_fftModels[v].first; 01028 m_fftModels.erase(v); 01029 01030 delete m_peakCaches[v]; 01031 m_peakCaches.erase(v); 01032 } 01033 01034 } else { 01035 01036 Layer::setLayerDormant(v, false); 01037 } 01038 } 01039 01040 void 01041 SpectrogramLayer::cacheInvalid() 01042 { 01043 #ifdef DEBUG_SPECTROGRAM_REPAINT 01044 SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl; 01045 #endif 01046 01047 invalidateImageCaches(); 01048 invalidateMagnitudes(); 01049 } 01050 01051 void 01052 SpectrogramLayer::cacheInvalid(int from, int to) 01053 { 01054 #ifdef DEBUG_SPECTROGRAM_REPAINT 01055 SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; 01056 #endif 01057 01058 invalidateImageCaches(from, to); 01059 invalidateMagnitudes(); 01060 } 01061 01062 void 01063 SpectrogramLayer::fillTimerTimedOut() 01064 { 01065 if (!m_model) return; 01066 01067 bool allDone = true; 01068 01069 #ifdef DEBUG_SPECTROGRAM_REPAINT 01070 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl; 01071 #endif 01072 01073 for (ViewFFTMap::iterator i = m_fftModels.begin(); 01074 i != m_fftModels.end(); ++i) { 01075 01076 const FFTModel *model = i->second.first; 01077 int lastFill = i->second.second; 01078 01079 if (model) { 01080 01081 int fill = model->getFillExtent(); 01082 01083 #ifdef DEBUG_SPECTROGRAM_REPAINT 01084 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl; 01085 #endif 01086 01087 if (fill >= lastFill) { 01088 if (fill >= m_model->getEndFrame() && lastFill > 0) { 01089 #ifdef DEBUG_SPECTROGRAM_REPAINT 01090 cerr << "complete!" << endl; 01091 #endif 01092 invalidateImageCaches(); 01093 i->second.second = -1; 01094 emit modelChanged(); 01095 01096 } else if (fill > lastFill) { 01097 #ifdef DEBUG_SPECTROGRAM_REPAINT 01098 cerr << "SpectrogramLayer: emitting modelChanged(" 01099 << lastFill << "," << fill << ")" << endl; 01100 #endif 01101 invalidateImageCaches(lastFill, fill); 01102 i->second.second = fill; 01103 emit modelChangedWithin(lastFill, fill); 01104 } 01105 } else { 01106 #ifdef DEBUG_SPECTROGRAM_REPAINT 01107 cerr << "SpectrogramLayer: going backwards, emitting modelChanged(" 01108 << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl; 01109 #endif 01110 invalidateImageCaches(); 01111 i->second.second = fill; 01112 emit modelChangedWithin(m_model->getStartFrame(), m_model->getEndFrame()); 01113 } 01114 01115 if (i->second.second >= 0) { 01116 allDone = false; 01117 } 01118 } 01119 } 01120 01121 if (allDone) { 01122 #ifdef DEBUG_SPECTROGRAM_REPAINT 01123 cerr << "SpectrogramLayer: all complete!" << endl; 01124 #endif 01125 delete m_updateTimer; 01126 m_updateTimer = 0; 01127 } 01128 } 01129 01130 bool 01131 SpectrogramLayer::hasLightBackground() const 01132 { 01133 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground(); 01134 } 01135 01136 void 01137 SpectrogramLayer::initialisePalette() 01138 { 01139 int formerRotation = m_colourRotation; 01140 01141 if (m_colourMap == (int)ColourMapper::BlackOnWhite) { 01142 m_palette.setColour(NO_VALUE, Qt::white); 01143 } else { 01144 m_palette.setColour(NO_VALUE, Qt::black); 01145 } 01146 01147 ColourMapper mapper(m_colourMap, 1.f, 255.f); 01148 01149 for (int pixel = 1; pixel < 256; ++pixel) { 01150 m_palette.setColour(pixel, mapper.map(pixel)); 01151 } 01152 01153 m_crosshairColour = mapper.getContrastingColour(); 01154 01155 m_colourRotation = 0; 01156 rotatePalette(m_colourRotation - formerRotation); 01157 m_colourRotation = formerRotation; 01158 01159 m_drawBuffer = QImage(); 01160 } 01161 01162 void 01163 SpectrogramLayer::rotatePalette(int distance) 01164 { 01165 QColor newPixels[256]; 01166 01167 newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE); 01168 01169 for (int pixel = 1; pixel < 256; ++pixel) { 01170 int target = pixel + distance; 01171 while (target < 1) target += 255; 01172 while (target > 255) target -= 255; 01173 newPixels[target] = m_palette.getColour(pixel); 01174 } 01175 01176 for (int pixel = 0; pixel < 256; ++pixel) { 01177 m_palette.setColour(pixel, newPixels[pixel]); 01178 } 01179 01180 m_drawBuffer = QImage(); 01181 } 01182 01183 unsigned char 01184 SpectrogramLayer::getDisplayValue(View *v, float input) const 01185 { 01186 int value; 01187 01188 float min = 0.f; 01189 float max = 1.f; 01190 01191 if (m_normalizeVisibleArea) { 01192 min = m_viewMags[v].getMin(); 01193 max = m_viewMags[v].getMax(); 01194 } else if (!m_normalizeColumns) { 01195 if (m_colourScale == LinearColourScale //|| 01196 // m_colourScale == MeterColourScale) { 01197 ) { 01198 max = 0.1f; 01199 } 01200 } 01201 01202 float thresh = -80.f; 01203 01204 if (max == 0.f) max = 1.f; 01205 if (max == min) min = max - 0.0001f; 01206 01207 switch (m_colourScale) { 01208 01209 default: 01210 case LinearColourScale: 01211 value = int(((input - min) / (max - min)) * 255.f) + 1; 01212 break; 01213 01214 case MeterColourScale: 01215 value = AudioLevel::multiplier_to_preview 01216 ((input - min) / (max - min), 254) + 1; 01217 break; 01218 01219 case dBSquaredColourScale: 01220 input = ((input - min) * (input - min)) / ((max - min) * (max - min)); 01221 if (input > 0.f) { 01222 input = 10.f * log10f(input); 01223 } else { 01224 input = thresh; 01225 } 01226 if (min > 0.f) { 01227 thresh = 10.f * log10f(min * min); 01228 if (thresh < -80.f) thresh = -80.f; 01229 } 01230 input = (input - thresh) / (-thresh); 01231 if (input < 0.f) input = 0.f; 01232 if (input > 1.f) input = 1.f; 01233 value = int(input * 255.f) + 1; 01234 break; 01235 01236 case dBColourScale: 01238 //In any case, we need to have some indication of what the dB 01239 //scale is relative to. 01240 input = (input - min) / (max - min); 01241 if (input > 0.f) { 01242 input = 10.f * log10f(input); 01243 } else { 01244 input = thresh; 01245 } 01246 if (min > 0.f) { 01247 thresh = 10.f * log10f(min); 01248 if (thresh < -80.f) thresh = -80.f; 01249 } 01250 input = (input - thresh) / (-thresh); 01251 if (input < 0.f) input = 0.f; 01252 if (input > 1.f) input = 1.f; 01253 value = int(input * 255.f) + 1; 01254 break; 01255 01256 case PhaseColourScale: 01257 value = int((input * 127.0 / M_PI) + 128); 01258 break; 01259 } 01260 01261 if (value > UCHAR_MAX) value = UCHAR_MAX; 01262 if (value < 0) value = 0; 01263 return value; 01264 } 01265 01266 float 01267 SpectrogramLayer::getEffectiveMinFrequency() const 01268 { 01269 int sr = m_model->getSampleRate(); 01270 float minf = float(sr) / m_fftSize; 01271 01272 if (m_minFrequency > 0.0) { 01273 int minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.01); 01274 if (minbin < 1) minbin = 1; 01275 minf = minbin * sr / m_fftSize; 01276 } 01277 01278 return minf; 01279 } 01280 01281 float 01282 SpectrogramLayer::getEffectiveMaxFrequency() const 01283 { 01284 int sr = m_model->getSampleRate(); 01285 float maxf = float(sr) / 2; 01286 01287 if (m_maxFrequency > 0.0) { 01288 int maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 01289 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 01290 maxf = maxbin * sr / m_fftSize; 01291 } 01292 01293 return maxf; 01294 } 01295 01296 bool 01297 SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const 01298 { 01299 Profiler profiler("SpectrogramLayer::getYBinRange"); 01300 01301 int h = v->height(); 01302 if (y < 0 || y >= h) return false; 01303 01304 int sr = m_model->getSampleRate(); 01305 float minf = getEffectiveMinFrequency(); 01306 float maxf = getEffectiveMaxFrequency(); 01307 01308 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 01309 01310 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); 01311 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); 01312 01313 // Now map these on to ("proportions of") actual bins, using raw 01314 // FFT size (unsmoothed) 01315 01316 q0 = (q0 * m_fftSize) / sr; 01317 q1 = (q1 * m_fftSize) / sr; 01318 01319 return true; 01320 } 01321 01322 bool 01323 SpectrogramLayer::getSmoothedYBinRange(View *v, int y, float &q0, float &q1) const 01324 { 01325 Profiler profiler("SpectrogramLayer::getSmoothedYBinRange"); 01326 01327 int h = v->height(); 01328 if (y < 0 || y >= h) return false; 01329 01330 int sr = m_model->getSampleRate(); 01331 float minf = getEffectiveMinFrequency(); 01332 float maxf = getEffectiveMaxFrequency(); 01333 01334 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 01335 01336 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); 01337 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); 01338 01339 // Now map these on to ("proportions of") actual bins, using raw 01340 // FFT size (unsmoothed) 01341 01342 q0 = (q0 * getFFTSize(v)) / sr; 01343 q1 = (q1 * getFFTSize(v)) / sr; 01344 01345 return true; 01346 } 01347 01348 bool 01349 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const 01350 { 01351 int modelStart = m_model->getStartFrame(); 01352 int modelEnd = m_model->getEndFrame(); 01353 01354 // Each pixel column covers an exact range of sample frames: 01355 int f0 = v->getFrameForX(x) - modelStart; 01356 int f1 = v->getFrameForX(x + 1) - modelStart - 1; 01357 01358 if (f1 < int(modelStart) || f0 > int(modelEnd)) { 01359 return false; 01360 } 01361 01362 // And that range may be drawn from a possibly non-integral 01363 // range of spectrogram windows: 01364 01365 int windowIncrement = getWindowIncrement(); 01366 s0 = float(f0) / windowIncrement; 01367 s1 = float(f1) / windowIncrement; 01368 01369 return true; 01370 } 01371 01372 bool 01373 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const 01374 { 01375 float s0 = 0, s1 = 0; 01376 if (!getXBinRange(v, x, s0, s1)) return false; 01377 01378 int s0i = int(s0 + 0.001); 01379 int s1i = int(s1); 01380 01381 int windowIncrement = getWindowIncrement(); 01382 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2; 01383 int w1 = s1i * windowIncrement + windowIncrement + 01384 (m_windowSize - windowIncrement)/2 - 1; 01385 01386 min = RealTime::frame2RealTime(w0, m_model->getSampleRate()); 01387 max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); 01388 return true; 01389 } 01390 01391 bool 01392 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) 01393 const 01394 { 01395 float q0 = 0, q1 = 0; 01396 if (!getYBinRange(v, y, q0, q1)) return false; 01397 01398 int q0i = int(q0 + 0.001); 01399 int q1i = int(q1); 01400 01401 int sr = m_model->getSampleRate(); 01402 01403 for (int q = q0i; q <= q1i; ++q) { 01404 if (q == q0i) freqMin = (sr * q) / m_fftSize; 01405 if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize; 01406 } 01407 return true; 01408 } 01409 01410 bool 01411 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y, 01412 float &freqMin, float &freqMax, 01413 float &adjFreqMin, float &adjFreqMax) 01414 const 01415 { 01416 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 01417 return false; 01418 } 01419 01420 FFTModel *fft = getFFTModel(v); 01421 if (!fft) return false; 01422 01423 float s0 = 0, s1 = 0; 01424 if (!getXBinRange(v, x, s0, s1)) return false; 01425 01426 float q0 = 0, q1 = 0; 01427 if (!getYBinRange(v, y, q0, q1)) return false; 01428 01429 int s0i = int(s0 + 0.001); 01430 int s1i = int(s1); 01431 01432 int q0i = int(q0 + 0.001); 01433 int q1i = int(q1); 01434 01435 int sr = m_model->getSampleRate(); 01436 01437 bool haveAdj = false; 01438 01439 bool peaksOnly = (m_binDisplay == PeakBins || 01440 m_binDisplay == PeakFrequencies); 01441 01442 for (int q = q0i; q <= q1i; ++q) { 01443 01444 for (int s = s0i; s <= s1i; ++s) { 01445 01446 if (!fft->isColumnAvailable(s)) continue; 01447 01448 float binfreq = (float(sr) * q) / m_windowSize; 01449 if (q == q0i) freqMin = binfreq; 01450 if (q == q1i) freqMax = binfreq; 01451 01452 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; 01453 01454 if (!fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) continue; 01455 01456 float freq = binfreq; 01457 01458 if (s < int(fft->getWidth()) - 1) { 01459 01460 fft->estimateStableFrequency(s, q, freq); 01461 01462 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; 01463 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; 01464 01465 haveAdj = true; 01466 } 01467 } 01468 } 01469 01470 if (!haveAdj) { 01471 adjFreqMin = adjFreqMax = 0.0; 01472 } 01473 01474 return haveAdj; 01475 } 01476 01477 bool 01478 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y, 01479 float &min, float &max, 01480 float &phaseMin, float &phaseMax) const 01481 { 01482 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 01483 return false; 01484 } 01485 01486 float q0 = 0, q1 = 0; 01487 if (!getYBinRange(v, y, q0, q1)) return false; 01488 01489 float s0 = 0, s1 = 0; 01490 if (!getXBinRange(v, x, s0, s1)) return false; 01491 01492 int q0i = int(q0 + 0.001); 01493 int q1i = int(q1); 01494 01495 int s0i = int(s0 + 0.001); 01496 int s1i = int(s1); 01497 01498 bool rv = false; 01499 01500 int zp = getZeroPadLevel(v); 01501 q0i *= zp + 1; 01502 q1i *= zp + 1; 01503 01504 FFTModel *fft = getFFTModel(v); 01505 01506 if (fft) { 01507 01508 int cw = fft->getWidth(); 01509 int ch = fft->getHeight(); 01510 01511 min = 0.0; 01512 max = 0.0; 01513 phaseMin = 0.0; 01514 phaseMax = 0.0; 01515 bool have = false; 01516 01517 for (int q = q0i; q <= q1i; ++q) { 01518 for (int s = s0i; s <= s1i; ++s) { 01519 if (s >= 0 && q >= 0 && s < cw && q < ch) { 01520 01521 if (!fft->isColumnAvailable(s)) continue; 01522 01523 float value; 01524 01525 value = fft->getPhaseAt(s, q); 01526 if (!have || value < phaseMin) { phaseMin = value; } 01527 if (!have || value > phaseMax) { phaseMax = value; } 01528 01529 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2); 01530 if (!have || value < min) { min = value; } 01531 if (!have || value > max) { max = value; } 01532 01533 have = true; 01534 } 01535 } 01536 } 01537 01538 if (have) { 01539 rv = true; 01540 } 01541 } 01542 01543 return rv; 01544 } 01545 01546 int 01547 SpectrogramLayer::getZeroPadLevel(const View *v) const 01548 { 01550 01551 if (m_binDisplay != AllBins) return 0; 01552 01553 Preferences::SpectrogramSmoothing smoothing = 01554 Preferences::getInstance()->getSpectrogramSmoothing(); 01555 01556 if (smoothing == Preferences::NoSpectrogramSmoothing || 01557 smoothing == Preferences::SpectrogramInterpolated) return 0; 01558 01559 if (m_frequencyScale == LogFrequencyScale) return 3; 01560 01561 int sr = m_model->getSampleRate(); 01562 01563 int maxbin = m_fftSize / 2; 01564 if (m_maxFrequency > 0) { 01565 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 01566 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 01567 } 01568 01569 int minbin = 1; 01570 if (m_minFrequency > 0) { 01571 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1); 01572 if (minbin < 1) minbin = 1; 01573 if (minbin >= maxbin) minbin = maxbin - 1; 01574 } 01575 01576 float perPixel = 01577 float(v->height()) / 01578 float((maxbin - minbin) / (m_zeroPadLevel + 1)); 01579 01580 if (perPixel > 2.8) { 01581 return 3; // 4x oversampling 01582 } else if (perPixel > 1.5) { 01583 return 1; // 2x 01584 } else { 01585 return 0; // 1x 01586 } 01587 } 01588 01589 int 01590 SpectrogramLayer::getFFTSize(const View *v) const 01591 { 01592 return m_fftSize * (getZeroPadLevel(v) + 1); 01593 } 01594 01595 FFTModel * 01596 SpectrogramLayer::getFFTModel(const View *v) const 01597 { 01598 if (!m_model) return 0; 01599 01600 int fftSize = getFFTSize(v); 01601 01602 if (m_fftModels.find(v) != m_fftModels.end()) { 01603 if (m_fftModels[v].first == 0) { 01604 #ifdef DEBUG_SPECTROGRAM_REPAINT 01605 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; 01606 #endif 01607 return 0; 01608 } 01609 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) { 01610 #ifdef DEBUG_SPECTROGRAM_REPAINT 01611 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; 01612 #endif 01613 delete m_fftModels[v].first; 01614 m_fftModels.erase(v); 01615 delete m_peakCaches[v]; 01616 m_peakCaches.erase(v); 01617 } else { 01618 #ifdef DEBUG_SPECTROGRAM_REPAINT 01619 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl; 01620 #endif 01621 return m_fftModels[v].first; 01622 } 01623 } 01624 01625 if (m_fftModels.find(v) == m_fftModels.end()) { 01626 01627 FFTModel *model = new FFTModel(m_model, 01628 m_channel, 01629 m_windowType, 01630 m_windowSize, 01631 getWindowIncrement(), 01632 fftSize, 01633 true, // polar 01634 StorageAdviser::SpeedCritical, 01635 m_candidateFillStartFrame); 01636 01637 if (!model->isOK()) { 01638 QMessageBox::critical 01639 (0, tr("FFT cache failed"), 01640 tr("Failed to create the FFT model for this spectrogram.\n" 01641 "There may be insufficient memory or disc space to continue.")); 01642 delete model; 01643 m_fftModels[v] = FFTFillPair(0, 0); 01644 return 0; 01645 } 01646 01647 if (!m_sliceableModel) { 01648 #ifdef DEBUG_SPECTROGRAM 01649 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << endl; 01650 #endif 01651 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); 01652 m_sliceableModel = model; 01653 } 01654 01655 m_fftModels[v] = FFTFillPair(model, 0); 01656 01657 model->resume(); 01658 01659 delete m_updateTimer; 01660 m_updateTimer = new QTimer((SpectrogramLayer *)this); 01661 connect(m_updateTimer, SIGNAL(timeout()), 01662 this, SLOT(fillTimerTimedOut())); 01663 m_updateTimer->start(200); 01664 } 01665 01666 return m_fftModels[v].first; 01667 } 01668 01669 Dense3DModelPeakCache * 01670 SpectrogramLayer::getPeakCache(const View *v) const 01671 { 01672 if (!m_peakCaches[v]) { 01673 FFTModel *f = getFFTModel(v); 01674 if (!f) return 0; 01675 m_peakCaches[v] = new Dense3DModelPeakCache(f, 8); 01676 } 01677 return m_peakCaches[v]; 01678 } 01679 01680 const Model * 01681 SpectrogramLayer::getSliceableModel() const 01682 { 01683 if (m_sliceableModel) return m_sliceableModel; 01684 if (m_fftModels.empty()) return 0; 01685 m_sliceableModel = m_fftModels.begin()->second.first; 01686 return m_sliceableModel; 01687 } 01688 01689 void 01690 SpectrogramLayer::invalidateFFTModels() 01691 { 01692 for (ViewFFTMap::iterator i = m_fftModels.begin(); 01693 i != m_fftModels.end(); ++i) { 01694 delete i->second.first; 01695 } 01696 for (PeakCacheMap::iterator i = m_peakCaches.begin(); 01697 i != m_peakCaches.end(); ++i) { 01698 delete i->second; 01699 } 01700 01701 m_fftModels.clear(); 01702 m_peakCaches.clear(); 01703 01704 if (m_sliceableModel) { 01705 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << endl; 01706 emit sliceableModelReplaced(m_sliceableModel, 0); 01707 m_sliceableModel = 0; 01708 } 01709 } 01710 01711 void 01712 SpectrogramLayer::invalidateMagnitudes() 01713 { 01714 m_viewMags.clear(); 01715 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin(); 01716 i != m_columnMags.end(); ++i) { 01717 *i = MagnitudeRange(); 01718 } 01719 } 01720 01721 bool 01722 SpectrogramLayer::updateViewMagnitudes(View *v) const 01723 { 01724 MagnitudeRange mag; 01725 01726 int x0 = 0, x1 = v->width(); 01727 float s00 = 0, s01 = 0, s10 = 0, s11 = 0; 01728 01729 if (!getXBinRange(v, x0, s00, s01)) { 01730 s00 = s01 = m_model->getStartFrame() / getWindowIncrement(); 01731 } 01732 01733 if (!getXBinRange(v, x1, s10, s11)) { 01734 s10 = s11 = m_model->getEndFrame() / getWindowIncrement(); 01735 } 01736 01737 int s0 = int(std::min(s00, s10) + 0.0001); 01738 int s1 = int(std::max(s01, s11) + 0.0001); 01739 01740 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; 01741 01742 if (int(m_columnMags.size()) <= s1) { 01743 m_columnMags.resize(s1 + 1); 01744 } 01745 01746 for (int s = s0; s <= s1; ++s) { 01747 if (m_columnMags[s].isSet()) { 01748 mag.sample(m_columnMags[s]); 01749 } 01750 } 01751 01752 #ifdef DEBUG_SPECTROGRAM_REPAINT 01753 SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols " 01754 << s0 << " -> " << s1 << " inclusive" << endl; 01755 #endif 01756 01757 if (!mag.isSet()) return false; 01758 if (mag == m_viewMags[v]) return false; 01759 m_viewMags[v] = mag; 01760 return true; 01761 } 01762 01763 void 01764 SpectrogramLayer::setSynchronousPainting(bool synchronous) 01765 { 01766 m_synchronous = synchronous; 01767 } 01768 01769 void 01770 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const 01771 { 01772 // What a lovely, old-fashioned function this is. 01773 // It's practically FORTRAN 77 in its clarity and linearity. 01774 01775 Profiler profiler("SpectrogramLayer::paint", false); 01776 01777 #ifdef DEBUG_SPECTROGRAM_REPAINT 01778 SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl; 01779 01780 cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; 01781 #endif 01782 01783 int startFrame = v->getStartFrame(); 01784 if (startFrame < 0) m_candidateFillStartFrame = 0; 01785 else m_candidateFillStartFrame = startFrame; 01786 01787 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 01788 return; 01789 } 01790 01791 if (isLayerDormant(v)) { 01792 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; 01793 } 01794 01795 // Need to do this even if !isLayerDormant, as that could mean v 01796 // is not in the dormancy map at all -- we need it to be present 01797 // and accountable for when determining whether we need the cache 01798 // in the cache-fill thread above. 01800 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); 01801 01802 int fftSize = getFFTSize(v); 01803 /* 01804 FFTModel *fft = getFFTModel(v); 01805 if (!fft) { 01806 cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl; 01807 return; 01808 } 01809 */ 01810 ImageCache &cache = m_imageCaches[v]; 01811 01812 #ifdef DEBUG_SPECTROGRAM_REPAINT 01813 SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache. 01814 01815 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl; 01816 #endif 01817 01818 #ifdef DEBUG_SPECTROGRAM_REPAINT 01819 bool stillCacheing = (m_updateTimer != 0); 01820 SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl; 01821 #endif 01822 01823 int zoomLevel = v->getZoomLevel(); 01824 01825 int x0 = 0; 01826 int x1 = v->width(); 01827 01828 bool recreateWholeImageCache = true; 01829 01830 x0 = rect.left(); 01831 x1 = rect.right() + 1; 01832 /* 01833 float xPixelRatio = float(fft->getResolution()) / float(zoomLevel); 01834 cerr << "xPixelRatio = " << xPixelRatio << endl; 01835 if (xPixelRatio < 1.f) xPixelRatio = 1.f; 01836 */ 01837 if (cache.validArea.width() > 0) { 01838 01839 int cw = cache.image.width(); 01840 int ch = cache.image.height(); 01841 01842 if (int(cache.zoomLevel) == zoomLevel && 01843 cw == v->width() && 01844 ch == v->height()) { 01845 01846 if (v->getXForFrame(cache.startFrame) == 01847 v->getXForFrame(startFrame) && 01848 cache.validArea.x() <= x0 && 01849 cache.validArea.x() + cache.validArea.width() >= x1) { 01850 01851 #ifdef DEBUG_SPECTROGRAM_REPAINT 01852 cerr << "SpectrogramLayer: image cache good" << endl; 01853 #endif 01854 01855 paint.drawImage(rect, cache.image, rect); 01857 // paint.drawImage(v->rect(), cache.image, 01858 // QRect(QPoint(0, 0), cache.image.size())); 01859 01860 illuminateLocalFeatures(v, paint); 01861 return; 01862 01863 } else { 01864 01865 #ifdef DEBUG_SPECTROGRAM_REPAINT 01866 cerr << "SpectrogramLayer: image cache partially OK" << endl; 01867 #endif 01868 01869 recreateWholeImageCache = false; 01870 01871 int dx = v->getXForFrame(cache.startFrame) - 01872 v->getXForFrame(startFrame); 01873 01874 #ifdef DEBUG_SPECTROGRAM_REPAINT 01875 cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl; 01876 #endif 01877 01878 if (dx != 0 && 01879 dx > -cw && 01880 dx < cw) { 01881 01882 int dxp = dx; 01883 if (dxp < 0) dxp = -dxp; 01884 int copy = (cw - dxp) * sizeof(QRgb); 01885 for (int y = 0; y < ch; ++y) { 01886 QRgb *line = (QRgb *)cache.image.scanLine(y); 01887 if (dx < 0) { 01888 memmove(line, line + dxp, copy); 01889 } else { 01890 memmove(line + dxp, line, copy); 01891 } 01892 } 01893 01894 int px = cache.validArea.x(); 01895 int pw = cache.validArea.width(); 01896 01897 if (dx < 0) { 01898 x0 = cw + dx; 01899 x1 = cw; 01900 px += dx; 01901 if (px < 0) { 01902 pw += px; 01903 px = 0; 01904 if (pw < 0) pw = 0; 01905 } 01906 } else { 01907 x0 = 0; 01908 x1 = dx; 01909 px += dx; 01910 if (px + pw > cw) { 01911 pw = int(cw) - px; 01912 if (pw < 0) pw = 0; 01913 } 01914 } 01915 01916 cache.validArea = 01917 QRect(px, cache.validArea.y(), 01918 pw, cache.validArea.height()); 01919 01920 #ifdef DEBUG_SPECTROGRAM_REPAINT 01921 cerr << "valid area now " 01922 << px << "," << cache.validArea.y() 01923 << " " << pw << "x" << cache.validArea.height() 01924 << endl; 01925 #endif 01926 /* 01927 paint.drawImage(rect & cache.validArea, 01928 cache.image, 01929 rect & cache.validArea); 01930 */ 01931 } else if (dx != 0) { 01932 01933 // we scrolled too far to be of use 01934 01935 #ifdef DEBUG_SPECTROGRAM_REPAINT 01936 cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl; 01937 #endif 01938 01939 cache.validArea = QRect(); 01940 recreateWholeImageCache = true; 01941 } 01942 } 01943 } else { 01944 #ifdef DEBUG_SPECTROGRAM_REPAINT 01945 cerr << "SpectrogramLayer: image cache useless" << endl; 01946 if (int(cache.zoomLevel) != zoomLevel) { 01947 cerr << "(cache zoomLevel " << cache.zoomLevel 01948 << " != " << zoomLevel << ")" << endl; 01949 } 01950 if (cw != v->width()) { 01951 cerr << "(cache width " << cw 01952 << " != " << v->width(); 01953 } 01954 if (ch != v->height()) { 01955 cerr << "(cache height " << ch 01956 << " != " << v->height(); 01957 } 01958 #endif 01959 cache.validArea = QRect(); 01960 // recreateWholeImageCache = true; 01961 } 01962 } 01963 01964 if (updateViewMagnitudes(v)) { 01965 #ifdef DEBUG_SPECTROGRAM_REPAINT 01966 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; 01967 #endif 01968 if (m_normalizeVisibleArea) { 01969 cache.validArea = QRect(); 01970 recreateWholeImageCache = true; 01971 } 01972 } else { 01973 #ifdef DEBUG_SPECTROGRAM_REPAINT 01974 cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; 01975 #endif 01976 } 01977 01978 if (recreateWholeImageCache) { 01979 x0 = 0; 01980 x1 = v->width(); 01981 } 01982 01983 struct timeval tv; 01984 (void)gettimeofday(&tv, 0); 01985 RealTime mainPaintStart = RealTime::fromTimeval(tv); 01986 01987 int paintBlockWidth = m_lastPaintBlockWidth; 01988 01989 if (m_synchronous) { 01990 if (paintBlockWidth < x1 - x0) { 01991 // always paint full width 01992 paintBlockWidth = x1 - x0; 01993 } 01994 } else { 01995 if (paintBlockWidth == 0) { 01996 paintBlockWidth = (300000 / zoomLevel); 01997 } else { 01998 RealTime lastTime = m_lastPaintTime; 01999 while (lastTime > RealTime::fromMilliseconds(200) && 02000 paintBlockWidth > 50) { 02001 paintBlockWidth /= 2; 02002 lastTime = lastTime / 2; 02003 } 02004 while (lastTime < RealTime::fromMilliseconds(90) && 02005 paintBlockWidth < 1500) { 02006 paintBlockWidth *= 2; 02007 lastTime = lastTime * 2; 02008 } 02009 } 02010 02011 if (paintBlockWidth < 20) paintBlockWidth = 20; 02012 } 02013 02014 #ifdef DEBUG_SPECTROGRAM_REPAINT 02015 cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl; 02016 #endif 02017 02018 // We always paint the full height when refreshing the cache. 02019 // Smaller heights can be used when painting direct from cache 02020 // (further up in this function), but we want to ensure the cache 02021 // is coherent without having to worry about vertical matching of 02022 // required and valid areas as well as horizontal. 02023 02024 int h = v->height(); 02025 02026 if (cache.validArea.width() > 0) { 02027 02028 // If part of the cache is known to be valid, select a strip 02029 // immediately to left or right of the valid part 02030 02034 02035 int vx0 = 0, vx1 = 0; 02036 vx0 = cache.validArea.x(); 02037 vx1 = cache.validArea.x() + cache.validArea.width(); 02038 02039 #ifdef DEBUG_SPECTROGRAM_REPAINT 02040 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl; 02041 #endif 02042 if (x0 < vx0) { 02043 if (x0 + paintBlockWidth < vx0) { 02044 x0 = vx0 - paintBlockWidth; 02045 } 02046 x1 = vx0; 02047 } else if (x0 >= vx1) { 02048 x0 = vx1; 02049 if (x1 > x0 + paintBlockWidth) { 02050 x1 = x0 + paintBlockWidth; 02051 } 02052 } else { 02053 // x0 is within the valid area 02054 if (x1 > vx1) { 02055 x0 = vx1; 02056 if (x0 + paintBlockWidth < x1) { 02057 x1 = x0 + paintBlockWidth; 02058 } 02059 } else { 02060 x1 = x0; // it's all valid, paint nothing 02061 } 02062 } 02063 02064 cache.validArea = QRect 02065 (std::min(vx0, x0), cache.validArea.y(), 02066 std::max(vx1 - std::min(vx0, x0), 02067 x1 - std::min(vx0, x0)), 02068 cache.validArea.height()); 02069 02070 #ifdef DEBUG_SPECTROGRAM_REPAINT 02071 cerr << "Valid area becomes " << cache.validArea.x() 02072 << ", " << cache.validArea.y() << ", " 02073 << cache.validArea.width() << "x" 02074 << cache.validArea.height() << endl; 02075 #endif 02076 02077 } else { 02078 if (x1 > x0 + paintBlockWidth) { 02079 int sfx = x1; 02080 if (startFrame < 0) sfx = v->getXForFrame(0); 02081 if (sfx >= x0 && sfx + paintBlockWidth <= x1) { 02082 x0 = sfx; 02083 x1 = x0 + paintBlockWidth; 02084 } else { 02085 int mid = (x1 + x0) / 2; 02086 x0 = mid - paintBlockWidth/2; 02087 x1 = x0 + paintBlockWidth; 02088 } 02089 } 02090 #ifdef DEBUG_SPECTROGRAM_REPAINT 02091 cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0) 02092 << "x" << h << endl; 02093 #endif 02094 cache.validArea = QRect(x0, 0, x1 - x0, h); 02095 } 02096 02097 /* 02098 if (xPixelRatio != 1.f) { 02099 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001); 02100 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001); 02101 } 02102 */ 02103 int w = x1 - x0; 02104 02105 #ifdef DEBUG_SPECTROGRAM_REPAINT 02106 cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl; 02107 #endif 02108 02109 int sr = m_model->getSampleRate(); 02110 02111 // Set minFreq and maxFreq to the frequency extents of the possibly 02112 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq 02113 // to the actual scale frequency extents (presumably not zero padded). 02114 02115 // If we are zero padding, we want to use the zero-padded 02116 // equivalents of the bins that we would be using if not zero 02117 // padded, to avoid spaces at the top and bottom of the display. 02118 02119 // Note fftSize is the actual zero-padded fft size, m_fftSize the 02120 // nominal fft size. 02121 02122 int maxbin = m_fftSize / 2; 02123 if (m_maxFrequency > 0) { 02124 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001); 02125 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 02126 } 02127 02128 int minbin = 1; 02129 if (m_minFrequency > 0) { 02130 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001); 02131 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl; 02132 if (minbin < 1) minbin = 1; 02133 if (minbin >= maxbin) minbin = maxbin - 1; 02134 } 02135 02136 int zpl = getZeroPadLevel(v) + 1; 02137 minbin = minbin * zpl; 02138 maxbin = (maxbin + 1) * zpl - 1; 02139 02140 float minFreq = (float(minbin) * sr) / fftSize; 02141 float maxFreq = (float(maxbin) * sr) / fftSize; 02142 02143 float displayMinFreq = minFreq; 02144 float displayMaxFreq = maxFreq; 02145 02146 if (fftSize != m_fftSize) { 02147 displayMinFreq = getEffectiveMinFrequency(); 02148 displayMaxFreq = getEffectiveMaxFrequency(); 02149 } 02150 02151 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl; 02152 02153 int increment = getWindowIncrement(); 02154 02155 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 02156 /* 02157 float yforbin[maxbin - minbin + 1]; 02158 02159 for (int q = minbin; q <= maxbin; ++q) { 02160 float f0 = (float(q) * sr) / fftSize; 02161 yforbin[q - minbin] = 02162 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, 02163 logarithmic); 02164 } 02165 */ 02166 MagnitudeRange overallMag = m_viewMags[v]; 02167 bool overallMagChanged = false; 02168 02169 #ifdef DEBUG_SPECTROGRAM_REPAINT 02170 cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; 02171 #endif 02172 02173 if (w == 0) { 02174 SVDEBUG << "*** NOTE: w == 0" << endl; 02175 } 02176 02177 #ifdef DEBUG_SPECTROGRAM_REPAINT 02178 int pixels = 0; 02179 #endif 02180 02181 Profiler outerprof("SpectrogramLayer::paint: all cols"); 02182 02183 // The draw buffer contains a fragment at either our pixel 02184 // resolution (if there is more than one time-bin per pixel) or 02185 // time-bin resolution (if a time-bin spans more than one pixel). 02186 // We need to ensure that it starts and ends at points where a 02187 // time-bin boundary occurs at an exact pixel boundary, and with a 02188 // certain amount of overlap across existing pixels so that we can 02189 // scale and draw from it without smoothing errors at the edges. 02190 02191 // If (getFrameForX(x) / increment) * increment == 02192 // getFrameForX(x), then x is a time-bin boundary. We want two 02193 // such boundaries at either side of the draw buffer -- one which 02194 // we draw up to, and one which we subsequently crop at. 02195 02196 bool bufferBinResolution = false; 02197 if (increment > zoomLevel) bufferBinResolution = true; 02198 02199 int leftBoundaryFrame = -1, leftCropFrame = -1; 02200 int rightBoundaryFrame = -1, rightCropFrame = -1; 02201 02202 int bufwid; 02203 02204 if (bufferBinResolution) { 02205 02206 for (int x = x0; ; --x) { 02207 int f = v->getFrameForX(x); 02208 if ((f / increment) * increment == f) { 02209 if (leftCropFrame == -1) leftCropFrame = f; 02210 else if (x < x0 - 2) { leftBoundaryFrame = f; break; } 02211 } 02212 } 02213 for (int x = x0 + w; ; ++x) { 02214 int f = v->getFrameForX(x); 02215 if ((f / increment) * increment == f) { 02216 if (rightCropFrame == -1) rightCropFrame = f; 02217 else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; } 02218 } 02219 } 02220 #ifdef DEBUG_SPECTROGRAM_REPAINT 02221 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl; 02222 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl; 02223 #endif 02224 02225 bufwid = (rightBoundaryFrame - leftBoundaryFrame) / increment; 02226 02227 } else { 02228 02229 bufwid = w; 02230 } 02231 02232 #ifdef __GNUC__ 02233 int binforx[bufwid]; 02234 float binfory[h]; 02235 #else 02236 int *binforx = (int *)alloca(bufwid * sizeof(int)); 02237 float *binfory = (float *)alloca(h * sizeof(float)); 02238 #endif 02239 02240 bool usePeaksCache = false; 02241 02242 if (bufferBinResolution) { 02243 for (int x = 0; x < bufwid; ++x) { 02244 binforx[x] = (leftBoundaryFrame / increment) + x; 02245 // cerr << "binforx[" << x << "] = " << binforx[x] << endl; 02246 } 02247 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); 02248 } else { 02249 for (int x = 0; x < bufwid; ++x) { 02250 float s0 = 0, s1 = 0; 02251 if (getXBinRange(v, x + x0, s0, s1)) { 02252 binforx[x] = int(s0 + 0.0001); 02253 } else { 02254 binforx[x] = -1; //??? 02255 } 02256 } 02257 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) { 02258 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); 02259 } 02260 usePeaksCache = (increment * 8) < zoomLevel; 02261 if (m_colourScale == PhaseColourScale) usePeaksCache = false; 02262 } 02263 02264 // No longer exists in Qt5: m_drawBuffer.setNumColors(256); 02265 for (int pixel = 0; pixel < 256; ++pixel) { 02266 m_drawBuffer.setColor(pixel, m_palette.getColour(pixel).rgb()); 02267 } 02268 02269 m_drawBuffer.fill(0); 02270 02271 if (m_binDisplay != PeakFrequencies) { 02272 02273 for (int y = 0; y < h; ++y) { 02274 float q0 = 0, q1 = 0; 02275 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { 02276 binfory[y] = -1; 02277 } else { 02278 binfory[y] = q0; 02279 // cerr << "binfory[" << y << "] = " << binfory[y] << endl; 02280 } 02281 } 02282 02283 paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache, 02284 overallMag, overallMagChanged); 02285 02286 } else { 02287 02288 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, 02289 minbin, maxbin, 02290 displayMinFreq, displayMaxFreq, 02291 logarithmic, 02292 overallMag, overallMagChanged); 02293 } 02294 02295 /* 02296 for (int x = 0; x < w / xPixelRatio; ++x) { 02297 02298 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column"); 02299 02300 runOutOfData = !paintColumnValues(v, fft, x0, x, 02301 minbin, maxbin, 02302 displayMinFreq, displayMaxFreq, 02303 xPixelRatio, 02304 h, yforbin); 02305 02306 if (runOutOfData) { 02307 #ifdef DEBUG_SPECTROGRAM_REPAINT 02308 cerr << "Run out of data -- dropping out of loop" << endl; 02309 #endif 02310 break; 02311 } 02312 } 02313 */ 02314 #ifdef DEBUG_SPECTROGRAM_REPAINT 02315 // cerr << pixels << " pixels drawn" << endl; 02316 #endif 02317 02318 if (overallMagChanged) { 02319 m_viewMags[v] = overallMag; 02320 #ifdef DEBUG_SPECTROGRAM_REPAINT 02321 cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl; 02322 #endif 02323 } else { 02324 #ifdef DEBUG_SPECTROGRAM_REPAINT 02325 cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; 02326 #endif 02327 } 02328 02329 outerprof.end(); 02330 02331 Profiler profiler2("SpectrogramLayer::paint: draw image"); 02332 02333 if (recreateWholeImageCache) { 02334 #ifdef DEBUG_SPECTROGRAM_REPAINT 02335 SVDEBUG << "Recreating image cache: width = " << v->width() 02336 << ", height = " << h << endl; 02337 #endif 02338 cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied); 02339 } 02340 02341 if (w > 0) { 02342 #ifdef DEBUG_SPECTROGRAM_REPAINT 02343 SVDEBUG << "Painting " << w << "x" << h 02344 << " from draw buffer at " << 0 << "," << 0 02345 << " to " << w << "x" << h << " on cache at " 02346 << x0 << "," << 0 << endl; 02347 #endif 02348 02349 QPainter cachePainter(&cache.image); 02350 02351 if (bufferBinResolution) { 02352 int scaledLeft = v->getXForFrame(leftBoundaryFrame); 02353 int scaledRight = v->getXForFrame(rightBoundaryFrame); 02354 #ifdef DEBUG_SPECTROGRAM_REPAINT 02355 SVDEBUG << "Rescaling image from " << bufwid 02356 << "x" << h << " to " 02357 << scaledRight-scaledLeft << "x" << h << endl; 02358 #endif 02359 Preferences::SpectrogramXSmoothing xsmoothing = 02360 Preferences::getInstance()->getSpectrogramXSmoothing(); 02361 // SVDEBUG << "xsmoothing == " << xsmoothing << endl; 02362 QImage scaled = m_drawBuffer.scaled 02363 (scaledRight - scaledLeft, h, 02364 Qt::IgnoreAspectRatio, 02365 ((xsmoothing == Preferences::SpectrogramXInterpolated) ? 02366 Qt::SmoothTransformation : Qt::FastTransformation)); 02367 int scaledLeftCrop = v->getXForFrame(leftCropFrame); 02368 int scaledRightCrop = v->getXForFrame(rightCropFrame); 02369 #ifdef DEBUG_SPECTROGRAM_REPAINT 02370 SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " 02371 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; 02372 #endif 02373 cachePainter.drawImage 02374 (QRect(scaledLeftCrop, 0, 02375 scaledRightCrop - scaledLeftCrop, h), 02376 scaled, 02377 QRect(scaledLeftCrop - scaledLeft, 0, 02378 scaledRightCrop - scaledLeftCrop, h)); 02379 } else { 02380 cachePainter.drawImage(QRect(x0, 0, w, h), 02381 m_drawBuffer, 02382 QRect(0, 0, w, h)); 02383 } 02384 02385 cachePainter.end(); 02386 } 02387 02388 QRect pr = rect & cache.validArea; 02389 02390 #ifdef DEBUG_SPECTROGRAM_REPAINT 02391 SVDEBUG << "Painting " << pr.width() << "x" << pr.height() 02392 << " from cache at " << pr.x() << "," << pr.y() 02393 << " to window" << endl; 02394 #endif 02395 02396 paint.drawImage(pr.x(), pr.y(), cache.image, 02397 pr.x(), pr.y(), pr.width(), pr.height()); 02399 // paint.drawImage(v->rect(), cache.image, 02400 // QRect(QPoint(0, 0), cache.image.size())); 02401 02402 cache.startFrame = startFrame; 02403 cache.zoomLevel = zoomLevel; 02404 02405 if (!m_synchronous) { 02406 02407 if (!m_normalizeVisibleArea || !overallMagChanged) { 02408 02409 if (cache.validArea.x() > 0) { 02410 #ifdef DEBUG_SPECTROGRAM_REPAINT 02411 SVDEBUG << "SpectrogramLayer::paint() updating left (0, " 02412 << cache.validArea.x() << ")" << endl; 02413 #endif 02414 v->update(0, 0, cache.validArea.x(), h); 02415 } 02416 02417 if (cache.validArea.x() + cache.validArea.width() < 02418 cache.image.width()) { 02419 #ifdef DEBUG_SPECTROGRAM_REPAINT 02420 SVDEBUG << "SpectrogramLayer::paint() updating right (" 02421 << cache.validArea.x() + cache.validArea.width() 02422 << ", " 02423 << cache.image.width() - (cache.validArea.x() + 02424 cache.validArea.width()) 02425 << ")" << endl; 02426 #endif 02427 v->update(cache.validArea.x() + cache.validArea.width(), 02428 0, 02429 cache.image.width() - (cache.validArea.x() + 02430 cache.validArea.width()), 02431 h); 02432 } 02433 } else { 02434 // overallMagChanged 02435 cerr << "\noverallMagChanged - updating all\n" << endl; 02436 cache.validArea = QRect(); 02437 v->update(); 02438 } 02439 } 02440 02441 illuminateLocalFeatures(v, paint); 02442 02443 #ifdef DEBUG_SPECTROGRAM_REPAINT 02444 SVDEBUG << "SpectrogramLayer::paint() returning" << endl; 02445 #endif 02446 02447 if (!m_synchronous) { 02448 m_lastPaintBlockWidth = paintBlockWidth; 02449 (void)gettimeofday(&tv, 0); 02450 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; 02451 } 02452 02454 } 02455 02456 bool 02457 SpectrogramLayer::paintDrawBufferPeakFrequencies(View *v, 02458 int w, 02459 int h, 02460 int *binforx, 02461 int minbin, 02462 int maxbin, 02463 float displayMinFreq, 02464 float displayMaxFreq, 02465 bool logarithmic, 02466 MagnitudeRange &overallMag, 02467 bool &overallMagChanged) const 02468 { 02469 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies"); 02470 02471 #ifdef DEBUG_SPECTROGRAM_REPAINT 02472 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; 02473 #endif 02474 if (minbin < 0) minbin = 0; 02475 if (maxbin < 0) maxbin = minbin+1; 02476 02477 FFTModel *fft = getFFTModel(v); 02478 if (!fft) return false; 02479 02480 FFTModel::PeakSet peakfreqs; 02481 02482 int psx = -1; 02483 02484 #ifdef __GNUC__ 02485 float values[maxbin - minbin + 1]; 02486 #else 02487 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); 02488 #endif 02489 02490 for (int x = 0; x < w; ++x) { 02491 02492 if (binforx[x] < 0) continue; 02493 02494 int sx0 = binforx[x]; 02495 int sx1 = sx0; 02496 if (x+1 < w) sx1 = binforx[x+1]; 02497 if (sx0 < 0) sx0 = sx1 - 1; 02498 if (sx0 < 0) continue; 02499 if (sx1 <= sx0) sx1 = sx0 + 1; 02500 02501 for (int sx = sx0; sx < sx1; ++sx) { 02502 02503 if (sx < 0 || sx >= int(fft->getWidth())) continue; 02504 02505 if (!m_synchronous) { 02506 if (!fft->isColumnAvailable(sx)) { 02507 #ifdef DEBUG_SPECTROGRAM_REPAINT 02508 cerr << "Met unavailable column at col " << sx << endl; 02509 #endif 02510 return false; 02511 } 02512 } 02513 02514 MagnitudeRange mag; 02515 02516 if (sx != psx) { 02517 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx, 02518 minbin, maxbin - 1); 02519 if (m_colourScale == PhaseColourScale) { 02520 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); 02521 } else if (m_normalizeColumns) { 02522 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 02523 } else if (m_normalizeHybrid) { 02524 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 02525 float max = fft->getMaximumMagnitudeAt(sx); 02526 if (max > 0.f) { 02527 for (int i = minbin; i <= maxbin; ++i) { 02528 values[i - minbin] *= log10(max); 02529 } 02530 } 02531 } else { 02532 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 02533 } 02534 psx = sx; 02535 } 02536 02537 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin(); 02538 pi != peakfreqs.end(); ++pi) { 02539 02540 int bin = pi->first; 02541 int freq = pi->second; 02542 02543 if (bin < minbin) continue; 02544 if (bin > maxbin) break; 02545 02546 float value = values[bin - minbin]; 02547 02548 if (m_colourScale != PhaseColourScale) { 02549 if (!m_normalizeColumns && !m_normalizeHybrid) { 02550 value /= (m_fftSize/2.f); 02551 } 02552 mag.sample(value); 02553 value *= m_gain; 02554 } 02555 02556 float y = v->getYForFrequency 02557 (freq, displayMinFreq, displayMaxFreq, logarithmic); 02558 02559 int iy = int(y + 0.5); 02560 if (iy < 0 || iy >= h) continue; 02561 02562 m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value)); 02563 } 02564 02565 if (mag.isSet()) { 02566 if (sx >= int(m_columnMags.size())) { 02567 #ifdef DEBUG_SPECTROGRAM 02568 cerr << "INTERNAL ERROR: " << sx << " >= " 02569 << m_columnMags.size() 02570 << " at SpectrogramLayer.cpp::paintDrawBuffer" 02571 << endl; 02572 #endif 02573 } else { 02574 m_columnMags[sx].sample(mag); 02575 if (overallMag.sample(mag)) overallMagChanged = true; 02576 } 02577 } 02578 } 02579 } 02580 02581 return true; 02582 } 02583 02584 bool 02585 SpectrogramLayer::paintDrawBuffer(View *v, 02586 int w, 02587 int h, 02588 int *binforx, 02589 float *binfory, 02590 bool usePeaksCache, 02591 MagnitudeRange &overallMag, 02592 bool &overallMagChanged) const 02593 { 02594 Profiler profiler("SpectrogramLayer::paintDrawBuffer"); 02595 02596 int minbin = int(binfory[0] + 0.0001); 02597 int maxbin = binfory[h-1]; 02598 02599 #ifdef DEBUG_SPECTROGRAM_REPAINT 02600 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; 02601 #endif 02602 if (minbin < 0) minbin = 0; 02603 if (maxbin < 0) maxbin = minbin+1; 02604 02605 DenseThreeDimensionalModel *sourceModel = 0; 02606 FFTModel *fft = 0; 02607 int divisor = 1; 02608 #ifdef DEBUG_SPECTROGRAM_REPAINT 02609 cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; 02610 #endif 02611 if (usePeaksCache) { 02612 sourceModel = getPeakCache(v); 02613 divisor = 8; 02614 minbin = 0; 02615 maxbin = sourceModel->getHeight(); 02616 } else { 02617 sourceModel = fft = getFFTModel(v); 02618 } 02619 02620 if (!sourceModel) return false; 02621 02622 bool interpolate = false; 02623 Preferences::SpectrogramSmoothing smoothing = 02624 Preferences::getInstance()->getSpectrogramSmoothing(); 02625 if (smoothing == Preferences::SpectrogramInterpolated || 02626 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { 02627 if (m_binDisplay != PeakBins && 02628 m_binDisplay != PeakFrequencies) { 02629 interpolate = true; 02630 } 02631 } 02632 02633 int psx = -1; 02634 02635 #ifdef __GNUC__ 02636 float autoarray[maxbin - minbin + 1]; 02637 float peaks[h]; 02638 #else 02639 float *autoarray = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); 02640 float *peaks = (float *)alloca(h * sizeof(float)); 02641 #endif 02642 02643 const float *values = autoarray; 02644 DenseThreeDimensionalModel::Column c; 02645 02646 for (int x = 0; x < w; ++x) { 02647 02648 if (binforx[x] < 0) continue; 02649 02650 // float columnGain = m_gain; 02651 float columnMax = 0.f; 02652 02653 int sx0 = binforx[x] / divisor; 02654 int sx1 = sx0; 02655 if (x+1 < w) sx1 = binforx[x+1] / divisor; 02656 if (sx0 < 0) sx0 = sx1 - 1; 02657 if (sx0 < 0) continue; 02658 if (sx1 <= sx0) sx1 = sx0 + 1; 02659 02660 for (int y = 0; y < h; ++y) peaks[y] = 0.f; 02661 02662 for (int sx = sx0; sx < sx1; ++sx) { 02663 02664 #ifdef DEBUG_SPECTROGRAM_REPAINT 02665 // cerr << "sx = " << sx << endl; 02666 #endif 02667 02668 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue; 02669 02670 if (!m_synchronous) { 02671 if (!sourceModel->isColumnAvailable(sx)) { 02672 #ifdef DEBUG_SPECTROGRAM_REPAINT 02673 cerr << "Met unavailable column at col " << sx << endl; 02674 #endif 02675 return false; 02676 } 02677 } 02678 02679 MagnitudeRange mag; 02680 02681 if (sx != psx) { 02682 if (fft) { 02683 #ifdef DEBUG_SPECTROGRAM_REPAINT 02684 SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl; 02685 #endif 02686 if (m_colourScale == PhaseColourScale) { 02687 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); 02688 } else if (m_normalizeColumns) { 02689 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 02690 } else if (m_normalizeHybrid) { 02691 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 02692 float max = fft->getMaximumMagnitudeAt(sx); 02693 for (int i = minbin; i <= maxbin; ++i) { 02694 if (max > 0.f) { 02695 autoarray[i - minbin] *= log10(max); 02696 } 02697 } 02698 } else { 02699 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 02700 } 02701 } else { 02702 #ifdef DEBUG_SPECTROGRAM_REPAINT 02703 SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl; 02704 #endif 02705 c = sourceModel->getColumn(sx); 02706 if (m_normalizeColumns || m_normalizeHybrid) { 02707 for (int y = 0; y < h; ++y) { 02708 if (c[y] > columnMax) columnMax = c[y]; 02709 } 02710 } 02711 values = c.constData() + minbin; 02712 } 02713 psx = sx; 02714 } 02715 02716 for (int y = 0; y < h; ++y) { 02717 02718 float sy0 = binfory[y]; 02719 float sy1 = sy0 + 1; 02720 if (y+1 < h) sy1 = binfory[y+1]; 02721 02722 float value = 0.f; 02723 02724 if (interpolate && fabsf(sy1 - sy0) < 1.f) { 02725 02726 float centre = (sy0 + sy1) / 2; 02727 float dist = (centre - 0.5) - lrintf(centre - 0.5); 02728 int bin = int(centre); 02729 int other = (dist < 0 ? (bin-1) : (bin+1)); 02730 if (bin < minbin) bin = minbin; 02731 if (bin > maxbin) bin = maxbin; 02732 if (other < minbin || other > maxbin) other = bin; 02733 float prop = 1.f - fabsf(dist); 02734 02735 float v0 = values[bin - minbin]; 02736 float v1 = values[other - minbin]; 02737 if (m_binDisplay == PeakBins) { 02738 if (bin == minbin || bin == maxbin || 02739 v0 < values[bin-minbin-1] || 02740 v0 < values[bin-minbin+1]) v0 = 0.f; 02741 if (other == minbin || other == maxbin || 02742 v1 < values[other-minbin-1] || 02743 v1 < values[other-minbin+1]) v1 = 0.f; 02744 } 02745 if (v0 == 0.f && v1 == 0.f) continue; 02746 value = prop * v0 + (1.f - prop) * v1; 02747 02748 if (m_colourScale != PhaseColourScale) { 02749 if (!m_normalizeColumns) { 02750 value /= (m_fftSize/2.f); 02751 } 02752 mag.sample(value); 02753 value *= m_gain; 02754 } 02755 02756 peaks[y] = value; 02757 02758 } else { 02759 02760 int by0 = int(sy0 + 0.0001); 02761 int by1 = int(sy1 + 0.0001); 02762 if (by1 < by0 + 1) by1 = by0 + 1; 02763 02764 for (int bin = by0; bin < by1; ++bin) { 02765 02766 value = values[bin - minbin]; 02767 if (m_binDisplay == PeakBins) { 02768 if (bin == minbin || bin == maxbin || 02769 value < values[bin-minbin-1] || 02770 value < values[bin-minbin+1]) continue; 02771 } 02772 02773 if (m_colourScale != PhaseColourScale) { 02774 if (!m_normalizeColumns) { 02775 value /= (m_fftSize/2.f); 02776 } 02777 mag.sample(value); 02778 value *= m_gain; 02779 } 02780 02781 if (value > peaks[y]) peaks[y] = value; 02782 } 02783 } 02784 } 02785 02786 if (mag.isSet()) { 02787 if (sx >= int(m_columnMags.size())) { 02788 #ifdef DEBUG_SPECTROGRAM 02789 cerr << "INTERNAL ERROR: " << sx << " >= " 02790 << m_columnMags.size() 02791 << " at SpectrogramLayer.cpp::paintDrawBuffer" 02792 << endl; 02793 #endif 02794 } else { 02795 m_columnMags[sx].sample(mag); 02796 if (overallMag.sample(mag)) overallMagChanged = true; 02797 } 02798 } 02799 } 02800 02801 for (int y = 0; y < h; ++y) { 02802 02803 float peak = peaks[y]; 02804 02805 if (m_colourScale != PhaseColourScale && 02806 (m_normalizeColumns || m_normalizeHybrid) && 02807 columnMax > 0.f) { 02808 peak /= columnMax; 02809 if (m_normalizeHybrid) { 02810 peak *= log10(columnMax); 02811 } 02812 } 02813 02814 unsigned char peakpix = getDisplayValue(v, peak); 02815 02816 m_drawBuffer.setPixel(x, h-y-1, peakpix); 02817 } 02818 } 02819 02820 return true; 02821 } 02822 02823 void 02824 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const 02825 { 02826 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures"); 02827 02828 QPoint localPos; 02829 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { 02830 return; 02831 } 02832 02833 // cerr << "SpectrogramLayer: illuminateLocalFeatures(" 02834 // << localPos.x() << "," << localPos.y() << ")" << endl; 02835 02836 float s0, s1; 02837 float f0, f1; 02838 02839 if (getXBinRange(v, localPos.x(), s0, s1) && 02840 getYBinSourceRange(v, localPos.y(), f0, f1)) { 02841 02842 int s0i = int(s0 + 0.001); 02843 int s1i = int(s1); 02844 02845 int x0 = v->getXForFrame(s0i * getWindowIncrement()); 02846 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement()); 02847 02848 int y1 = int(getYForFrequency(v, f1)); 02849 int y0 = int(getYForFrequency(v, f0)); 02850 02851 // cerr << "SpectrogramLayer: illuminate " 02852 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl; 02853 02854 paint.setPen(v->getForeground()); 02855 02857 02858 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); 02859 } 02860 } 02861 02862 float 02863 SpectrogramLayer::getYForFrequency(const View *v, float frequency) const 02864 { 02865 return v->getYForFrequency(frequency, 02866 getEffectiveMinFrequency(), 02867 getEffectiveMaxFrequency(), 02868 m_frequencyScale == LogFrequencyScale); 02869 } 02870 02871 float 02872 SpectrogramLayer::getFrequencyForY(const View *v, int y) const 02873 { 02874 return v->getFrequencyForY(y, 02875 getEffectiveMinFrequency(), 02876 getEffectiveMaxFrequency(), 02877 m_frequencyScale == LogFrequencyScale); 02878 } 02879 02880 int 02881 SpectrogramLayer::getCompletion(View *v) const 02882 { 02883 if (m_updateTimer == 0) return 100; 02884 if (m_fftModels.find(v) == m_fftModels.end()) return 100; 02885 02886 int completion = m_fftModels[v].first->getCompletion(); 02887 #ifdef DEBUG_SPECTROGRAM_REPAINT 02888 SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl; 02889 #endif 02890 return completion; 02891 } 02892 02893 QString 02894 SpectrogramLayer::getError(View *v) const 02895 { 02896 if (m_fftModels.find(v) == m_fftModels.end()) return ""; 02897 return m_fftModels[v].first->getError(); 02898 } 02899 02900 bool 02901 SpectrogramLayer::getValueExtents(float &min, float &max, 02902 bool &logarithmic, QString &unit) const 02903 { 02904 if (!m_model) return false; 02905 02906 int sr = m_model->getSampleRate(); 02907 min = float(sr) / m_fftSize; 02908 max = float(sr) / 2; 02909 02910 logarithmic = (m_frequencyScale == LogFrequencyScale); 02911 unit = "Hz"; 02912 return true; 02913 } 02914 02915 bool 02916 SpectrogramLayer::getDisplayExtents(float &min, float &max) const 02917 { 02918 min = getEffectiveMinFrequency(); 02919 max = getEffectiveMaxFrequency(); 02920 02921 // SVDEBUG << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << endl; 02922 return true; 02923 } 02924 02925 bool 02926 SpectrogramLayer::setDisplayExtents(float min, float max) 02927 { 02928 if (!m_model) return false; 02929 02930 // SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl; 02931 02932 if (min < 0) min = 0; 02933 if (max > m_model->getSampleRate()/2.f) max = m_model->getSampleRate()/2.f; 02934 02935 int minf = lrintf(min); 02936 int maxf = lrintf(max); 02937 02938 if (m_minFrequency == minf && m_maxFrequency == maxf) return true; 02939 02940 invalidateImageCaches(); 02941 invalidateMagnitudes(); 02942 02943 m_minFrequency = minf; 02944 m_maxFrequency = maxf; 02945 02946 emit layerParametersChanged(); 02947 02948 int vs = getCurrentVerticalZoomStep(); 02949 if (vs != m_lastEmittedZoomStep) { 02950 emit verticalZoomChanged(); 02951 m_lastEmittedZoomStep = vs; 02952 } 02953 02954 return true; 02955 } 02956 02957 bool 02958 SpectrogramLayer::getYScaleValue(const View *v, int y, 02959 float &value, QString &unit) const 02960 { 02961 value = getFrequencyForY(v, y); 02962 unit = "Hz"; 02963 return true; 02964 } 02965 02966 bool 02967 SpectrogramLayer::snapToFeatureFrame(View *, int &frame, 02968 int &resolution, 02969 SnapType snap) const 02970 { 02971 resolution = getWindowIncrement(); 02972 int left = (frame / resolution) * resolution; 02973 int right = left + resolution; 02974 02975 switch (snap) { 02976 case SnapLeft: frame = left; break; 02977 case SnapRight: frame = right; break; 02978 case SnapNearest: 02979 case SnapNeighbouring: 02980 if (frame - left > right - frame) frame = right; 02981 else frame = left; 02982 break; 02983 } 02984 02985 return true; 02986 } 02987 02988 void 02989 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e) 02990 { 02991 ImageCache &cache = m_imageCaches[v]; 02992 02993 cerr << "cache width: " << cache.image.width() << ", height: " 02994 << cache.image.height() << endl; 02995 02996 QImage image = cache.image; 02997 02998 ImageRegionFinder finder; 02999 QRect rect = finder.findRegionExtents(&image, e->pos()); 03000 if (rect.isValid()) { 03001 MeasureRect mr; 03002 setMeasureRectFromPixrect(v, mr, rect); 03003 CommandHistory::getInstance()->addCommand 03004 (new AddMeasurementRectCommand(this, mr)); 03005 } 03006 } 03007 03008 bool 03009 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint, 03010 QPoint cursorPos, 03011 std::vector<QRect> &extents) const 03012 { 03013 QRect vertical(cursorPos.x() - 12, 0, 12, v->height()); 03014 extents.push_back(vertical); 03015 03016 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); 03017 extents.push_back(horizontal); 03018 03019 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); 03020 03021 QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2, 03022 paint.fontMetrics().width("123456 Hz") + 2, 03023 paint.fontMetrics().height()); 03024 extents.push_back(freq); 03025 03026 QRect pitch(sw, cursorPos.y() + 2, 03027 paint.fontMetrics().width("C#10+50c") + 2, 03028 paint.fontMetrics().height()); 03029 extents.push_back(pitch); 03030 03031 QRect rt(cursorPos.x(), 03032 v->height() - paint.fontMetrics().height() - 2, 03033 paint.fontMetrics().width("1234.567 s"), 03034 paint.fontMetrics().height()); 03035 extents.push_back(rt); 03036 03037 int w(paint.fontMetrics().width("1234567890") + 2); 03038 QRect frame(cursorPos.x() - w - 2, 03039 v->height() - paint.fontMetrics().height() - 2, 03040 w, 03041 paint.fontMetrics().height()); 03042 extents.push_back(frame); 03043 03044 return true; 03045 } 03046 03047 void 03048 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint, 03049 QPoint cursorPos) const 03050 { 03051 paint.save(); 03052 03053 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); 03054 03055 QFont fn = paint.font(); 03056 if (fn.pointSize() > 8) { 03057 fn.setPointSize(fn.pointSize() - 1); 03058 paint.setFont(fn); 03059 } 03060 paint.setPen(m_crosshairColour); 03061 03062 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); 03063 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height()); 03064 03065 float fundamental = getFrequencyForY(v, cursorPos.y()); 03066 03067 v->drawVisibleText(paint, 03068 sw + 2, 03069 cursorPos.y() - 2, 03070 QString("%1 Hz").arg(fundamental), 03071 View::OutlinedText); 03072 03073 if (Pitch::isFrequencyInMidiRange(fundamental)) { 03074 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); 03075 v->drawVisibleText(paint, 03076 sw + 2, 03077 cursorPos.y() + paint.fontMetrics().ascent() + 2, 03078 pitchLabel, 03079 View::OutlinedText); 03080 } 03081 03082 int frame = v->getFrameForX(cursorPos.x()); 03083 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); 03084 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); 03085 QString frameLabel = QString("%1").arg(frame); 03086 v->drawVisibleText(paint, 03087 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, 03088 v->height() - 2, 03089 frameLabel, 03090 View::OutlinedText); 03091 v->drawVisibleText(paint, 03092 cursorPos.x() + 2, 03093 v->height() - 2, 03094 rtLabel, 03095 View::OutlinedText); 03096 03097 int harmonic = 2; 03098 03099 while (harmonic < 100) { 03100 03101 float hy = lrintf(getYForFrequency(v, fundamental * harmonic)); 03102 if (hy < 0 || hy > v->height()) break; 03103 03104 int len = 7; 03105 03106 if (harmonic % 2 == 0) { 03107 if (harmonic % 4 == 0) { 03108 len = 12; 03109 } else { 03110 len = 10; 03111 } 03112 } 03113 03114 paint.drawLine(cursorPos.x() - len, 03115 int(hy), 03116 cursorPos.x(), 03117 int(hy)); 03118 03119 ++harmonic; 03120 } 03121 03122 paint.restore(); 03123 } 03124 03125 QString 03126 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const 03127 { 03128 int x = pos.x(); 03129 int y = pos.y(); 03130 03131 if (!m_model || !m_model->isOK()) return ""; 03132 03133 float magMin = 0, magMax = 0; 03134 float phaseMin = 0, phaseMax = 0; 03135 float freqMin = 0, freqMax = 0; 03136 float adjFreqMin = 0, adjFreqMax = 0; 03137 QString pitchMin, pitchMax; 03138 RealTime rtMin, rtMax; 03139 03140 bool haveValues = false; 03141 03142 if (!getXBinSourceRange(v, x, rtMin, rtMax)) { 03143 return ""; 03144 } 03145 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) { 03146 haveValues = true; 03147 } 03148 03149 QString adjFreqText = "", adjPitchText = ""; 03150 03151 if (m_binDisplay == PeakFrequencies) { 03152 03153 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, 03154 adjFreqMin, adjFreqMax)) { 03155 return ""; 03156 } 03157 03158 if (adjFreqMin != adjFreqMax) { 03159 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") 03160 .arg(adjFreqMin).arg(adjFreqMax); 03161 } else { 03162 adjFreqText = tr("Peak Frequency:\t%1 Hz\n") 03163 .arg(adjFreqMin); 03164 } 03165 03166 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); 03167 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); 03168 03169 if (pmin != pmax) { 03170 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); 03171 } else { 03172 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); 03173 } 03174 03175 } else { 03176 03177 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; 03178 } 03179 03180 QString text; 03181 03182 if (rtMin != rtMax) { 03183 text += tr("Time:\t%1 - %2\n") 03184 .arg(rtMin.toText(true).c_str()) 03185 .arg(rtMax.toText(true).c_str()); 03186 } else { 03187 text += tr("Time:\t%1\n") 03188 .arg(rtMin.toText(true).c_str()); 03189 } 03190 03191 if (freqMin != freqMax) { 03192 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") 03193 .arg(adjFreqText) 03194 .arg(freqMin) 03195 .arg(freqMax) 03196 .arg(adjPitchText) 03197 .arg(Pitch::getPitchLabelForFrequency(freqMin)) 03198 .arg(Pitch::getPitchLabelForFrequency(freqMax)); 03199 } else { 03200 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") 03201 .arg(adjFreqText) 03202 .arg(freqMin) 03203 .arg(adjPitchText) 03204 .arg(Pitch::getPitchLabelForFrequency(freqMin)); 03205 } 03206 03207 if (haveValues) { 03208 float dbMin = AudioLevel::multiplier_to_dB(magMin); 03209 float dbMax = AudioLevel::multiplier_to_dB(magMax); 03210 QString dbMinString; 03211 QString dbMaxString; 03212 if (dbMin == AudioLevel::DB_FLOOR) { 03213 dbMinString = tr("-Inf"); 03214 } else { 03215 dbMinString = QString("%1").arg(lrintf(dbMin)); 03216 } 03217 if (dbMax == AudioLevel::DB_FLOOR) { 03218 dbMaxString = tr("-Inf"); 03219 } else { 03220 dbMaxString = QString("%1").arg(lrintf(dbMax)); 03221 } 03222 if (lrintf(dbMin) != lrintf(dbMax)) { 03223 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); 03224 } else { 03225 text += tr("dB:\t%1").arg(dbMinString); 03226 } 03227 if (phaseMin != phaseMax) { 03228 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); 03229 } else { 03230 text += tr("\nPhase:\t%1").arg(phaseMin); 03231 } 03232 } 03233 03234 return text; 03235 } 03236 03237 int 03238 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const 03239 { 03240 int cw; 03241 03242 cw = paint.fontMetrics().width("-80dB"); 03243 03244 return cw; 03245 } 03246 03247 int 03248 SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const 03249 { 03250 if (!m_model || !m_model->isOK()) return 0; 03251 03252 int cw = 0; 03253 if (detailed) cw = getColourScaleWidth(paint); 03254 03255 int tw = paint.fontMetrics().width(QString("%1") 03256 .arg(m_maxFrequency > 0 ? 03257 m_maxFrequency - 1 : 03258 m_model->getSampleRate() / 2)); 03259 03260 int fw = paint.fontMetrics().width(tr("43Hz")); 03261 if (tw < fw) tw = fw; 03262 03263 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); 03264 03265 return cw + tickw + tw + 13; 03266 } 03267 03268 void 03269 SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const 03270 { 03271 if (!m_model || !m_model->isOK()) { 03272 return; 03273 } 03274 03275 Profiler profiler("SpectrogramLayer::paintVerticalScale"); 03276 03278 03279 int h = rect.height(), w = rect.width(); 03280 03281 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); 03282 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0); 03283 03284 int bins = m_fftSize / 2; 03285 int sr = m_model->getSampleRate(); 03286 03287 if (m_maxFrequency > 0) { 03288 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 03289 if (bins > m_fftSize / 2) bins = m_fftSize / 2; 03290 } 03291 03292 int cw = 0; 03293 03294 if (detailed) cw = getColourScaleWidth(paint); 03295 int cbw = paint.fontMetrics().width("dB"); 03296 03297 int py = -1; 03298 int textHeight = paint.fontMetrics().height(); 03299 int toff = -textHeight + paint.fontMetrics().ascent() + 2; 03300 03301 if (detailed && (h > textHeight * 3 + 10)) { 03302 03303 int topLines = 2; 03304 if (m_colourScale == PhaseColourScale) topLines = 1; 03305 03306 int ch = h - textHeight * (topLines + 1) - 8; 03307 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); 03308 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); 03309 03310 QString top, bottom; 03311 float min = m_viewMags[v].getMin(); 03312 float max = m_viewMags[v].getMax(); 03313 03314 float dBmin = AudioLevel::multiplier_to_dB(min); 03315 float dBmax = AudioLevel::multiplier_to_dB(max); 03316 03317 if (dBmax < -60.f) dBmax = -60.f; 03318 else top = QString("%1").arg(lrintf(dBmax)); 03319 03320 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; 03321 bottom = QString("%1").arg(lrintf(dBmin)); 03322 03324 03325 if (m_colourScale != PhaseColourScale) { 03326 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2, 03327 2 + textHeight + toff, "dBFS"); 03328 } 03329 03330 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2, 03331 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top), 03332 2 + textHeight * topLines + toff + textHeight/2, top); 03333 03334 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom), 03335 h + toff - 3 - textHeight/2, bottom); 03336 03337 paint.save(); 03338 paint.setBrush(Qt::NoBrush); 03339 03340 int lasty = 0; 03341 int lastdb = 0; 03342 03343 for (int i = 0; i < ch; ++i) { 03344 03345 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1)); 03346 int idb = int(dBval); 03347 03348 float value = AudioLevel::dB_to_multiplier(dBval); 03349 int colour = getDisplayValue(v, value * m_gain); 03350 03351 paint.setPen(m_palette.getColour(colour)); 03352 03353 int y = textHeight * topLines + 4 + ch - i; 03354 03355 paint.drawLine(5 + cw - cbw, y, cw + 2, y); 03356 03357 if (i == 0) { 03358 lasty = y; 03359 lastdb = idb; 03360 } else if (i < ch - paint.fontMetrics().ascent() && 03361 idb != lastdb && 03362 ((abs(y - lasty) > textHeight && 03363 idb % 10 == 0) || 03364 (abs(y - lasty) > paint.fontMetrics().ascent() && 03365 idb % 5 == 0))) { 03366 paint.setPen(v->getBackground()); 03367 QString text = QString("%1").arg(idb); 03368 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text), 03369 y + toff + textHeight/2, text); 03370 paint.setPen(v->getForeground()); 03371 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y); 03372 lasty = y; 03373 lastdb = idb; 03374 } 03375 } 03376 paint.restore(); 03377 } 03378 03379 paint.drawLine(cw + 7, 0, cw + 7, h); 03380 03381 int bin = -1; 03382 03383 for (int y = 0; y < v->height(); ++y) { 03384 03385 float q0, q1; 03386 if (!getYBinRange(v, v->height() - y, q0, q1)) continue; 03387 03388 int vy; 03389 03390 if (int(q0) > bin) { 03391 vy = y; 03392 bin = int(q0); 03393 } else { 03394 continue; 03395 } 03396 03397 int freq = (sr * bin) / m_fftSize; 03398 03399 if (py >= 0 && (vy - py) < textHeight - 1) { 03400 if (m_frequencyScale == LinearFrequencyScale) { 03401 paint.drawLine(w - tickw, h - vy, w, h - vy); 03402 } 03403 continue; 03404 } 03405 03406 QString text = QString("%1").arg(freq); 03407 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC 03408 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); 03409 03410 if (h - vy - textHeight >= -2) { 03411 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); 03412 paint.drawText(tx, h - vy + toff, text); 03413 } 03414 03415 py = vy; 03416 } 03417 03418 if (m_frequencyScale == LogFrequencyScale) { 03419 03420 // piano keyboard 03421 03422 PianoScale().paintPianoVertical 03423 (v, paint, QRect(w - pkw - 1, 0, pkw, h), 03424 getEffectiveMinFrequency(), getEffectiveMaxFrequency()); 03425 } 03426 03427 m_haveDetailedScale = detailed; 03428 } 03429 03430 class SpectrogramRangeMapper : public RangeMapper 03431 { 03432 public: 03433 SpectrogramRangeMapper(int sr, int /* fftsize */) : 03434 m_dist(float(sr) / 2), 03435 m_s2(sqrtf(sqrtf(2))) { } 03436 ~SpectrogramRangeMapper() { } 03437 03438 virtual int getPositionForValue(float value) const { 03439 03440 float dist = m_dist; 03441 03442 int n = 0; 03443 03444 while (dist > (value + 0.00001) && dist > 0.1f) { 03445 dist /= m_s2; 03446 ++n; 03447 } 03448 03449 return n; 03450 } 03451 03452 virtual int getPositionForValueUnclamped(float value) const { 03453 // We don't really support this 03454 return getPositionForValue(value); 03455 } 03456 03457 virtual float getValueForPosition(int position) const { 03458 03459 // Vertical zoom step 0 shows the entire range from DC -> 03460 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of 03461 // step 0, and so on until the visible range is smaller than 03462 // the frequency step between bins at the current fft size. 03463 03464 float dist = m_dist; 03465 03466 int n = 0; 03467 while (n < position) { 03468 dist /= m_s2; 03469 ++n; 03470 } 03471 03472 return dist; 03473 } 03474 03475 virtual float getValueForPositionUnclamped(int position) const { 03476 // We don't really support this 03477 return getValueForPosition(position); 03478 } 03479 03480 virtual QString getUnit() const { return "Hz"; } 03481 03482 protected: 03483 float m_dist; 03484 float m_s2; 03485 }; 03486 03487 int 03488 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const 03489 { 03490 if (!m_model) return 0; 03491 03492 int sr = m_model->getSampleRate(); 03493 03494 SpectrogramRangeMapper mapper(sr, m_fftSize); 03495 03496 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001); 03497 int maxStep = mapper.getPositionForValue(0); 03498 int minStep = mapper.getPositionForValue(float(sr) / 2); 03499 03500 int initialMax = m_initialMaxFrequency; 03501 if (initialMax == 0) initialMax = sr / 2; 03502 03503 defaultStep = mapper.getPositionForValue(initialMax) - minStep; 03504 03505 // SVDEBUG << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << endl; 03506 03507 return maxStep - minStep; 03508 } 03509 03510 int 03511 SpectrogramLayer::getCurrentVerticalZoomStep() const 03512 { 03513 if (!m_model) return 0; 03514 03515 float dmin, dmax; 03516 getDisplayExtents(dmin, dmax); 03517 03518 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize); 03519 int n = mapper.getPositionForValue(dmax - dmin); 03520 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl; 03521 return n; 03522 } 03523 03524 void 03525 SpectrogramLayer::setVerticalZoomStep(int step) 03526 { 03527 if (!m_model) return; 03528 03529 float dmin = m_minFrequency, dmax = m_maxFrequency; 03530 // getDisplayExtents(dmin, dmax); 03531 03532 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl; 03533 03534 int sr = m_model->getSampleRate(); 03535 SpectrogramRangeMapper mapper(sr, m_fftSize); 03536 float newdist = mapper.getValueForPosition(step); 03537 03538 float newmin, newmax; 03539 03540 if (m_frequencyScale == LogFrequencyScale) { 03541 03542 // need to pick newmin and newmax such that 03543 // 03544 // (log(newmin) + log(newmax)) / 2 == logmid 03545 // and 03546 // newmax - newmin = newdist 03547 // 03548 // so log(newmax - newdist) + log(newmax) == 2logmid 03549 // log(newmax(newmax - newdist)) == 2logmid 03550 // newmax.newmax - newmax.newdist == exp(2logmid) 03551 // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0 03552 // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known 03553 // 03554 // positive root 03555 // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2 03556 // 03557 // but logmid = (log(dmin) + log(dmax)) / 2 03558 // so exp(2logmid) = exp(log(dmin) + log(dmax)) 03559 // = exp(log(dmin.dmax)) 03560 // = dmin.dmax 03561 // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2 03562 03563 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; 03564 newmin = newmax - newdist; 03565 03566 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; 03567 03568 } else { 03569 float dmid = (dmax + dmin) / 2; 03570 newmin = dmid - newdist / 2; 03571 newmax = dmid + newdist / 2; 03572 } 03573 03574 float mmin, mmax; 03575 mmin = 0; 03576 mmax = float(sr) / 2; 03577 03578 if (newmin < mmin) { 03579 newmax += (mmin - newmin); 03580 newmin = mmin; 03581 } 03582 if (newmax > mmax) { 03583 newmax = mmax; 03584 } 03585 03586 // SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; 03587 03588 setMinFrequency(lrintf(newmin)); 03589 setMaxFrequency(lrintf(newmax)); 03590 } 03591 03592 RangeMapper * 03593 SpectrogramLayer::getNewVerticalZoomRangeMapper() const 03594 { 03595 if (!m_model) return 0; 03596 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); 03597 } 03598 03599 void 03600 SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const 03601 { 03602 int y0 = 0; 03603 if (r.startY > 0.0) y0 = getYForFrequency(v, r.startY); 03604 03605 int y1 = y0; 03606 if (r.endY > 0.0) y1 = getYForFrequency(v, r.endY); 03607 03608 // SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl; 03609 03610 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); 03611 } 03612 03613 void 03614 SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const 03615 { 03616 if (start) { 03617 r.startY = getFrequencyForY(v, y); 03618 r.endY = r.startY; 03619 } else { 03620 r.endY = getFrequencyForY(v, y); 03621 } 03622 // SVDEBUG << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << endl; 03623 03624 } 03625 03626 void 03627 SpectrogramLayer::toXml(QTextStream &stream, 03628 QString indent, QString extraAttributes) const 03629 { 03630 QString s; 03631 03632 s += QString("channel=\"%1\" " 03633 "windowSize=\"%2\" " 03634 "windowHopLevel=\"%3\" " 03635 "gain=\"%4\" " 03636 "threshold=\"%5\" ") 03637 .arg(m_channel) 03638 .arg(m_windowSize) 03639 .arg(m_windowHopLevel) 03640 .arg(m_gain) 03641 .arg(m_threshold); 03642 03643 s += QString("minFrequency=\"%1\" " 03644 "maxFrequency=\"%2\" " 03645 "colourScale=\"%3\" " 03646 "colourScheme=\"%4\" " 03647 "colourRotation=\"%5\" " 03648 "frequencyScale=\"%6\" " 03649 "binDisplay=\"%7\" ") 03650 .arg(m_minFrequency) 03651 .arg(m_maxFrequency) 03652 .arg(m_colourScale) 03653 .arg(m_colourMap) 03654 .arg(m_colourRotation) 03655 .arg(m_frequencyScale) 03656 .arg(m_binDisplay); 03657 03658 s += QString("normalizeColumns=\"%1\" " 03659 "normalizeVisibleArea=\"%2\" " 03660 "normalizeHybrid=\"%3\" ") 03661 .arg(m_normalizeColumns ? "true" : "false") 03662 .arg(m_normalizeVisibleArea ? "true" : "false") 03663 .arg(m_normalizeHybrid ? "true" : "false"); 03664 03665 Layer::toXml(stream, indent, extraAttributes + " " + s); 03666 } 03667 03668 void 03669 SpectrogramLayer::setProperties(const QXmlAttributes &attributes) 03670 { 03671 bool ok = false; 03672 03673 int channel = attributes.value("channel").toInt(&ok); 03674 if (ok) setChannel(channel); 03675 03676 int windowSize = attributes.value("windowSize").toUInt(&ok); 03677 if (ok) setWindowSize(windowSize); 03678 03679 int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); 03680 if (ok) setWindowHopLevel(windowHopLevel); 03681 else { 03682 int windowOverlap = attributes.value("windowOverlap").toUInt(&ok); 03683 // a percentage value 03684 if (ok) { 03685 if (windowOverlap == 0) setWindowHopLevel(0); 03686 else if (windowOverlap == 25) setWindowHopLevel(1); 03687 else if (windowOverlap == 50) setWindowHopLevel(2); 03688 else if (windowOverlap == 75) setWindowHopLevel(3); 03689 else if (windowOverlap == 90) setWindowHopLevel(4); 03690 } 03691 } 03692 03693 float gain = attributes.value("gain").toFloat(&ok); 03694 if (ok) setGain(gain); 03695 03696 float threshold = attributes.value("threshold").toFloat(&ok); 03697 if (ok) setThreshold(threshold); 03698 03699 int minFrequency = attributes.value("minFrequency").toUInt(&ok); 03700 if (ok) { 03701 SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl; 03702 setMinFrequency(minFrequency); 03703 } 03704 03705 int maxFrequency = attributes.value("maxFrequency").toUInt(&ok); 03706 if (ok) { 03707 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl; 03708 setMaxFrequency(maxFrequency); 03709 } 03710 03711 ColourScale colourScale = (ColourScale) 03712 attributes.value("colourScale").toInt(&ok); 03713 if (ok) setColourScale(colourScale); 03714 03715 int colourMap = attributes.value("colourScheme").toInt(&ok); 03716 if (ok) setColourMap(colourMap); 03717 03718 int colourRotation = attributes.value("colourRotation").toInt(&ok); 03719 if (ok) setColourRotation(colourRotation); 03720 03721 FrequencyScale frequencyScale = (FrequencyScale) 03722 attributes.value("frequencyScale").toInt(&ok); 03723 if (ok) setFrequencyScale(frequencyScale); 03724 03725 BinDisplay binDisplay = (BinDisplay) 03726 attributes.value("binDisplay").toInt(&ok); 03727 if (ok) setBinDisplay(binDisplay); 03728 03729 bool normalizeColumns = 03730 (attributes.value("normalizeColumns").trimmed() == "true"); 03731 setNormalizeColumns(normalizeColumns); 03732 03733 bool normalizeVisibleArea = 03734 (attributes.value("normalizeVisibleArea").trimmed() == "true"); 03735 setNormalizeVisibleArea(normalizeVisibleArea); 03736 03737 bool normalizeHybrid = 03738 (attributes.value("normalizeHybrid").trimmed() == "true"); 03739 setNormalizeHybrid(normalizeHybrid); 03740 } 03741