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