annotate layer/SpectrogramLayer.h @ 607:5b72899d692b

Give a dedicated key to toggling the centre line, and move it out of the overlay level setting -- reducing number of overlay levels to 3. Introduce two distinct vertical scale types (so that we can hide the spectrogram colour scale part easily)
author Chris Cannam
date Mon, 30 Jan 2012 16:01:59 +0000
parents 4c484636d5ec
children 0dba6a391760
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@44 72 virtual bool snapToFeatureFrame(View *v, int &frame,
Chris@28 73 size_t &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@0 102 void setWindowSize(size_t);
Chris@0 103 size_t getWindowSize() const;
Chris@0 104
Chris@109 105 void setWindowHopLevel(size_t level);
Chris@97 106 size_t getWindowHopLevel() const;
Chris@0 107
Chris@0 108 void setWindowType(WindowType type);
Chris@0 109 WindowType getWindowType() const;
Chris@0 110
Chris@109 111 void setZeroPadLevel(size_t level);
Chris@109 112 size_t 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@37 130 void setMinFrequency(size_t);
Chris@37 131 size_t getMinFrequency() const;
Chris@37 132
Chris@0 133 void setMaxFrequency(size_t); // 0 -> no maximum
Chris@0 134 size_t 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@35 173
Chris@36 174 void setNormalizeColumns(bool n);
Chris@36 175 bool getNormalizeColumns() const;
Chris@36 176
Chris@120 177 void setNormalizeVisibleArea(bool n);
Chris@120 178 bool getNormalizeVisibleArea() const;
Chris@120 179
Chris@197 180 void setColourMap(int map);
Chris@197 181 int getColourMap() const;
Chris@0 182
Chris@9 183 /**
Chris@9 184 * Specify the colourmap rotation for the colour scale.
Chris@9 185 */
Chris@9 186 void setColourRotation(int);
Chris@9 187 int getColourRotation() const;
Chris@9 188
Chris@0 189 virtual VerticalPosition getPreferredFrameCountPosition() const {
Chris@0 190 return PositionTop;
Chris@0 191 }
Chris@0 192
Chris@15 193 virtual bool isLayerOpaque() const { return true; }
Chris@287 194
Chris@287 195 virtual ColourSignificance getLayerColourSignificance() const {
Chris@287 196 return ColourHasMeaningfulValue;
Chris@287 197 }
Chris@15 198
Chris@267 199 float getYForFrequency(const View *v, float frequency) const;
Chris@267 200 float getFrequencyForY(const View *v, int y) const;
Chris@42 201
Chris@115 202 virtual int getCompletion(View *v) const;
Chris@583 203 virtual QString getError(View *v) const;
Chris@0 204
Chris@101 205 virtual bool getValueExtents(float &min, float &max,
Chris@101 206 bool &logarithmic, QString &unit) const;
Chris@101 207
Chris@101 208 virtual bool getDisplayExtents(float &min, float &max) const;
Chris@79 209
Chris@120 210 virtual bool setDisplayExtents(float min, float max);
Chris@120 211
Chris@267 212 virtual bool getYScaleValue(const View *, int, float &, QString &) const;
Chris@261 213
Chris@316 214 virtual void toXml(QTextStream &stream, QString indent = "",
Chris@316 215 QString extraAttributes = "") const;
Chris@6 216
Chris@11 217 void setProperties(const QXmlAttributes &attributes);
Chris@11 218
Chris@47 219 virtual void setLayerDormant(const View *v, bool dormant);
Chris@29 220
Chris@248 221 virtual bool isLayerScrollable(const View *) const { return false; }
Chris@94 222
Chris@133 223 virtual int getVerticalZoomSteps(int &defaultStep) const;
Chris@133 224 virtual int getCurrentVerticalZoomStep() const;
Chris@133 225 virtual void setVerticalZoomStep(int);
Chris@187 226 virtual RangeMapper *getNewVerticalZoomRangeMapper() const;
Chris@133 227
Chris@193 228 virtual const Model *getSliceableModel() const;
Chris@193 229
Chris@0 230 protected slots:
Chris@0 231 void cacheInvalid();
Chris@0 232 void cacheInvalid(size_t startFrame, size_t endFrame);
Chris@122 233
Chris@122 234 void preferenceChanged(PropertyContainer::PropertyName name);
Chris@0 235
Chris@0 236 void fillTimerTimedOut();
Chris@0 237
Chris@0 238 protected:
Chris@0 239 const DenseTimeValueModel *m_model; // I do not own this
Chris@484 240
Chris@35 241 int m_channel;
Chris@35 242 size_t m_windowSize;
Chris@35 243 WindowType m_windowType;
Chris@97 244 size_t m_windowHopLevel;
Chris@109 245 size_t m_zeroPadLevel;
Chris@107 246 size_t m_fftSize;
Chris@35 247 float m_gain;
Chris@215 248 float m_initialGain;
Chris@37 249 float m_threshold;
Chris@215 250 float m_initialThreshold;
Chris@35 251 int m_colourRotation;
Chris@215 252 int m_initialRotation;
Chris@37 253 size_t m_minFrequency;
Chris@35 254 size_t m_maxFrequency;
Chris@135 255 size_t m_initialMaxFrequency;
Chris@35 256 ColourScale m_colourScale;
Chris@197 257 int m_colourMap;
Chris@77 258 QColor m_crosshairColour;
Chris@35 259 FrequencyScale m_frequencyScale;
Chris@37 260 BinDisplay m_binDisplay;
Chris@36 261 bool m_normalizeColumns;
Chris@120 262 bool m_normalizeVisibleArea;
Chris@133 263 int m_lastEmittedZoomStep;
Chris@389 264 bool m_synchronous;
Chris@0 265
Chris@215 266 mutable int m_lastPaintBlockWidth;
Chris@215 267 mutable RealTime m_lastPaintTime;
Chris@215 268
Chris@38 269 enum { NO_VALUE = 0 }; // colour index for unused pixels
Chris@38 270
Chris@197 271 class Palette
Chris@86 272 {
Chris@86 273 public:
Chris@86 274 QColor getColour(unsigned char index) const {
Chris@86 275 return m_colours[index];
Chris@86 276 }
Chris@86 277
Chris@86 278 void setColour(unsigned char index, QColor colour) {
Chris@86 279 m_colours[index] = colour;
Chris@86 280 }
Chris@86 281
Chris@86 282 private:
Chris@86 283 QColor m_colours[256];
Chris@86 284 };
Chris@86 285
Chris@197 286 Palette m_palette;
Chris@31 287
Chris@477 288 /**
Chris@478 289 * ImageCache covers the area of the view, at view resolution.
Chris@477 290 * Not all of it is necessarily valid at once (it is refreshed
Chris@477 291 * in parts when scrolling, for example).
Chris@477 292 */
Chris@478 293 struct ImageCache
Chris@95 294 {
Chris@478 295 QImage image;
Chris@95 296 QRect validArea;
Chris@95 297 long startFrame;
Chris@95 298 size_t zoomLevel;
Chris@95 299 };
Chris@478 300 typedef std::map<const View *, ImageCache> ViewImageCache;
Chris@478 301 void invalidateImageCaches();
Chris@478 302 void invalidateImageCaches(size_t startFrame, size_t endFrame);
Chris@478 303 mutable ViewImageCache m_imageCaches;
Chris@477 304
Chris@477 305 /**
Chris@477 306 * When painting, we draw directly onto the draw buffer and then
Chris@478 307 * copy this to the part of the image cache that needed refreshing
Chris@478 308 * before copying the image cache onto the window. (Remind me why
Chris@477 309 * we don't draw directly onto the cache?)
Chris@477 310 */
Chris@95 311 mutable QImage m_drawBuffer;
Chris@0 312
Chris@114 313 mutable QTimer *m_updateTimer;
Chris@110 314
Chris@44 315 mutable size_t m_candidateFillStartFrame;
Chris@0 316 bool m_exiting;
Chris@0 317
Chris@197 318 void initialisePalette();
Chris@197 319 void rotatePalette(int distance);
Chris@0 320
Chris@119 321 unsigned char getDisplayValue(View *v, float input) const;
Chris@40 322 float getInputForDisplayValue(unsigned char uc) const;
Chris@40 323
Chris@40 324 int getColourScaleWidth(QPainter &) const;
Chris@40 325
Chris@121 326 void illuminateLocalFeatures(View *v, QPainter &painter) const;
Chris@121 327
Chris@40 328 float getEffectiveMinFrequency() const;
Chris@40 329 float getEffectiveMaxFrequency() const;
Chris@38 330
Chris@0 331 struct LayerRange {
Chris@0 332 long startFrame;
Chris@0 333 int zoomLevel;
Chris@0 334 size_t modelStart;
Chris@0 335 size_t modelEnd;
Chris@0 336 };
Chris@486 337
Chris@486 338 // Note that the getYBin... methods return the nominal bin in the
Chris@486 339 // un-smoothed spectrogram. This is not necessarily the same bin
Chris@486 340 // as is pulled from the spectrogram and drawn at the given
Chris@486 341 // position, if the spectrogram has oversampling smoothing. Use
Chris@486 342 // getSmoothedYBinRange to obtain that.
Chris@486 343
Chris@44 344 bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const;
Chris@44 345 bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const;
Chris@486 346 bool getSmoothedYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const;
Chris@0 347
Chris@44 348 bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const;
Chris@44 349 bool getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 350 float &freqMin, float &freqMax,
Chris@35 351 float &adjFreqMin, float &adjFreqMax) const;
Chris@44 352 bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const;
Chris@44 353 bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max,
Chris@38 354 float &phaseMin, float &phaseMax) const;
Chris@0 355
Chris@0 356 size_t getWindowIncrement() const {
Chris@97 357 if (m_windowHopLevel == 0) return m_windowSize;
Chris@97 358 else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4;
Chris@97 359 else return m_windowSize / (1 << (m_windowHopLevel - 1));
Chris@0 360 }
Chris@113 361
Chris@114 362 size_t getZeroPadLevel(const View *v) const;
Chris@114 363 size_t getFFTSize(const View *v) const;
Chris@130 364 FFTModel *getFFTModel(const View *v) const;
Chris@484 365 Dense3DModelPeakCache *getPeakCache(const View *v) const;
Chris@130 366 void invalidateFFTModels();
Chris@115 367
Chris@130 368 typedef std::pair<FFTModel *, int> FFTFillPair; // model, last fill
Chris@115 369 typedef std::map<const View *, FFTFillPair> ViewFFTMap;
Chris@484 370 typedef std::map<const View *, Dense3DModelPeakCache *> PeakCacheMap;
Chris@130 371 mutable ViewFFTMap m_fftModels;
Chris@484 372 mutable PeakCacheMap m_peakCaches;
Chris@193 373 mutable Model *m_sliceableModel;
Chris@119 374
Chris@119 375 class MagnitudeRange {
Chris@119 376 public:
Chris@119 377 MagnitudeRange() : m_min(0), m_max(0) { }
Chris@119 378 bool operator==(const MagnitudeRange &r) {
Chris@119 379 return r.m_min == m_min && r.m_max == m_max;
Chris@119 380 }
Chris@119 381 bool isSet() const { return (m_min != 0 || m_max != 0); }
Chris@119 382 void set(float min, float max) {
Chris@119 383 m_min = convert(min);
Chris@119 384 m_max = convert(max);
Chris@119 385 if (m_max < m_min) m_max = m_min;
Chris@119 386 }
Chris@119 387 bool sample(float f) {
Chris@119 388 unsigned int ui = convert(f);
Chris@119 389 bool changed = false;
Chris@119 390 if (isSet()) {
Chris@119 391 if (ui < m_min) { m_min = ui; changed = true; }
Chris@119 392 if (ui > m_max) { m_max = ui; changed = true; }
Chris@119 393 } else {
Chris@119 394 m_max = m_min = ui;
Chris@119 395 changed = true;
Chris@119 396 }
Chris@119 397 return changed;
Chris@119 398 }
Chris@119 399 bool sample(const MagnitudeRange &r) {
Chris@119 400 bool changed = false;
Chris@119 401 if (isSet()) {
Chris@119 402 if (r.m_min < m_min) { m_min = r.m_min; changed = true; }
Chris@119 403 if (r.m_max > m_max) { m_max = r.m_max; changed = true; }
Chris@119 404 } else {
Chris@119 405 m_min = r.m_min;
Chris@119 406 m_max = r.m_max;
Chris@119 407 changed = true;
Chris@119 408 }
Chris@119 409 return changed;
Chris@119 410 }
Chris@119 411 float getMin() const { return float(m_min) / UINT_MAX; }
Chris@119 412 float getMax() const { return float(m_max) / UINT_MAX; }
Chris@119 413 private:
Chris@119 414 unsigned int m_min;
Chris@119 415 unsigned int m_max;
Chris@119 416 unsigned int convert(float f) {
Chris@119 417 if (f < 0.f) f = 0.f;
Chris@119 418 if (f > 1.f) f = 1.f;
Chris@119 419 return (unsigned int)(f * UINT_MAX);
Chris@119 420 }
Chris@119 421 };
Chris@119 422
Chris@119 423 typedef std::map<const View *, MagnitudeRange> ViewMagMap;
Chris@119 424 mutable ViewMagMap m_viewMags;
Chris@119 425 mutable std::vector<MagnitudeRange> m_columnMags;
Chris@119 426 void invalidateMagnitudes();
Chris@119 427 bool updateViewMagnitudes(View *v) const;
Chris@484 428 bool paintDrawBuffer(View *v, int w, int h,
Chris@490 429 int *binforx, float *binfory,
Chris@491 430 bool usePeaksCache,
Chris@491 431 MagnitudeRange &overallMag,
Chris@491 432 bool &overallMagChanged) const;
Chris@488 433 bool paintDrawBufferPeakFrequencies(View *v, int w, int h,
Chris@488 434 int *binforx,
Chris@488 435 int minbin,
Chris@488 436 int maxbin,
Chris@488 437 float displayMinFreq,
Chris@488 438 float displayMaxFreq,
Chris@491 439 bool logarithmic,
Chris@491 440 MagnitudeRange &overallMag,
Chris@491 441 bool &overallMagChanged) const;
Chris@273 442
Chris@273 443 virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const;
Chris@273 444 virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const;
Chris@0 445 };
Chris@0 446
Chris@0 447 #endif