Chris@0
|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 A waveform viewer and audio annotation editor.
|
Chris@5
|
5 Chris Cannam, Queen Mary University of London, 2005-2006
|
Chris@0
|
6
|
Chris@0
|
7 This is experimental software. Not for distribution.
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@30
|
10 #ifndef _SPECTROGRAM_LAYER_H_
|
Chris@30
|
11 #define _SPECTROGRAM_LAYER_H_
|
Chris@0
|
12
|
Chris@0
|
13 #include "base/Layer.h"
|
Chris@0
|
14 #include "base/Window.h"
|
Chris@0
|
15 #include "model/PowerOfSqrtTwoZoomConstraint.h"
|
Chris@0
|
16 #include "model/DenseTimeValueModel.h"
|
Chris@0
|
17
|
Chris@0
|
18 #include <QThread>
|
Chris@0
|
19 #include <QMutex>
|
Chris@0
|
20 #include <QWaitCondition>
|
Chris@0
|
21
|
Chris@0
|
22 #include <fftw3.h>
|
Chris@0
|
23
|
Chris@0
|
24 class View;
|
Chris@0
|
25 class QPainter;
|
Chris@0
|
26 class QImage;
|
Chris@0
|
27 class QPixmap;
|
Chris@0
|
28 class QTimer;
|
Chris@0
|
29 class RealTime;
|
Chris@0
|
30
|
Chris@0
|
31 /**
|
Chris@0
|
32 * SpectrogramLayer represents waveform data (obtained from a
|
Chris@0
|
33 * DenseTimeValueModel) in spectrogram form.
|
Chris@0
|
34 */
|
Chris@0
|
35
|
Chris@0
|
36 class SpectrogramLayer : public Layer,
|
Chris@31
|
37 public PowerOfSqrtTwoZoomConstraint
|
Chris@0
|
38 {
|
Chris@0
|
39 Q_OBJECT
|
Chris@0
|
40
|
Chris@0
|
41 public:
|
Chris@37
|
42 enum Configuration { FullRangeDb, MelodicRange, MelodicPeaks };
|
Chris@0
|
43
|
Chris@0
|
44 SpectrogramLayer(View *w, Configuration = FullRangeDb);
|
Chris@0
|
45 ~SpectrogramLayer();
|
Chris@0
|
46
|
Chris@0
|
47 virtual const ZoomConstraint *getZoomConstraint() const { return this; }
|
Chris@0
|
48 virtual const Model *getModel() const { return m_model; }
|
Chris@0
|
49 virtual void paint(QPainter &paint, QRect rect) const;
|
Chris@0
|
50
|
Chris@0
|
51 virtual int getVerticalScaleWidth(QPainter &) const;
|
Chris@0
|
52 virtual void paintVerticalScale(QPainter &paint, QRect rect) const;
|
Chris@0
|
53
|
Chris@25
|
54 virtual QString getFeatureDescription(QPoint &) const;
|
Chris@0
|
55
|
Chris@28
|
56 virtual bool snapToFeatureFrame(int &frame,
|
Chris@28
|
57 size_t &resolution,
|
Chris@28
|
58 SnapType snap) const;
|
Chris@13
|
59
|
Chris@0
|
60 void setModel(const DenseTimeValueModel *model);
|
Chris@0
|
61
|
Chris@0
|
62 virtual PropertyList getProperties() const;
|
Chris@0
|
63 virtual PropertyType getPropertyType(const PropertyName &) const;
|
Chris@0
|
64 virtual QString getPropertyGroupName(const PropertyName &) const;
|
Chris@0
|
65 virtual int getPropertyRangeAndValue(const PropertyName &,
|
Chris@0
|
66 int *min, int *max) const;
|
Chris@0
|
67 virtual QString getPropertyValueLabel(const PropertyName &,
|
Chris@0
|
68 int value) const;
|
Chris@0
|
69 virtual void setProperty(const PropertyName &, int value);
|
Chris@0
|
70
|
Chris@0
|
71 /**
|
Chris@0
|
72 * Specify the channel to use from the source model.
|
Chris@0
|
73 * A value of -1 means to mix all available channels.
|
Chris@0
|
74 * The default is channel 0.
|
Chris@0
|
75 */
|
Chris@0
|
76 void setChannel(int);
|
Chris@0
|
77 int getChannel() const;
|
Chris@0
|
78
|
Chris@0
|
79 void setWindowSize(size_t);
|
Chris@0
|
80 size_t getWindowSize() const;
|
Chris@0
|
81
|
Chris@0
|
82 void setWindowOverlap(size_t percent);
|
Chris@0
|
83 size_t getWindowOverlap() const;
|
Chris@0
|
84
|
Chris@0
|
85 void setWindowType(WindowType type);
|
Chris@0
|
86 WindowType getWindowType() const;
|
Chris@0
|
87
|
Chris@0
|
88 /**
|
Chris@0
|
89 * Set the gain multiplier for sample values in this view prior to
|
Chris@0
|
90 * FFT calculation.
|
Chris@0
|
91 *
|
Chris@0
|
92 * The default is 1.0.
|
Chris@0
|
93 */
|
Chris@0
|
94 void setGain(float gain);
|
Chris@0
|
95 float getGain() const;
|
Chris@0
|
96
|
Chris@37
|
97 /**
|
Chris@37
|
98 * Set the threshold for sample values to be shown in the FFT,
|
Chris@37
|
99 * in voltage units.
|
Chris@37
|
100 *
|
Chris@37
|
101 * The default is 0.0.
|
Chris@37
|
102 */
|
Chris@37
|
103 void setThreshold(float threshold);
|
Chris@37
|
104 float getThreshold() const;
|
Chris@37
|
105
|
Chris@37
|
106 void setMinFrequency(size_t);
|
Chris@37
|
107 size_t getMinFrequency() const;
|
Chris@37
|
108
|
Chris@0
|
109 void setMaxFrequency(size_t); // 0 -> no maximum
|
Chris@0
|
110 size_t getMaxFrequency() const;
|
Chris@0
|
111
|
Chris@37
|
112 enum ColourScale {
|
Chris@37
|
113 LinearColourScale,
|
Chris@37
|
114 MeterColourScale,
|
Chris@37
|
115 dBColourScale,
|
Chris@37
|
116 PhaseColourScale
|
Chris@37
|
117 };
|
Chris@0
|
118
|
Chris@0
|
119 /**
|
Chris@0
|
120 * Specify the scale for sample levels. See WaveformLayer for
|
Chris@0
|
121 * details of meter and dB scaling. The default is dBColourScale.
|
Chris@0
|
122 */
|
Chris@0
|
123 void setColourScale(ColourScale);
|
Chris@0
|
124 ColourScale getColourScale() const;
|
Chris@0
|
125
|
Chris@35
|
126 enum FrequencyScale {
|
Chris@35
|
127 LinearFrequencyScale,
|
Chris@35
|
128 LogFrequencyScale
|
Chris@35
|
129 };
|
Chris@0
|
130
|
Chris@0
|
131 /**
|
Chris@0
|
132 * Specify the scale for the y axis.
|
Chris@0
|
133 */
|
Chris@0
|
134 void setFrequencyScale(FrequencyScale);
|
Chris@0
|
135 FrequencyScale getFrequencyScale() const;
|
Chris@0
|
136
|
Chris@37
|
137 enum BinDisplay {
|
Chris@37
|
138 AllBins,
|
Chris@37
|
139 PeakBins,
|
Chris@37
|
140 PeakFrequencies
|
Chris@35
|
141 };
|
Chris@35
|
142
|
Chris@35
|
143 /**
|
Chris@35
|
144 * Specify the processing of frequency bins for the y axis.
|
Chris@35
|
145 */
|
Chris@37
|
146 void setBinDisplay(BinDisplay);
|
Chris@37
|
147 BinDisplay getBinDisplay() const;
|
Chris@35
|
148
|
Chris@36
|
149 void setNormalizeColumns(bool n);
|
Chris@36
|
150 bool getNormalizeColumns() const;
|
Chris@36
|
151
|
Chris@0
|
152 enum ColourScheme { DefaultColours, WhiteOnBlack, BlackOnWhite,
|
Chris@0
|
153 RedOnBlue, YellowOnBlack, RedOnBlack };
|
Chris@0
|
154
|
Chris@0
|
155 void setColourScheme(ColourScheme scheme);
|
Chris@0
|
156 ColourScheme getColourScheme() const;
|
Chris@0
|
157
|
Chris@9
|
158 /**
|
Chris@9
|
159 * Specify the colourmap rotation for the colour scale.
|
Chris@9
|
160 */
|
Chris@9
|
161 void setColourRotation(int);
|
Chris@9
|
162 int getColourRotation() const;
|
Chris@9
|
163
|
Chris@0
|
164 virtual VerticalPosition getPreferredFrameCountPosition() const {
|
Chris@0
|
165 return PositionTop;
|
Chris@0
|
166 }
|
Chris@0
|
167
|
Chris@15
|
168 virtual bool isLayerOpaque() const { return true; }
|
Chris@15
|
169
|
Chris@0
|
170 virtual int getCompletion() const;
|
Chris@0
|
171
|
Chris@6
|
172 virtual QString toXmlString(QString indent = "",
|
Chris@6
|
173 QString extraAttributes = "") const;
|
Chris@6
|
174
|
Chris@11
|
175 void setProperties(const QXmlAttributes &attributes);
|
Chris@11
|
176
|
Chris@33
|
177 virtual void setLayerDormant(bool dormant);
|
Chris@29
|
178
|
Chris@0
|
179 protected slots:
|
Chris@0
|
180 void cacheInvalid();
|
Chris@0
|
181 void cacheInvalid(size_t startFrame, size_t endFrame);
|
Chris@0
|
182
|
Chris@0
|
183 void fillTimerTimedOut();
|
Chris@0
|
184
|
Chris@0
|
185 protected:
|
Chris@0
|
186 const DenseTimeValueModel *m_model; // I do not own this
|
Chris@0
|
187
|
Chris@35
|
188 int m_channel;
|
Chris@35
|
189 size_t m_windowSize;
|
Chris@35
|
190 WindowType m_windowType;
|
Chris@35
|
191 size_t m_windowOverlap;
|
Chris@35
|
192 float m_gain;
|
Chris@37
|
193 float m_threshold;
|
Chris@35
|
194 int m_colourRotation;
|
Chris@37
|
195 size_t m_minFrequency;
|
Chris@35
|
196 size_t m_maxFrequency;
|
Chris@35
|
197 ColourScale m_colourScale;
|
Chris@35
|
198 ColourScheme m_colourScheme;
|
Chris@35
|
199 FrequencyScale m_frequencyScale;
|
Chris@37
|
200 BinDisplay m_binDisplay;
|
Chris@36
|
201 bool m_normalizeColumns;
|
Chris@0
|
202
|
Chris@37
|
203 enum { NO_VALUE = 0 };
|
Chris@37
|
204
|
Chris@31
|
205 // A QImage would do just as well here, and we originally used
|
Chris@31
|
206 // one: the problem is that we want to munlock() the memory it
|
Chris@31
|
207 // uses, and it's much easier to do that if we control it. This
|
Chris@31
|
208 // cache is hardwired to an effective 8-bit colour mapped layout.
|
Chris@31
|
209 class Cache {
|
Chris@31
|
210 public:
|
Chris@31
|
211 Cache(size_t width, size_t height);
|
Chris@31
|
212 ~Cache();
|
Chris@31
|
213
|
Chris@31
|
214 size_t getWidth() const;
|
Chris@31
|
215 size_t getHeight() const;
|
Chris@35
|
216
|
Chris@35
|
217 void resize(size_t width, size_t height);
|
Chris@31
|
218
|
Chris@31
|
219 unsigned char getValueAt(size_t x, size_t y) const;
|
Chris@31
|
220 void setValueAt(size_t x, size_t y, unsigned char value);
|
Chris@31
|
221
|
Chris@31
|
222 QColor getColour(unsigned char index) const;
|
Chris@31
|
223 void setColour(unsigned char index, QColor colour);
|
Chris@31
|
224
|
Chris@31
|
225 void fill(unsigned char value);
|
Chris@31
|
226
|
Chris@31
|
227 protected:
|
Chris@31
|
228 size_t m_width;
|
Chris@31
|
229 size_t m_height;
|
Chris@31
|
230 unsigned char *m_values;
|
Chris@31
|
231 QColor m_colours[256];
|
Chris@31
|
232 };
|
Chris@31
|
233
|
Chris@31
|
234 Cache *m_cache;
|
Chris@35
|
235 Cache *m_phaseAdjustCache;
|
Chris@31
|
236 bool m_cacheInvalid;
|
Chris@31
|
237
|
Chris@0
|
238 class CacheFillThread : public QThread
|
Chris@0
|
239 {
|
Chris@0
|
240 public:
|
Chris@0
|
241 CacheFillThread(SpectrogramLayer &layer) :
|
Chris@0
|
242 m_layer(layer), m_fillExtent(0) { }
|
Chris@0
|
243
|
Chris@0
|
244 size_t getFillExtent() const { return m_fillExtent; }
|
Chris@0
|
245 size_t getFillCompletion() const { return m_fillCompletion; }
|
Chris@0
|
246 virtual void run();
|
Chris@0
|
247
|
Chris@0
|
248 protected:
|
Chris@0
|
249 SpectrogramLayer &m_layer;
|
Chris@0
|
250 size_t m_fillExtent;
|
Chris@0
|
251 size_t m_fillCompletion;
|
Chris@0
|
252 };
|
Chris@0
|
253
|
Chris@0
|
254 void fillCache();
|
Chris@0
|
255
|
Chris@0
|
256 mutable QPixmap *m_pixmapCache;
|
Chris@0
|
257 mutable bool m_pixmapCacheInvalid;
|
Chris@0
|
258 mutable long m_pixmapCacheStartFrame;
|
Chris@0
|
259 mutable size_t m_pixmapCacheZoomLevel;
|
Chris@0
|
260
|
Chris@0
|
261 QWaitCondition m_condition;
|
Chris@0
|
262 mutable QMutex m_mutex;
|
Chris@0
|
263
|
Chris@0
|
264 CacheFillThread *m_fillThread;
|
Chris@0
|
265 QTimer *m_updateTimer;
|
Chris@0
|
266 size_t m_lastFillExtent;
|
Chris@0
|
267 bool m_cachedInitialVisibleArea;
|
Chris@0
|
268 bool m_exiting;
|
Chris@0
|
269
|
Chris@0
|
270 void setCacheColourmap();
|
Chris@9
|
271 void rotateCacheColourmap(int distance);
|
Chris@0
|
272
|
Chris@0
|
273 bool fillCacheColumn(int column,
|
Chris@0
|
274 double *inputBuffer,
|
Chris@0
|
275 fftw_complex *outputBuffer,
|
Chris@0
|
276 fftw_plan plan,
|
Chris@9
|
277 size_t windowSize,
|
Chris@9
|
278 size_t windowIncrement,
|
Chris@0
|
279 const Window<double> &windower,
|
Chris@35
|
280 bool resetStoredPhase)
|
Chris@0
|
281 const;
|
Chris@0
|
282
|
Chris@0
|
283 bool getYBinRange(int y, float &freqBinMin, float &freqBinMax) const;
|
Chris@0
|
284
|
Chris@0
|
285 struct LayerRange {
|
Chris@0
|
286 long startFrame;
|
Chris@0
|
287 int zoomLevel;
|
Chris@0
|
288 size_t modelStart;
|
Chris@0
|
289 size_t modelEnd;
|
Chris@0
|
290 };
|
Chris@20
|
291 bool getXBinRange(int x, float &windowMin, float &windowMax) const;
|
Chris@0
|
292
|
Chris@0
|
293 bool getYBinSourceRange(int y, float &freqMin, float &freqMax) const;
|
Chris@35
|
294 bool getAdjustedYBinSourceRange(int x, int y,
|
Chris@35
|
295 float &freqMin, float &freqMax,
|
Chris@35
|
296 float &adjFreqMin, float &adjFreqMax) const;
|
Chris@0
|
297 bool getXBinSourceRange(int x, RealTime &timeMin, RealTime &timeMax) const;
|
Chris@0
|
298 bool getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const;
|
Chris@0
|
299
|
Chris@0
|
300 size_t getWindowIncrement() const {
|
Chris@0
|
301 return m_windowSize - m_windowSize * m_windowOverlap / 100;
|
Chris@0
|
302 }
|
Chris@0
|
303 };
|
Chris@0
|
304
|
Chris@0
|
305 #endif
|