annotate CepstrumPitchTracker.cpp @ 12:cb88b9954eec track

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