annotate layer/SpectrogramLayer.h @ 77:fd348f36c0d3

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents 72fa239a4880
children 19bf27e4fb29
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@59 7 This file copyright 2006 Chris Cannam.
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@0 19 #include "base/Layer.h"
Chris@0 20 #include "base/Window.h"
Chris@71 21 #include "base/RealTime.h"
Chris@0 22 #include "model/PowerOfSqrtTwoZoomConstraint.h"
Chris@0 23 #include "model/DenseTimeValueModel.h"
Chris@0 24
Chris@0 25 #include <QThread>
Chris@0 26 #include <QMutex>
Chris@0 27 #include <QWaitCondition>
Chris@0 28
Chris@0 29 #include <fftw3.h>
Chris@0 30
Chris@38 31 #include <stdint.h>
Chris@38 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@0 38
Chris@0 39 /**
Chris@0 40 * SpectrogramLayer represents waveform data (obtained from a
Chris@0 41 * DenseTimeValueModel) in spectrogram form.
Chris@0 42 */
Chris@0 43
Chris@0 44 class SpectrogramLayer : public Layer,
Chris@31 45 public PowerOfSqrtTwoZoomConstraint
Chris@0 46 {
Chris@0 47 Q_OBJECT
Chris@0 48
Chris@0 49 public:
Chris@37 50 enum Configuration { FullRangeDb, MelodicRange, MelodicPeaks };
Chris@0 51
Chris@44 52 SpectrogramLayer(Configuration = FullRangeDb);
Chris@0 53 ~SpectrogramLayer();
Chris@0 54
Chris@0 55 virtual const ZoomConstraint *getZoomConstraint() const { return this; }
Chris@0 56 virtual const Model *getModel() const { return m_model; }
Chris@44 57 virtual void paint(View *v, QPainter &paint, QRect rect) const;
Chris@0 58
Chris@44 59 virtual int getVerticalScaleWidth(View *v, QPainter &) const;
Chris@44 60 virtual void paintVerticalScale(View *v, QPainter &paint, QRect rect) const;
Chris@0 61
Chris@77 62 virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos,
Chris@77 63 std::vector<QRect> &extents) const;
Chris@77 64 virtual void paintCrosshairs(View *, QPainter &, QPoint) const;
Chris@77 65
Chris@44 66 virtual QString getFeatureDescription(View *v, QPoint &) const;
Chris@0 67
Chris@44 68 virtual bool snapToFeatureFrame(View *v, int &frame,
Chris@28 69 size_t &resolution,
Chris@28 70 SnapType snap) const;
Chris@13 71
Chris@0 72 void setModel(const DenseTimeValueModel *model);
Chris@0 73
Chris@0 74 virtual PropertyList getProperties() const;
Chris@0 75 virtual PropertyType getPropertyType(const PropertyName &) const;
Chris@0 76 virtual QString getPropertyGroupName(const PropertyName &) const;
Chris@0 77 virtual int getPropertyRangeAndValue(const PropertyName &,
Chris@0 78 int *min, int *max) const;
Chris@0 79 virtual QString getPropertyValueLabel(const PropertyName &,
Chris@0 80 int value) const;
Chris@0 81 virtual void setProperty(const PropertyName &, int value);
Chris@0 82
Chris@0 83 /**
Chris@0 84 * Specify the channel to use from the source model.
Chris@0 85 * A value of -1 means to mix all available channels.
Chris@0 86 * The default is channel 0.
Chris@0 87 */
Chris@0 88 void setChannel(int);
Chris@0 89 int getChannel() const;
Chris@0 90
Chris@0 91 void setWindowSize(size_t);
Chris@0 92 size_t getWindowSize() const;
Chris@0 93
Chris@0 94 void setWindowOverlap(size_t percent);
Chris@0 95 size_t getWindowOverlap() const;
Chris@0 96
Chris@0 97 void setWindowType(WindowType type);
Chris@0 98 WindowType getWindowType() const;
Chris@0 99
Chris@0 100 /**
Chris@0 101 * Set the gain multiplier for sample values in this view prior to
Chris@0 102 * FFT calculation.
Chris@0 103 *
Chris@0 104 * The default is 1.0.
Chris@0 105 */
Chris@0 106 void setGain(float gain);
Chris@0 107 float getGain() const;
Chris@0 108
Chris@37 109 /**
Chris@37 110 * Set the threshold for sample values to be shown in the FFT,
Chris@37 111 * in voltage units.
Chris@37 112 *
Chris@37 113 * The default is 0.0.
Chris@37 114 */
Chris@37 115 void setThreshold(float threshold);
Chris@37 116 float getThreshold() const;
Chris@37 117
Chris@37 118 void setMinFrequency(size_t);
Chris@37 119 size_t getMinFrequency() const;
Chris@37 120
Chris@0 121 void setMaxFrequency(size_t); // 0 -> no maximum
Chris@0 122 size_t getMaxFrequency() const;
Chris@0 123
Chris@37 124 enum ColourScale {
Chris@37 125 LinearColourScale,
Chris@37 126 MeterColourScale,
Chris@37 127 dBColourScale,
Chris@37 128 PhaseColourScale
Chris@37 129 };
Chris@0 130
Chris@0 131 /**
Chris@0 132 * Specify the scale for sample levels. See WaveformLayer for
Chris@0 133 * details of meter and dB scaling. The default is dBColourScale.
Chris@0 134 */
Chris@0 135 void setColourScale(ColourScale);
Chris@0 136 ColourScale getColourScale() const;
Chris@0 137
Chris@35 138 enum FrequencyScale {
Chris@35 139 LinearFrequencyScale,
Chris@35 140 LogFrequencyScale
Chris@35 141 };
Chris@0 142
Chris@0 143 /**
Chris@0 144 * Specify the scale for the y axis.
Chris@0 145 */
Chris@0 146 void setFrequencyScale(FrequencyScale);
Chris@0 147 FrequencyScale getFrequencyScale() const;
Chris@0 148
Chris@37 149 enum BinDisplay {
Chris@37 150 AllBins,
Chris@37 151 PeakBins,
Chris@37 152 PeakFrequencies
Chris@35 153 };
Chris@35 154
Chris@35 155 /**
Chris@35 156 * Specify the processing of frequency bins for the y axis.
Chris@35 157 */
Chris@37 158 void setBinDisplay(BinDisplay);
Chris@37 159 BinDisplay getBinDisplay() const;
Chris@35 160
Chris@36 161 void setNormalizeColumns(bool n);
Chris@36 162 bool getNormalizeColumns() const;
Chris@36 163
Chris@0 164 enum ColourScheme { DefaultColours, WhiteOnBlack, BlackOnWhite,
Chris@71 165 RedOnBlue, YellowOnBlack, BlueOnBlack, Rainbow };
Chris@0 166
Chris@0 167 void setColourScheme(ColourScheme scheme);
Chris@0 168 ColourScheme getColourScheme() const;
Chris@0 169
Chris@9 170 /**
Chris@9 171 * Specify the colourmap rotation for the colour scale.
Chris@9 172 */
Chris@9 173 void setColourRotation(int);
Chris@9 174 int getColourRotation() const;
Chris@9 175
Chris@0 176 virtual VerticalPosition getPreferredFrameCountPosition() const {
Chris@0 177 return PositionTop;
Chris@0 178 }
Chris@0 179
Chris@15 180 virtual bool isLayerOpaque() const { return true; }
Chris@15 181
Chris@44 182 float getYForFrequency(View *v, float frequency) const;
Chris@44 183 float getFrequencyForY(View *v, int y) const;
Chris@42 184
Chris@0 185 virtual int getCompletion() const;
Chris@0 186
Chris@6 187 virtual QString toXmlString(QString indent = "",
Chris@6 188 QString extraAttributes = "") const;
Chris@6 189
Chris@11 190 void setProperties(const QXmlAttributes &attributes);
Chris@11 191
Chris@47 192 virtual void setLayerDormant(const View *v, bool dormant);
Chris@29 193
Chris@0 194 protected slots:
Chris@0 195 void cacheInvalid();
Chris@0 196 void cacheInvalid(size_t startFrame, size_t endFrame);
Chris@0 197
Chris@0 198 void fillTimerTimedOut();
Chris@0 199
Chris@0 200 protected:
Chris@0 201 const DenseTimeValueModel *m_model; // I do not own this
Chris@0 202
Chris@35 203 int m_channel;
Chris@35 204 size_t m_windowSize;
Chris@35 205 WindowType m_windowType;
Chris@35 206 size_t m_windowOverlap;
Chris@35 207 float m_gain;
Chris@37 208 float m_threshold;
Chris@35 209 int m_colourRotation;
Chris@37 210 size_t m_minFrequency;
Chris@35 211 size_t m_maxFrequency;
Chris@35 212 ColourScale m_colourScale;
Chris@35 213 ColourScheme m_colourScheme;
Chris@77 214 QColor m_crosshairColour;
Chris@35 215 FrequencyScale m_frequencyScale;
Chris@37 216 BinDisplay m_binDisplay;
Chris@36 217 bool m_normalizeColumns;
Chris@0 218
Chris@38 219 // At the moment we cache one unsigned char per bin for the
Chris@38 220 // magnitude -- which is nothing like precise enough to allow us
Chris@38 221 // to subsequently adjust gain etc without recalculating the
Chris@38 222 // cached values -- plus optionally one unsigned char per bin for
Chris@38 223 // phase-adjusted frequency.
Chris@37 224
Chris@38 225 // To speed up redrawing after parameter changes, we would like to
Chris@38 226 // cache magnitude in a way that can have gain applied afterwards
Chris@38 227 // and can determine whether something is a peak or not, and also
Chris@38 228 // cache phase rather than only phase-adjusted frequency so that
Chris@38 229 // we don't have to recalculate if switching between phase and
Chris@38 230 // magnitude displays.
Chris@38 231
Chris@38 232 // This implies probably 16 bits for a normalized magnitude (in
Chris@38 233 // dB?) and at most 16 bits for phase. 16 or 32 bits per bin
Chris@38 234 // instead of 8 or 16.
Chris@38 235
Chris@38 236 // Each column's magnitudes are expected to be stored normalized
Chris@38 237 // to [0,1] with respect to the column, so the normalization
Chris@38 238 // factor should be calculated before all values in a column, and
Chris@38 239 // set appropriately.
Chris@38 240
Chris@31 241 class Cache {
Chris@31 242 public:
Chris@38 243 Cache(); // of size zero, call resize() before using
Chris@31 244 ~Cache();
Chris@31 245
Chris@38 246 size_t getWidth() const { return m_width; }
Chris@38 247 size_t getHeight() const { return m_height; }
Chris@38 248
Chris@38 249 void resize(size_t width, size_t height);
Chris@38 250 void reset(); // zero-fill or 1-fill as appropriate without changing size
Chris@38 251
Chris@38 252 float getMagnitudeAt(size_t x, size_t y) const {
Chris@38 253 return getNormalizedMagnitudeAt(x, y) * m_factor[x];
Chris@38 254 }
Chris@35 255
Chris@38 256 float getNormalizedMagnitudeAt(size_t x, size_t y) const {
Chris@44 257 return float(m_magnitude[x][y]) / 65535.0;
Chris@38 258 }
Chris@31 259
Chris@38 260 float getPhaseAt(size_t x, size_t y) const {
Chris@44 261 int16_t i = (int16_t)m_phase[x][y];
Chris@39 262 return (float(i) / 32767.0) * M_PI;
Chris@38 263 }
Chris@31 264
Chris@38 265 bool isLocalPeak(size_t x, size_t y) const {
Chris@44 266 if (y > 0 && m_magnitude[x][y] < m_magnitude[x][y-1]) return false;
Chris@44 267 if (y < m_height-1 && m_magnitude[x][y] < m_magnitude[x][y+1]) return false;
Chris@38 268 return true;
Chris@38 269 }
Chris@31 270
Chris@38 271 bool isOverThreshold(size_t x, size_t y, float threshold) const {
Chris@38 272 if (threshold == 0.0) return true;
Chris@38 273 return getMagnitudeAt(x, y) > threshold;
Chris@38 274 }
Chris@38 275
Chris@38 276 void setNormalizationFactor(size_t x, float factor) {
Chris@41 277 if (x < m_width) m_factor[x] = factor;
Chris@38 278 }
Chris@38 279
Chris@38 280 void setMagnitudeAt(size_t x, size_t y, float mag) {
Chris@38 281 // norm factor must already be set
Chris@38 282 setNormalizedMagnitudeAt(x, y, mag / m_factor[x]);
Chris@38 283 }
Chris@38 284
Chris@38 285 void setNormalizedMagnitudeAt(size_t x, size_t y, float norm) {
Chris@41 286 if (x < m_width && y < m_height) {
Chris@44 287 m_magnitude[x][y] = uint16_t(norm * 65535.0);
Chris@41 288 }
Chris@38 289 }
Chris@38 290
Chris@38 291 void setPhaseAt(size_t x, size_t y, float phase) {
Chris@38 292 // phase in range -pi -> pi
Chris@41 293 if (x < m_width && y < m_height) {
Chris@44 294 m_phase[x][y] = uint16_t(int16_t((phase * 32767) / M_PI));
Chris@41 295 }
Chris@38 296 }
Chris@38 297
Chris@38 298 QColor getColour(unsigned char index) const {
Chris@38 299 return m_colours[index];
Chris@38 300 }
Chris@38 301
Chris@38 302 void setColour(unsigned char index, QColor colour) {
Chris@38 303 m_colours[index] = colour;
Chris@38 304 }
Chris@38 305
Chris@38 306 private:
Chris@31 307 size_t m_width;
Chris@31 308 size_t m_height;
Chris@38 309 uint16_t **m_magnitude;
Chris@38 310 uint16_t **m_phase;
Chris@38 311 float *m_factor;
Chris@31 312 QColor m_colours[256];
Chris@38 313
Chris@38 314 void resize(uint16_t **&, size_t, size_t);
Chris@31 315 };
Chris@38 316
Chris@38 317 enum { NO_VALUE = 0 }; // colour index for unused pixels
Chris@38 318
Chris@31 319 Cache *m_cache;
Chris@31 320 bool m_cacheInvalid;
Chris@31 321
Chris@0 322 class CacheFillThread : public QThread
Chris@0 323 {
Chris@0 324 public:
Chris@0 325 CacheFillThread(SpectrogramLayer &layer) :
Chris@0 326 m_layer(layer), m_fillExtent(0) { }
Chris@0 327
Chris@0 328 size_t getFillExtent() const { return m_fillExtent; }
Chris@0 329 size_t getFillCompletion() const { return m_fillCompletion; }
Chris@0 330 virtual void run();
Chris@0 331
Chris@0 332 protected:
Chris@0 333 SpectrogramLayer &m_layer;
Chris@0 334 size_t m_fillExtent;
Chris@0 335 size_t m_fillCompletion;
Chris@0 336 };
Chris@0 337
Chris@0 338 void fillCache();
Chris@0 339
Chris@0 340 mutable QPixmap *m_pixmapCache;
Chris@0 341 mutable bool m_pixmapCacheInvalid;
Chris@0 342 mutable long m_pixmapCacheStartFrame;
Chris@0 343 mutable size_t m_pixmapCacheZoomLevel;
Chris@0 344
Chris@0 345 QWaitCondition m_condition;
Chris@0 346 mutable QMutex m_mutex;
Chris@0 347
Chris@0 348 CacheFillThread *m_fillThread;
Chris@0 349 QTimer *m_updateTimer;
Chris@44 350 mutable size_t m_candidateFillStartFrame;
Chris@0 351 size_t m_lastFillExtent;
Chris@0 352 bool m_exiting;
Chris@0 353
Chris@0 354 void setCacheColourmap();
Chris@9 355 void rotateCacheColourmap(int distance);
Chris@0 356
Chris@38 357 void fillCacheColumn(int column,
Chris@0 358 double *inputBuffer,
Chris@0 359 fftw_complex *outputBuffer,
Chris@0 360 fftw_plan plan,
Chris@9 361 size_t windowSize,
Chris@9 362 size_t windowIncrement,
Chris@38 363 const Window<double> &windower)
Chris@0 364 const;
Chris@0 365
Chris@38 366 static float calculateFrequency(size_t bin,
Chris@38 367 size_t windowSize,
Chris@38 368 size_t windowIncrement,
Chris@38 369 size_t sampleRate,
Chris@38 370 float previousPhase,
Chris@38 371 float currentPhase,
Chris@38 372 bool &steadyState);
Chris@38 373
Chris@38 374 unsigned char getDisplayValue(float input) const;
Chris@40 375 float getInputForDisplayValue(unsigned char uc) const;
Chris@40 376
Chris@40 377 int getColourScaleWidth(QPainter &) const;
Chris@40 378
Chris@40 379 float getEffectiveMinFrequency() const;
Chris@40 380 float getEffectiveMaxFrequency() const;
Chris@38 381
Chris@0 382 struct LayerRange {
Chris@0 383 long startFrame;
Chris@0 384 int zoomLevel;
Chris@0 385 size_t modelStart;
Chris@0 386 size_t modelEnd;
Chris@0 387 };
Chris@44 388 bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const;
Chris@44 389 bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const;
Chris@0 390
Chris@44 391 bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const;
Chris@44 392 bool getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 393 float &freqMin, float &freqMax,
Chris@35 394 float &adjFreqMin, float &adjFreqMax) const;
Chris@44 395 bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const;
Chris@44 396 bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max,
Chris@38 397 float &phaseMin, float &phaseMax) const;
Chris@0 398
Chris@0 399 size_t getWindowIncrement() const {
Chris@0 400 return m_windowSize - m_windowSize * m_windowOverlap / 100;
Chris@0 401 }
Chris@0 402 };
Chris@0 403
Chris@0 404 #endif