Mercurial > hg > svgui
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 }; |