Mercurial > hg > svgui
diff 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 |
line wrap: on
line diff
--- a/layer/SpectrogramLayer.h Wed Feb 22 17:45:18 2006 +0000 +++ b/layer/SpectrogramLayer.h Thu Feb 23 18:01:31 2006 +0000 @@ -21,6 +21,8 @@ #include <fftw3.h> +#include <stdint.h> + class View; class QPainter; class QImage; @@ -200,39 +202,102 @@ BinDisplay m_binDisplay; bool m_normalizeColumns; - enum { NO_VALUE = 0 }; + // At the moment we cache one unsigned char per bin for the + // magnitude -- which is nothing like precise enough to allow us + // to subsequently adjust gain etc without recalculating the + // cached values -- plus optionally one unsigned char per bin for + // phase-adjusted frequency. - // A QImage would do just as well here, and we originally used - // one: the problem is that we want to munlock() the memory it - // uses, and it's much easier to do that if we control it. This - // cache is hardwired to an effective 8-bit colour mapped layout. + // To speed up redrawing after parameter changes, we would like to + // cache magnitude in a way that can have gain applied afterwards + // and can determine whether something is a peak or not, and also + // cache phase rather than only phase-adjusted frequency so that + // we don't have to recalculate if switching between phase and + // magnitude displays. + + // This implies probably 16 bits for a normalized magnitude (in + // dB?) and at most 16 bits for phase. 16 or 32 bits per bin + // instead of 8 or 16. + + // Each column's magnitudes are expected to be stored normalized + // to [0,1] with respect to the column, so the normalization + // factor should be calculated before all values in a column, and + // set appropriately. + class Cache { public: - Cache(size_t width, size_t height); + Cache(); // of size zero, call resize() before using ~Cache(); - size_t getWidth() const; - size_t getHeight() const; + size_t getWidth() const { return m_width; } + size_t getHeight() const { return m_height; } + + void resize(size_t width, size_t height); + void reset(); // zero-fill or 1-fill as appropriate without changing size + + float getMagnitudeAt(size_t x, size_t y) const { + return getNormalizedMagnitudeAt(x, y) * m_factor[x]; + } - void resize(size_t width, size_t height); - - unsigned char getValueAt(size_t x, size_t y) const; - void setValueAt(size_t x, size_t y, unsigned char value); + float getNormalizedMagnitudeAt(size_t x, size_t y) const { + return float(m_magnitude[y][x]) / 65535.0; + } - QColor getColour(unsigned char index) const; - void setColour(unsigned char index, QColor colour); + float getPhaseAt(size_t x, size_t y) const { + return (float(m_phase[y][x]) / 32767.0) * M_PI; + } - void fill(unsigned char value); + bool isLocalPeak(size_t x, size_t y) const { + if (y > 0 && m_magnitude[y][x] < m_magnitude[y-1][x]) return false; + if (y < m_height-1 && m_magnitude[y][x] < m_magnitude[y+1][x]) return false; + return true; + } - protected: + bool isOverThreshold(size_t x, size_t y, float threshold) const { + if (threshold == 0.0) return true; + return getMagnitudeAt(x, y) > threshold; + } + + void setNormalizationFactor(size_t x, float factor) { + m_factor[x] = factor; + } + + void setMagnitudeAt(size_t x, size_t y, float mag) { + // norm factor must already be set + setNormalizedMagnitudeAt(x, y, mag / m_factor[x]); + } + + void setNormalizedMagnitudeAt(size_t x, size_t y, float norm) { + m_magnitude[y][x] = uint16_t(norm * 65535.0); + } + + void setPhaseAt(size_t x, size_t y, float phase) { + // phase in range -pi -> pi + m_phase[y][x] = uint16_t((phase * 32767) / M_PI); + } + + QColor getColour(unsigned char index) const { + return m_colours[index]; + } + + void setColour(unsigned char index, QColor colour) { + m_colours[index] = colour; + } + + private: size_t m_width; size_t m_height; - unsigned char *m_values; + uint16_t **m_magnitude; + uint16_t **m_phase; + float *m_factor; QColor m_colours[256]; + + void resize(uint16_t **&, size_t, size_t); }; - + + enum { NO_VALUE = 0 }; // colour index for unused pixels + Cache *m_cache; - Cache *m_phaseAdjustCache; bool m_cacheInvalid; class CacheFillThread : public QThread @@ -264,22 +329,30 @@ CacheFillThread *m_fillThread; QTimer *m_updateTimer; size_t m_lastFillExtent; - bool m_cachedInitialVisibleArea; bool m_exiting; void setCacheColourmap(); void rotateCacheColourmap(int distance); - bool fillCacheColumn(int column, + void fillCacheColumn(int column, double *inputBuffer, fftw_complex *outputBuffer, fftw_plan plan, size_t windowSize, size_t windowIncrement, - const Window<double> &windower, - bool resetStoredPhase) + const Window<double> &windower) const; + static float calculateFrequency(size_t bin, + size_t windowSize, + size_t windowIncrement, + size_t sampleRate, + float previousPhase, + float currentPhase, + bool &steadyState); + + unsigned char getDisplayValue(float input) const; + bool getYBinRange(int y, float &freqBinMin, float &freqBinMax) const; struct LayerRange { @@ -295,7 +368,8 @@ float &freqMin, float &freqMax, float &adjFreqMin, float &adjFreqMax) const; bool getXBinSourceRange(int x, RealTime &timeMin, RealTime &timeMax) const; - bool getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const; + bool getXYBinSourceRange(int x, int y, float &min, float &max, + float &phaseMin, float &phaseMax) const; size_t getWindowIncrement() const { return m_windowSize - m_windowSize * m_windowOverlap / 100;