annotate CepstrumPitchTracker.cpp @ 20:7786d595d2f2 track

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