comparison layer/SpectrogramLayer.h @ 38:beb801473743

* Rearrange spectrogram cacheing so that gain, normalization, instantaneous frequency calculations etc can be done from the cached data (increasing the size of the cache, but also the usability).
author Chris Cannam
date Thu, 23 Feb 2006 18:01:31 +0000
parents 21d061e66177
children 5ce844ec854a
comparison
equal deleted inserted replaced
37:21d061e66177 38:beb801473743
18 #include <QThread> 18 #include <QThread>
19 #include <QMutex> 19 #include <QMutex>
20 #include <QWaitCondition> 20 #include <QWaitCondition>
21 21
22 #include <fftw3.h> 22 #include <fftw3.h>
23
24 #include <stdint.h>
23 25
24 class View; 26 class View;
25 class QPainter; 27 class QPainter;
26 class QImage; 28 class QImage;
27 class QPixmap; 29 class QPixmap;
198 ColourScheme m_colourScheme; 200 ColourScheme m_colourScheme;
199 FrequencyScale m_frequencyScale; 201 FrequencyScale m_frequencyScale;
200 BinDisplay m_binDisplay; 202 BinDisplay m_binDisplay;
201 bool m_normalizeColumns; 203 bool m_normalizeColumns;
202 204
203 enum { NO_VALUE = 0 }; 205 // At the moment we cache one unsigned char per bin for the
204 206 // magnitude -- which is nothing like precise enough to allow us
205 // A QImage would do just as well here, and we originally used 207 // to subsequently adjust gain etc without recalculating the
206 // one: the problem is that we want to munlock() the memory it 208 // cached values -- plus optionally one unsigned char per bin for
207 // uses, and it's much easier to do that if we control it. This 209 // phase-adjusted frequency.
208 // cache is hardwired to an effective 8-bit colour mapped layout. 210
211 // To speed up redrawing after parameter changes, we would like to
212 // cache magnitude in a way that can have gain applied afterwards
213 // and can determine whether something is a peak or not, and also
214 // cache phase rather than only phase-adjusted frequency so that
215 // we don't have to recalculate if switching between phase and
216 // magnitude displays.
217
218 // This implies probably 16 bits for a normalized magnitude (in
219 // dB?) and at most 16 bits for phase. 16 or 32 bits per bin
220 // instead of 8 or 16.
221
222 // Each column's magnitudes are expected to be stored normalized
223 // to [0,1] with respect to the column, so the normalization
224 // factor should be calculated before all values in a column, and
225 // set appropriately.
226
209 class Cache { 227 class Cache {
210 public: 228 public:
211 Cache(size_t width, size_t height); 229 Cache(); // of size zero, call resize() before using
212 ~Cache(); 230 ~Cache();
213 231
214 size_t getWidth() const; 232 size_t getWidth() const { return m_width; }
215 size_t getHeight() const; 233 size_t getHeight() const { return m_height; }
216 234
217 void resize(size_t width, size_t height); 235 void resize(size_t width, size_t height);
236 void reset(); // zero-fill or 1-fill as appropriate without changing size
218 237
219 unsigned char getValueAt(size_t x, size_t y) const; 238 float getMagnitudeAt(size_t x, size_t y) const {
220 void setValueAt(size_t x, size_t y, unsigned char value); 239 return getNormalizedMagnitudeAt(x, y) * m_factor[x];
221 240 }
222 QColor getColour(unsigned char index) const; 241
223 void setColour(unsigned char index, QColor colour); 242 float getNormalizedMagnitudeAt(size_t x, size_t y) const {
224 243 return float(m_magnitude[y][x]) / 65535.0;
225 void fill(unsigned char value); 244 }
226 245
227 protected: 246 float getPhaseAt(size_t x, size_t y) const {
247 return (float(m_phase[y][x]) / 32767.0) * M_PI;
248 }
249
250 bool isLocalPeak(size_t x, size_t y) const {
251 if (y > 0 && m_magnitude[y][x] < m_magnitude[y-1][x]) return false;
252 if (y < m_height-1 && m_magnitude[y][x] < m_magnitude[y+1][x]) return false;
253 return true;
254 }
255
256 bool isOverThreshold(size_t x, size_t y, float threshold) const {
257 if (threshold == 0.0) return true;
258 return getMagnitudeAt(x, y) > threshold;
259 }
260
261 void setNormalizationFactor(size_t x, float factor) {
262 m_factor[x] = factor;
263 }
264
265 void setMagnitudeAt(size_t x, size_t y, float mag) {
266 // norm factor must already be set
267 setNormalizedMagnitudeAt(x, y, mag / m_factor[x]);
268 }
269
270 void setNormalizedMagnitudeAt(size_t x, size_t y, float norm) {
271 m_magnitude[y][x] = uint16_t(norm * 65535.0);
272 }
273
274 void setPhaseAt(size_t x, size_t y, float phase) {
275 // phase in range -pi -> pi
276 m_phase[y][x] = uint16_t((phase * 32767) / M_PI);
277 }
278
279 QColor getColour(unsigned char index) const {
280 return m_colours[index];
281 }
282
283 void setColour(unsigned char index, QColor colour) {
284 m_colours[index] = colour;
285 }
286
287 private:
228 size_t m_width; 288 size_t m_width;
229 size_t m_height; 289 size_t m_height;
230 unsigned char *m_values; 290 uint16_t **m_magnitude;
291 uint16_t **m_phase;
292 float *m_factor;
231 QColor m_colours[256]; 293 QColor m_colours[256];
232 }; 294
233 295 void resize(uint16_t **&, size_t, size_t);
296 };
297
298 enum { NO_VALUE = 0 }; // colour index for unused pixels
299
234 Cache *m_cache; 300 Cache *m_cache;
235 Cache *m_phaseAdjustCache;
236 bool m_cacheInvalid; 301 bool m_cacheInvalid;
237 302
238 class CacheFillThread : public QThread 303 class CacheFillThread : public QThread
239 { 304 {
240 public: 305 public:
262 mutable QMutex m_mutex; 327 mutable QMutex m_mutex;
263 328
264 CacheFillThread *m_fillThread; 329 CacheFillThread *m_fillThread;
265 QTimer *m_updateTimer; 330 QTimer *m_updateTimer;
266 size_t m_lastFillExtent; 331 size_t m_lastFillExtent;
267 bool m_cachedInitialVisibleArea;
268 bool m_exiting; 332 bool m_exiting;
269 333
270 void setCacheColourmap(); 334 void setCacheColourmap();
271 void rotateCacheColourmap(int distance); 335 void rotateCacheColourmap(int distance);
272 336
273 bool fillCacheColumn(int column, 337 void fillCacheColumn(int column,
274 double *inputBuffer, 338 double *inputBuffer,
275 fftw_complex *outputBuffer, 339 fftw_complex *outputBuffer,
276 fftw_plan plan, 340 fftw_plan plan,
277 size_t windowSize, 341 size_t windowSize,
278 size_t windowIncrement, 342 size_t windowIncrement,
279 const Window<double> &windower, 343 const Window<double> &windower)
280 bool resetStoredPhase)
281 const; 344 const;
345
346 static float calculateFrequency(size_t bin,
347 size_t windowSize,
348 size_t windowIncrement,
349 size_t sampleRate,
350 float previousPhase,
351 float currentPhase,
352 bool &steadyState);
353
354 unsigned char getDisplayValue(float input) const;
282 355
283 bool getYBinRange(int y, float &freqBinMin, float &freqBinMax) const; 356 bool getYBinRange(int y, float &freqBinMin, float &freqBinMax) const;
284 357
285 struct LayerRange { 358 struct LayerRange {
286 long startFrame; 359 long startFrame;
293 bool getYBinSourceRange(int y, float &freqMin, float &freqMax) const; 366 bool getYBinSourceRange(int y, float &freqMin, float &freqMax) const;
294 bool getAdjustedYBinSourceRange(int x, int y, 367 bool getAdjustedYBinSourceRange(int x, int y,
295 float &freqMin, float &freqMax, 368 float &freqMin, float &freqMax,
296 float &adjFreqMin, float &adjFreqMax) const; 369 float &adjFreqMin, float &adjFreqMax) const;
297 bool getXBinSourceRange(int x, RealTime &timeMin, RealTime &timeMax) const; 370 bool getXBinSourceRange(int x, RealTime &timeMin, RealTime &timeMax) const;
298 bool getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const; 371 bool getXYBinSourceRange(int x, int y, float &min, float &max,
372 float &phaseMin, float &phaseMax) const;
299 373
300 size_t getWindowIncrement() const { 374 size_t getWindowIncrement() const {
301 return m_windowSize - m_windowSize * m_windowOverlap / 100; 375 return m_windowSize - m_windowSize * m_windowOverlap / 100;
302 } 376 }
303 }; 377 };