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