Mercurial > hg > svcore
comparison data/model/FFTModel.h @ 1365:3382d914e110
Merge from branch 3.0-integration
author | Chris Cannam |
---|---|
date | Fri, 13 Jan 2017 10:29:44 +0000 |
parents | 54af1e21705c |
children | fad8f533ca13 |
comparison
equal
deleted
inserted
replaced
1272:6a7ea3bd0e10 | 1365:3382d914e110 |
---|---|
14 */ | 14 */ |
15 | 15 |
16 #ifndef FFT_MODEL_H | 16 #ifndef FFT_MODEL_H |
17 #define FFT_MODEL_H | 17 #define FFT_MODEL_H |
18 | 18 |
19 #include "data/fft/FFTDataServer.h" | |
20 #include "DenseThreeDimensionalModel.h" | 19 #include "DenseThreeDimensionalModel.h" |
20 #include "DenseTimeValueModel.h" | |
21 | |
22 #include "base/Window.h" | |
23 | |
24 #include <bqfft/FFT.h> | |
25 #include <bqvec/Allocators.h> | |
21 | 26 |
22 #include <set> | 27 #include <set> |
23 #include <map> | 28 #include <vector> |
29 #include <complex> | |
30 #include <deque> | |
24 | 31 |
25 /** | 32 /** |
26 * An implementation of DenseThreeDimensionalModel that makes FFT data | 33 * An implementation of DenseThreeDimensionalModel that makes FFT data |
27 * derived from a DenseTimeValueModel available as a generic data | 34 * derived from a DenseTimeValueModel available as a generic data |
28 * grid. The FFT data is acquired using FFTDataServer. Note that any | 35 * grid. |
29 * of the accessor functions may throw AllocationFailed if a cache | |
30 * resize fails. | |
31 */ | 36 */ |
32 | |
33 class FFTModel : public DenseThreeDimensionalModel | 37 class FFTModel : public DenseThreeDimensionalModel |
34 { | 38 { |
35 Q_OBJECT | 39 Q_OBJECT |
40 | |
41 //!!! threading requirements? | |
42 //!!! doubles? since we're not caching much | |
36 | 43 |
37 public: | 44 public: |
38 /** | 45 /** |
39 * Construct an FFT model derived from the given | 46 * Construct an FFT model derived from the given |
40 * DenseTimeValueModel, with the given window parameters and FFT | 47 * DenseTimeValueModel, with the given window parameters and FFT |
41 * size (which may exceed the window size, for zero-padded FFTs). | 48 * size (which may exceed the window size, for zero-padded FFTs). |
42 * | 49 * |
43 * If the model has multiple channels use only the given channel, | 50 * If the model has multiple channels use only the given channel, |
44 * unless the channel is -1 in which case merge all available | 51 * unless the channel is -1 in which case merge all available |
45 * channels. | 52 * channels. |
46 * | |
47 * If polar is true, the data will normally be retrieved from the | |
48 * FFT model in magnitude/phase form; otherwise it will normally | |
49 * be retrieved in "cartesian" real/imaginary form. The results | |
50 * should be the same either way, but a "polar" model addressed in | |
51 * "cartesian" form or vice versa may suffer a performance | |
52 * penalty. | |
53 * | |
54 * The fillFromColumn argument gives a hint that the FFT data | |
55 * server should aim to start calculating FFT data at that column | |
56 * number if possible, as that is likely to be requested first. | |
57 */ | 53 */ |
58 FFTModel(const DenseTimeValueModel *model, | 54 FFTModel(const DenseTimeValueModel *model, |
59 int channel, | 55 int channel, |
60 WindowType windowType, | 56 WindowType windowType, |
61 int windowSize, | 57 int windowSize, |
62 int windowIncrement, | 58 int windowIncrement, |
63 int fftSize, | 59 int fftSize); |
64 bool polar, | |
65 StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria, | |
66 sv_frame_t fillFromFrame = 0); | |
67 ~FFTModel(); | 60 ~FFTModel(); |
68 | |
69 inline float getMagnitudeAt(int x, int y) { | |
70 return m_server->getMagnitudeAt(x << m_xshift, y << m_yshift); | |
71 } | |
72 inline float getNormalizedMagnitudeAt(int x, int y) { | |
73 return m_server->getNormalizedMagnitudeAt(x << m_xshift, y << m_yshift); | |
74 } | |
75 inline float getMaximumMagnitudeAt(int x) { | |
76 return m_server->getMaximumMagnitudeAt(x << m_xshift); | |
77 } | |
78 inline float getPhaseAt(int x, int y) { | |
79 return m_server->getPhaseAt(x << m_xshift, y << m_yshift); | |
80 } | |
81 inline void getValuesAt(int x, int y, float &real, float &imaginary) { | |
82 m_server->getValuesAt(x << m_xshift, y << m_yshift, real, imaginary); | |
83 } | |
84 inline bool isColumnAvailable(int x) const { | |
85 return m_server->isColumnReady(x << m_xshift); | |
86 } | |
87 | |
88 inline bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) { | |
89 return m_server->getMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio()); | |
90 } | |
91 inline bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) { | |
92 return m_server->getNormalizedMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio()); | |
93 } | |
94 inline bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) { | |
95 return m_server->getPhasesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio()); | |
96 } | |
97 inline bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) { | |
98 return m_server->getValuesAt(x << m_xshift, reals, imaginaries, minbin << m_yshift, count, getYRatio()); | |
99 } | |
100 | |
101 inline sv_frame_t getFillExtent() const { return m_server->getFillExtent(); } | |
102 | 61 |
103 // DenseThreeDimensionalModel and Model methods: | 62 // DenseThreeDimensionalModel and Model methods: |
104 // | 63 // |
105 inline virtual int getWidth() const { | 64 virtual int getWidth() const; |
106 return m_server->getWidth() >> m_xshift; | 65 virtual int getHeight() const; |
107 } | 66 virtual float getValueAt(int x, int y) const { return getMagnitudeAt(x, y); } |
108 inline virtual int getHeight() const { | 67 virtual bool isOK() const { return m_model && m_model->isOK(); } |
109 // If there is no y-shift, the server's height (based on its | 68 virtual sv_frame_t getStartFrame() const { return 0; } |
110 // fftsize/2 + 1) is correct. If there is a shift, then the | |
111 // server is using a larger fft size than we want, so we shift | |
112 // it right as many times as necessary, but then we need to | |
113 // re-add the "+1" part (because ((fftsize*2)/2 + 1) / 2 != | |
114 // fftsize/2 + 1). | |
115 return (m_server->getHeight() >> m_yshift) + (m_yshift > 0 ? 1 : 0); | |
116 } | |
117 virtual float getValueAt(int x, int y) const { | |
118 return const_cast<FFTModel *>(this)->getMagnitudeAt(x, y); | |
119 } | |
120 virtual bool isOK() const { | |
121 // Return true if the model was constructed successfully (not | |
122 // necessarily whether an error has occurred since | |
123 // construction, use getError for that) | |
124 return m_server && m_server->getModel(); | |
125 } | |
126 virtual sv_frame_t getStartFrame() const { | |
127 return 0; | |
128 } | |
129 virtual sv_frame_t getEndFrame() const { | 69 virtual sv_frame_t getEndFrame() const { |
130 return sv_frame_t(getWidth()) * getResolution() + getResolution(); | 70 return sv_frame_t(getWidth()) * getResolution() + getResolution(); |
131 } | 71 } |
132 virtual sv_samplerate_t getSampleRate() const; | 72 virtual sv_samplerate_t getSampleRate() const { |
133 virtual int getResolution() const { | 73 return isOK() ? m_model->getSampleRate() : 0; |
134 return m_server->getWindowIncrement() << m_xshift; | |
135 } | 74 } |
136 virtual int getYBinCount() const { | 75 virtual int getResolution() const { return m_windowIncrement; } |
137 return getHeight(); | 76 virtual int getYBinCount() const { return getHeight(); } |
77 virtual float getMinimumLevel() const { return 0.f; } // Can't provide | |
78 virtual float getMaximumLevel() const { return 1.f; } // Can't provide | |
79 virtual Column getColumn(int x) const; // magnitudes | |
80 virtual Column getPhases(int x) const; | |
81 virtual QString getBinName(int n) const; | |
82 virtual bool shouldUseLogValueScale() const { return true; } | |
83 virtual int getCompletion() const { | |
84 int c = 100; | |
85 if (m_model) { | |
86 if (m_model->isReady(&c)) return 100; | |
87 } | |
88 return c; | |
138 } | 89 } |
139 virtual float getMinimumLevel() const { | 90 virtual QString getError() const { return ""; } //!!!??? |
140 return 0.f; // Can't provide | 91 virtual sv_frame_t getFillExtent() const { return getEndFrame(); } |
141 } | |
142 virtual float getMaximumLevel() const { | |
143 return 1.f; // Can't provide | |
144 } | |
145 virtual Column getColumn(int x) const; | |
146 virtual QString getBinName(int n) const; | |
147 | 92 |
148 virtual bool shouldUseLogValueScale() const { | 93 // FFTModel methods: |
149 return true; // Although obviously it's up to the user... | 94 // |
150 } | 95 int getChannel() const { return m_channel; } |
96 WindowType getWindowType() const { return m_windowType; } | |
97 int getWindowSize() const { return m_windowSize; } | |
98 int getWindowIncrement() const { return m_windowIncrement; } | |
99 int getFFTSize() const { return m_fftSize; } | |
100 | |
101 //!!! review which of these are ever actually called | |
102 | |
103 float getMagnitudeAt(int x, int y) const; | |
104 float getMaximumMagnitudeAt(int x) const; | |
105 float getPhaseAt(int x, int y) const; | |
106 void getValuesAt(int x, int y, float &real, float &imaginary) const; | |
107 bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const; | |
108 bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) const; | |
109 bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) const; | |
151 | 110 |
152 /** | 111 /** |
153 * Calculate an estimated frequency for a stable signal in this | 112 * Calculate an estimated frequency for a stable signal in this |
154 * bin, using phase unwrapping. This will be completely wrong if | 113 * bin, using phase unwrapping. This will be completely wrong if |
155 * the signal is not stable here. | 114 * the signal is not stable here. |
169 /** | 128 /** |
170 * Return locations of peak bins in the range [ymin,ymax]. If | 129 * Return locations of peak bins in the range [ymin,ymax]. If |
171 * ymax is zero, getHeight()-1 will be used. | 130 * ymax is zero, getHeight()-1 will be used. |
172 */ | 131 */ |
173 virtual PeakLocationSet getPeaks(PeakPickType type, int x, | 132 virtual PeakLocationSet getPeaks(PeakPickType type, int x, |
174 int ymin = 0, int ymax = 0); | 133 int ymin = 0, int ymax = 0) const; |
175 | 134 |
176 /** | 135 /** |
177 * Return locations and estimated stable frequencies of peak bins. | 136 * Return locations and estimated stable frequencies of peak bins. |
178 */ | 137 */ |
179 virtual PeakSet getPeakFrequencies(PeakPickType type, int x, | 138 virtual PeakSet getPeakFrequencies(PeakPickType type, int x, |
180 int ymin = 0, int ymax = 0); | 139 int ymin = 0, int ymax = 0) const; |
181 | |
182 virtual int getCompletion() const { return m_server->getFillCompletion(); } | |
183 virtual QString getError() const { return m_server->getError(); } | |
184 | |
185 virtual void suspend() { m_server->suspend(); } | |
186 virtual void suspendWrites() { m_server->suspendWrites(); } | |
187 virtual void resume() { m_server->resume(); } | |
188 | 140 |
189 QString getTypeName() const { return tr("FFT"); } | 141 QString getTypeName() const { return tr("FFT"); } |
190 | 142 |
191 public slots: | 143 public slots: |
192 void sourceModelAboutToBeDeleted(); | 144 void sourceModelAboutToBeDeleted(); |
193 | 145 |
194 private: | 146 private: |
195 FFTModel(const FFTModel &); // not implemented | 147 FFTModel(const FFTModel &); // not implemented |
196 FFTModel &operator=(const FFTModel &); // not implemented | 148 FFTModel &operator=(const FFTModel &); // not implemented |
197 | 149 |
198 FFTDataServer *m_server; | 150 const DenseTimeValueModel *m_model; |
199 int m_xshift; | 151 int m_channel; |
200 int m_yshift; | 152 WindowType m_windowType; |
201 | 153 int m_windowSize; |
202 FFTDataServer *getServer(const DenseTimeValueModel *, | 154 int m_windowIncrement; |
203 int, WindowType, int, int, int, | 155 int m_fftSize; |
204 bool, StorageAdviser::Criteria, sv_frame_t); | 156 Window<float> m_windower; |
205 | 157 mutable breakfastquay::FFT m_fft; |
158 | |
206 int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate, | 159 int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate, |
207 int bin, float &percentile) const; | 160 int bin, float &percentile) const; |
208 | 161 |
209 int getYRatio() { | 162 std::pair<sv_frame_t, sv_frame_t> getSourceSampleRange(int column) const { |
210 int ys = m_yshift; | 163 sv_frame_t startFrame = m_windowIncrement * sv_frame_t(column); |
211 int r = 1; | 164 sv_frame_t endFrame = startFrame + m_windowSize; |
212 while (ys) { --ys; r <<= 1; } | 165 // Cols are centred on the audio sample (e.g. col 0 is centred at sample 0) |
213 return r; | 166 startFrame -= m_windowSize / 2; |
167 endFrame -= m_windowSize / 2; | |
168 return { startFrame, endFrame }; | |
214 } | 169 } |
170 | |
171 typedef std::vector<float, breakfastquay::StlAllocator<float>> fvec; | |
172 typedef std::vector<std::complex<float>, | |
173 breakfastquay::StlAllocator<std::complex<float>>> cvec; | |
174 | |
175 cvec getFFTColumn(int column) const; | |
176 fvec getSourceSamples(int column) const; | |
177 fvec getSourceData(std::pair<sv_frame_t, sv_frame_t>) const; | |
178 fvec getSourceDataUncached(std::pair<sv_frame_t, sv_frame_t>) const; | |
179 | |
180 struct SavedSourceData { | |
181 std::pair<sv_frame_t, sv_frame_t> range; | |
182 fvec data; | |
183 }; | |
184 mutable SavedSourceData m_savedData; | |
185 | |
186 struct SavedColumn { | |
187 int n; | |
188 cvec col; | |
189 }; | |
190 mutable std::deque<SavedColumn> m_cached; | |
191 size_t m_cacheSize; | |
215 }; | 192 }; |
216 | 193 |
217 #endif | 194 #endif |