Chris@58: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@59: Sonic Visualiser Chris@59: An audio file viewer and annotation editor. Chris@59: Centre for Digital Music, Queen Mary, University of London. Chris@182: This file copyright 2006 Chris Cannam and QMUL. Chris@0: Chris@59: This program is free software; you can redistribute it and/or Chris@59: modify it under the terms of the GNU General Public License as Chris@59: published by the Free Software Foundation; either version 2 of the Chris@59: License, or (at your option) any later version. See the file Chris@59: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@30: #ifndef _SPECTROGRAM_LAYER_H_ Chris@30: #define _SPECTROGRAM_LAYER_H_ Chris@0: Chris@193: #include "SliceableLayer.h" Chris@0: #include "base/Window.h" Chris@71: #include "base/RealTime.h" Chris@92: #include "base/Thread.h" Chris@122: #include "base/PropertyContainer.h" Chris@128: #include "data/model/PowerOfSqrtTwoZoomConstraint.h" Chris@128: #include "data/model/DenseTimeValueModel.h" Chris@130: #include "data/model/FFTModel.h" Chris@0: Chris@0: #include Chris@0: #include Chris@95: #include Chris@95: #include Chris@0: Chris@0: class View; Chris@0: class QPainter; Chris@0: class QImage; Chris@0: class QPixmap; Chris@0: class QTimer; Chris@130: class FFTModel; Chris@484: class Dense3DModelPeakCache; Chris@114: Chris@0: Chris@0: /** Chris@0: * SpectrogramLayer represents waveform data (obtained from a Chris@0: * DenseTimeValueModel) in spectrogram form. Chris@0: */ Chris@0: Chris@193: class SpectrogramLayer : public SliceableLayer, Chris@31: public PowerOfSqrtTwoZoomConstraint Chris@0: { Chris@0: Q_OBJECT Chris@0: Chris@0: public: Chris@37: enum Configuration { FullRangeDb, MelodicRange, MelodicPeaks }; Chris@0: Chris@44: SpectrogramLayer(Configuration = FullRangeDb); Chris@0: ~SpectrogramLayer(); Chris@0: Chris@0: virtual const ZoomConstraint *getZoomConstraint() const { return this; } Chris@0: virtual const Model *getModel() const { return m_model; } Chris@916: virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; Chris@389: virtual void setSynchronousPainting(bool synchronous); Chris@0: Chris@918: virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const; Chris@918: virtual void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const; Chris@0: Chris@918: virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos, Chris@77: std::vector &extents) const; Chris@918: virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const; Chris@77: Chris@918: virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; Chris@0: Chris@918: virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, Chris@805: int &resolution, Chris@28: SnapType snap) const; Chris@13: Chris@918: virtual void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *); Chris@283: Chris@224: virtual bool hasLightBackground() const; Chris@224: Chris@0: void setModel(const DenseTimeValueModel *model); Chris@0: Chris@0: virtual PropertyList getProperties() const; Chris@87: virtual QString getPropertyLabel(const PropertyName &) const; Chris@335: virtual QString getPropertyIconName(const PropertyName &) const; Chris@0: virtual PropertyType getPropertyType(const PropertyName &) const; Chris@0: virtual QString getPropertyGroupName(const PropertyName &) const; Chris@0: virtual int getPropertyRangeAndValue(const PropertyName &, Chris@216: int *min, int *max, int *deflt) const; Chris@0: virtual QString getPropertyValueLabel(const PropertyName &, Chris@0: int value) const; Chris@862: virtual QString getPropertyValueIconName(const PropertyName &, Chris@862: int value) const; Chris@167: virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; Chris@0: virtual void setProperty(const PropertyName &, int value); Chris@0: Chris@0: /** Chris@0: * Specify the channel to use from the source model. Chris@0: * A value of -1 means to mix all available channels. Chris@0: * The default is channel 0. Chris@0: */ Chris@0: void setChannel(int); Chris@0: int getChannel() const; Chris@0: Chris@805: void setWindowSize(int); Chris@805: int getWindowSize() const; Chris@0: Chris@805: void setWindowHopLevel(int level); Chris@805: int getWindowHopLevel() const; Chris@0: Chris@0: void setWindowType(WindowType type); Chris@0: WindowType getWindowType() const; Chris@0: Chris@805: void setZeroPadLevel(int level); Chris@805: int getZeroPadLevel() const; Chris@109: Chris@0: /** Chris@110: * Set the gain multiplier for sample values in this view. Chris@0: * The default is 1.0. Chris@0: */ Chris@0: void setGain(float gain); Chris@0: float getGain() const; Chris@0: Chris@37: /** Chris@110: * Set the threshold for sample values to qualify for being shown Chris@110: * in the FFT, in voltage units. Chris@37: * Chris@37: * The default is 0.0. Chris@37: */ Chris@37: void setThreshold(float threshold); Chris@37: float getThreshold() const; Chris@37: Chris@805: void setMinFrequency(int); Chris@805: int getMinFrequency() const; Chris@37: Chris@805: void setMaxFrequency(int); // 0 -> no maximum Chris@805: int getMaxFrequency() const; Chris@0: Chris@37: enum ColourScale { Chris@37: LinearColourScale, Chris@37: MeterColourScale, Chris@215: dBSquaredColourScale, Chris@37: dBColourScale, Chris@37: PhaseColourScale Chris@37: }; Chris@0: Chris@0: /** Chris@0: * Specify the scale for sample levels. See WaveformLayer for Chris@0: * details of meter and dB scaling. The default is dBColourScale. Chris@0: */ Chris@0: void setColourScale(ColourScale); Chris@0: ColourScale getColourScale() const; Chris@0: Chris@35: enum FrequencyScale { Chris@35: LinearFrequencyScale, Chris@35: LogFrequencyScale Chris@35: }; Chris@0: Chris@0: /** Chris@0: * Specify the scale for the y axis. Chris@0: */ Chris@0: void setFrequencyScale(FrequencyScale); Chris@0: FrequencyScale getFrequencyScale() const; Chris@0: Chris@37: enum BinDisplay { Chris@37: AllBins, Chris@37: PeakBins, Chris@37: PeakFrequencies Chris@35: }; Chris@35: Chris@35: /** Chris@35: * Specify the processing of frequency bins for the y axis. Chris@35: */ Chris@37: void setBinDisplay(BinDisplay); Chris@37: BinDisplay getBinDisplay() const; Chris@862: Chris@862: enum Normalization { Chris@862: NoNormalization, Chris@862: NormalizeColumns, Chris@862: NormalizeVisibleArea, Chris@862: NormalizeHybrid Chris@862: }; Chris@36: Chris@719: /** Chris@862: * Specify the normalization mode for bin values. Chris@719: */ Chris@862: void setNormalization(Normalization); Chris@862: Normalization getNormalization() const; Chris@120: Chris@719: /** Chris@862: * Specify the colour map. See ColourMapper for the colour map Chris@862: * values. Chris@719: */ Chris@197: void setColourMap(int map); Chris@197: int getColourMap() const; Chris@0: Chris@9: /** Chris@9: * Specify the colourmap rotation for the colour scale. Chris@9: */ Chris@9: void setColourRotation(int); Chris@9: int getColourRotation() const; Chris@9: Chris@0: virtual VerticalPosition getPreferredFrameCountPosition() const { Chris@0: return PositionTop; Chris@0: } Chris@0: Chris@15: virtual bool isLayerOpaque() const { return true; } Chris@287: Chris@287: virtual ColourSignificance getLayerColourSignificance() const { Chris@287: return ColourHasMeaningfulValue; Chris@287: } Chris@15: Chris@918: double getYForFrequency(const LayerGeometryProvider *v, double frequency) const; Chris@918: double getFrequencyForY(const LayerGeometryProvider *v, int y) const; Chris@42: Chris@918: virtual int getCompletion(LayerGeometryProvider *v) const; Chris@918: virtual QString getError(LayerGeometryProvider *v) const; Chris@0: Chris@905: virtual bool getValueExtents(double &min, double &max, Chris@101: bool &logarithmic, QString &unit) const; Chris@101: Chris@905: virtual bool getDisplayExtents(double &min, double &max) const; Chris@79: Chris@905: virtual bool setDisplayExtents(double min, double max); Chris@120: Chris@918: virtual bool getYScaleValue(const LayerGeometryProvider *, int, double &, QString &) const; Chris@261: Chris@316: virtual void toXml(QTextStream &stream, QString indent = "", Chris@316: QString extraAttributes = "") const; Chris@6: Chris@11: void setProperties(const QXmlAttributes &attributes); Chris@11: Chris@918: virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); Chris@29: Chris@918: virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; } Chris@94: Chris@133: virtual int getVerticalZoomSteps(int &defaultStep) const; Chris@133: virtual int getCurrentVerticalZoomStep() const; Chris@133: virtual void setVerticalZoomStep(int); Chris@187: virtual RangeMapper *getNewVerticalZoomRangeMapper() const; Chris@133: Chris@193: virtual const Model *getSliceableModel() const; Chris@193: Chris@0: protected slots: Chris@0: void cacheInvalid(); Chris@906: void cacheInvalid(sv_frame_t startFrame, sv_frame_t endFrame); Chris@122: Chris@122: void preferenceChanged(PropertyContainer::PropertyName name); Chris@0: Chris@0: protected: Chris@0: const DenseTimeValueModel *m_model; // I do not own this Chris@484: Chris@35: int m_channel; Chris@805: int m_windowSize; Chris@35: WindowType m_windowType; Chris@805: int m_windowHopLevel; Chris@805: int m_zeroPadLevel; Chris@805: int m_fftSize; Chris@35: float m_gain; Chris@215: float m_initialGain; Chris@37: float m_threshold; Chris@215: float m_initialThreshold; Chris@35: int m_colourRotation; Chris@215: int m_initialRotation; Chris@805: int m_minFrequency; Chris@805: int m_maxFrequency; Chris@805: int m_initialMaxFrequency; Chris@35: ColourScale m_colourScale; Chris@197: int m_colourMap; Chris@77: QColor m_crosshairColour; Chris@35: FrequencyScale m_frequencyScale; Chris@37: BinDisplay m_binDisplay; Chris@862: Normalization m_normalization; Chris@133: int m_lastEmittedZoomStep; Chris@389: bool m_synchronous; Chris@0: Chris@608: mutable bool m_haveDetailedScale; Chris@215: mutable int m_lastPaintBlockWidth; Chris@215: mutable RealTime m_lastPaintTime; Chris@215: Chris@38: enum { NO_VALUE = 0 }; // colour index for unused pixels Chris@38: Chris@197: class Palette Chris@86: { Chris@86: public: Chris@86: QColor getColour(unsigned char index) const { Chris@86: return m_colours[index]; Chris@86: } Chris@86: Chris@86: void setColour(unsigned char index, QColor colour) { Chris@86: m_colours[index] = colour; Chris@86: } Chris@86: Chris@86: private: Chris@86: QColor m_colours[256]; Chris@86: }; Chris@86: Chris@197: Palette m_palette; Chris@31: Chris@477: /** Chris@478: * ImageCache covers the area of the view, at view resolution. Chris@477: * Not all of it is necessarily valid at once (it is refreshed Chris@477: * in parts when scrolling, for example). Chris@477: */ Chris@478: struct ImageCache Chris@95: { Chris@478: QImage image; Chris@95: QRect validArea; Chris@907: sv_frame_t startFrame; Chris@805: int zoomLevel; Chris@95: }; Chris@478: typedef std::map ViewImageCache; Chris@478: void invalidateImageCaches(); Chris@906: void invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame); Chris@478: mutable ViewImageCache m_imageCaches; Chris@477: Chris@477: /** Chris@477: * When painting, we draw directly onto the draw buffer and then Chris@478: * copy this to the part of the image cache that needed refreshing Chris@478: * before copying the image cache onto the window. (Remind me why Chris@477: * we don't draw directly onto the cache?) Chris@477: */ Chris@95: mutable QImage m_drawBuffer; Chris@0: Chris@0: bool m_exiting; Chris@0: Chris@197: void initialisePalette(); Chris@197: void rotatePalette(int distance); Chris@0: Chris@918: unsigned char getDisplayValue(LayerGeometryProvider *v, double input) const; Chris@40: Chris@40: int getColourScaleWidth(QPainter &) const; Chris@40: Chris@918: void illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &painter) const; Chris@121: Chris@905: double getEffectiveMinFrequency() const; Chris@905: double getEffectiveMaxFrequency() const; Chris@38: Chris@486: // Note that the getYBin... methods return the nominal bin in the Chris@486: // un-smoothed spectrogram. This is not necessarily the same bin Chris@486: // as is pulled from the spectrogram and drawn at the given Chris@486: // position, if the spectrogram has oversampling smoothing. Use Chris@486: // getSmoothedYBinRange to obtain that. Chris@486: Chris@918: bool getXBinRange(LayerGeometryProvider *v, int x, double &windowMin, double &windowMax) const; Chris@918: bool getYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const; Chris@918: bool getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const; Chris@0: Chris@918: bool getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const; Chris@918: bool getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, Chris@905: double &freqMin, double &freqMax, Chris@905: double &adjFreqMin, double &adjFreqMax) const; Chris@918: bool getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &timeMin, RealTime &timeMax) const; Chris@918: bool getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, double &min, double &max, Chris@905: double &phaseMin, double &phaseMax) const; Chris@0: Chris@805: int getWindowIncrement() const { Chris@97: if (m_windowHopLevel == 0) return m_windowSize; Chris@97: else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4; Chris@97: else return m_windowSize / (1 << (m_windowHopLevel - 1)); Chris@0: } Chris@113: Chris@918: int getZeroPadLevel(const LayerGeometryProvider *v) const; Chris@918: int getFFTSize(const LayerGeometryProvider *v) const; Chris@918: FFTModel *getFFTModel(const LayerGeometryProvider *v) const; Chris@918: Dense3DModelPeakCache *getPeakCache(const LayerGeometryProvider *v) const; Chris@130: void invalidateFFTModels(); Chris@115: Chris@985: typedef std::map ViewFFTMap; Chris@484: typedef std::map PeakCacheMap; Chris@130: mutable ViewFFTMap m_fftModels; Chris@484: mutable PeakCacheMap m_peakCaches; Chris@193: mutable Model *m_sliceableModel; Chris@119: Chris@119: class MagnitudeRange { Chris@119: public: Chris@119: MagnitudeRange() : m_min(0), m_max(0) { } Chris@119: bool operator==(const MagnitudeRange &r) { Chris@119: return r.m_min == m_min && r.m_max == m_max; Chris@119: } Chris@905: bool isSet() const { return (m_min != 0.f || m_max != 0.f); } Chris@119: void set(float min, float max) { Chris@905: m_min = min; Chris@905: m_max = max; Chris@119: if (m_max < m_min) m_max = m_min; Chris@119: } Chris@119: bool sample(float f) { Chris@119: bool changed = false; Chris@119: if (isSet()) { Chris@905: if (f < m_min) { m_min = f; changed = true; } Chris@905: if (f > m_max) { m_max = f; changed = true; } Chris@119: } else { Chris@905: m_max = m_min = f; Chris@119: changed = true; Chris@119: } Chris@119: return changed; Chris@119: } Chris@119: bool sample(const MagnitudeRange &r) { Chris@119: bool changed = false; Chris@119: if (isSet()) { Chris@119: if (r.m_min < m_min) { m_min = r.m_min; changed = true; } Chris@119: if (r.m_max > m_max) { m_max = r.m_max; changed = true; } Chris@119: } else { Chris@119: m_min = r.m_min; Chris@119: m_max = r.m_max; Chris@119: changed = true; Chris@119: } Chris@119: return changed; Chris@119: } Chris@905: float getMin() const { return m_min; } Chris@905: float getMax() const { return m_max; } Chris@119: private: Chris@905: float m_min; Chris@905: float m_max; Chris@119: }; Chris@119: Chris@918: typedef std::map ViewMagMap; Chris@119: mutable ViewMagMap m_viewMags; Chris@119: mutable std::vector m_columnMags; Chris@119: void invalidateMagnitudes(); Chris@918: bool updateViewMagnitudes(LayerGeometryProvider *v) const; Chris@918: bool paintDrawBuffer(LayerGeometryProvider *v, int w, int h, Chris@907: const std::vector &binforx, Chris@907: const std::vector &binfory, Chris@491: bool usePeaksCache, Chris@491: MagnitudeRange &overallMag, Chris@491: bool &overallMagChanged) const; Chris@918: bool paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, int w, int h, Chris@907: const std::vector &binforx, Chris@488: int minbin, Chris@488: int maxbin, Chris@905: double displayMinFreq, Chris@905: double displayMaxFreq, Chris@491: bool logarithmic, Chris@491: MagnitudeRange &overallMag, Chris@491: bool &overallMagChanged) const; Chris@273: Chris@918: virtual void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const; Chris@918: virtual void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const; Chris@0: }; Chris@0: Chris@0: #endif