annotate CepstrumPitchTracker.cpp @ 16:d717911aca3c track

A first crack at turning frequencies into notes
author Chris Cannam
date Tue, 03 Jul 2012 21:10:56 +0100
parents bd7fb10646fc
children 088da53a2869
rev   line source
Chris@3 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@3 2 /*
Chris@3 3 Permission is hereby granted, free of charge, to any person
Chris@3 4 obtaining a copy of this software and associated documentation
Chris@3 5 files (the "Software"), to deal in the Software without
Chris@3 6 restriction, including without limitation the rights to use, copy,
Chris@3 7 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@3 8 of the Software, and to permit persons to whom the Software is
Chris@3 9 furnished to do so, subject to the following conditions:
Chris@3 10
Chris@3 11 The above copyright notice and this permission notice shall be
Chris@3 12 included in all copies or substantial portions of the Software.
Chris@3 13
Chris@3 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@3 15 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@3 16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@3 17 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
Chris@3 18 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@3 19 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@3 20 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@3 21 */
Chris@3 22
Chris@3 23 #include "CepstrumPitchTracker.h"
Chris@3 24
Chris@3 25 #include <vector>
Chris@3 26 #include <algorithm>
Chris@3 27
Chris@3 28 #include <cstdio>
Chris@3 29 #include <cmath>
Chris@3 30 #include <complex>
Chris@3 31
Chris@3 32 using std::string;
Chris@7 33 using std::vector;
Chris@16 34 using Vamp::RealTime;
Chris@7 35
Chris@8 36 CepstrumPitchTracker::Hypothesis::Hypothesis()
Chris@7 37 {
Chris@8 38 m_state = New;
Chris@7 39 m_age = 0;
Chris@7 40 }
Chris@7 41
Chris@11 42 CepstrumPitchTracker::Hypothesis::~Hypothesis()
Chris@11 43 {
Chris@11 44 }
Chris@11 45
Chris@7 46 bool
Chris@7 47 CepstrumPitchTracker::Hypothesis::isWithinTolerance(Estimate s)
Chris@7 48 {
Chris@7 49 if (m_pending.empty()) {
Chris@7 50 return true;
Chris@7 51 }
Chris@16 52
Chris@16 53 Estimate first = m_pending[0];
Chris@7 54 Estimate last = m_pending[m_pending.size()-1];
Chris@16 55
Chris@16 56 // check we are within a relatively close tolerance of the last
Chris@16 57 // candidate
Chris@7 58 double r = s.freq / last.freq;
Chris@7 59 int cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@16 60 if (cents < -40 || cents > 40) return false;
Chris@16 61
Chris@16 62 // and within a wider tolerance of our starting candidate
Chris@16 63 r = s.freq / first.freq;
Chris@16 64 cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@16 65 if (cents < -80 || cents > 80) return false;
Chris@16 66
Chris@16 67 return true;
Chris@7 68 }
Chris@7 69
Chris@7 70 bool
Chris@7 71 CepstrumPitchTracker::Hypothesis::isSatisfied()
Chris@7 72 {
Chris@15 73 if (m_pending.empty()) return false;
Chris@15 74
Chris@15 75 double meanConfidence = 0.0;
Chris@15 76 for (int i = 0; i < m_pending.size(); ++i) {
Chris@15 77 meanConfidence += m_pending[i].confidence;
Chris@15 78 }
Chris@15 79 meanConfidence /= m_pending.size();
Chris@15 80
Chris@15 81 int lengthRequired = int(2.0 / meanConfidence + 0.5);
Chris@15 82 std::cerr << "meanConfidence = " << meanConfidence << ", lengthRequired = " << lengthRequired << ", length = " << m_pending.size() << std::endl;
Chris@15 83
Chris@15 84 return (m_pending.size() > lengthRequired);
Chris@7 85 }
Chris@7 86
Chris@8 87 void
Chris@8 88 CepstrumPitchTracker::Hypothesis::advanceTime()
Chris@8 89 {
Chris@8 90 ++m_age;
Chris@8 91 }
Chris@8 92
Chris@7 93 bool
Chris@7 94 CepstrumPitchTracker::Hypothesis::test(Estimate s)
Chris@7 95 {
Chris@8 96 bool accept = false;
Chris@8 97
Chris@8 98 switch (m_state) {
Chris@8 99
Chris@8 100 case New:
Chris@8 101 m_state = Provisional;
Chris@8 102 accept = true;
Chris@8 103 break;
Chris@8 104
Chris@8 105 case Provisional:
Chris@8 106 if (m_age > 3) {
Chris@8 107 m_state = Rejected;
Chris@8 108 } else if (isWithinTolerance(s)) {
Chris@8 109 accept = true;
Chris@8 110 }
Chris@8 111 break;
Chris@8 112
Chris@8 113 case Satisfied:
Chris@8 114 if (m_age > 3) {
Chris@8 115 m_state = Expired;
Chris@8 116 } else if (isWithinTolerance(s)) {
Chris@8 117 accept = true;
Chris@8 118 }
Chris@8 119 break;
Chris@8 120
Chris@8 121 case Rejected:
Chris@8 122 break;
Chris@8 123
Chris@8 124 case Expired:
Chris@8 125 break;
Chris@7 126 }
Chris@7 127
Chris@8 128 if (accept) {
Chris@8 129 m_pending.push_back(s);
Chris@8 130 m_age = 0;
Chris@8 131 if (m_state == Provisional && isSatisfied()) {
Chris@8 132 m_state = Satisfied;
Chris@7 133 }
Chris@7 134 }
Chris@7 135
Chris@14 136 return accept && (m_state == Satisfied);
Chris@8 137 }
Chris@7 138
Chris@7 139 CepstrumPitchTracker::Hypothesis::State
Chris@7 140 CepstrumPitchTracker::Hypothesis::getState()
Chris@7 141 {
Chris@7 142 return m_state;
Chris@7 143 }
Chris@7 144
Chris@12 145 int
Chris@12 146 CepstrumPitchTracker::Hypothesis::getPendingLength()
Chris@12 147 {
Chris@12 148 return m_pending.size();
Chris@12 149 }
Chris@12 150
Chris@7 151 CepstrumPitchTracker::Hypothesis::Estimates
Chris@7 152 CepstrumPitchTracker::Hypothesis::getAcceptedEstimates()
Chris@7 153 {
Chris@7 154 if (m_state == Satisfied || m_state == Expired) {
Chris@7 155 return m_pending;
Chris@7 156 } else {
Chris@7 157 return Estimates();
Chris@7 158 }
Chris@7 159 }
Chris@7 160
Chris@16 161 CepstrumPitchTracker::Hypothesis::Note
Chris@16 162 CepstrumPitchTracker::Hypothesis::getAveragedNote()
Chris@16 163 {
Chris@16 164 Note n;
Chris@16 165
Chris@16 166 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@16 167 n.freq = 0.0;
Chris@16 168 n.time = RealTime::zeroTime;
Chris@16 169 n.duration = RealTime::zeroTime;
Chris@16 170 return n;
Chris@16 171 }
Chris@16 172
Chris@16 173 n.time = m_pending.begin()->time;
Chris@16 174
Chris@16 175 Estimates::iterator i = m_pending.end();
Chris@16 176 --i;
Chris@16 177 n.duration = i->time - n.time;
Chris@16 178
Chris@16 179 // just mean frequency for now, but this isn't at all right
Chris@16 180 double acc = 0.0;
Chris@16 181 for (int i = 0; i < m_pending.size(); ++i) {
Chris@16 182 acc += m_pending[i].freq;
Chris@16 183 }
Chris@16 184 acc /= m_pending.size();
Chris@16 185 n.freq = acc;
Chris@16 186
Chris@16 187 return n;
Chris@16 188 }
Chris@16 189
Chris@11 190 void
Chris@16 191 CepstrumPitchTracker::Hypothesis::addFeatures(FeatureSet &fs)
Chris@11 192 {
Chris@11 193 for (int i = 0; i < m_pending.size(); ++i) {
Chris@11 194 Feature f;
Chris@11 195 f.hasTimestamp = true;
Chris@11 196 f.timestamp = m_pending[i].time;
Chris@11 197 f.values.push_back(m_pending[i].freq);
Chris@16 198 fs[0].push_back(f);
Chris@11 199 }
Chris@16 200
Chris@16 201 Feature nf;
Chris@16 202 nf.hasTimestamp = true;
Chris@16 203 nf.hasDuration = true;
Chris@16 204 Note n = getAveragedNote();
Chris@16 205 nf.timestamp = n.time;
Chris@16 206 nf.duration = n.duration;
Chris@16 207 nf.values.push_back(n.freq);
Chris@16 208 fs[1].push_back(nf);
Chris@11 209 }
Chris@3 210
Chris@3 211 CepstrumPitchTracker::CepstrumPitchTracker(float inputSampleRate) :
Chris@3 212 Plugin(inputSampleRate),
Chris@3 213 m_channels(0),
Chris@3 214 m_stepSize(256),
Chris@3 215 m_blockSize(1024),
Chris@3 216 m_fmin(50),
Chris@3 217 m_fmax(1000),
Chris@5 218 m_vflen(3),
Chris@3 219 m_binFrom(0),
Chris@3 220 m_binTo(0),
Chris@15 221 m_bins(0)
Chris@3 222 {
Chris@3 223 }
Chris@3 224
Chris@3 225 CepstrumPitchTracker::~CepstrumPitchTracker()
Chris@3 226 {
Chris@3 227 }
Chris@3 228
Chris@3 229 string
Chris@3 230 CepstrumPitchTracker::getIdentifier() const
Chris@3 231 {
Chris@3 232 return "cepstrum-pitch";
Chris@3 233 }
Chris@3 234
Chris@3 235 string
Chris@3 236 CepstrumPitchTracker::getName() const
Chris@3 237 {
Chris@3 238 return "Cepstrum Pitch Tracker";
Chris@3 239 }
Chris@3 240
Chris@3 241 string
Chris@3 242 CepstrumPitchTracker::getDescription() const
Chris@3 243 {
Chris@3 244 return "Estimate f0 of monophonic material using a cepstrum method.";
Chris@3 245 }
Chris@3 246
Chris@3 247 string
Chris@3 248 CepstrumPitchTracker::getMaker() const
Chris@3 249 {
Chris@3 250 return "Chris Cannam";
Chris@3 251 }
Chris@3 252
Chris@3 253 int
Chris@3 254 CepstrumPitchTracker::getPluginVersion() const
Chris@3 255 {
Chris@3 256 // Increment this each time you release a version that behaves
Chris@3 257 // differently from the previous one
Chris@3 258 return 1;
Chris@3 259 }
Chris@3 260
Chris@3 261 string
Chris@3 262 CepstrumPitchTracker::getCopyright() const
Chris@3 263 {
Chris@3 264 return "Freely redistributable (BSD license)";
Chris@3 265 }
Chris@3 266
Chris@3 267 CepstrumPitchTracker::InputDomain
Chris@3 268 CepstrumPitchTracker::getInputDomain() const
Chris@3 269 {
Chris@3 270 return FrequencyDomain;
Chris@3 271 }
Chris@3 272
Chris@3 273 size_t
Chris@3 274 CepstrumPitchTracker::getPreferredBlockSize() const
Chris@3 275 {
Chris@3 276 return 1024;
Chris@3 277 }
Chris@3 278
Chris@3 279 size_t
Chris@3 280 CepstrumPitchTracker::getPreferredStepSize() const
Chris@3 281 {
Chris@3 282 return 256;
Chris@3 283 }
Chris@3 284
Chris@3 285 size_t
Chris@3 286 CepstrumPitchTracker::getMinChannelCount() const
Chris@3 287 {
Chris@3 288 return 1;
Chris@3 289 }
Chris@3 290
Chris@3 291 size_t
Chris@3 292 CepstrumPitchTracker::getMaxChannelCount() const
Chris@3 293 {
Chris@3 294 return 1;
Chris@3 295 }
Chris@3 296
Chris@3 297 CepstrumPitchTracker::ParameterList
Chris@3 298 CepstrumPitchTracker::getParameterDescriptors() const
Chris@3 299 {
Chris@3 300 ParameterList list;
Chris@3 301 return list;
Chris@3 302 }
Chris@3 303
Chris@3 304 float
Chris@3 305 CepstrumPitchTracker::getParameter(string identifier) const
Chris@3 306 {
Chris@3 307 return 0.f;
Chris@3 308 }
Chris@3 309
Chris@3 310 void
Chris@3 311 CepstrumPitchTracker::setParameter(string identifier, float value)
Chris@3 312 {
Chris@3 313 }
Chris@3 314
Chris@3 315 CepstrumPitchTracker::ProgramList
Chris@3 316 CepstrumPitchTracker::getPrograms() const
Chris@3 317 {
Chris@3 318 ProgramList list;
Chris@3 319 return list;
Chris@3 320 }
Chris@3 321
Chris@3 322 string
Chris@3 323 CepstrumPitchTracker::getCurrentProgram() const
Chris@3 324 {
Chris@3 325 return ""; // no programs
Chris@3 326 }
Chris@3 327
Chris@3 328 void
Chris@3 329 CepstrumPitchTracker::selectProgram(string name)
Chris@3 330 {
Chris@3 331 }
Chris@3 332
Chris@3 333 CepstrumPitchTracker::OutputList
Chris@3 334 CepstrumPitchTracker::getOutputDescriptors() const
Chris@3 335 {
Chris@3 336 OutputList outputs;
Chris@3 337
Chris@3 338 int n = 0;
Chris@3 339
Chris@3 340 OutputDescriptor d;
Chris@3 341
Chris@3 342 d.identifier = "f0";
Chris@3 343 d.name = "Estimated f0";
Chris@3 344 d.description = "Estimated fundamental frequency";
Chris@3 345 d.unit = "Hz";
Chris@3 346 d.hasFixedBinCount = true;
Chris@3 347 d.binCount = 1;
Chris@3 348 d.hasKnownExtents = true;
Chris@3 349 d.minValue = m_fmin;
Chris@3 350 d.maxValue = m_fmax;
Chris@3 351 d.isQuantized = false;
Chris@3 352 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@3 353 d.sampleRate = (m_inputSampleRate / m_stepSize);
Chris@3 354 d.hasDuration = false;
Chris@3 355 outputs.push_back(d);
Chris@3 356
Chris@16 357 d.identifier = "notes";
Chris@16 358 d.name = "Notes";
Chris@16 359 d.description = "Derived fixed-pitch note frequencies";
Chris@16 360 d.unit = "Hz";
Chris@16 361 d.hasFixedBinCount = true;
Chris@16 362 d.binCount = 1;
Chris@16 363 d.hasKnownExtents = true;
Chris@16 364 d.minValue = m_fmin;
Chris@16 365 d.maxValue = m_fmax;
Chris@16 366 d.isQuantized = false;
Chris@16 367 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@16 368 d.sampleRate = (m_inputSampleRate / m_stepSize);
Chris@16 369 d.hasDuration = true;
Chris@16 370 outputs.push_back(d);
Chris@16 371
Chris@3 372 return outputs;
Chris@3 373 }
Chris@3 374
Chris@3 375 bool
Chris@3 376 CepstrumPitchTracker::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@3 377 {
Chris@3 378 if (channels < getMinChannelCount() ||
Chris@3 379 channels > getMaxChannelCount()) return false;
Chris@3 380
Chris@3 381 // std::cerr << "CepstrumPitchTracker::initialise: channels = " << channels
Chris@3 382 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
Chris@3 383 // << std::endl;
Chris@3 384
Chris@3 385 m_channels = channels;
Chris@3 386 m_stepSize = stepSize;
Chris@3 387 m_blockSize = blockSize;
Chris@3 388
Chris@3 389 m_binFrom = int(m_inputSampleRate / m_fmax);
Chris@3 390 m_binTo = int(m_inputSampleRate / m_fmin);
Chris@3 391
Chris@3 392 if (m_binTo >= (int)m_blockSize / 2) {
Chris@3 393 m_binTo = m_blockSize / 2 - 1;
Chris@3 394 }
Chris@3 395
Chris@3 396 m_bins = (m_binTo - m_binFrom) + 1;
Chris@3 397
Chris@3 398 reset();
Chris@3 399
Chris@3 400 return true;
Chris@3 401 }
Chris@3 402
Chris@3 403 void
Chris@3 404 CepstrumPitchTracker::reset()
Chris@3 405 {
Chris@3 406 }
Chris@3 407
Chris@3 408 void
Chris@15 409 CepstrumPitchTracker::filter(const double *cep, double *data)
Chris@3 410 {
Chris@3 411 for (int i = 0; i < m_bins; ++i) {
Chris@5 412 double v = 0;
Chris@5 413 int n = 0;
Chris@5 414 // average according to the vertical filter length
Chris@5 415 for (int j = -m_vflen/2; j <= m_vflen/2; ++j) {
Chris@5 416 int ix = i + m_binFrom + j;
Chris@5 417 if (ix >= 0 && ix < m_blockSize) {
Chris@5 418 v += cep[ix];
Chris@5 419 ++n;
Chris@5 420 }
Chris@5 421 }
Chris@15 422 data[i] = v / n;
Chris@3 423 }
Chris@6 424 }
Chris@6 425
Chris@3 426 CepstrumPitchTracker::FeatureSet
Chris@16 427 CepstrumPitchTracker::process(const float *const *inputBuffers, RealTime timestamp)
Chris@3 428 {
Chris@3 429 FeatureSet fs;
Chris@3 430
Chris@3 431 int bs = m_blockSize;
Chris@3 432 int hs = m_blockSize/2 + 1;
Chris@3 433
Chris@3 434 double *rawcep = new double[bs];
Chris@3 435 double *io = new double[bs];
Chris@3 436 double *logmag = new double[bs];
Chris@3 437
Chris@4 438 // The "inverse symmetric" method. Seems to be the most reliable
Chris@3 439
Chris@3 440 for (int i = 0; i < hs; ++i) {
Chris@3 441
Chris@3 442 double power =
Chris@3 443 inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
Chris@3 444 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1];
Chris@3 445 double mag = sqrt(power);
Chris@3 446
Chris@3 447 double lm = log(mag + 0.00000001);
Chris@3 448
Chris@4 449 logmag[i] = lm;
Chris@4 450 if (i > 0) logmag[bs - i] = lm;
Chris@3 451 }
Chris@3 452
Chris@4 453 fft(bs, true, logmag, 0, rawcep, io);
Chris@3 454
Chris@3 455 delete[] logmag;
Chris@3 456 delete[] io;
Chris@3 457
Chris@3 458 int n = m_bins;
Chris@3 459 double *data = new double[n];
Chris@3 460 filter(rawcep, data);
Chris@3 461 delete[] rawcep;
Chris@3 462
Chris@6 463 double abstot = 0.0;
Chris@6 464
Chris@6 465 for (int i = 0; i < n; ++i) {
Chris@6 466 abstot += fabs(data[i]);
Chris@6 467 }
Chris@6 468
Chris@3 469 double maxval = 0.0;
Chris@6 470 int maxbin = -1;
Chris@3 471
Chris@3 472 for (int i = 0; i < n; ++i) {
Chris@3 473 if (data[i] > maxval) {
Chris@3 474 maxval = data[i];
Chris@3 475 maxbin = i;
Chris@3 476 }
Chris@3 477 }
Chris@3 478
Chris@15 479 if (maxbin < 0) {
Chris@15 480 delete[] data;
Chris@15 481 return fs;
Chris@15 482 }
Chris@15 483
Chris@15 484 double nextPeakVal = 0.0;
Chris@15 485 for (int i = 1; i+1 < n; ++i) {
Chris@15 486 if (data[i] > data[i-1] &&
Chris@15 487 data[i] > data[i+1] &&
Chris@15 488 i != maxbin &&
Chris@15 489 data[i] > nextPeakVal) {
Chris@15 490 nextPeakVal = data[i];
Chris@15 491 }
Chris@15 492 }
Chris@8 493
Chris@8 494 double peakfreq = m_inputSampleRate / (maxbin + m_binFrom);
Chris@15 495
Chris@15 496 double confidence = 0.0;
Chris@15 497 if (nextPeakVal != 0.0) {
Chris@15 498 confidence = ((maxval / nextPeakVal) - 1.0) / 4.0;
Chris@15 499 if (confidence > 1.0) confidence = 1.0;
Chris@15 500 }
Chris@15 501
Chris@8 502 Hypothesis::Estimate e;
Chris@8 503 e.freq = peakfreq;
Chris@8 504 e.time = timestamp;
Chris@15 505 e.confidence = confidence;
Chris@8 506
Chris@8 507 m_accepted.advanceTime();
Chris@11 508
Chris@8 509 for (int i = 0; i < m_possible.size(); ++i) {
Chris@8 510 m_possible[i].advanceTime();
Chris@8 511 }
Chris@8 512
Chris@11 513 if (!m_accepted.test(e)) {
Chris@13 514
Chris@11 515 int candidate = -1;
Chris@13 516 bool accepted = false;
Chris@13 517
Chris@11 518 for (int i = 0; i < m_possible.size(); ++i) {
Chris@11 519 if (m_possible[i].test(e)) {
Chris@13 520 accepted = true;
Chris@11 521 if (m_possible[i].getState() == Hypothesis::Satisfied) {
Chris@11 522 candidate = i;
Chris@11 523 }
Chris@11 524 break;
Chris@11 525 }
Chris@11 526 }
Chris@12 527
Chris@13 528 if (!accepted) {
Chris@13 529 Hypothesis h;
Chris@13 530 h.test(e); //!!! must succeed as h is new, so perhaps there should be a ctor for this
Chris@13 531 m_possible.push_back(h);
Chris@13 532 }
Chris@13 533
Chris@11 534 if (m_accepted.getState() == Hypothesis::Expired) {
Chris@16 535 m_accepted.addFeatures(fs);
Chris@12 536 }
Chris@12 537
Chris@12 538 if (m_accepted.getState() == Hypothesis::Expired ||
Chris@12 539 m_accepted.getState() == Hypothesis::Rejected) {
Chris@11 540 if (candidate >= 0) {
Chris@11 541 m_accepted = m_possible[candidate];
Chris@11 542 } else {
Chris@11 543 m_accepted = Hypothesis();
Chris@11 544 }
Chris@11 545 }
Chris@8 546
Chris@14 547 // reap rejected/expired hypotheses from possible list
Chris@14 548 Hypotheses toReap = m_possible;
Chris@14 549 m_possible.clear();
Chris@14 550 for (int i = 0; i < toReap.size(); ++i) {
Chris@14 551 Hypothesis h = toReap[i];
Chris@14 552 if (h.getState() != Hypothesis::Rejected &&
Chris@14 553 h.getState() != Hypothesis::Expired) {
Chris@14 554 m_possible.push_back(h);
Chris@14 555 }
Chris@14 556 }
Chris@14 557 }
Chris@14 558
Chris@15 559 std::cerr << "accepted length = " << m_accepted.getPendingLength()
Chris@15 560 << ", state = " << m_accepted.getState()
Chris@15 561 << ", hypothesis count = " << m_possible.size() << std::endl;
Chris@12 562
Chris@3 563 delete[] data;
Chris@3 564 return fs;
Chris@3 565 }
Chris@3 566
Chris@3 567 CepstrumPitchTracker::FeatureSet
Chris@3 568 CepstrumPitchTracker::getRemainingFeatures()
Chris@3 569 {
Chris@3 570 FeatureSet fs;
Chris@15 571 if (m_accepted.getState() == Hypothesis::Satisfied) {
Chris@16 572 m_accepted.addFeatures(fs);
Chris@11 573 }
Chris@3 574 return fs;
Chris@3 575 }
Chris@3 576
Chris@3 577 void
Chris@3 578 CepstrumPitchTracker::fft(unsigned int n, bool inverse,
Chris@3 579 double *ri, double *ii, double *ro, double *io)
Chris@3 580 {
Chris@3 581 if (!ri || !ro || !io) return;
Chris@3 582
Chris@3 583 unsigned int bits;
Chris@3 584 unsigned int i, j, k, m;
Chris@3 585 unsigned int blockSize, blockEnd;
Chris@3 586
Chris@3 587 double tr, ti;
Chris@3 588
Chris@3 589 if (n < 2) return;
Chris@3 590 if (n & (n-1)) return;
Chris@3 591
Chris@3 592 double angle = 2.0 * M_PI;
Chris@3 593 if (inverse) angle = -angle;
Chris@3 594
Chris@3 595 for (i = 0; ; ++i) {
Chris@3 596 if (n & (1 << i)) {
Chris@3 597 bits = i;
Chris@3 598 break;
Chris@3 599 }
Chris@3 600 }
Chris@3 601
Chris@3 602 static unsigned int tableSize = 0;
Chris@3 603 static int *table = 0;
Chris@3 604
Chris@3 605 if (tableSize != n) {
Chris@3 606
Chris@3 607 delete[] table;
Chris@3 608
Chris@3 609 table = new int[n];
Chris@3 610
Chris@3 611 for (i = 0; i < n; ++i) {
Chris@3 612
Chris@3 613 m = i;
Chris@3 614
Chris@3 615 for (j = k = 0; j < bits; ++j) {
Chris@3 616 k = (k << 1) | (m & 1);
Chris@3 617 m >>= 1;
Chris@3 618 }
Chris@3 619
Chris@3 620 table[i] = k;
Chris@3 621 }
Chris@3 622
Chris@3 623 tableSize = n;
Chris@3 624 }
Chris@3 625
Chris@3 626 if (ii) {
Chris@3 627 for (i = 0; i < n; ++i) {
Chris@3 628 ro[table[i]] = ri[i];
Chris@3 629 io[table[i]] = ii[i];
Chris@3 630 }
Chris@3 631 } else {
Chris@3 632 for (i = 0; i < n; ++i) {
Chris@3 633 ro[table[i]] = ri[i];
Chris@3 634 io[table[i]] = 0.0;
Chris@3 635 }
Chris@3 636 }
Chris@3 637
Chris@3 638 blockEnd = 1;
Chris@3 639
Chris@3 640 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
Chris@3 641
Chris@3 642 double delta = angle / (double)blockSize;
Chris@3 643 double sm2 = -sin(-2 * delta);
Chris@3 644 double sm1 = -sin(-delta);
Chris@3 645 double cm2 = cos(-2 * delta);
Chris@3 646 double cm1 = cos(-delta);
Chris@3 647 double w = 2 * cm1;
Chris@3 648 double ar[3], ai[3];
Chris@3 649
Chris@3 650 for (i = 0; i < n; i += blockSize) {
Chris@3 651
Chris@3 652 ar[2] = cm2;
Chris@3 653 ar[1] = cm1;
Chris@3 654
Chris@3 655 ai[2] = sm2;
Chris@3 656 ai[1] = sm1;
Chris@3 657
Chris@3 658 for (j = i, m = 0; m < blockEnd; j++, m++) {
Chris@3 659
Chris@3 660 ar[0] = w * ar[1] - ar[2];
Chris@3 661 ar[2] = ar[1];
Chris@3 662 ar[1] = ar[0];
Chris@3 663
Chris@3 664 ai[0] = w * ai[1] - ai[2];
Chris@3 665 ai[2] = ai[1];
Chris@3 666 ai[1] = ai[0];
Chris@3 667
Chris@3 668 k = j + blockEnd;
Chris@3 669 tr = ar[0] * ro[k] - ai[0] * io[k];
Chris@3 670 ti = ar[0] * io[k] + ai[0] * ro[k];
Chris@3 671
Chris@3 672 ro[k] = ro[j] - tr;
Chris@3 673 io[k] = io[j] - ti;
Chris@3 674
Chris@3 675 ro[j] += tr;
Chris@3 676 io[j] += ti;
Chris@3 677 }
Chris@3 678 }
Chris@3 679
Chris@3 680 blockEnd = blockSize;
Chris@3 681 }
Chris@3 682 }
Chris@3 683
Chris@3 684