annotate data/model/FFTModel.cpp @ 1196:c7b9c902642f spectrogram-minor-refactor

Fix threshold in spectrogram -- it wasn't working in the last release. There is a new protocol for this. Formerly the threshold parameter had a range from -50dB to 0 with the default at -50, and -50 treated internally as "no threshold". However, there was a hardcoded, hidden internal threshold for spectrogram colour mapping at -80dB with anything below this being rounded to zero. Now the threshold parameter has range -81 to -1 with the default at -80, -81 is treated internally as "no threshold", and there is no hidden internal threshold. So the default behaviour is the same as before, an effective -80dB threshold, but it is now possible to change this in both directions. Sessions reloaded from prior versions may look slightly different because, if the session says there should be no threshold, there will now actually be no threshold instead of having the hidden internal one. Still need to do something in the UI to make it apparent that the -81dB setting removes the threshold entirely. This is at least no worse than the previous, also obscured, magic -50dB setting.
author Chris Cannam
date Mon, 01 Aug 2016 16:21:01 +0100
parents 6d09ad2ab21f
children 825d0d7641ba
rev   line source
Chris@152 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@152 2
Chris@152 3 /*
Chris@152 4 Sonic Visualiser
Chris@152 5 An audio file viewer and annotation editor.
Chris@152 6 Centre for Digital Music, Queen Mary, University of London.
Chris@152 7 This file copyright 2006 Chris Cannam.
Chris@152 8
Chris@152 9 This program is free software; you can redistribute it and/or
Chris@152 10 modify it under the terms of the GNU General Public License as
Chris@152 11 published by the Free Software Foundation; either version 2 of the
Chris@152 12 License, or (at your option) any later version. See the file
Chris@152 13 COPYING included with this distribution for more information.
Chris@152 14 */
Chris@152 15
Chris@152 16 #include "FFTModel.h"
Chris@152 17 #include "DenseTimeValueModel.h"
Chris@152 18
Chris@183 19 #include "base/Profiler.h"
Chris@275 20 #include "base/Pitch.h"
Chris@183 21
Chris@402 22 #include <algorithm>
Chris@402 23
Chris@152 24 #include <cassert>
Chris@1090 25 #include <deque>
Chris@152 26
Chris@608 27 #ifndef __GNUC__
Chris@608 28 #include <alloca.h>
Chris@608 29 #endif
Chris@608 30
Chris@1090 31 using namespace std;
Chris@1090 32
Chris@152 33 FFTModel::FFTModel(const DenseTimeValueModel *model,
Chris@152 34 int channel,
Chris@152 35 WindowType windowType,
Chris@929 36 int windowSize,
Chris@929 37 int windowIncrement,
Chris@1090 38 int fftSize) :
Chris@1090 39 m_model(model),
Chris@1090 40 m_channel(channel),
Chris@1090 41 m_windowType(windowType),
Chris@1090 42 m_windowSize(windowSize),
Chris@1090 43 m_windowIncrement(windowIncrement),
Chris@1090 44 m_fftSize(fftSize),
Chris@1091 45 m_windower(windowType, windowSize),
Chris@1093 46 m_fft(fftSize),
Chris@1093 47 m_cacheSize(3)
Chris@152 48 {
Chris@1091 49 if (m_windowSize > m_fftSize) {
Chris@1091 50 cerr << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize
Chris@1091 51 << ") must be at least FFT size (" << m_fftSize << ")" << endl;
Chris@1091 52 throw invalid_argument("FFTModel window size must be at least FFT size");
Chris@1091 53 }
Chris@1133 54
Chris@1133 55 connect(model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@1133 56 connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
Chris@1133 57 this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)));
Chris@152 58 }
Chris@152 59
Chris@152 60 FFTModel::~FFTModel()
Chris@152 61 {
Chris@152 62 }
Chris@152 63
Chris@360 64 void
Chris@360 65 FFTModel::sourceModelAboutToBeDeleted()
Chris@360 66 {
Chris@1090 67 if (m_model) {
Chris@1090 68 cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl;
Chris@1090 69 m_model = 0;
Chris@360 70 }
Chris@360 71 }
Chris@360 72
Chris@1091 73 int
Chris@1091 74 FFTModel::getWidth() const
Chris@1091 75 {
Chris@1091 76 if (!m_model) return 0;
Chris@1091 77 return int((m_model->getEndFrame() - m_model->getStartFrame())
Chris@1091 78 / m_windowIncrement) + 1;
Chris@1091 79 }
Chris@1091 80
Chris@1091 81 int
Chris@1091 82 FFTModel::getHeight() const
Chris@1091 83 {
Chris@1091 84 return m_fftSize / 2 + 1;
Chris@1091 85 }
Chris@1091 86
Chris@152 87 QString
Chris@929 88 FFTModel::getBinName(int n) const
Chris@152 89 {
Chris@1040 90 sv_samplerate_t sr = getSampleRate();
Chris@152 91 if (!sr) return "";
Chris@204 92 QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2));
Chris@152 93 return name;
Chris@152 94 }
Chris@152 95
Chris@1091 96 FFTModel::Column
Chris@1091 97 FFTModel::getColumn(int x) const
Chris@1091 98 {
Chris@1091 99 auto cplx = getFFTColumn(x);
Chris@1091 100 Column col;
Chris@1154 101 col.reserve(cplx.size());
Chris@1091 102 for (auto c: cplx) col.push_back(abs(c));
Chris@1154 103 return move(col);
Chris@1091 104 }
Chris@1091 105
Chris@1091 106 float
Chris@1091 107 FFTModel::getMagnitudeAt(int x, int y) const
Chris@1091 108 {
Chris@1093 109 if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) return 0.f;
Chris@1093 110 auto col = getFFTColumn(x);
Chris@1093 111 return abs(col[y]);
Chris@1091 112 }
Chris@1091 113
Chris@1091 114 float
Chris@1091 115 FFTModel::getMaximumMagnitudeAt(int x) const
Chris@1091 116 {
Chris@1091 117 Column col(getColumn(x));
Chris@1092 118 float max = 0.f;
Chris@1154 119 int n = int(col.size());
Chris@1154 120 for (int i = 0; i < n; ++i) {
Chris@1092 121 if (col[i] > max) max = col[i];
Chris@1092 122 }
Chris@1092 123 return max;
Chris@1091 124 }
Chris@1091 125
Chris@1091 126 float
Chris@1091 127 FFTModel::getPhaseAt(int x, int y) const
Chris@1091 128 {
Chris@1093 129 if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) return 0.f;
Chris@1091 130 return arg(getFFTColumn(x)[y]);
Chris@1091 131 }
Chris@1091 132
Chris@1091 133 void
Chris@1091 134 FFTModel::getValuesAt(int x, int y, float &re, float &im) const
Chris@1091 135 {
Chris@1091 136 auto col = getFFTColumn(x);
Chris@1091 137 re = col[y].real();
Chris@1091 138 im = col[y].imag();
Chris@1091 139 }
Chris@1091 140
Chris@1091 141 bool
Chris@1091 142 FFTModel::getMagnitudesAt(int x, float *values, int minbin, int count) const
Chris@1091 143 {
Chris@1091 144 if (count == 0) count = getHeight();
Chris@1091 145 auto col = getFFTColumn(x);
Chris@1091 146 for (int i = 0; i < count; ++i) {
Chris@1091 147 values[i] = abs(col[minbin + i]);
Chris@1091 148 }
Chris@1091 149 return true;
Chris@1091 150 }
Chris@1091 151
Chris@1091 152 bool
Chris@1091 153 FFTModel::getPhasesAt(int x, float *values, int minbin, int count) const
Chris@1091 154 {
Chris@1091 155 if (count == 0) count = getHeight();
Chris@1091 156 auto col = getFFTColumn(x);
Chris@1091 157 for (int i = 0; i < count; ++i) {
Chris@1091 158 values[i] = arg(col[minbin + i]);
Chris@1091 159 }
Chris@1091 160 return true;
Chris@1091 161 }
Chris@1091 162
Chris@1091 163 bool
Chris@1091 164 FFTModel::getValuesAt(int x, float *reals, float *imags, int minbin, int count) const
Chris@1091 165 {
Chris@1091 166 if (count == 0) count = getHeight();
Chris@1091 167 auto col = getFFTColumn(x);
Chris@1091 168 for (int i = 0; i < count; ++i) {
Chris@1091 169 reals[i] = col[minbin + i].real();
Chris@1091 170 }
Chris@1091 171 for (int i = 0; i < count; ++i) {
Chris@1091 172 imags[i] = col[minbin + i].imag();
Chris@1091 173 }
Chris@1091 174 return true;
Chris@1091 175 }
Chris@1091 176
Chris@1091 177 vector<float>
Chris@1091 178 FFTModel::getSourceSamples(int column) const
Chris@1091 179 {
Chris@1094 180 // m_fftSize may be greater than m_windowSize, but not the reverse
Chris@1094 181
Chris@1094 182 // cerr << "getSourceSamples(" << column << ")" << endl;
Chris@1094 183
Chris@1091 184 auto range = getSourceSampleRange(column);
Chris@1094 185 auto data = getSourceData(range);
Chris@1094 186
Chris@1091 187 int off = (m_fftSize - m_windowSize) / 2;
Chris@1094 188
Chris@1094 189 if (off == 0) {
Chris@1094 190 return data;
Chris@1094 191 } else {
Chris@1094 192 vector<float> pad(off, 0.f);
Chris@1094 193 vector<float> padded;
Chris@1094 194 padded.reserve(m_fftSize);
Chris@1094 195 padded.insert(padded.end(), pad.begin(), pad.end());
Chris@1094 196 padded.insert(padded.end(), data.begin(), data.end());
Chris@1094 197 padded.insert(padded.end(), pad.begin(), pad.end());
Chris@1094 198 return padded;
Chris@1094 199 }
Chris@1094 200 }
Chris@1094 201
Chris@1094 202 vector<float>
Chris@1094 203 FFTModel::getSourceData(pair<sv_frame_t, sv_frame_t> range) const
Chris@1094 204 {
Chris@1094 205 // cerr << "getSourceData(" << range.first << "," << range.second
Chris@1094 206 // << "): saved range is (" << m_savedData.range.first
Chris@1094 207 // << "," << m_savedData.range.second << ")" << endl;
Chris@1094 208
Chris@1100 209 if (m_savedData.range == range) {
Chris@1100 210 return m_savedData.data;
Chris@1100 211 }
Chris@1094 212
Chris@1094 213 if (range.first < m_savedData.range.second &&
Chris@1094 214 range.first >= m_savedData.range.first &&
Chris@1094 215 range.second > m_savedData.range.second) {
Chris@1094 216
Chris@1100 217 sv_frame_t discard = range.first - m_savedData.range.first;
Chris@1100 218
Chris@1100 219 vector<float> acc(m_savedData.data.begin() + discard,
Chris@1100 220 m_savedData.data.end());
Chris@1094 221
Chris@1095 222 vector<float> rest =
Chris@1095 223 getSourceDataUncached({ m_savedData.range.second, range.second });
Chris@1100 224
Chris@1100 225 acc.insert(acc.end(), rest.begin(), rest.end());
Chris@1094 226
Chris@1095 227 m_savedData = { range, acc };
Chris@1095 228 return acc;
Chris@1095 229
Chris@1095 230 } else {
Chris@1095 231
Chris@1095 232 auto data = getSourceDataUncached(range);
Chris@1095 233 m_savedData = { range, data };
Chris@1095 234 return data;
Chris@1094 235 }
Chris@1095 236 }
Chris@1094 237
Chris@1095 238 vector<float>
Chris@1095 239 FFTModel::getSourceDataUncached(pair<sv_frame_t, sv_frame_t> range) const
Chris@1095 240 {
Chris@1091 241 decltype(range.first) pfx = 0;
Chris@1091 242 if (range.first < 0) {
Chris@1091 243 pfx = -range.first;
Chris@1091 244 range = { 0, range.second };
Chris@1091 245 }
Chris@1096 246
Chris@1096 247 auto data = m_model->getData(m_channel,
Chris@1096 248 range.first,
Chris@1096 249 range.second - range.first);
Chris@1096 250
Chris@1096 251 // don't return a partial frame
Chris@1096 252 data.resize(range.second - range.first, 0.f);
Chris@1096 253
Chris@1096 254 if (pfx > 0) {
Chris@1096 255 vector<float> pad(pfx, 0.f);
Chris@1096 256 data.insert(data.begin(), pad.begin(), pad.end());
Chris@1096 257 }
Chris@1096 258
Chris@1091 259 if (m_channel == -1) {
Chris@1091 260 int channels = m_model->getChannelCount();
Chris@1091 261 if (channels > 1) {
Chris@1096 262 int n = int(data.size());
Chris@1096 263 float factor = 1.f / float(channels);
Chris@1100 264 // use mean instead of sum for fft model input
Chris@1096 265 for (int i = 0; i < n; ++i) {
Chris@1096 266 data[i] *= factor;
Chris@1091 267 }
Chris@1091 268 }
Chris@1091 269 }
Chris@1094 270
Chris@1094 271 return data;
Chris@1091 272 }
Chris@1091 273
Chris@1091 274 vector<complex<float>>
Chris@1093 275 FFTModel::getFFTColumn(int n) const
Chris@1091 276 {
Chris@1093 277 for (auto &incache : m_cached) {
Chris@1093 278 if (incache.n == n) {
Chris@1093 279 return incache.col;
Chris@1093 280 }
Chris@1093 281 }
Chris@1093 282
Chris@1093 283 auto samples = getSourceSamples(n);
Chris@1100 284 m_windower.cut(samples.data());
Chris@1093 285 auto col = m_fft.process(samples);
Chris@1093 286
Chris@1093 287 SavedColumn sc { n, col };
Chris@1093 288 if (m_cached.size() >= m_cacheSize) {
Chris@1093 289 m_cached.pop_front();
Chris@1093 290 }
Chris@1093 291 m_cached.push_back(sc);
Chris@1093 292
Chris@1154 293 return move(col);
Chris@1091 294 }
Chris@1091 295
Chris@275 296 bool
Chris@1045 297 FFTModel::estimateStableFrequency(int x, int y, double &frequency)
Chris@275 298 {
Chris@275 299 if (!isOK()) return false;
Chris@275 300
Chris@1090 301 frequency = double(y * getSampleRate()) / m_fftSize;
Chris@275 302
Chris@275 303 if (x+1 >= getWidth()) return false;
Chris@275 304
Chris@275 305 // At frequency f, a phase shift of 2pi (one cycle) happens in 1/f sec.
Chris@275 306 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
Chris@275 307 // At window size w, for bin b, f is b*sr/w.
Chris@275 308 // thus 2pi phase shift happens in w/(b*sr) sec.
Chris@275 309 // We need to know what phase shift we expect from h/sr sec.
Chris@275 310 // -> 2pi * ((h/sr) / (w/(b*sr)))
Chris@275 311 // = 2pi * ((h * b * sr) / (w * sr))
Chris@275 312 // = 2pi * (h * b) / w.
Chris@275 313
Chris@1038 314 double oldPhase = getPhaseAt(x, y);
Chris@1038 315 double newPhase = getPhaseAt(x+1, y);
Chris@275 316
Chris@929 317 int incr = getResolution();
Chris@275 318
Chris@1090 319 double expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / m_fftSize;
Chris@275 320
Chris@1038 321 double phaseError = princarg(newPhase - expectedPhase);
Chris@275 322
Chris@275 323 // The new frequency estimate based on the phase error resulting
Chris@275 324 // from assuming the "native" frequency of this bin
Chris@275 325
Chris@275 326 frequency =
Chris@1090 327 (getSampleRate() * (expectedPhase + phaseError - oldPhase)) /
Chris@1045 328 (2.0 * M_PI * incr);
Chris@275 329
Chris@275 330 return true;
Chris@275 331 }
Chris@275 332
Chris@275 333 FFTModel::PeakLocationSet
Chris@1191 334 FFTModel::getPeaks(PeakPickType type, int x, int ymin, int ymax) const
Chris@275 335 {
Chris@551 336 Profiler profiler("FFTModel::getPeaks");
Chris@551 337
Chris@275 338 FFTModel::PeakLocationSet peaks;
Chris@275 339 if (!isOK()) return peaks;
Chris@275 340
Chris@275 341 if (ymax == 0 || ymax > getHeight() - 1) {
Chris@275 342 ymax = getHeight() - 1;
Chris@275 343 }
Chris@275 344
Chris@275 345 if (type == AllPeaks) {
Chris@551 346 int minbin = ymin;
Chris@551 347 if (minbin > 0) minbin = minbin - 1;
Chris@551 348 int maxbin = ymax;
Chris@551 349 if (maxbin < getHeight() - 1) maxbin = maxbin + 1;
Chris@551 350 const int n = maxbin - minbin + 1;
Chris@608 351 #ifdef __GNUC__
Chris@551 352 float values[n];
Chris@608 353 #else
Chris@608 354 float *values = (float *)alloca(n * sizeof(float));
Chris@608 355 #endif
Chris@551 356 getMagnitudesAt(x, values, minbin, maxbin - minbin + 1);
Chris@929 357 for (int bin = ymin; bin <= ymax; ++bin) {
Chris@551 358 if (bin == minbin || bin == maxbin) continue;
Chris@551 359 if (values[bin - minbin] > values[bin - minbin - 1] &&
Chris@551 360 values[bin - minbin] > values[bin - minbin + 1]) {
Chris@275 361 peaks.insert(bin);
Chris@275 362 }
Chris@275 363 }
Chris@275 364 return peaks;
Chris@275 365 }
Chris@275 366
Chris@551 367 Column values = getColumn(x);
Chris@1154 368 int nv = int(values.size());
Chris@275 369
Chris@500 370 float mean = 0.f;
Chris@1154 371 for (int i = 0; i < nv; ++i) mean += values[i];
Chris@1154 372 if (nv > 0) mean = mean / float(values.size());
Chris@1038 373
Chris@275 374 // For peak picking we use a moving median window, picking the
Chris@275 375 // highest value within each continuous region of values that
Chris@275 376 // exceed the median. For pitch adaptivity, we adjust the window
Chris@275 377 // size to a roughly constant pitch range (about four tones).
Chris@275 378
Chris@1040 379 sv_samplerate_t sampleRate = getSampleRate();
Chris@275 380
Chris@1090 381 deque<float> window;
Chris@1090 382 vector<int> inrange;
Chris@280 383 float dist = 0.5;
Chris@500 384
Chris@929 385 int medianWinSize = getPeakPickWindowSize(type, sampleRate, ymin, dist);
Chris@929 386 int halfWin = medianWinSize/2;
Chris@275 387
Chris@929 388 int binmin;
Chris@275 389 if (ymin > halfWin) binmin = ymin - halfWin;
Chris@275 390 else binmin = 0;
Chris@275 391
Chris@929 392 int binmax;
Chris@1154 393 if (ymax + halfWin < nv) binmax = ymax + halfWin;
Chris@1154 394 else binmax = nv - 1;
Chris@275 395
Chris@929 396 int prevcentre = 0;
Chris@500 397
Chris@929 398 for (int bin = binmin; bin <= binmax; ++bin) {
Chris@275 399
Chris@275 400 float value = values[bin];
Chris@275 401
Chris@275 402 window.push_back(value);
Chris@275 403
Chris@280 404 // so-called median will actually be the dist*100'th percentile
Chris@280 405 medianWinSize = getPeakPickWindowSize(type, sampleRate, bin, dist);
Chris@275 406 halfWin = medianWinSize/2;
Chris@275 407
Chris@929 408 while ((int)window.size() > medianWinSize) {
Chris@500 409 window.pop_front();
Chris@500 410 }
Chris@500 411
Chris@1038 412 int actualSize = int(window.size());
Chris@275 413
Chris@275 414 if (type == MajorPitchAdaptivePeaks) {
Chris@1154 415 if (ymax + halfWin < nv) binmax = ymax + halfWin;
Chris@1154 416 else binmax = nv - 1;
Chris@275 417 }
Chris@275 418
Chris@1090 419 deque<float> sorted(window);
Chris@1090 420 sort(sorted.begin(), sorted.end());
Chris@1038 421 float median = sorted[int(float(sorted.size()) * dist)];
Chris@275 422
Chris@929 423 int centrebin = 0;
Chris@500 424 if (bin > actualSize/2) centrebin = bin - actualSize/2;
Chris@500 425
Chris@500 426 while (centrebin > prevcentre || bin == binmin) {
Chris@275 427
Chris@500 428 if (centrebin > prevcentre) ++prevcentre;
Chris@500 429
Chris@500 430 float centre = values[prevcentre];
Chris@500 431
Chris@500 432 if (centre > median) {
Chris@500 433 inrange.push_back(centrebin);
Chris@500 434 }
Chris@500 435
Chris@1154 436 if (centre <= median || centrebin+1 == nv) {
Chris@500 437 if (!inrange.empty()) {
Chris@929 438 int peakbin = 0;
Chris@500 439 float peakval = 0.f;
Chris@929 440 for (int i = 0; i < (int)inrange.size(); ++i) {
Chris@500 441 if (i == 0 || values[inrange[i]] > peakval) {
Chris@500 442 peakval = values[inrange[i]];
Chris@500 443 peakbin = inrange[i];
Chris@500 444 }
Chris@500 445 }
Chris@500 446 inrange.clear();
Chris@500 447 if (peakbin >= ymin && peakbin <= ymax) {
Chris@500 448 peaks.insert(peakbin);
Chris@275 449 }
Chris@275 450 }
Chris@275 451 }
Chris@500 452
Chris@500 453 if (bin == binmin) break;
Chris@275 454 }
Chris@275 455 }
Chris@275 456
Chris@275 457 return peaks;
Chris@275 458 }
Chris@275 459
Chris@929 460 int
Chris@1040 461 FFTModel::getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate,
Chris@929 462 int bin, float &percentile) const
Chris@275 463 {
Chris@280 464 percentile = 0.5;
Chris@275 465 if (type == MajorPeaks) return 10;
Chris@275 466 if (bin == 0) return 3;
Chris@280 467
Chris@1091 468 double binfreq = (sampleRate * bin) / m_fftSize;
Chris@1038 469 double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq);
Chris@280 470
Chris@1091 471 int hibin = int(lrint((hifreq * m_fftSize) / sampleRate));
Chris@275 472 int medianWinSize = hibin - bin;
Chris@275 473 if (medianWinSize < 3) medianWinSize = 3;
Chris@280 474
Chris@1091 475 percentile = 0.5f + float(binfreq / sampleRate);
Chris@280 476
Chris@275 477 return medianWinSize;
Chris@275 478 }
Chris@275 479
Chris@275 480 FFTModel::PeakSet
Chris@929 481 FFTModel::getPeakFrequencies(PeakPickType type, int x,
Chris@1191 482 int ymin, int ymax) const
Chris@275 483 {
Chris@551 484 Profiler profiler("FFTModel::getPeakFrequencies");
Chris@551 485
Chris@275 486 PeakSet peaks;
Chris@275 487 if (!isOK()) return peaks;
Chris@275 488 PeakLocationSet locations = getPeaks(type, x, ymin, ymax);
Chris@275 489
Chris@1040 490 sv_samplerate_t sampleRate = getSampleRate();
Chris@929 491 int incr = getResolution();
Chris@275 492
Chris@275 493 // This duplicates some of the work of estimateStableFrequency to
Chris@275 494 // allow us to retrieve the phases in two separate vertical
Chris@275 495 // columns, instead of jumping back and forth between columns x and
Chris@275 496 // x+1, which may be significantly slower if re-seeking is needed
Chris@275 497
Chris@1090 498 vector<float> phases;
Chris@275 499 for (PeakLocationSet::iterator i = locations.begin();
Chris@275 500 i != locations.end(); ++i) {
Chris@275 501 phases.push_back(getPhaseAt(x, *i));
Chris@275 502 }
Chris@275 503
Chris@929 504 int phaseIndex = 0;
Chris@275 505 for (PeakLocationSet::iterator i = locations.begin();
Chris@275 506 i != locations.end(); ++i) {
Chris@1038 507 double oldPhase = phases[phaseIndex];
Chris@1038 508 double newPhase = getPhaseAt(x+1, *i);
Chris@1090 509 double expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / m_fftSize;
Chris@1038 510 double phaseError = princarg(newPhase - expectedPhase);
Chris@1038 511 double frequency =
Chris@275 512 (sampleRate * (expectedPhase + phaseError - oldPhase))
Chris@275 513 / (2 * M_PI * incr);
Chris@1045 514 peaks[*i] = frequency;
Chris@275 515 ++phaseIndex;
Chris@275 516 }
Chris@275 517
Chris@275 518 return peaks;
Chris@275 519 }
Chris@275 520