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 }; |
