Chris@0: /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: A waveform viewer and audio annotation editor. Chris@5: Chris Cannam, Queen Mary University of London, 2005-2006 Chris@0: Chris@0: This is experimental software. Not for distribution. Chris@0: */ Chris@0: Chris@30: #ifndef _SPECTROGRAM_LAYER_H_ Chris@30: #define _SPECTROGRAM_LAYER_H_ Chris@0: Chris@0: #include "base/Layer.h" Chris@0: #include "base/Window.h" Chris@0: #include "model/PowerOfSqrtTwoZoomConstraint.h" Chris@0: #include "model/DenseTimeValueModel.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include Chris@0: Chris@0: class View; Chris@0: class QPainter; Chris@0: class QImage; Chris@0: class QPixmap; Chris@0: class QTimer; Chris@0: class RealTime; Chris@0: Chris@0: /** Chris@0: * SpectrogramLayer represents waveform data (obtained from a Chris@0: * DenseTimeValueModel) in spectrogram form. Chris@0: */ Chris@0: Chris@0: class SpectrogramLayer : public Layer, Chris@31: public PowerOfSqrtTwoZoomConstraint Chris@0: { Chris@0: Q_OBJECT Chris@0: Chris@0: public: Chris@0: enum Configuration { FullRangeDb, MelodicRange }; Chris@0: Chris@0: SpectrogramLayer(View *w, 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@0: virtual void paint(QPainter &paint, QRect rect) const; Chris@0: Chris@0: virtual int getVerticalScaleWidth(QPainter &) const; Chris@0: virtual void paintVerticalScale(QPainter &paint, QRect rect) const; Chris@0: Chris@25: virtual QString getFeatureDescription(QPoint &) const; Chris@0: Chris@28: virtual bool snapToFeatureFrame(int &frame, Chris@28: size_t &resolution, Chris@28: SnapType snap) const; Chris@13: Chris@0: void setModel(const DenseTimeValueModel *model); Chris@0: Chris@0: virtual PropertyList getProperties() 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@0: int *min, int *max) const; Chris@0: virtual QString getPropertyValueLabel(const PropertyName &, Chris@0: int value) 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@0: void setWindowSize(size_t); Chris@0: size_t getWindowSize() const; Chris@0: Chris@0: void setWindowOverlap(size_t percent); Chris@0: size_t getWindowOverlap() const; Chris@0: Chris@0: void setWindowType(WindowType type); Chris@0: WindowType getWindowType() const; Chris@0: Chris@0: /** Chris@0: * Set the gain multiplier for sample values in this view prior to Chris@0: * FFT calculation. Chris@0: * Chris@0: * The default is 1.0. Chris@0: */ Chris@0: void setGain(float gain); Chris@0: float getGain() const; Chris@0: Chris@0: void setMaxFrequency(size_t); // 0 -> no maximum Chris@0: size_t getMaxFrequency() const; Chris@0: Chris@0: enum ColourScale { LinearColourScale, MeterColourScale, dBColourScale, Chris@0: PhaseColourScale }; 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@0: enum FrequencyScale { LinearFrequencyScale, LogFrequencyScale }; 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@0: enum ColourScheme { DefaultColours, WhiteOnBlack, BlackOnWhite, Chris@0: RedOnBlue, YellowOnBlack, RedOnBlack }; Chris@0: Chris@0: void setColourScheme(ColourScheme scheme); Chris@0: ColourScheme getColourScheme() 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@15: Chris@0: virtual int getCompletion() const; Chris@0: Chris@6: virtual QString toXmlString(QString indent = "", Chris@6: QString extraAttributes = "") const; Chris@6: Chris@11: void setProperties(const QXmlAttributes &attributes); Chris@11: Chris@29: void setLayerDormant(); Chris@29: Chris@0: protected slots: Chris@0: void cacheInvalid(); Chris@0: void cacheInvalid(size_t startFrame, size_t endFrame); Chris@0: Chris@0: void fillTimerTimedOut(); Chris@0: Chris@0: protected: Chris@0: const DenseTimeValueModel *m_model; // I do not own this Chris@0: Chris@6: int m_channel; Chris@6: size_t m_windowSize; Chris@6: WindowType m_windowType; Chris@6: size_t m_windowOverlap; Chris@6: float m_gain; Chris@9: int m_colourRotation; Chris@6: size_t m_maxFrequency; Chris@6: ColourScale m_colourScale; Chris@6: ColourScheme m_colourScheme; Chris@0: FrequencyScale m_frequencyScale; Chris@0: Chris@31: // A QImage would do just as well here, and we originally used Chris@31: // one: the problem is that we want to munlock() the memory it Chris@31: // uses, and it's much easier to do that if we control it. This Chris@31: // cache is hardwired to an effective 8-bit colour mapped layout. Chris@31: class Cache { Chris@31: public: Chris@31: Cache(size_t width, size_t height); Chris@31: ~Cache(); Chris@31: Chris@31: size_t getWidth() const; Chris@31: size_t getHeight() const; Chris@31: Chris@31: unsigned char getValueAt(size_t x, size_t y) const; Chris@31: void setValueAt(size_t x, size_t y, unsigned char value); Chris@31: Chris@31: QColor getColour(unsigned char index) const; Chris@31: void setColour(unsigned char index, QColor colour); Chris@31: Chris@31: void fill(unsigned char value); Chris@31: Chris@31: protected: Chris@31: size_t m_width; Chris@31: size_t m_height; Chris@31: unsigned char *m_values; Chris@31: QColor m_colours[256]; Chris@31: }; Chris@31: Chris@31: Cache *m_cache; Chris@31: bool m_cacheInvalid; Chris@31: Chris@0: class CacheFillThread : public QThread Chris@0: { Chris@0: public: Chris@0: CacheFillThread(SpectrogramLayer &layer) : Chris@0: m_layer(layer), m_fillExtent(0) { } Chris@0: Chris@0: size_t getFillExtent() const { return m_fillExtent; } Chris@0: size_t getFillCompletion() const { return m_fillCompletion; } Chris@0: virtual void run(); Chris@0: Chris@0: protected: Chris@0: SpectrogramLayer &m_layer; Chris@0: size_t m_fillExtent; Chris@0: size_t m_fillCompletion; Chris@0: }; Chris@0: Chris@0: void fillCache(); Chris@0: Chris@0: mutable QPixmap *m_pixmapCache; Chris@0: mutable bool m_pixmapCacheInvalid; Chris@0: mutable long m_pixmapCacheStartFrame; Chris@0: mutable size_t m_pixmapCacheZoomLevel; Chris@0: Chris@0: QWaitCondition m_condition; Chris@0: mutable QMutex m_mutex; Chris@0: Chris@0: CacheFillThread *m_fillThread; Chris@0: QTimer *m_updateTimer; Chris@0: size_t m_lastFillExtent; Chris@0: bool m_cachedInitialVisibleArea; Chris@29: mutable bool m_dormant; Chris@0: bool m_exiting; Chris@0: Chris@0: void setCacheColourmap(); Chris@9: void rotateCacheColourmap(int distance); Chris@0: Chris@0: bool fillCacheColumn(int column, Chris@0: double *inputBuffer, Chris@0: fftw_complex *outputBuffer, Chris@0: fftw_plan plan, Chris@9: size_t windowSize, Chris@9: size_t windowIncrement, Chris@0: const Window &windower, Chris@0: bool lock) Chris@0: const; Chris@0: Chris@0: bool getYBinRange(int y, float &freqBinMin, float &freqBinMax) const; Chris@0: Chris@0: struct LayerRange { Chris@0: long startFrame; Chris@0: int zoomLevel; Chris@0: size_t modelStart; Chris@0: size_t modelEnd; Chris@0: }; Chris@20: bool getXBinRange(int x, float &windowMin, float &windowMax) const; Chris@0: Chris@0: bool getYBinSourceRange(int y, float &freqMin, float &freqMax) const; Chris@0: bool getXBinSourceRange(int x, RealTime &timeMin, RealTime &timeMax) const; Chris@0: bool getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const; Chris@0: Chris@0: size_t getWindowIncrement() const { Chris@0: return m_windowSize - m_windowSize * m_windowOverlap / 100; Chris@0: } Chris@0: }; Chris@0: Chris@0: #endif