annotate data/model/FFTModel.cpp @ 1211:5a1198083d9a piper

Pull out model creation into the transformer thread run(), so that all communications with the plugin server happen on a single thread. Then make the model accessor wait for them to be created (which still happens right at the start of processing) before returning.
author Chris Cannam
date Mon, 17 Oct 2016 14:18:23 +0100
parents 6f7a440b6218
children 6b847a59d908
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@1200 106 FFTModel::Column
Chris@1200 107 FFTModel::getPhases(int x) const
Chris@1200 108 {
Chris@1200 109 auto cplx = getFFTColumn(x);
Chris@1200 110 Column col;
Chris@1200 111 col.reserve(cplx.size());
Chris@1201 112 for (auto c: cplx) {
Chris@1201 113 col.push_back(arg(c));
Chris@1201 114 }
Chris@1200 115 return move(col);
Chris@1200 116 }
Chris@1200 117
Chris@1091 118 float
Chris@1091 119 FFTModel::getMagnitudeAt(int x, int y) const
Chris@1091 120 {
Chris@1093 121 if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) return 0.f;
Chris@1093 122 auto col = getFFTColumn(x);
Chris@1093 123 return abs(col[y]);
Chris@1091 124 }
Chris@1091 125
Chris@1091 126 float
Chris@1091 127 FFTModel::getMaximumMagnitudeAt(int x) const
Chris@1091 128 {
Chris@1091 129 Column col(getColumn(x));
Chris@1092 130 float max = 0.f;
Chris@1154 131 int n = int(col.size());
Chris@1154 132 for (int i = 0; i < n; ++i) {
Chris@1092 133 if (col[i] > max) max = col[i];
Chris@1092 134 }
Chris@1092 135 return max;
Chris@1091 136 }
Chris@1091 137
Chris@1091 138 float
Chris@1091 139 FFTModel::getPhaseAt(int x, int y) const
Chris@1091 140 {
Chris@1093 141 if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) return 0.f;
Chris@1091 142 return arg(getFFTColumn(x)[y]);
Chris@1091 143 }
Chris@1091 144
Chris@1091 145 void
Chris@1091 146 FFTModel::getValuesAt(int x, int y, float &re, float &im) const
Chris@1091 147 {
Chris@1091 148 auto col = getFFTColumn(x);
Chris@1091 149 re = col[y].real();
Chris@1091 150 im = col[y].imag();
Chris@1091 151 }
Chris@1091 152
Chris@1091 153 bool
Chris@1091 154 FFTModel::getMagnitudesAt(int x, float *values, int minbin, int count) const
Chris@1091 155 {
Chris@1091 156 if (count == 0) count = getHeight();
Chris@1091 157 auto col = getFFTColumn(x);
Chris@1091 158 for (int i = 0; i < count; ++i) {
Chris@1091 159 values[i] = abs(col[minbin + i]);
Chris@1091 160 }
Chris@1091 161 return true;
Chris@1091 162 }
Chris@1091 163
Chris@1091 164 bool
Chris@1091 165 FFTModel::getPhasesAt(int x, float *values, int minbin, int count) const
Chris@1091 166 {
Chris@1091 167 if (count == 0) count = getHeight();
Chris@1091 168 auto col = getFFTColumn(x);
Chris@1091 169 for (int i = 0; i < count; ++i) {
Chris@1091 170 values[i] = arg(col[minbin + i]);
Chris@1091 171 }
Chris@1091 172 return true;
Chris@1091 173 }
Chris@1091 174
Chris@1091 175 bool
Chris@1091 176 FFTModel::getValuesAt(int x, float *reals, float *imags, int minbin, int count) const
Chris@1091 177 {
Chris@1091 178 if (count == 0) count = getHeight();
Chris@1091 179 auto col = getFFTColumn(x);
Chris@1091 180 for (int i = 0; i < count; ++i) {
Chris@1091 181 reals[i] = col[minbin + i].real();
Chris@1091 182 }
Chris@1091 183 for (int i = 0; i < count; ++i) {
Chris@1091 184 imags[i] = col[minbin + i].imag();
Chris@1091 185 }
Chris@1091 186 return true;
Chris@1091 187 }
Chris@1091 188
Chris@1091 189 vector<float>
Chris@1091 190 FFTModel::getSourceSamples(int column) const
Chris@1091 191 {
Chris@1094 192 // m_fftSize may be greater than m_windowSize, but not the reverse
Chris@1094 193
Chris@1094 194 // cerr << "getSourceSamples(" << column << ")" << endl;
Chris@1094 195
Chris@1091 196 auto range = getSourceSampleRange(column);
Chris@1094 197 auto data = getSourceData(range);
Chris@1094 198
Chris@1091 199 int off = (m_fftSize - m_windowSize) / 2;
Chris@1094 200
Chris@1094 201 if (off == 0) {
Chris@1094 202 return data;
Chris@1094 203 } else {
Chris@1094 204 vector<float> pad(off, 0.f);
Chris@1094 205 vector<float> padded;
Chris@1094 206 padded.reserve(m_fftSize);
Chris@1094 207 padded.insert(padded.end(), pad.begin(), pad.end());
Chris@1094 208 padded.insert(padded.end(), data.begin(), data.end());
Chris@1094 209 padded.insert(padded.end(), pad.begin(), pad.end());
Chris@1094 210 return padded;
Chris@1094 211 }
Chris@1094 212 }
Chris@1094 213
Chris@1094 214 vector<float>
Chris@1094 215 FFTModel::getSourceData(pair<sv_frame_t, sv_frame_t> range) const
Chris@1094 216 {
Chris@1094 217 // cerr << "getSourceData(" << range.first << "," << range.second
Chris@1094 218 // << "): saved range is (" << m_savedData.range.first
Chris@1094 219 // << "," << m_savedData.range.second << ")" << endl;
Chris@1094 220
Chris@1100 221 if (m_savedData.range == range) {
Chris@1100 222 return m_savedData.data;
Chris@1100 223 }
Chris@1094 224
Chris@1094 225 if (range.first < m_savedData.range.second &&
Chris@1094 226 range.first >= m_savedData.range.first &&
Chris@1094 227 range.second > m_savedData.range.second) {
Chris@1094 228
Chris@1100 229 sv_frame_t discard = range.first - m_savedData.range.first;
Chris@1100 230
Chris@1100 231 vector<float> acc(m_savedData.data.begin() + discard,
Chris@1100 232 m_savedData.data.end());
Chris@1094 233
Chris@1095 234 vector<float> rest =
Chris@1095 235 getSourceDataUncached({ m_savedData.range.second, range.second });
Chris@1100 236
Chris@1100 237 acc.insert(acc.end(), rest.begin(), rest.end());
Chris@1094 238
Chris@1095 239 m_savedData = { range, acc };
Chris@1095 240 return acc;
Chris@1095 241
Chris@1095 242 } else {
Chris@1095 243
Chris@1095 244 auto data = getSourceDataUncached(range);
Chris@1095 245 m_savedData = { range, data };
Chris@1095 246 return data;
Chris@1094 247 }
Chris@1095 248 }
Chris@1094 249
Chris@1095 250 vector<float>
Chris@1095 251 FFTModel::getSourceDataUncached(pair<sv_frame_t, sv_frame_t> range) const
Chris@1095 252 {
Chris@1091 253 decltype(range.first) pfx = 0;
Chris@1091 254 if (range.first < 0) {
Chris@1091 255 pfx = -range.first;
Chris@1091 256 range = { 0, range.second };
Chris@1091 257 }
Chris@1096 258
Chris@1096 259 auto data = m_model->getData(m_channel,
Chris@1096 260 range.first,
Chris@1096 261 range.second - range.first);
Chris@1096 262
Chris@1096 263 // don't return a partial frame
Chris@1096 264 data.resize(range.second - range.first, 0.f);
Chris@1096 265
Chris@1096 266 if (pfx > 0) {
Chris@1096 267 vector<float> pad(pfx, 0.f);
Chris@1096 268 data.insert(data.begin(), pad.begin(), pad.end());
Chris@1096 269 }
Chris@1096 270
Chris@1091 271 if (m_channel == -1) {
Chris@1091 272 int channels = m_model->getChannelCount();
Chris@1091 273 if (channels > 1) {
Chris@1096 274 int n = int(data.size());
Chris@1096 275 float factor = 1.f / float(channels);
Chris@1100 276 // use mean instead of sum for fft model input
Chris@1096 277 for (int i = 0; i < n; ++i) {
Chris@1096 278 data[i] *= factor;
Chris@1091 279 }
Chris@1091 280 }
Chris@1091 281 }
Chris@1094 282
Chris@1094 283 return data;
Chris@1091 284 }
Chris@1091 285
Chris@1091 286 vector<complex<float>>
Chris@1093 287 FFTModel::getFFTColumn(int n) const
Chris@1091 288 {
Chris@1093 289 for (auto &incache : m_cached) {
Chris@1093 290 if (incache.n == n) {
Chris@1093 291 return incache.col;
Chris@1093 292 }
Chris@1093 293 }
Chris@1093 294
Chris@1093 295 auto samples = getSourceSamples(n);
Chris@1100 296 m_windower.cut(samples.data());
Chris@1093 297 auto col = m_fft.process(samples);
Chris@1093 298
Chris@1093 299 SavedColumn sc { n, col };
Chris@1093 300 if (m_cached.size() >= m_cacheSize) {
Chris@1093 301 m_cached.pop_front();
Chris@1093 302 }
Chris@1093 303 m_cached.push_back(sc);
Chris@1093 304
Chris@1154 305 return move(col);
Chris@1091 306 }
Chris@1091 307
Chris@275 308 bool
Chris@1045 309 FFTModel::estimateStableFrequency(int x, int y, double &frequency)
Chris@275 310 {
Chris@275 311 if (!isOK()) return false;
Chris@275 312
Chris@1090 313 frequency = double(y * getSampleRate()) / m_fftSize;
Chris@275 314
Chris@275 315 if (x+1 >= getWidth()) return false;
Chris@275 316
Chris@275 317 // At frequency f, a phase shift of 2pi (one cycle) happens in 1/f sec.
Chris@275 318 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
Chris@275 319 // At window size w, for bin b, f is b*sr/w.
Chris@275 320 // thus 2pi phase shift happens in w/(b*sr) sec.
Chris@275 321 // We need to know what phase shift we expect from h/sr sec.
Chris@275 322 // -> 2pi * ((h/sr) / (w/(b*sr)))
Chris@275 323 // = 2pi * ((h * b * sr) / (w * sr))
Chris@275 324 // = 2pi * (h * b) / w.
Chris@275 325
Chris@1038 326 double oldPhase = getPhaseAt(x, y);
Chris@1038 327 double newPhase = getPhaseAt(x+1, y);
Chris@275 328
Chris@929 329 int incr = getResolution();
Chris@275 330
Chris@1090 331 double expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / m_fftSize;
Chris@275 332
Chris@1038 333 double phaseError = princarg(newPhase - expectedPhase);
Chris@275 334
Chris@275 335 // The new frequency estimate based on the phase error resulting
Chris@275 336 // from assuming the "native" frequency of this bin
Chris@275 337
Chris@275 338 frequency =
Chris@1090 339 (getSampleRate() * (expectedPhase + phaseError - oldPhase)) /
Chris@1045 340 (2.0 * M_PI * incr);
Chris@275 341
Chris@275 342 return true;
Chris@275 343 }
Chris@275 344
Chris@275 345 FFTModel::PeakLocationSet
Chris@1191 346 FFTModel::getPeaks(PeakPickType type, int x, int ymin, int ymax) const
Chris@275 347 {
Chris@551 348 Profiler profiler("FFTModel::getPeaks");
Chris@551 349
Chris@275 350 FFTModel::PeakLocationSet peaks;
Chris@275 351 if (!isOK()) return peaks;
Chris@275 352
Chris@275 353 if (ymax == 0 || ymax > getHeight() - 1) {
Chris@275 354 ymax = getHeight() - 1;
Chris@275 355 }
Chris@275 356
Chris@275 357 if (type == AllPeaks) {
Chris@551 358 int minbin = ymin;
Chris@551 359 if (minbin > 0) minbin = minbin - 1;
Chris@551 360 int maxbin = ymax;
Chris@551 361 if (maxbin < getHeight() - 1) maxbin = maxbin + 1;
Chris@551 362 const int n = maxbin - minbin + 1;
Chris@608 363 #ifdef __GNUC__
Chris@551 364 float values[n];
Chris@608 365 #else
Chris@608 366 float *values = (float *)alloca(n * sizeof(float));
Chris@608 367 #endif
Chris@551 368 getMagnitudesAt(x, values, minbin, maxbin - minbin + 1);
Chris@929 369 for (int bin = ymin; bin <= ymax; ++bin) {
Chris@551 370 if (bin == minbin || bin == maxbin) continue;
Chris@551 371 if (values[bin - minbin] > values[bin - minbin - 1] &&
Chris@551 372 values[bin - minbin] > values[bin - minbin + 1]) {
Chris@275 373 peaks.insert(bin);
Chris@275 374 }
Chris@275 375 }
Chris@275 376 return peaks;
Chris@275 377 }
Chris@275 378
Chris@551 379 Column values = getColumn(x);
Chris@1154 380 int nv = int(values.size());
Chris@275 381
Chris@500 382 float mean = 0.f;
Chris@1154 383 for (int i = 0; i < nv; ++i) mean += values[i];
Chris@1154 384 if (nv > 0) mean = mean / float(values.size());
Chris@1038 385
Chris@275 386 // For peak picking we use a moving median window, picking the
Chris@275 387 // highest value within each continuous region of values that
Chris@275 388 // exceed the median. For pitch adaptivity, we adjust the window
Chris@275 389 // size to a roughly constant pitch range (about four tones).
Chris@275 390
Chris@1040 391 sv_samplerate_t sampleRate = getSampleRate();
Chris@275 392
Chris@1090 393 deque<float> window;
Chris@1090 394 vector<int> inrange;
Chris@280 395 float dist = 0.5;
Chris@500 396
Chris@929 397 int medianWinSize = getPeakPickWindowSize(type, sampleRate, ymin, dist);
Chris@929 398 int halfWin = medianWinSize/2;
Chris@275 399
Chris@929 400 int binmin;
Chris@275 401 if (ymin > halfWin) binmin = ymin - halfWin;
Chris@275 402 else binmin = 0;
Chris@275 403
Chris@929 404 int binmax;
Chris@1154 405 if (ymax + halfWin < nv) binmax = ymax + halfWin;
Chris@1154 406 else binmax = nv - 1;
Chris@275 407
Chris@929 408 int prevcentre = 0;
Chris@500 409
Chris@929 410 for (int bin = binmin; bin <= binmax; ++bin) {
Chris@275 411
Chris@275 412 float value = values[bin];
Chris@275 413
Chris@275 414 window.push_back(value);
Chris@275 415
Chris@280 416 // so-called median will actually be the dist*100'th percentile
Chris@280 417 medianWinSize = getPeakPickWindowSize(type, sampleRate, bin, dist);
Chris@275 418 halfWin = medianWinSize/2;
Chris@275 419
Chris@929 420 while ((int)window.size() > medianWinSize) {
Chris@500 421 window.pop_front();
Chris@500 422 }
Chris@500 423
Chris@1038 424 int actualSize = int(window.size());
Chris@275 425
Chris@275 426 if (type == MajorPitchAdaptivePeaks) {
Chris@1154 427 if (ymax + halfWin < nv) binmax = ymax + halfWin;
Chris@1154 428 else binmax = nv - 1;
Chris@275 429 }
Chris@275 430
Chris@1090 431 deque<float> sorted(window);
Chris@1090 432 sort(sorted.begin(), sorted.end());
Chris@1038 433 float median = sorted[int(float(sorted.size()) * dist)];
Chris@275 434
Chris@929 435 int centrebin = 0;
Chris@500 436 if (bin > actualSize/2) centrebin = bin - actualSize/2;
Chris@500 437
Chris@500 438 while (centrebin > prevcentre || bin == binmin) {
Chris@275 439
Chris@500 440 if (centrebin > prevcentre) ++prevcentre;
Chris@500 441
Chris@500 442 float centre = values[prevcentre];
Chris@500 443
Chris@500 444 if (centre > median) {
Chris@500 445 inrange.push_back(centrebin);
Chris@500 446 }
Chris@500 447
Chris@1154 448 if (centre <= median || centrebin+1 == nv) {
Chris@500 449 if (!inrange.empty()) {
Chris@929 450 int peakbin = 0;
Chris@500 451 float peakval = 0.f;
Chris@929 452 for (int i = 0; i < (int)inrange.size(); ++i) {
Chris@500 453 if (i == 0 || values[inrange[i]] > peakval) {
Chris@500 454 peakval = values[inrange[i]];
Chris@500 455 peakbin = inrange[i];
Chris@500 456 }
Chris@500 457 }
Chris@500 458 inrange.clear();
Chris@500 459 if (peakbin >= ymin && peakbin <= ymax) {
Chris@500 460 peaks.insert(peakbin);
Chris@275 461 }
Chris@275 462 }
Chris@275 463 }
Chris@500 464
Chris@500 465 if (bin == binmin) break;
Chris@275 466 }
Chris@275 467 }
Chris@275 468
Chris@275 469 return peaks;
Chris@275 470 }
Chris@275 471
Chris@929 472 int
Chris@1040 473 FFTModel::getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate,
Chris@929 474 int bin, float &percentile) const
Chris@275 475 {
Chris@280 476 percentile = 0.5;
Chris@275 477 if (type == MajorPeaks) return 10;
Chris@275 478 if (bin == 0) return 3;
Chris@280 479
Chris@1091 480 double binfreq = (sampleRate * bin) / m_fftSize;
Chris@1038 481 double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq);
Chris@280 482
Chris@1091 483 int hibin = int(lrint((hifreq * m_fftSize) / sampleRate));
Chris@275 484 int medianWinSize = hibin - bin;
Chris@275 485 if (medianWinSize < 3) medianWinSize = 3;
Chris@280 486
Chris@1091 487 percentile = 0.5f + float(binfreq / sampleRate);
Chris@280 488
Chris@275 489 return medianWinSize;
Chris@275 490 }
Chris@275 491
Chris@275 492 FFTModel::PeakSet
Chris@929 493 FFTModel::getPeakFrequencies(PeakPickType type, int x,
Chris@1191 494 int ymin, int ymax) const
Chris@275 495 {
Chris@551 496 Profiler profiler("FFTModel::getPeakFrequencies");
Chris@551 497
Chris@275 498 PeakSet peaks;
Chris@275 499 if (!isOK()) return peaks;
Chris@275 500 PeakLocationSet locations = getPeaks(type, x, ymin, ymax);
Chris@275 501
Chris@1040 502 sv_samplerate_t sampleRate = getSampleRate();
Chris@929 503 int incr = getResolution();
Chris@275 504
Chris@275 505 // This duplicates some of the work of estimateStableFrequency to
Chris@275 506 // allow us to retrieve the phases in two separate vertical
Chris@275 507 // columns, instead of jumping back and forth between columns x and
Chris@275 508 // x+1, which may be significantly slower if re-seeking is needed
Chris@275 509
Chris@1090 510 vector<float> phases;
Chris@275 511 for (PeakLocationSet::iterator i = locations.begin();
Chris@275 512 i != locations.end(); ++i) {
Chris@275 513 phases.push_back(getPhaseAt(x, *i));
Chris@275 514 }
Chris@275 515
Chris@929 516 int phaseIndex = 0;
Chris@275 517 for (PeakLocationSet::iterator i = locations.begin();
Chris@275 518 i != locations.end(); ++i) {
Chris@1038 519 double oldPhase = phases[phaseIndex];
Chris@1038 520 double newPhase = getPhaseAt(x+1, *i);
Chris@1090 521 double expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / m_fftSize;
Chris@1038 522 double phaseError = princarg(newPhase - expectedPhase);
Chris@1038 523 double frequency =
Chris@275 524 (sampleRate * (expectedPhase + phaseError - oldPhase))
Chris@275 525 / (2 * M_PI * incr);
Chris@1045 526 peaks[*i] = frequency;
Chris@275 527 ++phaseIndex;
Chris@275 528 }
Chris@275 529
Chris@275 530 return peaks;
Chris@275 531 }
Chris@275 532