annotate layer/SpectrogramLayer.h @ 959:2633a1d73e39

Address #1214, layer import produces wrong layer type. We needed a more principled approach to making sure the format gets updated properly and the dialog elements are consistent (basically separating making the dialog elements consistent from subsequently updating the format). This change should provide that, though there may be gotchas still.
author Chris Cannam
date Tue, 12 May 2015 12:31:37 +0100
parents 28d05ae8741c
children 94e4952a6774 8053c0dfa919
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@30 16 #ifndef _SPECTROGRAM_LAYER_H_
Chris@30 17 #define _SPECTROGRAM_LAYER_H_
Chris@0 18
Chris@193 19 #include "SliceableLayer.h"
Chris@0 20 #include "base/Window.h"
Chris@71 21 #include "base/RealTime.h"
Chris@92 22 #include "base/Thread.h"
Chris@122 23 #include "base/PropertyContainer.h"
Chris@128 24 #include "data/model/PowerOfSqrtTwoZoomConstraint.h"
Chris@128 25 #include "data/model/DenseTimeValueModel.h"
Chris@130 26 #include "data/model/FFTModel.h"
Chris@0 27
Chris@0 28 #include <QMutex>
Chris@0 29 #include <QWaitCondition>
Chris@95 30 #include <QImage>
Chris@95 31 #include <QPixmap>
Chris@0 32
Chris@0 33 class View;
Chris@0 34 class QPainter;
Chris@0 35 class QImage;
Chris@0 36 class QPixmap;
Chris@0 37 class QTimer;
Chris@130 38 class FFTModel;
Chris@484 39 class Dense3DModelPeakCache;
Chris@114 40
Chris@0 41
Chris@0 42 /**
Chris@0 43 * SpectrogramLayer represents waveform data (obtained from a
Chris@0 44 * DenseTimeValueModel) in spectrogram form.
Chris@0 45 */
Chris@0 46
Chris@193 47 class SpectrogramLayer : public SliceableLayer,
Chris@31 48 public PowerOfSqrtTwoZoomConstraint
Chris@0 49 {
Chris@0 50 Q_OBJECT
Chris@0 51
Chris@0 52 public:
Chris@37 53 enum Configuration { FullRangeDb, MelodicRange, MelodicPeaks };
Chris@0 54
Chris@44 55 SpectrogramLayer(Configuration = FullRangeDb);
Chris@0 56 ~SpectrogramLayer();
Chris@0 57
Chris@0 58 virtual const ZoomConstraint *getZoomConstraint() const { return this; }
Chris@0 59 virtual const Model *getModel() const { return m_model; }
Chris@44 60 virtual void paint(View *v, QPainter &paint, QRect rect) const;
Chris@389 61 virtual void setSynchronousPainting(bool synchronous);
Chris@0 62
Chris@607 63 virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const;
Chris@607 64 virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const;
Chris@0 65
Chris@77 66 virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos,
Chris@77 67 std::vector<QRect> &extents) const;
Chris@77 68 virtual void paintCrosshairs(View *, QPainter &, QPoint) const;
Chris@77 69
Chris@44 70 virtual QString getFeatureDescription(View *v, QPoint &) const;
Chris@0 71
Chris@905 72 virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
Chris@805 73 int &resolution,
Chris@28 74 SnapType snap) const;
Chris@13 75
Chris@283 76 virtual void measureDoubleClick(View *, QMouseEvent *);
Chris@283 77
Chris@224 78 virtual bool hasLightBackground() const;
Chris@224 79
Chris@0 80 void setModel(const DenseTimeValueModel *model);
Chris@0 81
Chris@0 82 virtual PropertyList getProperties() const;
Chris@87 83 virtual QString getPropertyLabel(const PropertyName &) const;
Chris@335 84 virtual QString getPropertyIconName(const PropertyName &) const;
Chris@0 85 virtual PropertyType getPropertyType(const PropertyName &) const;
Chris@0 86 virtual QString getPropertyGroupName(const PropertyName &) const;
Chris@0 87 virtual int getPropertyRangeAndValue(const PropertyName &,
Chris@216 88 int *min, int *max, int *deflt) const;
Chris@0 89 virtual QString getPropertyValueLabel(const PropertyName &,
Chris@0 90 int value) const;
Chris@167 91 virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const;
Chris@0 92 virtual void setProperty(const PropertyName &, int value);
Chris@0 93
Chris@0 94 /**
Chris@0 95 * Specify the channel to use from the source model.
Chris@0 96 * A value of -1 means to mix all available channels.
Chris@0 97 * The default is channel 0.
Chris@0 98 */
Chris@0 99 void setChannel(int);
Chris@0 100 int getChannel() const;
Chris@0 101
Chris@805 102 void setWindowSize(int);
Chris@805 103 int getWindowSize() const;
Chris@0 104
Chris@805 105 void setWindowHopLevel(int level);
Chris@805 106 int getWindowHopLevel() const;
Chris@0 107
Chris@0 108 void setWindowType(WindowType type);
Chris@0 109 WindowType getWindowType() const;
Chris@0 110
Chris@805 111 void setZeroPadLevel(int level);
Chris@805 112 int getZeroPadLevel() const;
Chris@109 113
Chris@0 114 /**
Chris@110 115 * Set the gain multiplier for sample values in this view.
Chris@0 116 * The default is 1.0.
Chris@0 117 */
Chris@0 118 void setGain(float gain);
Chris@0 119 float getGain() const;
Chris@0 120
Chris@37 121 /**
Chris@110 122 * Set the threshold for sample values to qualify for being shown
Chris@110 123 * in the FFT, in voltage units.
Chris@37 124 *
Chris@37 125 * The default is 0.0.
Chris@37 126 */
Chris@37 127 void setThreshold(float threshold);
Chris@37 128 float getThreshold() const;
Chris@37 129
Chris@805 130 void setMinFrequency(int);
Chris@805 131 int getMinFrequency() const;
Chris@37 132
Chris@805 133 void setMaxFrequency(int); // 0 -> no maximum
Chris@805 134 int getMaxFrequency() const;
Chris@0 135
Chris@37 136 enum ColourScale {
Chris@37 137 LinearColourScale,
Chris@37 138 MeterColourScale,
Chris@215 139 dBSquaredColourScale,
Chris@37 140 dBColourScale,
Chris@37 141 PhaseColourScale
Chris@37 142 };
Chris@0 143
Chris@0 144 /**
Chris@0 145 * Specify the scale for sample levels. See WaveformLayer for
Chris@0 146 * details of meter and dB scaling. The default is dBColourScale.
Chris@0 147 */
Chris@0 148 void setColourScale(ColourScale);
Chris@0 149 ColourScale getColourScale() const;
Chris@0 150
Chris@35 151 enum FrequencyScale {
Chris@35 152 LinearFrequencyScale,
Chris@35 153 LogFrequencyScale
Chris@35 154 };
Chris@0 155
Chris@0 156 /**
Chris@0 157 * Specify the scale for the y axis.
Chris@0 158 */
Chris@0 159 void setFrequencyScale(FrequencyScale);
Chris@0 160 FrequencyScale getFrequencyScale() const;
Chris@0 161
Chris@37 162 enum BinDisplay {
Chris@37 163 AllBins,
Chris@37 164 PeakBins,
Chris@37 165 PeakFrequencies
Chris@35 166 };
Chris@35 167
Chris@35 168 /**
Chris@35 169 * Specify the processing of frequency bins for the y axis.
Chris@35 170 */
Chris@37 171 void setBinDisplay(BinDisplay);
Chris@37 172 BinDisplay getBinDisplay() const;
Chris@719 173
Chris@719 174 /**
Chris@719 175 * Normalize each column to its maximum value, independent of its
Chris@719 176 * neighbours.
Chris@719 177 */
Chris@36 178 void setNormalizeColumns(bool n);
Chris@36 179 bool getNormalizeColumns() const;
Chris@36 180
Chris@719 181 /**
Chris@719 182 * Normalize each value against the maximum in the visible region.
Chris@719 183 */
Chris@120 184 void setNormalizeVisibleArea(bool n);
Chris@120 185 bool getNormalizeVisibleArea() const;
Chris@120 186
Chris@719 187 /**
Chris@719 188 * Normalize each column to its maximum value, and then scale by
Chris@719 189 * the log of the (absolute) maximum value.
Chris@719 190 */
Chris@719 191 void setNormalizeHybrid(bool n);
Chris@719 192 bool getNormalizeHybrid() const;
Chris@719 193
Chris@197 194 void setColourMap(int map);
Chris@197 195 int getColourMap() const;
Chris@0 196
Chris@9 197 /**
Chris@9 198 * Specify the colourmap rotation for the colour scale.
Chris@9 199 */
Chris@9 200 void setColourRotation(int);
Chris@9 201 int getColourRotation() const;
Chris@9 202
Chris@0 203 virtual VerticalPosition getPreferredFrameCountPosition() const {
Chris@0 204 return PositionTop;
Chris@0 205 }
Chris@0 206
Chris@15 207 virtual bool isLayerOpaque() const { return true; }
Chris@287 208
Chris@287 209 virtual ColourSignificance getLayerColourSignificance() const {
Chris@287 210 return ColourHasMeaningfulValue;
Chris@287 211 }
Chris@15 212
Chris@905 213 double getYForFrequency(const View *v, double frequency) const;
Chris@905 214 double getFrequencyForY(const View *v, int y) const;
Chris@42 215
Chris@115 216 virtual int getCompletion(View *v) const;
Chris@583 217 virtual QString getError(View *v) const;
Chris@0 218
Chris@905 219 virtual bool getValueExtents(double &min, double &max,
Chris@101 220 bool &logarithmic, QString &unit) const;
Chris@101 221
Chris@905 222 virtual bool getDisplayExtents(double &min, double &max) const;
Chris@79 223
Chris@905 224 virtual bool setDisplayExtents(double min, double max);
Chris@120 225
Chris@905 226 virtual bool getYScaleValue(const View *, int, double &, QString &) const;
Chris@261 227
Chris@316 228 virtual void toXml(QTextStream &stream, QString indent = "",
Chris@316 229 QString extraAttributes = "") const;
Chris@6 230
Chris@11 231 void setProperties(const QXmlAttributes &attributes);
Chris@11 232
Chris@47 233 virtual void setLayerDormant(const View *v, bool dormant);
Chris@29 234
Chris@248 235 virtual bool isLayerScrollable(const View *) const { return false; }
Chris@94 236
Chris@133 237 virtual int getVerticalZoomSteps(int &defaultStep) const;
Chris@133 238 virtual int getCurrentVerticalZoomStep() const;
Chris@133 239 virtual void setVerticalZoomStep(int);
Chris@187 240 virtual RangeMapper *getNewVerticalZoomRangeMapper() const;
Chris@133 241
Chris@193 242 virtual const Model *getSliceableModel() const;
Chris@193 243
Chris@0 244 protected slots:
Chris@0 245 void cacheInvalid();
Chris@906 246 void cacheInvalid(sv_frame_t startFrame, sv_frame_t endFrame);
Chris@122 247
Chris@122 248 void preferenceChanged(PropertyContainer::PropertyName name);
Chris@0 249
Chris@0 250 void fillTimerTimedOut();
Chris@0 251
Chris@0 252 protected:
Chris@0 253 const DenseTimeValueModel *m_model; // I do not own this
Chris@484 254
Chris@35 255 int m_channel;
Chris@805 256 int m_windowSize;
Chris@35 257 WindowType m_windowType;
Chris@805 258 int m_windowHopLevel;
Chris@805 259 int m_zeroPadLevel;
Chris@805 260 int m_fftSize;
Chris@35 261 float m_gain;
Chris@215 262 float m_initialGain;
Chris@37 263 float m_threshold;
Chris@215 264 float m_initialThreshold;
Chris@35 265 int m_colourRotation;
Chris@215 266 int m_initialRotation;
Chris@805 267 int m_minFrequency;
Chris@805 268 int m_maxFrequency;
Chris@805 269 int m_initialMaxFrequency;
Chris@35 270 ColourScale m_colourScale;
Chris@197 271 int m_colourMap;
Chris@77 272 QColor m_crosshairColour;
Chris@35 273 FrequencyScale m_frequencyScale;
Chris@37 274 BinDisplay m_binDisplay;
Chris@36 275 bool m_normalizeColumns;
Chris@120 276 bool m_normalizeVisibleArea;
Chris@719 277 bool m_normalizeHybrid;
Chris@133 278 int m_lastEmittedZoomStep;
Chris@389 279 bool m_synchronous;
Chris@0 280
Chris@608 281 mutable bool m_haveDetailedScale;
Chris@215 282 mutable int m_lastPaintBlockWidth;
Chris@215 283 mutable RealTime m_lastPaintTime;
Chris@215 284
Chris@38 285 enum { NO_VALUE = 0 }; // colour index for unused pixels
Chris@38 286
Chris@197 287 class Palette
Chris@86 288 {
Chris@86 289 public:
Chris@86 290 QColor getColour(unsigned char index) const {
Chris@86 291 return m_colours[index];
Chris@86 292 }
Chris@86 293
Chris@86 294 void setColour(unsigned char index, QColor colour) {
Chris@86 295 m_colours[index] = colour;
Chris@86 296 }
Chris@86 297
Chris@86 298 private:
Chris@86 299 QColor m_colours[256];
Chris@86 300 };
Chris@86 301
Chris@197 302 Palette m_palette;
Chris@31 303
Chris@477 304 /**
Chris@478 305 * ImageCache covers the area of the view, at view resolution.
Chris@477 306 * Not all of it is necessarily valid at once (it is refreshed
Chris@477 307 * in parts when scrolling, for example).
Chris@477 308 */
Chris@478 309 struct ImageCache
Chris@95 310 {
Chris@478 311 QImage image;
Chris@95 312 QRect validArea;
Chris@907 313 sv_frame_t startFrame;
Chris@805 314 int zoomLevel;
Chris@95 315 };
Chris@478 316 typedef std::map<const View *, ImageCache> ViewImageCache;
Chris@478 317 void invalidateImageCaches();
Chris@906 318 void invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame);
Chris@478 319 mutable ViewImageCache m_imageCaches;
Chris@477 320
Chris@477 321 /**
Chris@477 322 * When painting, we draw directly onto the draw buffer and then
Chris@478 323 * copy this to the part of the image cache that needed refreshing
Chris@478 324 * before copying the image cache onto the window. (Remind me why
Chris@477 325 * we don't draw directly onto the cache?)
Chris@477 326 */
Chris@95 327 mutable QImage m_drawBuffer;
Chris@0 328
Chris@114 329 mutable QTimer *m_updateTimer;
Chris@110 330
Chris@906 331 mutable sv_frame_t m_candidateFillStartFrame;
Chris@0 332 bool m_exiting;
Chris@0 333
Chris@197 334 void initialisePalette();
Chris@197 335 void rotatePalette(int distance);
Chris@0 336
Chris@907 337 unsigned char getDisplayValue(View *v, double input) const;
Chris@40 338
Chris@40 339 int getColourScaleWidth(QPainter &) const;
Chris@40 340
Chris@121 341 void illuminateLocalFeatures(View *v, QPainter &painter) const;
Chris@121 342
Chris@905 343 double getEffectiveMinFrequency() const;
Chris@905 344 double getEffectiveMaxFrequency() const;
Chris@38 345
Chris@486 346 // Note that the getYBin... methods return the nominal bin in the
Chris@486 347 // un-smoothed spectrogram. This is not necessarily the same bin
Chris@486 348 // as is pulled from the spectrogram and drawn at the given
Chris@486 349 // position, if the spectrogram has oversampling smoothing. Use
Chris@486 350 // getSmoothedYBinRange to obtain that.
Chris@486 351
Chris@905 352 bool getXBinRange(View *v, int x, double &windowMin, double &windowMax) const;
Chris@905 353 bool getYBinRange(View *v, int y, double &freqBinMin, double &freqBinMax) const;
Chris@905 354 bool getSmoothedYBinRange(View *v, int y, double &freqBinMin, double &freqBinMax) const;
Chris@0 355
Chris@905 356 bool getYBinSourceRange(View *v, int y, double &freqMin, double &freqMax) const;
Chris@44 357 bool getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@905 358 double &freqMin, double &freqMax,
Chris@905 359 double &adjFreqMin, double &adjFreqMax) const;
Chris@44 360 bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const;
Chris@905 361 bool getXYBinSourceRange(View *v, int x, int y, double &min, double &max,
Chris@905 362 double &phaseMin, double &phaseMax) const;
Chris@0 363
Chris@805 364 int getWindowIncrement() const {
Chris@97 365 if (m_windowHopLevel == 0) return m_windowSize;
Chris@97 366 else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4;
Chris@97 367 else return m_windowSize / (1 << (m_windowHopLevel - 1));
Chris@0 368 }
Chris@113 369
Chris@805 370 int getZeroPadLevel(const View *v) const;
Chris@805 371 int getFFTSize(const View *v) const;
Chris@130 372 FFTModel *getFFTModel(const View *v) const;
Chris@484 373 Dense3DModelPeakCache *getPeakCache(const View *v) const;
Chris@130 374 void invalidateFFTModels();
Chris@115 375
Chris@906 376 typedef std::pair<FFTModel *, sv_frame_t> FFTFillPair; // model, last fill
Chris@115 377 typedef std::map<const View *, FFTFillPair> ViewFFTMap;
Chris@484 378 typedef std::map<const View *, Dense3DModelPeakCache *> PeakCacheMap;
Chris@130 379 mutable ViewFFTMap m_fftModels;
Chris@484 380 mutable PeakCacheMap m_peakCaches;
Chris@193 381 mutable Model *m_sliceableModel;
Chris@119 382
Chris@119 383 class MagnitudeRange {
Chris@119 384 public:
Chris@119 385 MagnitudeRange() : m_min(0), m_max(0) { }
Chris@119 386 bool operator==(const MagnitudeRange &r) {
Chris@119 387 return r.m_min == m_min && r.m_max == m_max;
Chris@119 388 }
Chris@905 389 bool isSet() const { return (m_min != 0.f || m_max != 0.f); }
Chris@119 390 void set(float min, float max) {
Chris@905 391 m_min = min;
Chris@905 392 m_max = max;
Chris@119 393 if (m_max < m_min) m_max = m_min;
Chris@119 394 }
Chris@119 395 bool sample(float f) {
Chris@119 396 bool changed = false;
Chris@119 397 if (isSet()) {
Chris@905 398 if (f < m_min) { m_min = f; changed = true; }
Chris@905 399 if (f > m_max) { m_max = f; changed = true; }
Chris@119 400 } else {
Chris@905 401 m_max = m_min = f;
Chris@119 402 changed = true;
Chris@119 403 }
Chris@119 404 return changed;
Chris@119 405 }
Chris@119 406 bool sample(const MagnitudeRange &r) {
Chris@119 407 bool changed = false;
Chris@119 408 if (isSet()) {
Chris@119 409 if (r.m_min < m_min) { m_min = r.m_min; changed = true; }
Chris@119 410 if (r.m_max > m_max) { m_max = r.m_max; changed = true; }
Chris@119 411 } else {
Chris@119 412 m_min = r.m_min;
Chris@119 413 m_max = r.m_max;
Chris@119 414 changed = true;
Chris@119 415 }
Chris@119 416 return changed;
Chris@119 417 }
Chris@905 418 float getMin() const { return m_min; }
Chris@905 419 float getMax() const { return m_max; }
Chris@119 420 private:
Chris@905 421 float m_min;
Chris@905 422 float m_max;
Chris@119 423 };
Chris@119 424
Chris@119 425 typedef std::map<const View *, MagnitudeRange> ViewMagMap;
Chris@119 426 mutable ViewMagMap m_viewMags;
Chris@119 427 mutable std::vector<MagnitudeRange> m_columnMags;
Chris@119 428 void invalidateMagnitudes();
Chris@119 429 bool updateViewMagnitudes(View *v) const;
Chris@484 430 bool paintDrawBuffer(View *v, int w, int h,
Chris@907 431 const std::vector<int> &binforx,
Chris@907 432 const std::vector<double> &binfory,
Chris@491 433 bool usePeaksCache,
Chris@491 434 MagnitudeRange &overallMag,
Chris@491 435 bool &overallMagChanged) const;
Chris@488 436 bool paintDrawBufferPeakFrequencies(View *v, int w, int h,
Chris@907 437 const std::vector<int> &binforx,
Chris@488 438 int minbin,
Chris@488 439 int maxbin,
Chris@905 440 double displayMinFreq,
Chris@905 441 double displayMaxFreq,
Chris@491 442 bool logarithmic,
Chris@491 443 MagnitudeRange &overallMag,
Chris@491 444 bool &overallMagChanged) const;
Chris@273 445
Chris@273 446 virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const;
Chris@273 447 virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const;
Chris@0 448 };
Chris@0 449
Chris@0 450 #endif