comparison data/model/FFTModel.cpp @ 275:522f82311e4e

* Pull peak-picker out of SpectrumLayer and into FFTModel; use combined peak-picker and frequency estimator for SpectrogramLayer (makes the peak frequency spectrogram a bit quicker) * Add more information to spectrum and spectrogram crosshairs
author Chris Cannam
date Wed, 04 Jul 2007 15:29:16 +0000
parents 29b70bdaacdc
children daf89d31f45c
comparison
equal deleted inserted replaced
274:e412f65884ee 275:522f82311e4e
15 15
16 #include "FFTModel.h" 16 #include "FFTModel.h"
17 #include "DenseTimeValueModel.h" 17 #include "DenseTimeValueModel.h"
18 18
19 #include "base/Profiler.h" 19 #include "base/Profiler.h"
20 #include "base/Pitch.h"
20 21
21 #include <cassert> 22 #include <cassert>
22 23
23 FFTModel::FFTModel(const DenseTimeValueModel *model, 24 FFTModel::FFTModel(const DenseTimeValueModel *model,
24 int channel, 25 int channel,
101 if (!sr) return ""; 102 if (!sr) return "";
102 QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2)); 103 QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2));
103 return name; 104 return name;
104 } 105 }
105 106
107 bool
108 FFTModel::estimateStableFrequency(size_t x, size_t y, float &frequency)
109 {
110 if (!isOK()) return false;
111
112 size_t sampleRate = m_server->getModel()->getSampleRate();
113
114 size_t fftSize = m_server->getFFTSize() >> m_yshift;
115 frequency = (float(y) * sampleRate) / fftSize;
116
117 if (x+1 >= getWidth()) return false;
118
119 // At frequency f, a phase shift of 2pi (one cycle) happens in 1/f sec.
120 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
121 // At window size w, for bin b, f is b*sr/w.
122 // thus 2pi phase shift happens in w/(b*sr) sec.
123 // We need to know what phase shift we expect from h/sr sec.
124 // -> 2pi * ((h/sr) / (w/(b*sr)))
125 // = 2pi * ((h * b * sr) / (w * sr))
126 // = 2pi * (h * b) / w.
127
128 float oldPhase = getPhaseAt(x, y);
129 float newPhase = getPhaseAt(x+1, y);
130
131 size_t incr = getResolution();
132
133 float expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / fftSize;
134
135 float phaseError = princargf(newPhase - expectedPhase);
136
137 // bool stable = (fabsf(phaseError) < (1.1f * (m_windowIncrement * M_PI) / m_fftSize));
138
139 // The new frequency estimate based on the phase error resulting
140 // from assuming the "native" frequency of this bin
141
142 frequency =
143 (sampleRate * (expectedPhase + phaseError - oldPhase)) /
144 (2 * M_PI * incr);
145
146 return true;
147 }
148
149 FFTModel::PeakLocationSet
150 FFTModel::getPeaks(PeakPickType type, size_t x, size_t ymin, size_t ymax)
151 {
152 FFTModel::PeakLocationSet peaks;
153 if (!isOK()) return peaks;
154
155 if (ymax == 0 || ymax > getHeight() - 1) {
156 ymax = getHeight() - 1;
157 }
158
159 Column values;
160
161 if (type == AllPeaks) {
162 for (size_t y = ymin; y <= ymax; ++y) {
163 values.push_back(getMagnitudeAt(x, y));
164 }
165 size_t i = 0;
166 for (size_t bin = ymin; bin <= ymax; ++bin) {
167 if ((i == 0 || values[i] > values[i-1]) &&
168 (i == values.size()-1 || values[i] >= values[i+1])) {
169 peaks.insert(bin);
170 }
171 ++i;
172 }
173 return peaks;
174 }
175
176 getColumn(x, values);
177
178 // For peak picking we use a moving median window, picking the
179 // highest value within each continuous region of values that
180 // exceed the median. For pitch adaptivity, we adjust the window
181 // size to a roughly constant pitch range (about four tones).
182
183 size_t sampleRate = getSampleRate();
184
185 std::deque<float> window;
186 std::vector<size_t> inrange;
187 size_t medianWinSize = getPeakPickWindowSize(type, sampleRate, ymin);
188 size_t halfWin = medianWinSize/2;
189
190 size_t binmin;
191 if (ymin > halfWin) binmin = ymin - halfWin;
192 else binmin = 0;
193
194 size_t binmax;
195 if (ymax + halfWin < values.size()) binmax = ymax + halfWin;
196 else binmax = values.size()-1;
197
198 for (size_t bin = binmin; bin <= binmax; ++bin) {
199
200 float value = values[bin];
201
202 window.push_back(value);
203
204 medianWinSize = getPeakPickWindowSize(type, sampleRate, bin);
205 halfWin = medianWinSize/2;
206
207 while (window.size() > medianWinSize) window.pop_front();
208
209 if (type == MajorPitchAdaptivePeaks) {
210 if (ymax + halfWin < values.size()) binmax = ymax + halfWin;
211 else binmax = values.size()-1;
212 }
213
214 std::deque<float> sorted(window);
215 std::sort(sorted.begin(), sorted.end());
216 float median = sorted[sorted.size()/2];
217
218 if (value > median) {
219 inrange.push_back(bin);
220 }
221
222 if (value <= median || bin+1 == values.size()) {
223 size_t peakbin = 0;
224 float peakval = 0.f;
225 if (!inrange.empty()) {
226 for (size_t i = 0; i < inrange.size(); ++i) {
227 if (i == 0 || values[inrange[i]] > peakval) {
228 peakval = values[inrange[i]];
229 peakbin = inrange[i];
230 }
231 }
232 inrange.clear();
233 if (peakbin >= ymin && peakbin <= ymax) {
234 peaks.insert(peakbin);
235 }
236 }
237 }
238 }
239
240 return peaks;
241 }
242
243 size_t
244 FFTModel::getPeakPickWindowSize(PeakPickType type, size_t sampleRate, size_t bin) const
245 {
246 if (type == MajorPeaks) return 10;
247 if (bin == 0) return 3;
248 size_t fftSize = m_server->getFFTSize() >> m_yshift;
249 float binfreq = (sampleRate * bin) / fftSize;
250 float hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq);
251 int hibin = lrintf((hifreq * fftSize) / sampleRate);
252 int medianWinSize = hibin - bin;
253 if (medianWinSize < 3) medianWinSize = 3;
254 return medianWinSize;
255 }
256
257 FFTModel::PeakSet
258 FFTModel::getPeakFrequencies(PeakPickType type, size_t x,
259 size_t ymin, size_t ymax)
260 {
261 PeakSet peaks;
262 if (!isOK()) return peaks;
263 PeakLocationSet locations = getPeaks(type, x, ymin, ymax);
264
265 size_t sampleRate = getSampleRate();
266 size_t fftSize = m_server->getFFTSize() >> m_yshift;
267 size_t incr = getResolution();
268
269 // This duplicates some of the work of estimateStableFrequency to
270 // allow us to retrieve the phases in two separate vertical
271 // columns, instead of jumping back and forth between columns x and
272 // x+1, which may be significantly slower if re-seeking is needed
273
274 std::vector<float> phases;
275 for (PeakLocationSet::iterator i = locations.begin();
276 i != locations.end(); ++i) {
277 phases.push_back(getPhaseAt(x, *i));
278 }
279
280 size_t phaseIndex = 0;
281 for (PeakLocationSet::iterator i = locations.begin();
282 i != locations.end(); ++i) {
283 float oldPhase = phases[phaseIndex];
284 float newPhase = getPhaseAt(x+1, *i);
285 float expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / fftSize;
286 float phaseError = princargf(newPhase - expectedPhase);
287 float frequency =
288 (sampleRate * (expectedPhase + phaseError - oldPhase))
289 / (2 * M_PI * incr);
290 // bool stable = (fabsf(phaseError) < (1.1f * (incr * M_PI) / fftSize));
291 // if (stable)
292 peaks[*i] = frequency;
293 ++phaseIndex;
294 }
295
296 return peaks;
297 }
298
106 Model * 299 Model *
107 FFTModel::clone() const 300 FFTModel::clone() const
108 { 301 {
109 return new FFTModel(*this); 302 return new FFTModel(*this);
110 } 303 }