lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: This file copyright 2006 Chris Cannam and QMUL. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: #ifndef _SPECTROGRAM_LAYER_H_ lbajardsilogic@0: #define _SPECTROGRAM_LAYER_H_ lbajardsilogic@0: lbajardsilogic@0: #include "SliceableLayer.h" lbajardsilogic@0: #include "base/Window.h" lbajardsilogic@0: #include "base/RealTime.h" lbajardsilogic@0: #include "base/Thread.h" lbajardsilogic@0: #include "base/PropertyContainer.h" lbajardsilogic@0: #include "data/model/PowerOfSqrtTwoZoomConstraint.h" lbajardsilogic@0: #include "data/model/DenseTimeValueModel.h" lbajardsilogic@0: #include "data/model/FFTModel.h" lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: class View; lbajardsilogic@0: class QPainter; lbajardsilogic@0: class QImage; lbajardsilogic@0: class QPixmap; lbajardsilogic@0: class QTimer; lbajardsilogic@0: class FFTModel; lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * SpectrogramLayer represents waveform data (obtained from a lbajardsilogic@0: * DenseTimeValueModel) in spectrogram form. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: class SpectrogramLayer : public SliceableLayer, lbajardsilogic@0: public PowerOfSqrtTwoZoomConstraint lbajardsilogic@0: { lbajardsilogic@0: Q_OBJECT lbajardsilogic@0: lbajardsilogic@0: public: lbajardsilogic@0: enum Configuration { FullRangeDb, MelodicRange, MelodicPeaks }; lbajardsilogic@0: lbajardsilogic@0: SpectrogramLayer(Configuration = FullRangeDb); lbajardsilogic@0: ~SpectrogramLayer(); lbajardsilogic@0: lbajardsilogic@0: virtual const ZoomConstraint *getZoomConstraint() const { return this; } lbajardsilogic@0: virtual const Model *getModel() const { return m_model; } lbajardsilogic@0: virtual void paint(View *v, QPainter &paint, QRect rect) const; lbajardsilogic@0: lbajardsilogic@0: virtual int getVerticalScaleWidth(View *v, QPainter &) const; lbajardsilogic@0: virtual void paintVerticalScale(View *v, QPainter &paint, QRect rect) const; lbajardsilogic@0: lbajardsilogic@0: virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos, lbajardsilogic@0: std::vector &extents) const; lbajardsilogic@0: virtual void paintCrosshairs(View *, QPainter &, QPoint) const; lbajardsilogic@0: lbajardsilogic@0: virtual QString getFeatureDescription(View *v, QPoint &) const; lbajardsilogic@0: lbajardsilogic@0: virtual bool snapToFeatureFrame(View *v, int &frame, lbajardsilogic@0: size_t &resolution, lbajardsilogic@0: SnapType snap) const; lbajardsilogic@0: lbajardsilogic@0: virtual bool hasLightBackground() const; lbajardsilogic@0: lbajardsilogic@0: void setModel(const DenseTimeValueModel *model); lbajardsilogic@0: lbajardsilogic@0: virtual PropertyList getProperties() const; lbajardsilogic@0: virtual QString getPropertyLabel(const PropertyName &) const; lbajardsilogic@0: virtual PropertyType getPropertyType(const PropertyName &) const; lbajardsilogic@0: virtual QString getPropertyGroupName(const PropertyName &) const; lbajardsilogic@0: virtual int getPropertyRangeAndValue(const PropertyName &, lbajardsilogic@0: int *min, int *max, int *deflt) const; lbajardsilogic@0: virtual QString getPropertyValueLabel(const PropertyName &, lbajardsilogic@0: int value) const; lbajardsilogic@0: virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; lbajardsilogic@0: virtual void setProperty(const PropertyName &, int value); lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Specify the channel to use from the source model. lbajardsilogic@0: * A value of -1 means to mix all available channels. lbajardsilogic@0: * The default is channel 0. lbajardsilogic@0: */ lbajardsilogic@0: void setChannel(int); lbajardsilogic@0: int getChannel() const; lbajardsilogic@0: lbajardsilogic@0: void setWindowSize(size_t); lbajardsilogic@0: size_t getWindowSize() const; lbajardsilogic@0: lbajardsilogic@0: void setWindowHopLevel(size_t level); lbajardsilogic@0: size_t getWindowHopLevel() const; lbajardsilogic@0: lbajardsilogic@0: void setWindowType(WindowType type); lbajardsilogic@0: WindowType getWindowType() const; lbajardsilogic@0: lbajardsilogic@0: void setZeroPadLevel(size_t level); lbajardsilogic@0: size_t getZeroPadLevel() const; lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Set the gain multiplier for sample values in this view. lbajardsilogic@0: * The default is 1.0. lbajardsilogic@0: */ lbajardsilogic@0: void setGain(float gain); lbajardsilogic@0: float getGain() const; lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Set the threshold for sample values to qualify for being shown lbajardsilogic@0: * in the FFT, in voltage units. lbajardsilogic@0: * lbajardsilogic@0: * The default is 0.0. lbajardsilogic@0: */ lbajardsilogic@0: void setThreshold(float threshold); lbajardsilogic@0: float getThreshold() const; lbajardsilogic@0: lbajardsilogic@0: void setMinFrequency(size_t); lbajardsilogic@0: size_t getMinFrequency() const; lbajardsilogic@0: lbajardsilogic@0: void setMaxFrequency(size_t); // 0 -> no maximum lbajardsilogic@0: size_t getMaxFrequency() const; lbajardsilogic@0: lbajardsilogic@0: enum ColourScale { lbajardsilogic@0: LinearColourScale, lbajardsilogic@0: MeterColourScale, lbajardsilogic@0: dBSquaredColourScale, lbajardsilogic@0: dBColourScale, lbajardsilogic@0: PhaseColourScale lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Specify the scale for sample levels. See WaveformLayer for lbajardsilogic@0: * details of meter and dB scaling. The default is dBColourScale. lbajardsilogic@0: */ lbajardsilogic@0: void setColourScale(ColourScale); lbajardsilogic@0: ColourScale getColourScale() const; lbajardsilogic@0: lbajardsilogic@0: enum FrequencyScale { lbajardsilogic@0: LinearFrequencyScale, lbajardsilogic@0: LogFrequencyScale lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Specify the scale for the y axis. lbajardsilogic@0: */ lbajardsilogic@0: void setFrequencyScale(FrequencyScale); lbajardsilogic@0: FrequencyScale getFrequencyScale() const; lbajardsilogic@0: lbajardsilogic@0: enum BinDisplay { lbajardsilogic@0: AllBins, lbajardsilogic@0: PeakBins, lbajardsilogic@0: PeakFrequencies lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Specify the processing of frequency bins for the y axis. lbajardsilogic@0: */ lbajardsilogic@0: void setBinDisplay(BinDisplay); lbajardsilogic@0: BinDisplay getBinDisplay() const; lbajardsilogic@0: lbajardsilogic@0: void setNormalizeColumns(bool n); lbajardsilogic@0: bool getNormalizeColumns() const; lbajardsilogic@0: lbajardsilogic@0: void setNormalizeVisibleArea(bool n); lbajardsilogic@0: bool getNormalizeVisibleArea() const; lbajardsilogic@0: lbajardsilogic@0: void setColourMap(int map); lbajardsilogic@0: int getColourMap() const; lbajardsilogic@0: lbajardsilogic@0: /** lbajardsilogic@0: * Specify the colourmap rotation for the colour scale. lbajardsilogic@0: */ lbajardsilogic@0: void setColourRotation(int); lbajardsilogic@0: int getColourRotation() const; lbajardsilogic@0: lbajardsilogic@0: virtual VerticalPosition getPreferredFrameCountPosition() const { lbajardsilogic@0: return PositionTop; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: virtual bool isLayerOpaque() const { return true; } lbajardsilogic@0: virtual bool isLayerColourSignificant() const { return true; } lbajardsilogic@0: lbajardsilogic@0: float getYForFrequency(View *v, float frequency) const; lbajardsilogic@0: float getFrequencyForY(View *v, int y) const; lbajardsilogic@0: lbajardsilogic@0: virtual int getCompletion(View *v) const; lbajardsilogic@0: lbajardsilogic@0: virtual bool getValueExtents(float &min, float &max, lbajardsilogic@0: bool &logarithmic, QString &unit) const; lbajardsilogic@0: lbajardsilogic@0: virtual bool getDisplayExtents(float &min, float &max) const; lbajardsilogic@0: lbajardsilogic@0: virtual bool setDisplayExtents(float min, float max); lbajardsilogic@0: lbajardsilogic@0: virtual QString toXmlString(QString indent = "", lbajardsilogic@0: QString extraAttributes = "") const; lbajardsilogic@0: lbajardsilogic@18: virtual QString toEasaierXmlString(QString indent = "", lbajardsilogic@18: QString extraAttributes = "") const; lbajardsilogic@18: lbajardsilogic@0: void setProperties(const QXmlAttributes &attributes); lbajardsilogic@0: lbajardsilogic@0: virtual void setLayerDormant(const View *v, bool dormant); lbajardsilogic@0: lbajardsilogic@0: virtual bool isLayerScrollable(const View *) const { return false; } lbajardsilogic@0: lbajardsilogic@0: virtual int getVerticalZoomSteps(int &defaultStep) const; lbajardsilogic@0: virtual int getCurrentVerticalZoomStep() const; lbajardsilogic@0: virtual void setVerticalZoomStep(int); lbajardsilogic@0: virtual RangeMapper *getNewVerticalZoomRangeMapper() const; lbajardsilogic@0: lbajardsilogic@0: virtual const Model *getSliceableModel() const; lbajardsilogic@0: lbajardsilogic@0: protected slots: lbajardsilogic@0: void cacheInvalid(); lbajardsilogic@0: void cacheInvalid(size_t startFrame, size_t endFrame); lbajardsilogic@0: lbajardsilogic@0: void preferenceChanged(PropertyContainer::PropertyName name); lbajardsilogic@0: lbajardsilogic@0: void fillTimerTimedOut(); lbajardsilogic@0: lbajardsilogic@0: protected: lbajardsilogic@0: const DenseTimeValueModel *m_model; // I do not own this lbajardsilogic@0: lbajardsilogic@0: int m_channel; lbajardsilogic@0: size_t m_windowSize; lbajardsilogic@0: WindowType m_windowType; lbajardsilogic@0: size_t m_windowHopLevel; lbajardsilogic@0: size_t m_zeroPadLevel; lbajardsilogic@0: size_t m_fftSize; lbajardsilogic@0: float m_gain; lbajardsilogic@0: float m_initialGain; lbajardsilogic@0: float m_threshold; lbajardsilogic@0: float m_initialThreshold; lbajardsilogic@0: int m_colourRotation; lbajardsilogic@0: int m_initialRotation; lbajardsilogic@0: size_t m_minFrequency; lbajardsilogic@0: size_t m_maxFrequency; lbajardsilogic@0: size_t m_initialMaxFrequency; lbajardsilogic@0: ColourScale m_colourScale; lbajardsilogic@0: int m_colourMap; lbajardsilogic@0: QColor m_crosshairColour; lbajardsilogic@0: FrequencyScale m_frequencyScale; lbajardsilogic@0: BinDisplay m_binDisplay; lbajardsilogic@0: bool m_normalizeColumns; lbajardsilogic@0: bool m_normalizeVisibleArea; lbajardsilogic@0: int m_lastEmittedZoomStep; lbajardsilogic@0: lbajardsilogic@0: mutable int m_lastPaintBlockWidth; lbajardsilogic@0: mutable RealTime m_lastPaintTime; lbajardsilogic@0: lbajardsilogic@0: enum { NO_VALUE = 0 }; // colour index for unused pixels lbajardsilogic@0: lbajardsilogic@0: class Palette lbajardsilogic@0: { lbajardsilogic@0: public: lbajardsilogic@0: QColor getColour(unsigned char index) const { lbajardsilogic@0: return m_colours[index]; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void setColour(unsigned char index, QColor colour) { lbajardsilogic@0: m_colours[index] = colour; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: private: lbajardsilogic@0: QColor m_colours[256]; lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: Palette m_palette; lbajardsilogic@0: lbajardsilogic@0: struct PixmapCache lbajardsilogic@0: { lbajardsilogic@0: QPixmap pixmap; lbajardsilogic@0: QRect validArea; lbajardsilogic@0: long startFrame; lbajardsilogic@0: size_t zoomLevel; lbajardsilogic@0: }; lbajardsilogic@0: typedef std::map ViewPixmapCache; lbajardsilogic@0: void invalidatePixmapCaches(); lbajardsilogic@0: void invalidatePixmapCaches(size_t startFrame, size_t endFrame); lbajardsilogic@0: mutable ViewPixmapCache m_pixmapCaches; lbajardsilogic@0: mutable QImage m_drawBuffer; lbajardsilogic@0: lbajardsilogic@0: mutable QTimer *m_updateTimer; lbajardsilogic@0: lbajardsilogic@0: mutable size_t m_candidateFillStartFrame; lbajardsilogic@0: bool m_exiting; lbajardsilogic@0: lbajardsilogic@0: void initialisePalette(); lbajardsilogic@0: void rotatePalette(int distance); lbajardsilogic@0: lbajardsilogic@0: static float calculateFrequency(size_t bin, lbajardsilogic@0: size_t windowSize, lbajardsilogic@0: size_t windowIncrement, lbajardsilogic@0: size_t sampleRate, lbajardsilogic@0: float previousPhase, lbajardsilogic@0: float currentPhase, lbajardsilogic@0: bool &steadyState); lbajardsilogic@0: lbajardsilogic@0: unsigned char getDisplayValue(View *v, float input) const; lbajardsilogic@0: float getInputForDisplayValue(unsigned char uc) const; lbajardsilogic@0: lbajardsilogic@0: int getColourScaleWidth(QPainter &) const; lbajardsilogic@0: lbajardsilogic@0: void illuminateLocalFeatures(View *v, QPainter &painter) const; lbajardsilogic@0: lbajardsilogic@0: float getEffectiveMinFrequency() const; lbajardsilogic@0: float getEffectiveMaxFrequency() const; lbajardsilogic@0: lbajardsilogic@0: struct LayerRange { lbajardsilogic@0: long startFrame; lbajardsilogic@0: int zoomLevel; lbajardsilogic@0: size_t modelStart; lbajardsilogic@0: size_t modelEnd; lbajardsilogic@0: }; lbajardsilogic@0: bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const; lbajardsilogic@0: bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const; lbajardsilogic@0: lbajardsilogic@0: bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const; lbajardsilogic@0: bool getAdjustedYBinSourceRange(View *v, int x, int y, lbajardsilogic@0: float &freqMin, float &freqMax, lbajardsilogic@0: float &adjFreqMin, float &adjFreqMax) const; lbajardsilogic@0: bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const; lbajardsilogic@0: bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max, lbajardsilogic@0: float &phaseMin, float &phaseMax) const; lbajardsilogic@0: lbajardsilogic@0: size_t getWindowIncrement() const { lbajardsilogic@0: if (m_windowHopLevel == 0) return m_windowSize; lbajardsilogic@0: else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4; lbajardsilogic@0: else return m_windowSize / (1 << (m_windowHopLevel - 1)); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t getZeroPadLevel(const View *v) const; lbajardsilogic@0: size_t getFFTSize(const View *v) const; lbajardsilogic@0: FFTModel *getFFTModel(const View *v) const; lbajardsilogic@0: void invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: typedef std::pair FFTFillPair; // model, last fill lbajardsilogic@0: typedef std::map ViewFFTMap; lbajardsilogic@0: typedef std::vector FloatVector; lbajardsilogic@0: mutable ViewFFTMap m_fftModels; lbajardsilogic@0: mutable Model *m_sliceableModel; lbajardsilogic@0: lbajardsilogic@0: class MagnitudeRange { lbajardsilogic@0: public: lbajardsilogic@0: MagnitudeRange() : m_min(0), m_max(0) { } lbajardsilogic@0: bool operator==(const MagnitudeRange &r) { lbajardsilogic@0: return r.m_min == m_min && r.m_max == m_max; lbajardsilogic@0: } lbajardsilogic@0: bool isSet() const { return (m_min != 0 || m_max != 0); } lbajardsilogic@0: void set(float min, float max) { lbajardsilogic@0: m_min = convert(min); lbajardsilogic@0: m_max = convert(max); lbajardsilogic@0: if (m_max < m_min) m_max = m_min; lbajardsilogic@0: } lbajardsilogic@0: bool sample(float f) { lbajardsilogic@0: unsigned int ui = convert(f); lbajardsilogic@0: bool changed = false; lbajardsilogic@0: if (isSet()) { lbajardsilogic@0: if (ui < m_min) { m_min = ui; changed = true; } lbajardsilogic@0: if (ui > m_max) { m_max = ui; changed = true; } lbajardsilogic@0: } else { lbajardsilogic@0: m_max = m_min = ui; lbajardsilogic@0: changed = true; lbajardsilogic@0: } lbajardsilogic@0: return changed; lbajardsilogic@0: } lbajardsilogic@0: bool sample(const MagnitudeRange &r) { lbajardsilogic@0: bool changed = false; lbajardsilogic@0: if (isSet()) { lbajardsilogic@0: if (r.m_min < m_min) { m_min = r.m_min; changed = true; } lbajardsilogic@0: if (r.m_max > m_max) { m_max = r.m_max; changed = true; } lbajardsilogic@0: } else { lbajardsilogic@0: m_min = r.m_min; lbajardsilogic@0: m_max = r.m_max; lbajardsilogic@0: changed = true; lbajardsilogic@0: } lbajardsilogic@0: return changed; lbajardsilogic@0: } lbajardsilogic@0: float getMin() const { return float(m_min) / UINT_MAX; } lbajardsilogic@0: float getMax() const { return float(m_max) / UINT_MAX; } lbajardsilogic@0: private: lbajardsilogic@0: unsigned int m_min; lbajardsilogic@0: unsigned int m_max; lbajardsilogic@0: unsigned int convert(float f) { lbajardsilogic@0: if (f < 0.f) f = 0.f; lbajardsilogic@0: if (f > 1.f) f = 1.f; lbajardsilogic@0: return (unsigned int)(f * UINT_MAX); lbajardsilogic@0: } lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: typedef std::map ViewMagMap; lbajardsilogic@0: mutable ViewMagMap m_viewMags; lbajardsilogic@0: mutable std::vector m_columnMags; lbajardsilogic@0: void invalidateMagnitudes(); lbajardsilogic@0: bool updateViewMagnitudes(View *v) const; lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: #endif