annotate data/model/FFTModel.cpp @ 1228:a2091d148d7f project-file-rework

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