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