annotate layer/SpectrogramLayer.h @ 561:aced8ec09bc8

* Complete the overhaul of CSV file import; now you can pick the purpose for each column in the file, and SV should do the rest. The most significant practical improvement here is that we can now handle files in which time and duration do not necessarily appear in known columns.
author Chris Cannam
date Mon, 19 Jul 2010 17:08:56 +0000
parents c74e511a3c96
children 4c484636d5ec
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@44 63 virtual int getVerticalScaleWidth(View *v, QPainter &) const;
Chris@44 64 virtual void paintVerticalScale(View *v, 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@0 203
Chris@101 204 virtual bool getValueExtents(float &min, float &max,
Chris@101 205 bool &logarithmic, QString &unit) const;
Chris@101 206
Chris@101 207 virtual bool getDisplayExtents(float &min, float &max) const;
Chris@79 208
Chris@120 209 virtual bool setDisplayExtents(float min, float max);
Chris@120 210
Chris@267 211 virtual bool getYScaleValue(const View *, int, float &, QString &) const;
Chris@261 212
Chris@316 213 virtual void toXml(QTextStream &stream, QString indent = "",
Chris@316 214 QString extraAttributes = "") const;
Chris@6 215
Chris@11 216 void setProperties(const QXmlAttributes &attributes);
Chris@11 217
Chris@47 218 virtual void setLayerDormant(const View *v, bool dormant);
Chris@29 219
Chris@248 220 virtual bool isLayerScrollable(const View *) const { return false; }
Chris@94 221
Chris@133 222 virtual int getVerticalZoomSteps(int &defaultStep) const;
Chris@133 223 virtual int getCurrentVerticalZoomStep() const;
Chris@133 224 virtual void setVerticalZoomStep(int);
Chris@187 225 virtual RangeMapper *getNewVerticalZoomRangeMapper() const;
Chris@133 226
Chris@193 227 virtual const Model *getSliceableModel() const;
Chris@193 228
Chris@0 229 protected slots:
Chris@0 230 void cacheInvalid();
Chris@0 231 void cacheInvalid(size_t startFrame, size_t endFrame);
Chris@122 232
Chris@122 233 void preferenceChanged(PropertyContainer::PropertyName name);
Chris@0 234
Chris@0 235 void fillTimerTimedOut();
Chris@0 236
Chris@0 237 protected:
Chris@0 238 const DenseTimeValueModel *m_model; // I do not own this
Chris@484 239
Chris@35 240 int m_channel;
Chris@35 241 size_t m_windowSize;
Chris@35 242 WindowType m_windowType;
Chris@97 243 size_t m_windowHopLevel;
Chris@109 244 size_t m_zeroPadLevel;
Chris@107 245 size_t m_fftSize;
Chris@35 246 float m_gain;
Chris@215 247 float m_initialGain;
Chris@37 248 float m_threshold;
Chris@215 249 float m_initialThreshold;
Chris@35 250 int m_colourRotation;
Chris@215 251 int m_initialRotation;
Chris@37 252 size_t m_minFrequency;
Chris@35 253 size_t m_maxFrequency;
Chris@135 254 size_t m_initialMaxFrequency;
Chris@35 255 ColourScale m_colourScale;
Chris@197 256 int m_colourMap;
Chris@77 257 QColor m_crosshairColour;
Chris@35 258 FrequencyScale m_frequencyScale;
Chris@37 259 BinDisplay m_binDisplay;
Chris@36 260 bool m_normalizeColumns;
Chris@120 261 bool m_normalizeVisibleArea;
Chris@133 262 int m_lastEmittedZoomStep;
Chris@389 263 bool m_synchronous;
Chris@0 264
Chris@215 265 mutable int m_lastPaintBlockWidth;
Chris@215 266 mutable RealTime m_lastPaintTime;
Chris@215 267
Chris@38 268 enum { NO_VALUE = 0 }; // colour index for unused pixels
Chris@38 269
Chris@197 270 class Palette
Chris@86 271 {
Chris@86 272 public:
Chris@86 273 QColor getColour(unsigned char index) const {
Chris@86 274 return m_colours[index];
Chris@86 275 }
Chris@86 276
Chris@86 277 void setColour(unsigned char index, QColor colour) {
Chris@86 278 m_colours[index] = colour;
Chris@86 279 }
Chris@86 280
Chris@86 281 private:
Chris@86 282 QColor m_colours[256];
Chris@86 283 };
Chris@86 284
Chris@197 285 Palette m_palette;
Chris@31 286
Chris@477 287 /**
Chris@478 288 * ImageCache covers the area of the view, at view resolution.
Chris@477 289 * Not all of it is necessarily valid at once (it is refreshed
Chris@477 290 * in parts when scrolling, for example).
Chris@477 291 */
Chris@478 292 struct ImageCache
Chris@95 293 {
Chris@478 294 QImage image;
Chris@95 295 QRect validArea;
Chris@95 296 long startFrame;
Chris@95 297 size_t zoomLevel;
Chris@95 298 };
Chris@478 299 typedef std::map<const View *, ImageCache> ViewImageCache;
Chris@478 300 void invalidateImageCaches();
Chris@478 301 void invalidateImageCaches(size_t startFrame, size_t endFrame);
Chris@478 302 mutable ViewImageCache m_imageCaches;
Chris@477 303
Chris@477 304 /**
Chris@477 305 * When painting, we draw directly onto the draw buffer and then
Chris@478 306 * copy this to the part of the image cache that needed refreshing
Chris@478 307 * before copying the image cache onto the window. (Remind me why
Chris@477 308 * we don't draw directly onto the cache?)
Chris@477 309 */
Chris@95 310 mutable QImage m_drawBuffer;
Chris@0 311
Chris@114 312 mutable QTimer *m_updateTimer;
Chris@110 313
Chris@44 314 mutable size_t m_candidateFillStartFrame;
Chris@0 315 bool m_exiting;
Chris@0 316
Chris@197 317 void initialisePalette();
Chris@197 318 void rotatePalette(int distance);
Chris@0 319
Chris@119 320 unsigned char getDisplayValue(View *v, float input) const;
Chris@40 321 float getInputForDisplayValue(unsigned char uc) const;
Chris@40 322
Chris@40 323 int getColourScaleWidth(QPainter &) const;
Chris@40 324
Chris@121 325 void illuminateLocalFeatures(View *v, QPainter &painter) const;
Chris@121 326
Chris@40 327 float getEffectiveMinFrequency() const;
Chris@40 328 float getEffectiveMaxFrequency() const;
Chris@38 329
Chris@0 330 struct LayerRange {
Chris@0 331 long startFrame;
Chris@0 332 int zoomLevel;
Chris@0 333 size_t modelStart;
Chris@0 334 size_t modelEnd;
Chris@0 335 };
Chris@486 336
Chris@486 337 // Note that the getYBin... methods return the nominal bin in the
Chris@486 338 // un-smoothed spectrogram. This is not necessarily the same bin
Chris@486 339 // as is pulled from the spectrogram and drawn at the given
Chris@486 340 // position, if the spectrogram has oversampling smoothing. Use
Chris@486 341 // getSmoothedYBinRange to obtain that.
Chris@486 342
Chris@44 343 bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const;
Chris@44 344 bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const;
Chris@486 345 bool getSmoothedYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const;
Chris@0 346
Chris@44 347 bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const;
Chris@44 348 bool getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 349 float &freqMin, float &freqMax,
Chris@35 350 float &adjFreqMin, float &adjFreqMax) const;
Chris@44 351 bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const;
Chris@44 352 bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max,
Chris@38 353 float &phaseMin, float &phaseMax) const;
Chris@0 354
Chris@0 355 size_t getWindowIncrement() const {
Chris@97 356 if (m_windowHopLevel == 0) return m_windowSize;
Chris@97 357 else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4;
Chris@97 358 else return m_windowSize / (1 << (m_windowHopLevel - 1));
Chris@0 359 }
Chris@113 360
Chris@114 361 size_t getZeroPadLevel(const View *v) const;
Chris@114 362 size_t getFFTSize(const View *v) const;
Chris@130 363 FFTModel *getFFTModel(const View *v) const;
Chris@484 364 Dense3DModelPeakCache *getPeakCache(const View *v) const;
Chris@130 365 void invalidateFFTModels();
Chris@115 366
Chris@130 367 typedef std::pair<FFTModel *, int> FFTFillPair; // model, last fill
Chris@115 368 typedef std::map<const View *, FFTFillPair> ViewFFTMap;
Chris@484 369 typedef std::map<const View *, Dense3DModelPeakCache *> PeakCacheMap;
Chris@130 370 mutable ViewFFTMap m_fftModels;
Chris@484 371 mutable PeakCacheMap m_peakCaches;
Chris@193 372 mutable Model *m_sliceableModel;
Chris@119 373
Chris@119 374 class MagnitudeRange {
Chris@119 375 public:
Chris@119 376 MagnitudeRange() : m_min(0), m_max(0) { }
Chris@119 377 bool operator==(const MagnitudeRange &r) {
Chris@119 378 return r.m_min == m_min && r.m_max == m_max;
Chris@119 379 }
Chris@119 380 bool isSet() const { return (m_min != 0 || m_max != 0); }
Chris@119 381 void set(float min, float max) {
Chris@119 382 m_min = convert(min);
Chris@119 383 m_max = convert(max);
Chris@119 384 if (m_max < m_min) m_max = m_min;
Chris@119 385 }
Chris@119 386 bool sample(float f) {
Chris@119 387 unsigned int ui = convert(f);
Chris@119 388 bool changed = false;
Chris@119 389 if (isSet()) {
Chris@119 390 if (ui < m_min) { m_min = ui; changed = true; }
Chris@119 391 if (ui > m_max) { m_max = ui; changed = true; }
Chris@119 392 } else {
Chris@119 393 m_max = m_min = ui;
Chris@119 394 changed = true;
Chris@119 395 }
Chris@119 396 return changed;
Chris@119 397 }
Chris@119 398 bool sample(const MagnitudeRange &r) {
Chris@119 399 bool changed = false;
Chris@119 400 if (isSet()) {
Chris@119 401 if (r.m_min < m_min) { m_min = r.m_min; changed = true; }
Chris@119 402 if (r.m_max > m_max) { m_max = r.m_max; changed = true; }
Chris@119 403 } else {
Chris@119 404 m_min = r.m_min;
Chris@119 405 m_max = r.m_max;
Chris@119 406 changed = true;
Chris@119 407 }
Chris@119 408 return changed;
Chris@119 409 }
Chris@119 410 float getMin() const { return float(m_min) / UINT_MAX; }
Chris@119 411 float getMax() const { return float(m_max) / UINT_MAX; }
Chris@119 412 private:
Chris@119 413 unsigned int m_min;
Chris@119 414 unsigned int m_max;
Chris@119 415 unsigned int convert(float f) {
Chris@119 416 if (f < 0.f) f = 0.f;
Chris@119 417 if (f > 1.f) f = 1.f;
Chris@119 418 return (unsigned int)(f * UINT_MAX);
Chris@119 419 }
Chris@119 420 };
Chris@119 421
Chris@119 422 typedef std::map<const View *, MagnitudeRange> ViewMagMap;
Chris@119 423 mutable ViewMagMap m_viewMags;
Chris@119 424 mutable std::vector<MagnitudeRange> m_columnMags;
Chris@119 425 void invalidateMagnitudes();
Chris@119 426 bool updateViewMagnitudes(View *v) const;
Chris@484 427 bool paintDrawBuffer(View *v, int w, int h,
Chris@490 428 int *binforx, float *binfory,
Chris@491 429 bool usePeaksCache,
Chris@491 430 MagnitudeRange &overallMag,
Chris@491 431 bool &overallMagChanged) const;
Chris@488 432 bool paintDrawBufferPeakFrequencies(View *v, int w, int h,
Chris@488 433 int *binforx,
Chris@488 434 int minbin,
Chris@488 435 int maxbin,
Chris@488 436 float displayMinFreq,
Chris@488 437 float displayMaxFreq,
Chris@491 438 bool logarithmic,
Chris@491 439 MagnitudeRange &overallMag,
Chris@491 440 bool &overallMagChanged) const;
Chris@273 441
Chris@273 442 virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const;
Chris@273 443 virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const;
Chris@0 444 };
Chris@0 445
Chris@0 446 #endif