SpectrogramLayer.cpp

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

Generated on Wed Feb 20 15:45:28 2008 for SonicVisualiser by  doxygen 1.5.1