annotate SimpleCepstrum.cpp @ 3:a6f9ece68482

Add choice of cepstrum transform method
author Chris Cannam
date Fri, 22 Jun 2012 23:01:00 +0100
parents e6faf01e25d8
children 3467d995ea2b
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 #include "SimpleCepstrum.h"
Chris@0 4
Chris@0 5 #include <vector>
Chris@0 6 #include <algorithm>
Chris@0 7
Chris@0 8 #include <cstdio>
Chris@0 9 #include <cmath>
Chris@0 10
Chris@0 11 using std::string;
Chris@0 12
Chris@0 13 SimpleCepstrum::SimpleCepstrum(float inputSampleRate) :
Chris@0 14 Plugin(inputSampleRate),
Chris@0 15 m_channels(0),
Chris@0 16 m_stepSize(256),
Chris@0 17 m_blockSize(1024),
Chris@0 18 m_fmin(50),
Chris@0 19 m_fmax(1000),
Chris@3 20 m_clamp(false),
Chris@3 21 m_method(InverseSymmetric)
Chris@0 22 {
Chris@0 23 }
Chris@0 24
Chris@0 25 SimpleCepstrum::~SimpleCepstrum()
Chris@0 26 {
Chris@0 27 }
Chris@0 28
Chris@0 29 string
Chris@0 30 SimpleCepstrum::getIdentifier() const
Chris@0 31 {
Chris@0 32 return "simple-cepstrum";
Chris@0 33 }
Chris@0 34
Chris@0 35 string
Chris@0 36 SimpleCepstrum::getName() const
Chris@0 37 {
Chris@0 38 return "Simple Cepstrum";
Chris@0 39 }
Chris@0 40
Chris@0 41 string
Chris@0 42 SimpleCepstrum::getDescription() const
Chris@0 43 {
Chris@2 44 return "Return simple cepstral data from DFT bins. This plugin is intended for casual inspection of cepstral data. It returns a lot of different sorts of data and is quite slow; it's not a good way to extract a single feature rapidly.";
Chris@0 45 }
Chris@0 46
Chris@0 47 string
Chris@0 48 SimpleCepstrum::getMaker() const
Chris@0 49 {
Chris@0 50 // Your name here
Chris@0 51 return "";
Chris@0 52 }
Chris@0 53
Chris@0 54 int
Chris@0 55 SimpleCepstrum::getPluginVersion() const
Chris@0 56 {
Chris@0 57 // Increment this each time you release a version that behaves
Chris@0 58 // differently from the previous one
Chris@0 59 return 1;
Chris@0 60 }
Chris@0 61
Chris@0 62 string
Chris@0 63 SimpleCepstrum::getCopyright() const
Chris@0 64 {
Chris@0 65 // This function is not ideally named. It does not necessarily
Chris@0 66 // need to say who made the plugin -- getMaker does that -- but it
Chris@0 67 // should indicate the terms under which it is distributed. For
Chris@0 68 // example, "Copyright (year). All Rights Reserved", or "GPL"
Chris@0 69 return "";
Chris@0 70 }
Chris@0 71
Chris@0 72 SimpleCepstrum::InputDomain
Chris@0 73 SimpleCepstrum::getInputDomain() const
Chris@0 74 {
Chris@0 75 return FrequencyDomain;
Chris@0 76 }
Chris@0 77
Chris@0 78 size_t
Chris@0 79 SimpleCepstrum::getPreferredBlockSize() const
Chris@0 80 {
Chris@0 81 return 1024;
Chris@0 82 }
Chris@0 83
Chris@0 84 size_t
Chris@0 85 SimpleCepstrum::getPreferredStepSize() const
Chris@0 86 {
Chris@0 87 return 256;
Chris@0 88 }
Chris@0 89
Chris@0 90 size_t
Chris@0 91 SimpleCepstrum::getMinChannelCount() const
Chris@0 92 {
Chris@0 93 return 1;
Chris@0 94 }
Chris@0 95
Chris@0 96 size_t
Chris@0 97 SimpleCepstrum::getMaxChannelCount() const
Chris@0 98 {
Chris@0 99 return 1;
Chris@0 100 }
Chris@0 101
Chris@0 102 SimpleCepstrum::ParameterList
Chris@0 103 SimpleCepstrum::getParameterDescriptors() const
Chris@0 104 {
Chris@0 105 ParameterList list;
Chris@0 106
Chris@0 107 ParameterDescriptor d;
Chris@0 108
Chris@0 109 d.identifier = "fmin";
Chris@0 110 d.name = "Minimum frequency";
Chris@0 111 d.description = "";
Chris@0 112 d.unit = "Hz";
Chris@0 113 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 114 d.maxValue = m_inputSampleRate / 2;
Chris@0 115 d.defaultValue = 50;
Chris@0 116 d.isQuantized = false;
Chris@0 117 list.push_back(d);
Chris@0 118
Chris@0 119 d.identifier = "fmax";
Chris@0 120 d.name = "Maximum frequency";
Chris@0 121 d.description = "";
Chris@0 122 d.unit = "Hz";
Chris@0 123 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 124 d.maxValue = m_inputSampleRate / 2;
Chris@0 125 d.defaultValue = 1000;
Chris@0 126 d.isQuantized = false;
Chris@0 127 list.push_back(d);
Chris@0 128
Chris@3 129 d.identifier = "method";
Chris@3 130 d.name = "Cepstrum transform method";
Chris@3 131 d.unit = "";
Chris@3 132 d.minValue = 0;
Chris@3 133 d.maxValue = 3;
Chris@3 134 d.defaultValue = 0;
Chris@3 135 d.isQuantized = true;
Chris@3 136 d.quantizeStep = 1;
Chris@3 137 d.valueNames.push_back("Inverse symmetric");
Chris@3 138 d.valueNames.push_back("Inverse asymmetric");
Chris@3 139 d.valueNames.push_back("Forward magnitude");
Chris@3 140 d.valueNames.push_back("Forward difference");
Chris@3 141 list.push_back(d);
Chris@3 142
Chris@0 143 d.identifier = "clamp";
Chris@0 144 d.name = "Clamp negative values in cepstrum at zero";
Chris@0 145 d.unit = "";
Chris@0 146 d.minValue = 0;
Chris@0 147 d.maxValue = 1;
Chris@0 148 d.defaultValue = 0;
Chris@0 149 d.isQuantized = true;
Chris@0 150 d.quantizeStep = 1;
Chris@3 151 d.valueNames.clear();
Chris@0 152 list.push_back(d);
Chris@0 153
Chris@0 154 return list;
Chris@0 155 }
Chris@0 156
Chris@0 157 float
Chris@0 158 SimpleCepstrum::getParameter(string identifier) const
Chris@0 159 {
Chris@0 160 if (identifier == "fmin") return m_fmin;
Chris@0 161 else if (identifier == "fmax") return m_fmax;
Chris@0 162 else if (identifier == "clamp") return (m_clamp ? 1 : 0);
Chris@3 163 else if (identifier == "method") return (int)m_method;
Chris@0 164 else return 0.f;
Chris@0 165 }
Chris@0 166
Chris@0 167 void
Chris@0 168 SimpleCepstrum::setParameter(string identifier, float value)
Chris@0 169 {
Chris@0 170 if (identifier == "fmin") m_fmin = value;
Chris@0 171 else if (identifier == "fmax") m_fmax = value;
Chris@0 172 else if (identifier == "clamp") m_clamp = (value > 0.5);
Chris@3 173 else if (identifier == "method") m_method = Method(int(value + 0.5));
Chris@0 174 }
Chris@0 175
Chris@0 176 SimpleCepstrum::ProgramList
Chris@0 177 SimpleCepstrum::getPrograms() const
Chris@0 178 {
Chris@0 179 ProgramList list;
Chris@0 180 return list;
Chris@0 181 }
Chris@0 182
Chris@0 183 string
Chris@0 184 SimpleCepstrum::getCurrentProgram() const
Chris@0 185 {
Chris@0 186 return ""; // no programs
Chris@0 187 }
Chris@0 188
Chris@0 189 void
Chris@0 190 SimpleCepstrum::selectProgram(string name)
Chris@0 191 {
Chris@0 192 }
Chris@0 193
Chris@0 194 SimpleCepstrum::OutputList
Chris@0 195 SimpleCepstrum::getOutputDescriptors() const
Chris@0 196 {
Chris@0 197 OutputList outputs;
Chris@0 198
Chris@0 199 int n = 0;
Chris@0 200
Chris@0 201 OutputDescriptor d;
Chris@2 202
Chris@0 203 d.identifier = "f0";
Chris@0 204 d.name = "Estimated fundamental frequency";
Chris@0 205 d.description = "";
Chris@0 206 d.unit = "";
Chris@0 207 d.hasFixedBinCount = true;
Chris@0 208 d.binCount = 1;
Chris@0 209 d.hasKnownExtents = true;
Chris@0 210 d.minValue = m_fmin;
Chris@0 211 d.maxValue = m_fmax;
Chris@0 212 d.isQuantized = false;
Chris@0 213 d.sampleType = OutputDescriptor::OneSamplePerStep;
Chris@0 214 d.hasDuration = false;
Chris@2 215 /*
Chris@0 216 m_f0Output = n++;
Chris@0 217 outputs.push_back(d);
Chris@2 218 */
Chris@0 219
Chris@0 220 d.identifier = "raw_cepstral_peak";
Chris@0 221 d.name = "Frequency corresponding to raw cepstral peak";
Chris@2 222 d.description = "Return the frequency whose period corresponds to the quefrency with the maximum value within the specified range of the cepstrum";
Chris@0 223 d.unit = "Hz";
Chris@0 224 m_rawOutput = n++;
Chris@0 225 outputs.push_back(d);
Chris@0 226
Chris@0 227 d.identifier = "variance";
Chris@0 228 d.name = "Variance of cepstral bins in range";
Chris@0 229 d.unit = "";
Chris@2 230 d.description = "Return the variance of bin values within the specified range of the cepstrum";
Chris@0 231 m_varOutput = n++;
Chris@0 232 outputs.push_back(d);
Chris@0 233
Chris@0 234 d.identifier = "peak";
Chris@0 235 d.name = "Peak value";
Chris@0 236 d.unit = "";
Chris@2 237 d.description = "Return the value found in the maximum-valued bin within the specified range of the cepstrum";
Chris@0 238 m_pvOutput = n++;
Chris@0 239 outputs.push_back(d);
Chris@0 240
Chris@0 241 d.identifier = "peak_to_mean";
Chris@0 242 d.name = "Peak-to-mean distance";
Chris@0 243 d.unit = "";
Chris@2 244 d.description = "Return the difference between maximum and mean bin values within the specified range of the cepstrum";
Chris@0 245 m_p2mOutput = n++;
Chris@0 246 outputs.push_back(d);
Chris@0 247
Chris@0 248 d.identifier = "cepstrum";
Chris@0 249 d.name = "Cepstrum";
Chris@0 250 d.unit = "";
Chris@2 251 d.description = "The unprocessed cepstrum bins within the specified range";
Chris@0 252
Chris@0 253 int from = int(m_inputSampleRate / m_fmax);
Chris@0 254 int to = int(m_inputSampleRate / m_fmin);
Chris@0 255 if (to >= (int)m_blockSize / 2) {
Chris@0 256 to = m_blockSize / 2 - 1;
Chris@0 257 }
Chris@0 258 d.binCount = to - from + 1;
Chris@0 259 for (int i = from; i <= to; ++i) {
Chris@0 260 float freq = m_inputSampleRate / i;
Chris@0 261 char buffer[10];
Chris@2 262 sprintf(buffer, "%.2f Hz", freq);
Chris@0 263 d.binNames.push_back(buffer);
Chris@0 264 }
Chris@0 265
Chris@0 266 d.hasKnownExtents = false;
Chris@0 267 m_cepOutput = n++;
Chris@0 268 outputs.push_back(d);
Chris@0 269
Chris@0 270 d.identifier = "am";
Chris@0 271 d.name = "Cepstrum bins relative to mean";
Chris@2 272 d.description = "The cepstrum bins within the specified range, expressed as a value relative to the mean bin value in the range, with values below the mean clamped to zero";
Chris@0 273 m_amOutput = n++;
Chris@0 274 outputs.push_back(d);
Chris@0 275
Chris@2 276 d.identifier = "env";
Chris@2 277 d.name = "Spectral envelope";
Chris@2 278 d.description = "Envelope calculated from the cepstral values below the specified minimum";
Chris@2 279 d.binCount = m_blockSize/2 + 1;
Chris@2 280 d.binNames.clear();
Chris@2 281 for (int i = 0; i < d.binCount; ++i) {
Chris@2 282 float freq = (m_inputSampleRate / m_blockSize) * i;
Chris@2 283 char buffer[10];
Chris@2 284 sprintf(buffer, "%.2f Hz", freq);
Chris@2 285 d.binNames.push_back(buffer);
Chris@2 286 }
Chris@2 287 m_envOutput = n++;
Chris@2 288 outputs.push_back(d);
Chris@2 289
Chris@2 290 d.identifier = "es";
Chris@2 291 d.name = "Spectrum without envelope";
Chris@2 292 d.description = "Magnitude of spectrum values divided by calculated envelope values, to deconvolve the envelope";
Chris@2 293 m_esOutput = n++;
Chris@2 294 outputs.push_back(d);
Chris@2 295
Chris@0 296 return outputs;
Chris@0 297 }
Chris@0 298
Chris@0 299 bool
Chris@0 300 SimpleCepstrum::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 301 {
Chris@0 302 if (channels < getMinChannelCount() ||
Chris@0 303 channels > getMaxChannelCount()) return false;
Chris@0 304
Chris@0 305 // std::cerr << "SimpleCepstrum::initialise: channels = " << channels
Chris@0 306 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
Chris@0 307 // << std::endl;
Chris@0 308
Chris@0 309 m_channels = channels;
Chris@0 310 m_stepSize = stepSize;
Chris@0 311 m_blockSize = blockSize;
Chris@0 312
Chris@0 313 return true;
Chris@0 314 }
Chris@0 315
Chris@0 316 void
Chris@0 317 SimpleCepstrum::reset()
Chris@0 318 {
Chris@0 319 }
Chris@0 320
Chris@0 321 SimpleCepstrum::FeatureSet
Chris@0 322 SimpleCepstrum::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 323 {
Chris@1 324 FeatureSet fs;
Chris@1 325
Chris@0 326 int bs = m_blockSize;
Chris@0 327 int hs = m_blockSize/2 + 1;
Chris@0 328
Chris@0 329 double *logmag = new double[bs];
Chris@3 330
Chris@0 331 for (int i = 0; i < hs; ++i) {
Chris@3 332
Chris@0 333 double mag = sqrt(inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
Chris@0 334 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]);
Chris@3 335 double lm = log(mag + 0.00000001);
Chris@3 336
Chris@3 337 switch (m_method) {
Chris@3 338 case InverseSymmetric:
Chris@3 339 logmag[i] = lm;
Chris@3 340 if (i > 0) logmag[bs - i] = lm;
Chris@3 341 break;
Chris@3 342 case InverseAsymmetric:
Chris@3 343 logmag[i] = lm;
Chris@3 344 if (i > 0) logmag[bs - i] = 0;
Chris@3 345 break;
Chris@3 346 case ForwardMagnitude:
Chris@3 347 case ForwardDifference:
Chris@3 348 logmag[bs/2 + i - 1] = lm;
Chris@3 349 if (i < hs-1) {
Chris@3 350 logmag[bs/2 - i - 1] = lm;
Chris@3 351 }
Chris@3 352 break;
Chris@0 353 }
Chris@0 354 }
Chris@0 355
Chris@0 356 double *cep = new double[bs];
Chris@3 357 double *io = new double[bs];
Chris@3 358
Chris@3 359 if (m_method == InverseSymmetric ||
Chris@3 360 m_method == InverseAsymmetric) {
Chris@3 361
Chris@3 362 fft(bs, true, logmag, 0, cep, io);
Chris@3 363
Chris@3 364 } else {
Chris@3 365
Chris@3 366 fft(bs, false, logmag, 0, cep, io);
Chris@3 367
Chris@3 368 if (m_method == ForwardDifference) {
Chris@3 369 for (int i = 0; i < hs; ++i) {
Chris@3 370 cep[i] = fabs(io[i]) - fabs(cep[i]);
Chris@3 371 }
Chris@3 372 } else {
Chris@3 373 for (int i = 0; i < hs; ++i) {
Chris@3 374 cep[i] = sqrt(cep[i]*cep[i] + io[i]*io[i]);
Chris@3 375 }
Chris@3 376 }
Chris@3 377 }
Chris@0 378
Chris@0 379 if (m_clamp) {
Chris@0 380 for (int i = 0; i < bs; ++i) {
Chris@0 381 if (cep[i] < 0) cep[i] = 0;
Chris@0 382 }
Chris@0 383 }
Chris@0 384
Chris@0 385 int from = int(m_inputSampleRate / m_fmax);
Chris@0 386 int to = int(m_inputSampleRate / m_fmin);
Chris@0 387
Chris@0 388 if (to >= bs / 2) {
Chris@0 389 to = bs / 2 - 1;
Chris@0 390 }
Chris@0 391
Chris@0 392 Feature cf;
Chris@0 393 for (int i = from; i <= to; ++i) {
Chris@0 394 cf.values.push_back(cep[i]);
Chris@0 395 }
Chris@0 396 fs[m_cepOutput].push_back(cf);
Chris@0 397
Chris@0 398 float maxval = 0.f;
Chris@0 399 int maxbin = 0;
Chris@0 400
Chris@0 401 for (int i = from; i <= to; ++i) {
Chris@0 402 if (cep[i] > maxval) {
Chris@0 403 maxval = cep[i];
Chris@0 404 maxbin = i;
Chris@0 405 }
Chris@0 406 }
Chris@0 407
Chris@0 408 Feature rf;
Chris@0 409 if (maxbin > 0) {
Chris@0 410 rf.values.push_back(m_inputSampleRate / maxbin);
Chris@0 411 } else {
Chris@0 412 rf.values.push_back(0);
Chris@0 413 }
Chris@0 414 fs[m_rawOutput].push_back(rf);
Chris@0 415
Chris@0 416 float mean = 0;
Chris@0 417 for (int i = from; i <= to; ++i) {
Chris@0 418 mean += cep[i];
Chris@0 419 }
Chris@0 420 mean /= (to - from) + 1;
Chris@0 421
Chris@0 422 float variance = 0;
Chris@0 423 for (int i = from; i <= to; ++i) {
Chris@0 424 float dev = fabsf(cep[i] - mean);
Chris@0 425 variance += dev * dev;
Chris@0 426 }
Chris@0 427 variance /= (to - from) + 1;
Chris@0 428
Chris@0 429 Feature vf;
Chris@0 430 vf.values.push_back(variance);
Chris@0 431 fs[m_varOutput].push_back(vf);
Chris@0 432
Chris@0 433 Feature pf;
Chris@0 434 pf.values.push_back(maxval - mean);
Chris@0 435 fs[m_p2mOutput].push_back(pf);
Chris@0 436
Chris@0 437 Feature pv;
Chris@0 438 pv.values.push_back(maxval);
Chris@0 439 fs[m_pvOutput].push_back(pv);
Chris@0 440
Chris@0 441 Feature am;
Chris@0 442 for (int i = from; i <= to; ++i) {
Chris@0 443 if (cep[i] < mean) am.values.push_back(0);
Chris@0 444 else am.values.push_back(cep[i] - mean);
Chris@0 445 }
Chris@0 446 fs[m_amOutput].push_back(am);
Chris@0 447
Chris@2 448 // destructively wipe the higher cepstral bins in order to
Chris@2 449 // calculate the envelope
Chris@2 450 cep[0] /= 2;
Chris@2 451 cep[from-1] /= 2;
Chris@2 452 for (int i = 0; i < from; ++i) {
Chris@2 453 cep[i] /= bs;
Chris@2 454 }
Chris@2 455 for (int i = from; i < bs; ++i) {
Chris@2 456 cep[i] = 0;
Chris@2 457 }
Chris@3 458 fft(bs, false, cep, 0, logmag, io);
Chris@2 459 for (int i = 0; i < hs; ++i) {
Chris@2 460 logmag[i] = exp(logmag[i]);
Chris@2 461 }
Chris@2 462 Feature env;
Chris@2 463 for (int i = 0; i < hs; ++i) {
Chris@2 464 env.values.push_back(logmag[i]);
Chris@2 465 }
Chris@2 466 fs[m_envOutput].push_back(env);
Chris@2 467
Chris@2 468 Feature es;
Chris@2 469 for (int i = 0; i < hs; ++i) {
Chris@2 470 double re = inputBuffers[0][i*2 ] / logmag[i];
Chris@2 471 double im = inputBuffers[0][i*2+1] / logmag[i];
Chris@2 472 double mag = sqrt(re*re + im*im);
Chris@2 473 es.values.push_back(mag);
Chris@2 474 }
Chris@2 475 fs[m_esOutput].push_back(es);
Chris@2 476
Chris@3 477 delete[] io;
Chris@0 478 delete[] logmag;
Chris@0 479 delete[] cep;
Chris@0 480
Chris@0 481 return fs;
Chris@0 482 }
Chris@0 483
Chris@0 484 SimpleCepstrum::FeatureSet
Chris@0 485 SimpleCepstrum::getRemainingFeatures()
Chris@0 486 {
Chris@0 487 FeatureSet fs;
Chris@0 488 return fs;
Chris@0 489 }
Chris@0 490
Chris@0 491 void
Chris@0 492 SimpleCepstrum::fft(unsigned int n, bool inverse,
Chris@0 493 double *ri, double *ii, double *ro, double *io)
Chris@0 494 {
Chris@0 495 if (!ri || !ro || !io) return;
Chris@0 496
Chris@0 497 unsigned int bits;
Chris@0 498 unsigned int i, j, k, m;
Chris@0 499 unsigned int blockSize, blockEnd;
Chris@0 500
Chris@0 501 double tr, ti;
Chris@0 502
Chris@0 503 if (n < 2) return;
Chris@0 504 if (n & (n-1)) return;
Chris@0 505
Chris@0 506 double angle = 2.0 * M_PI;
Chris@0 507 if (inverse) angle = -angle;
Chris@0 508
Chris@0 509 for (i = 0; ; ++i) {
Chris@0 510 if (n & (1 << i)) {
Chris@0 511 bits = i;
Chris@0 512 break;
Chris@0 513 }
Chris@0 514 }
Chris@0 515
Chris@0 516 static unsigned int tableSize = 0;
Chris@0 517 static int *table = 0;
Chris@0 518
Chris@0 519 if (tableSize != n) {
Chris@0 520
Chris@0 521 delete[] table;
Chris@0 522
Chris@0 523 table = new int[n];
Chris@0 524
Chris@0 525 for (i = 0; i < n; ++i) {
Chris@0 526
Chris@0 527 m = i;
Chris@0 528
Chris@0 529 for (j = k = 0; j < bits; ++j) {
Chris@0 530 k = (k << 1) | (m & 1);
Chris@0 531 m >>= 1;
Chris@0 532 }
Chris@0 533
Chris@0 534 table[i] = k;
Chris@0 535 }
Chris@0 536
Chris@0 537 tableSize = n;
Chris@0 538 }
Chris@0 539
Chris@0 540 if (ii) {
Chris@0 541 for (i = 0; i < n; ++i) {
Chris@0 542 ro[table[i]] = ri[i];
Chris@0 543 io[table[i]] = ii[i];
Chris@0 544 }
Chris@0 545 } else {
Chris@0 546 for (i = 0; i < n; ++i) {
Chris@0 547 ro[table[i]] = ri[i];
Chris@0 548 io[table[i]] = 0.0;
Chris@0 549 }
Chris@0 550 }
Chris@0 551
Chris@0 552 blockEnd = 1;
Chris@0 553
Chris@0 554 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
Chris@0 555
Chris@0 556 double delta = angle / (double)blockSize;
Chris@0 557 double sm2 = -sin(-2 * delta);
Chris@0 558 double sm1 = -sin(-delta);
Chris@0 559 double cm2 = cos(-2 * delta);
Chris@0 560 double cm1 = cos(-delta);
Chris@0 561 double w = 2 * cm1;
Chris@0 562 double ar[3], ai[3];
Chris@0 563
Chris@0 564 for (i = 0; i < n; i += blockSize) {
Chris@0 565
Chris@0 566 ar[2] = cm2;
Chris@0 567 ar[1] = cm1;
Chris@0 568
Chris@0 569 ai[2] = sm2;
Chris@0 570 ai[1] = sm1;
Chris@0 571
Chris@0 572 for (j = i, m = 0; m < blockEnd; j++, m++) {
Chris@0 573
Chris@0 574 ar[0] = w * ar[1] - ar[2];
Chris@0 575 ar[2] = ar[1];
Chris@0 576 ar[1] = ar[0];
Chris@0 577
Chris@0 578 ai[0] = w * ai[1] - ai[2];
Chris@0 579 ai[2] = ai[1];
Chris@0 580 ai[1] = ai[0];
Chris@0 581
Chris@0 582 k = j + blockEnd;
Chris@0 583 tr = ar[0] * ro[k] - ai[0] * io[k];
Chris@0 584 ti = ar[0] * io[k] + ai[0] * ro[k];
Chris@0 585
Chris@0 586 ro[k] = ro[j] - tr;
Chris@0 587 io[k] = io[j] - ti;
Chris@0 588
Chris@0 589 ro[j] += tr;
Chris@0 590 io[j] += ti;
Chris@0 591 }
Chris@0 592 }
Chris@0 593
Chris@0 594 blockEnd = blockSize;
Chris@0 595 }
Chris@0 596 }
Chris@0 597
Chris@0 598